Skip to content
Open
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
59 changes: 59 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,65 @@ jobs:
- name: Test agentic CLI end to end
run: python3 scripts/tests/agentic-cli-e2e-test.py

embedded-code-interpreter:
name: Embedded code interpreter
runs-on: ubuntu-latest
timeout-minutes: 30
env:
CARGO_INCREMENTAL: "0"
CARGO_PROFILE_DEV_DEBUG: "0"
CARGO_PROFILE_TEST_DEBUG: "0"
ERYX_PRECOMPILE_INSTALL_ROOT: ${{ github.workspace }}/.ci/eryx-precompile
XDG_CACHE_HOME: ${{ github.workspace }}/.ci/eryx-cache
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # v1
with:
toolchain: 1.98.1
components: clippy

- name: Cache Rust dependencies
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
cache-bin: false
cache-targets: false

- name: Cache Eryx precompiler
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v4
with:
path: ${{ env.ERYX_PRECOMPILE_INSTALL_ROOT }}
key: eryx-precompile-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('Cargo.lock') }}

- name: Add Eryx precompiler to PATH
run: echo "${ERYX_PRECOMPILE_INSTALL_ROOT}/bin" >> "${GITHUB_PATH}"

- name: Test Eryx runtime setup script
run: bash scripts/tests/setup-eryx-runtime-test.sh

- name: Prepare Eryx runtime
run: ./scripts/setup-eryx-runtime.sh

- name: Create private Eryx temporary directory
run: install -d -m 700 "${RUNNER_TEMP}/eryx-tmp"

- name: Run feature-enabled clippy
env:
TMPDIR: ${{ runner.temp }}/eryx-tmp
run: |
cargo clippy -p agentic-server-core --all-targets --features embedded-code-interpreter -- -D warnings
cargo clippy -p agentic-server --all-targets --features embedded-code-interpreter -- -D warnings

- name: Run feature-enabled tests
env:
TMPDIR: ${{ runner.temp }}/eryx-tmp
run: |
cargo test -p agentic-server-core --lib --features embedded-code-interpreter
cargo test -p agentic-server-core --test code_interpreter_characterization_test --features embedded-code-interpreter
cargo test -p agentic-server --bin agentic-server --features embedded-code-interpreter code_interpreter

