Skip to content

Commit be752b8

Browse files
committed
Add runtime session header support for OpenAI configs
1 parent e990a4a commit be752b8

5 files changed

Lines changed: 411 additions & 53 deletions

File tree

core/src/agent_api.rs

Lines changed: 66 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -783,39 +783,9 @@ impl Agent {
783783
) -> Result<AgentSession> {
784784
let opts = options.unwrap_or_default();
785785

786-
let llm_client = if let Some(ref model) = opts.model {
787-
let (provider_name, model_id) = model
788-
.split_once('/')
789-
.context("model format must be 'provider/model' (e.g., 'openai/gpt-4o')")?;
790-
791-
let mut llm_config = self
792-
.code_config
793-
.llm_config(provider_name, model_id)
794-
.with_context(|| {
795-
format!("provider '{provider_name}' or model '{model_id}' not found in config")
796-
})?;
797-
798-
if let Some(temp) = opts.temperature {
799-
llm_config = llm_config.with_temperature(temp);
800-
}
801-
if let Some(budget) = opts.thinking_budget {
802-
llm_config = llm_config.with_thinking_budget(budget);
803-
}
804-
805-
crate::llm::create_client_with_config(llm_config)
806-
} else {
807-
if opts.temperature.is_some() || opts.thinking_budget.is_some() {
808-
tracing::warn!(
809-
"temperature/thinking_budget set without model override — these will be ignored. \
810-
Use with_model() to apply LLM parameter overrides."
811-
);
812-
}
813-
self.llm_client.clone()
814-
};
815-
816786
// Merge global MCP manager with any session-level one from opts.
817787
// If both exist, session-level servers are added into the global manager.
818-
let merged_opts = match (&self.global_mcp, &opts.mcp_manager) {
788+
let mut merged_opts = match (&self.global_mcp, &opts.mcp_manager) {
819789
(Some(global), Some(session)) => {
820790
let global = Arc::clone(global);
821791
let session_mgr = Arc::clone(session);
@@ -857,6 +827,13 @@ impl Agent {
857827
_ => opts,
858828
};
859829

830+
let session_id = merged_opts
831+
.session_id
832+
.clone()
833+
.unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
834+
merged_opts.session_id = Some(session_id.clone());
835+
let llm_client = self.resolve_session_llm_client(&merged_opts, Some(&session_id))?;
836+
860837
self.build_session(workspace.into(), llm_client, &merged_opts)
861838
}
862839

@@ -963,21 +940,7 @@ impl Agent {
963940
// Build session with the saved workspace
964941
let mut opts = options;
965942
opts.session_id = Some(data.id.clone());
966-
967-
let llm_client = if let Some(ref model) = opts.model {
968-
let (provider_name, model_id) = model
969-
.split_once('/')
970-
.context("model format must be 'provider/model'")?;
971-
let llm_config = self
972-
.code_config
973-
.llm_config(provider_name, model_id)
974-
.with_context(|| {
975-
format!("provider '{provider_name}' or model '{model_id}' not found")
976-
})?;
977-
crate::llm::create_client_with_config(llm_config)
978-
} else {
979-
self.llm_client.clone()
980-
};
943+
let llm_client = self.resolve_session_llm_client(&opts, Some(&data.id))?;
981944

982945
let session = self.build_session(data.config.workspace.clone(), llm_client, &opts)?;
983946

@@ -987,6 +950,53 @@ impl Agent {
987950
Ok(session)
988951
}
989952

953+
fn resolve_session_llm_client(
954+
&self,
955+
opts: &SessionOptions,
956+
session_id: Option<&str>,
957+
) -> Result<Arc<dyn LlmClient>> {
958+
let model_ref = if let Some(ref model) = opts.model {
959+
model.as_str()
960+
} else {
961+
if opts.temperature.is_some() || opts.thinking_budget.is_some() {
962+
tracing::warn!(
963+
"temperature/thinking_budget set without model override — these will be ignored. \
964+
Use with_model() to apply LLM parameter overrides."
965+
);
966+
}
967+
self.code_config
968+
.default_model
969+
.as_deref()
970+
.context("default_model must be set in 'provider/model' format")?
971+
};
972+
973+
let (provider_name, model_id) = model_ref
974+
.split_once('/')
975+
.context("model format must be 'provider/model' (e.g., 'openai/gpt-4o')")?;
976+
977+
let mut llm_config = self
978+
.code_config
979+
.llm_config(provider_name, model_id)
980+
.with_context(|| {
981+
format!("provider '{provider_name}' or model '{model_id}' not found in config")
982+
})?;
983+
984+
if opts.model.is_some() {
985+
if let Some(temp) = opts.temperature {
986+
llm_config = llm_config.with_temperature(temp);
987+
}
988+
if let Some(budget) = opts.thinking_budget {
989+
llm_config = llm_config.with_thinking_budget(budget);
990+
}
991+
}
992+
993+
if let Some(session_id) = session_id {
994+
llm_config = llm_config.with_session_id(session_id);
995+
}
996+
997+
Ok(crate::llm::create_client_with_config(llm_config))
998+
}
999+
9901000
fn build_session(
9911001
&self,
9921002
workspace: String,
@@ -2710,12 +2720,16 @@ mod tests {
27102720
name: "anthropic".to_string(),
27112721
api_key: Some("test-key".to_string()),
27122722
base_url: None,
2723+
headers: std::collections::HashMap::new(),
2724+
session_id_header: None,
27132725
models: vec![ModelConfig {
27142726
id: "claude-sonnet-4-20250514".to_string(),
27152727
name: "Claude Sonnet 4".to_string(),
27162728
family: "claude-sonnet".to_string(),
27172729
api_key: None,
27182730
base_url: None,
2731+
headers: std::collections::HashMap::new(),
2732+
session_id_header: None,
27192733
attachment: false,
27202734
reasoning: false,
27212735
tool_call: true,
@@ -2730,12 +2744,16 @@ mod tests {
27302744
name: "openai".to_string(),
27312745
api_key: Some("test-openai-key".to_string()),
27322746
base_url: None,
2747+
headers: std::collections::HashMap::new(),
2748+
session_id_header: None,
27332749
models: vec![ModelConfig {
27342750
id: "gpt-4o".to_string(),
27352751
name: "GPT-4o".to_string(),
27362752
family: "gpt-4".to_string(),
27372753
api_key: None,
27382754
base_url: None,
2755+
headers: std::collections::HashMap::new(),
2756+
session_id_header: None,
27392757
attachment: false,
27402758
reasoning: false,
27412759
tool_call: true,
@@ -3387,6 +3405,8 @@ dir content
33873405
name: "anthropic".to_string(),
33883406
api_key: Some("test-key".to_string()),
33893407
base_url: None,
3408+
headers: std::collections::HashMap::new(),
3409+
session_id_header: None,
33903410
models: vec![],
33913411
}],
33923412
..Default::default()

0 commit comments

Comments
 (0)