Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions codex-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions codex-rs/core/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,7 @@ impl ModelClient {
self.state.thread_id.to_string().as_bytes(),
);
let mut prefix = Vec::new();
let tools = if model_info.use_responses_lite {
let mut tools = if model_info.use_responses_lite {
if !prompt.tools.is_empty() {
let tools = create_tools_json_for_responses_lite(&prompt.tools)?;
prefix.push(ResponseItem::AdditionalTools {
Expand All @@ -926,7 +926,7 @@ impl ModelClient {
}
None
} else {
Some(create_tools_raw_json_for_responses_api(&prompt.tools)?.into())
Some(create_tools_raw_json_for_responses_api(&prompt.tools)?)
};
if !prompt.base_instructions.text.is_empty() {
let mut instructions = ContextualUserFragment::into(BaseInstructionsFragment(
Expand All @@ -950,6 +950,7 @@ impl ModelClient {
*encrypted_function_args = None;
}
}
codex_model_provider::codec_outbound(&mut tools, &mut input)?;
}
let reasoning = self.build_reasoning(model_info, effort, summary);
let stream_options = (self.state.concurrent_reasoning_summaries_enabled
Expand Down Expand Up @@ -996,7 +997,7 @@ impl ModelClient {
let request = ResponsesApiRequest {
model: model_info.slug.clone(),
input,
tools,
tools: tools.map(Into::into),
tool_choice: "auto".to_string(),
parallel_tool_calls: prompt.parallel_tool_calls && !model_info.use_responses_lite,
reasoning: Some(reasoning),
Expand Down
1 change: 1 addition & 0 deletions codex-rs/core/src/session/turn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2809,6 +2809,7 @@ async fn try_run_sampling_request(
}
}
ResponseEvent::OutputItemAdded(mut item) => {
item = codex_model_provider::codec_inbound(item);
assign_missing_streamed_response_item_id(&mut item, /*active_item*/ None);
sess.reserve_assistant_message_order(&turn_context, &item)
.await;
Expand Down
1 change: 1 addition & 0 deletions codex-rs/core/src/stream_events_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ pub(crate) async fn handle_output_item_done(
item: ResponseItem,
previously_active_item: Option<TurnItem>,
) -> Result<OutputItemResult> {
let item = codex_model_provider::codec_inbound(item);
let mut output = OutputItemResult::default();
let plan_mode = ctx.step_context.turn.mode() == ModeKind::Plan;

Expand Down
16 changes: 0 additions & 16 deletions codex-rs/core/src/tools/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,21 +344,6 @@ impl ToolRouter {
.await
}

/// codex-local H8: some backends (Ollama) flatten `namespace` into the name
/// as `ns.tool`. Split only when the literal name misses but the split hits.
pub(crate) fn unflatten_tool_name(&self, name: ToolName) -> ToolName {
match name.name.split_once('.') {
Some((ns, n))
if name.is_default_namespace()
&& self.registry.tool(&name).is_none()
&& self.registry.tool(&ToolName::namespaced(ns, n)).is_some() =>
{
ToolName::namespaced(ns, n)
}
_ => name,
}
}

#[allow(clippy::too_many_arguments)]
async fn dispatch_tool_call_with_code_mode_result_inner(
&self,
Expand All @@ -376,7 +361,6 @@ impl ToolRouter {
payload,
..
} = call;
let tool_name = self.unflatten_tool_name(tool_name);

// Keep the legacy ToolInvocation.turn field tied to the same request state until handlers migrate.
let turn = Arc::clone(&step_context.turn);
Expand Down
18 changes: 0 additions & 18 deletions codex-rs/core/src/tools/router_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -649,21 +649,3 @@ fn namespace_function_names(specs: &[ToolSpec], namespace_name: &str) -> Vec<Str
})
.unwrap_or_default()
}

#[tokio::test]
async fn unflatten_tool_name_splits_only_registered_dotted_names() {
let (_, turn) = make_session_and_context().await;
let turn = Arc::new(turn);
let step_context = StepContext::for_test(Arc::clone(&turn));
let router = test_tool_router(
step_context.as_ref(),
Vec::new(),
Vec::new(),
&turn.dynamic_tools,
);

let split = router.unflatten_tool_name(ToolName::plain("functions.exec_command"));
assert_eq!(split, ToolName::namespaced("functions", "exec_command"));
let unknown = ToolName::plain("nope.exec_command");
assert_eq!(router.unflatten_tool_name(unknown.clone()), unknown);
}
20 changes: 20 additions & 0 deletions codex-rs/local/cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pub enum Action {
/// Delegate to upstream with this argv; the caller must first set
/// `CODEX_HOME` to `codex_home`.
Run {
engine: Engine,
argv: Vec<OsString>,
codex_home: PathBuf,
/// Engine root to probe before delegating; `None` skips the preflight.
Expand Down Expand Up @@ -127,13 +128,24 @@ pub fn prepare(args: Vec<OsString>, env: EnvFn, user_home: Option<PathBuf>) -> R
.flatten()
.filter(|_| want_preflight);
Ok(Action::Run {
engine: applied.engine,
argv: applied.argv,
codex_home: homes.local,
preflight_root,
probe_model,
})
}

/// Wire-codec mode for an engine. llama.cpp's namespace handling is unverified, and
/// flatten is always safe, so only Ollama gets the dotted-inbound mode.
pub fn codec_mode(engine: Engine) -> codex_local_models::codec::Mode {
use codex_local_models::codec::Mode;
match engine {
Engine::Ollama => Mode::DottedInbound,
Engine::LmStudio | Engine::LlamaCpp => Mode::Flatten,
}
}

/// Root that upstream will end up using: the injected base_url override, else the
/// seeded config's base_url, else the env/built-in root.
fn effective_root(argv: &[OsString], homes: &Homes, fallback: &str) -> String {
Expand Down Expand Up @@ -182,6 +194,14 @@ pub(crate) fn env_ollama_root(env: EnvFn) -> Result<Option<String>> {

#[cfg(test)]
mod tests {
#[test]
fn engine_to_codec_mode() {
use codex_local_models::codec::Mode;
assert_eq!(codec_mode(Engine::Ollama), Mode::DottedInbound);
assert_eq!(codec_mode(Engine::LmStudio), Mode::Flatten);
assert_eq!(codec_mode(Engine::LlamaCpp), Mode::Flatten);
}

use super::*;
use std::fs;

Expand Down
6 changes: 4 additions & 2 deletions codex-rs/local/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,19 @@ fn main() -> anyhow::Result<()> {
&codex_local_cli::process_env,
codex_local_cli::user_home_dir(),
)?;
let (argv, codex_home, preflight_root, probe_model) = match action {
let (engine, argv, codex_home, preflight_root, probe_model) = match action {
codex_local_cli::Action::Version(v) => {
println!("{v}");
return Ok(());
}
codex_local_cli::Action::SyncConfig => return Ok(()),
codex_local_cli::Action::Run {
engine,
argv,
codex_home,
preflight_root,
probe_model,
} => (argv, codex_home, preflight_root, probe_model),
} => (engine, argv, codex_home, preflight_root, probe_model),
};
if let Some(root) = &preflight_root {
let down = codex_local_cli::preflight_check(root);
Expand All @@ -80,6 +81,7 @@ fn main() -> anyhow::Result<()> {
// SAFETY: still single-threaded; no runtime or worker threads exist yet.
unsafe { std::env::set_var("CODEX_HOME", &codex_home) };
codex_local_models::register();
codex_local_models::codec::register(codex_local_cli::codec_mode(engine));
codex_local_resume::register();
let remote_control_disabled = codex_app_server::take_remote_control_disabled_env();
arg0_dispatch_or_else(move |arg0_paths: Arg0DispatchPaths| async move {
Expand Down
3 changes: 2 additions & 1 deletion codex-rs/local/models/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ codex-model-provider-info = { workspace = true }
codex-models-manager = { workspace = true }
codex-protocol = { workspace = true }
reqwest = { workspace = true, features = ["json"] }
serde_json = { workspace = true }
serde_json = { workspace = true, features = ["raw_value"] }
sha2 = { workspace = true }
tracing = { workspace = true }
tokio = { workspace = true, features = ["rt", "macros", "sync"] }

Expand Down
Loading