postgres:
runs-on: ubuntu-latest
services:
Expand Down
35 changes: 34 additions & 1 deletion .rust-file-sizes.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,39 @@
"crates/agentic-server/src/auth.rs": 799,
"crates/agentic-server/src/handler/websocket/responses.rs": 841
},
"exceptions": {},
"exceptions": {
"crates/agentic-server-core/src/config.rs": {
"limit": 582,
"reason": "Operator-owned code-interpreter limits and their cross-field validation extend the shared runtime configuration model."
},
"crates/agentic-server-core/src/events/types.rs": {
"limit": 518,
"reason": "Code-interpreter SSE lifecycle variants extend the shared exhaustive event and item type mappings."
},
"crates/agentic-server-core/src/executor/gateway.rs": {
"limit": 759,
"reason": "Gateway-owned code-interpreter calls use the existing tool loop and emit their public lifecycle there."
},
"crates/agentic-server-core/src/tool/code_interpreter.rs": {
"limit": 529,
"reason": "The dedicated Eryx executor keeps sandbox admission, cancellation, bounded output capture, and gateway execution lifecycle together."
Comment on lines +32 to +34
},
"crates/agentic-server-core/src/tool/registry.rs": {
"limit": 541,
"reason": "The code interpreter follows the existing registry validation and executor registration flow."
},
"crates/agentic-server-core/src/types/io/output.rs": {
"limit": 1116,
"reason": "The shared output-item enum and exhaustive helpers include the typed code-interpreter output item."
},
"crates/agentic-server-core/src/types/tools/params.rs": {
"limit": 573,
"reason": "The shared tagged tool declaration includes the closed gateway code-interpreter contract and schema."
},
"crates/agentic-server/src/main.rs": {
"limit": 611,
"reason": "Server configuration applies documented environment precedence to code-interpreter resource limits."
}
},
"generated": {}
}
44 changes: 28 additions & 16 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,11 @@ access happen — those live in `tool/`, `executor/`, and `storage/` respectivel
is the seam between the OpenAI-shaped request and vLLM's contract. It: flattens Codex
namespace tool members to model-visible names, validates every declared tool
(`ResponsesTool::validate()`), and normalizes each supported model-visible tool to
`UpstreamTool::Function` (`ResponsesTool::to_function_tools()`). File search, code
interpreter, and unknown typed declarations currently normalize to no upstream
tool; every declaration that does reach vLLM is `type: "function"`, because that's
the only tool type it speaks. The conversion also resolves/validates `tool_choice`
`UpstreamTool::Function` (`ResponsesTool::to_function_tools()`). File search and
unknown typed declarations normalize to no upstream tool; a feature-enabled,
runtime-ready code interpreter normalizes to a fixed function contract. Every
declaration that does reach vLLM is `type: "function"`, because that's the only
tool type it speaks. The conversion also resolves/validates `tool_choice`
and applies `ResponsesInput::model_input()`. It's called from
`executor/upstream.rs`'s `fetch_blocking_payload` and `fetch_stream_payload` — the
two functions that actually build the outbound request to vLLM.
Expand Down Expand Up @@ -750,17 +751,24 @@ round:
deadline for the entire round or total call latency. Timeout, execution, and tool-config
failures become failed tool outputs that can be fed back to the model instead of
failing the whole response. A tool registered as gateway-owned without an
implementation (currently file search/code interpreter) likewise produces an error
tool result.
implementation (currently file search) likewise produces an error tool result.
- Parallel safety is a per-handler contract. `GatewayExecutor::supports_parallel_execution`
defaults to `false`; registration turns that into a `GatewayBinding::self_exclusion`
semaphore. The semaphore serializes only simultaneous calls to the **same
model-visible tool name**. It never blocks different tools from running concurrently.
MCP and web search opt into same-tool parallel execution.
- Each scheduler slot retains its `GatewayEventPlan`; `emit_gateway_start_events` and
`emit_gateway_completed_events` synthesize the OpenAI lifecycle for gateway-executed
web search/MCP calls from those same slots. The ordinary path emits all planned start
events, executes the round concurrently, then emits ordered completed/failed events.
`emit_gateway_completed_events` synthesize public lifecycle events for gateway-executed
web search, MCP, and optional code-interpreter calls from those same slots. A code-interpreter
call emits `output_item.added`, `code_interpreter_call.in_progress`, the
`code_interpreter_call_code.delta`/`done` pair, `code_interpreter_call.interpreting`,
`code_interpreter_call.completed`, and `output_item.done`, with indexes and sequence numbers
assigned by `GatewayStreamAccumulator`. The dispatcher suppresses the canonical upstream
`function_call` lifecycle after classifying the call as gateway-executed. Native upstream
`code_interpreter_call` items instead follow the accumulator's typed lifecycle and pass through;
contradictory item kinds at one output index are handled by ingestion before translation. The
ordinary path emits all planned start events, executes the round concurrently, then emits ordered
completed/failed events.
- Streaming may receive client-visible output interleaved with gateway calls. In that
case `engine.rs::execute_and_emit_ordered_output_calls` temporarily groups deferred
upstream frames by `output_index`, executes the same `GatewayScheduler` concurrently,
Expand Down Expand Up @@ -926,8 +934,11 @@ RequestPayload::to_upstream_request
`RequestPayload::to_upstream_request` is the only request-level seam that prepares
tools for vLLM. New callers must use it rather than rebuilding function schemas or
normalizing declarations in the executor. Declared placeholders that are not yet
supported, currently file search and code interpreter, produce no upstream function
declaration until they have a complete handler and execution path.
supported, currently file search, produce no upstream function declaration until they
have a complete handler and execution path. Code interpreter is an opt-in gateway
executor: it normalizes only in builds with the `embedded-code-interpreter` feature,
and requests fail closed unless operator enablement and Eryx runtime readiness also
succeed.

| Component | Responsibility |
| --- | --- |
Expand All @@ -941,13 +952,14 @@ declaration until they have a complete handler and execution path.
`to_function_tools()`. These are the declaration-level validation and normalization
entry points used by `RequestPayload::to_upstream_request`. Each supported variant's
policy belongs to its corresponding `ToolHandler`: `FunctionHandler`,
`ToolSearchHandler`, `McpHandler`, `WebSearchHandler`, `CodexNamespaceHandler`, or
`CustomHandler`. Web search's fixed canonical builder is shared with
`WebSearchHandler::normalize`; it remains one schema even though it has no
`ToolSearchHandler`, `McpHandler`, `WebSearchHandler`, `CodexNamespaceHandler`,
`CustomHandler`, or `CodeInterpreterHandler`. Web search's fixed canonical builder
is shared with `WebSearchHandler::normalize`; it remains one schema even though it has no
per-declaration normalization state. The method name is plural because namespace and
MCP declarations may expand to several model-visible function tools.
`FileSearch`/`CodeInterpreter` remain unsupported placeholders and normalize to
nothing.
`FileSearch` remains an unsupported placeholder and normalizes to nothing. The
code interpreter normalizes to one fixed function contract only in feature-enabled
builds and is bound to `EryxCodeInterpreterExecutor` after startup readiness checks.
- **`handler.rs`** — the two traits every tool type reasons about:
```rust
pub trait ToolHandler: Send + Sync {
Expand Down
Loading