chore: refactor tool handling (#4510)

# Tool System Refactor

- Centralizes tool definitions and execution in `core/src/tools/*`:
specs (`spec.rs`), handlers (`handlers/*`), router (`router.rs`),
registry/dispatch (`registry.rs`), and shared context (`context.rs`).
One registry now builds the model-visible tool list and binds handlers.
- Router converts model responses to tool calls; Registry dispatches
with consistent telemetry via `codex-rs/otel` and unified error
handling. Function, Local Shell, MCP, and experimental `unified_exec`
all flow through this path; legacy shell aliases still work.
- Rationale: reduce per‑tool boilerplate, keep spec/handler in sync, and
make adding tools predictable and testable.

Example: `read_file`
- Spec: `core/src/tools/spec.rs` (see `create_read_file_tool`,
registered by `build_specs`).
- Handler: `core/src/tools/handlers/read_file.rs` (absolute `file_path`,
1‑indexed `offset`, `limit`, `L#: ` prefixes, safe truncation).
- E2E test: `core/tests/suite/read_file.rs` validates the tool returns
the requested lines.

## Next steps:
- Decompose `handle_container_exec_with_params` 
- Add parallel tool calls
This commit is contained in:
jif-oai
2025-10-03 13:21:06 +01:00
committed by GitHub
parent 69cb72f842
commit 33d3ecbccc
48 changed files with 5288 additions and 2006 deletions

View File

@@ -14,6 +14,7 @@ use eventsource_stream::EventStreamError as StreamError;
use reqwest::Error;
use reqwest::Response;
use serde::Serialize;
use std::borrow::Cow;
use std::fmt::Display;
use std::time::Duration;
use std::time::Instant;
@@ -366,10 +367,10 @@ impl OtelEventManager {
call_id: &str,
arguments: &str,
f: F,
) -> Result<String, E>
) -> Result<(String, bool), E>
where
F: FnOnce() -> Fut,
Fut: Future<Output = Result<String, E>>,
Fut: Future<Output = Result<(String, bool), E>>,
E: Display,
{
let start = Instant::now();
@@ -377,10 +378,12 @@ impl OtelEventManager {
let duration = start.elapsed();
let (output, success) = match &result {
Ok(content) => (content, true),
Err(error) => (&error.to_string(), false),
Ok((preview, success)) => (Cow::Borrowed(preview.as_str()), *success),
Err(error) => (Cow::Owned(error.to_string()), false),
};
let success_str = if success { "true" } else { "false" };
tracing::event!(
tracing::Level::INFO,
event.name = "codex.tool_result",
@@ -396,7 +399,8 @@ impl OtelEventManager {
call_id = %call_id,
arguments = %arguments,
duration_ms = %duration.as_millis(),
success = %success,
success = %success_str,
// `output` is truncated by the tool layer before reaching telemetry.
output = %output,
);