codex and piagent available for coding-oriented execution. Check model settings, tools, and callbacks before switching
Complete model configuration first. Volcengine and BytePlus each require their own model endpoint and API Key
Default runtime
Omittingruntime is equivalent to runtime="adk", suitable for standard model calls, structured output, and tool workflows
main.py
python main.py to print the final response. Use Runner.run_async for event and streaming handling
Parallel synchronous tool execution
Synchronous functions in the ADK runtime can block other async tasks. Settool_thread_pool_config to run synchronous tools in worker threads, allowing concurrent execution when the model issues multiple calls in one turn
parallel_tools.py
python parallel_tools.py to query both products. The model decides whether to issue calls together; the thread pool does not split tasks itself. Tools sharing mutable data or connections must support concurrent access
RunConfig.tool_thread_pool_config takes precedence over the agent setting. It affects ADK synchronous tools, not Codex or PiAgent execution. See code sandbox for parallel run_code session isolation and VEADK_RUN_CODE_ISOLATE_PARALLEL_CALLS
Switch the execution backend
Use the Codex runtime
Install the optional SDK and CLI binary dependencies:model_name, model_api_base, and model_api_key. Defaults use an isolated workspace, workspace_write, disabled network, and denied escalation. This example keeps those settings explicit
codex_runtime.py
python codex_runtime.py. Register function and MCP tools through Agent.tools. Local skills in ADK SkillToolset can be passed to the runtime; see the compatibility table for legacy skills_mode restrictions
Codex security configuration
Agent.codex_runtime_config accepts CodexRuntimeConfig or a matching dictionary
VeADK enforces session isolation through a custom permissions profile: under
workspace_write and read_only, sandbox commands run with a VeADK-generated permissions configuration based on read-only access, denying access to temporary directories and non-shared workspace roots, and only allowing the current session’s working directory and skills. A session’s commands cannot read or write another session’s workspace, rollouts, or configuration. Other files on the host (such as application configuration or a local SQLite session database) remain readable; isolate tenants at the container or OS-user level
These environment variables override constructor settings:
Codex process reuse and concurrency
Withdirect transport and thread_mode="resume", tool-free turns and turns exposing stable ADK/MCP tools reuse processes by default. Set mcp_process_reuse=False or VEADK_CODEX_MCP_PROCESS_REUSE=0 to disable MCP process reuse; tool-free turns can still be reused. Each turn rebinds the executor, callbacks, working directory, and tool budget. Before execution, the SDK-confirmed thread/turn ID is validated; stale turns, missing or mismatched identities, and duplicate in-process request IDs are rejected. Idle processes do not accept tool execution. Reuse occurs only when the storage, session, thread, model, permissions, skills, instruction, and tool declarations are unchanged and the stored thread identity and version still match. Processes are closed on error, interruption, save failure, or when the request lifecycle cannot be determined. VEADK_CODEX_PROCESS_POOL_SIZE=0 keeps a fresh process for every turn.
Reuse avoids repeatedly injecting skills and environment context on warm paths; necessary context is still preserved on cold recovery. An existing empty working directory is no longer reported as reset; newly created or rebuilt directories still are.
Idle processes are released automatically after the idle timeout. To release early:
await veadk.runtime.codex.release_codex_session(app_name, user_id, session_id) closes a session’s persistent process (call when deleting a session); await veadk.runtime.codex.shutdown_codex_runtime() closes all persistent processes plus the MCP bridge and shim, called automatically when an AgentKit application shuts down. If a VeADK process is killed, its Codex subprocesses exit with it; subsequent VeADK processes on the same host clean up locked workspaces and directories only when they can prove the owner has exited
Parallel and batch tool workloads
Independent async function tools can already execute concurrently when Codex emits them in the same model response. Mark independence in application instructions and concise tool descriptions; dependent reads and writes must retain their required ordering. An application-owned batch tool can bound independent reads and return a result or explicit error for each item. Authorize each item: ADK tools execute on the host, so the Codex sandbox does not replace business permissions. One batch is one SDK tool invocation, and the application must cap the number of business reads separately. The checkout provides a local example and benchmark,scripts/benchmark_codex_tools.py, with single, parallel, serial, and batch scenarios. Its bounded_batch_lookup example limits admission, applies per-item timeouts, preserves cancellation, and reports partial failures. It never combines arbitrary registered tools automatically.
CODEX_RUN_SMOKE=1 uv run --extra dev pytest tests/runtime/codex/test_codex_tool_workloads.py -m codex_smoke -p no:xdist -rs.
Model transport
model_transport controls how Codex reaches the model and runs tools:
In
auto mode, Volcengine Ark (*.volces.com), BytePlus ModelArk (*.bytepluses.com), and api.openai.com endpoints use direct; all others use shim
Thread resume
Withthread_mode="resume", each session keeps one Codex thread. The thread’s rollout is stored alongside the session (in its database when the short-term memory is database-backed) and resumed on the next turn, so Codex keeps its own full history instead of being handed a replayed transcript. ephemeral starts a fresh thread every invocation
Thread resume only works with the
direct transport. The shim transport is always ephemeral because the tools it runs never reach Codex’s history. Thread persistence depends on session storage; with in-memory session storage, threads do not survive a process restartSteer a running turn
Runner.steer(session_id, text) adds an instruction to the turn currently running for a session without starting a new one. The agent adjusts course without losing its progress
steer.py
steer returns True when the text was delivered to a running turn, False when no turn is running or the runtime does not support steering. Only the direct transport supports it, and the turn must be running in the current process
Codex observability
Lifecycle notifications, function calls, and MCP calls become ADK events for sessions, traces, and frontends. Log fields such asinvocation_id, call_id, tool, status, and duration_ms correlate operations. Token usage is exposed through codex_event_type=token_usage events
Runtime logs are not complete traces of every model call. Events, sessions, and configured exporters may contain task and tool content; control access and retention when exporting them
Configure transient-error retries
Use PiAgent
piagent_runtime.py
python piagent_runtime.py. VeADK checks PIAGENT_BINARY, then its managed cache, and downloads a Pi Release if neither is available. Download checksums are verified only when PIAGENT_BINARY_SHA256 is set. Preinstall the binary and specify its path for offline and production environments
Use the DeepSeek Harness runtime
The DeepSeek Harness runtime delegates the agent inner loop to the DeepSeek Harness SDK. The binary is acquired at install time through a Python extra, and the subprocess starts lazily at runtime without a first-run download Install the optional SDK and matching binary dependencies:veadk-python[dsh] depends on deepseek-harness-sdk, which in turn depends on a matching deepseek-harness-runtime-bin. The dsh executable arrives with the wheel; VeADK does not download a binary at runtime. Importing VeADK or constructing an Agent does not start DSH; the SDK starts the subprocess when execution first reaches the runtime entry point
One effective configuration reuses one SDK object and DSH subprocess. The same VeADK app/user/session/agent maps to a stable DSH session identifier. VeADK session history remains the source of truth for short-term memory and is serialized into the prompt as structured conversation data; the DSH session identifier stays stable but is not the sole memory source
ADK function tools and MCP tools are expanded before the DSH run and executed through generated tool plugins and an authenticated local callback server. sub_agents use the same injected transfer path as other external runtimes. ADK SkillToolset and legacy skills are materialized into an isolated directory and registered as DSH native skill plugins. Root-session DSH streaming output maps to ADK partial events, and DSH native tool events map to ADK function call/response events
Model settings remain model_name, model_api_base, and model_api_key. The following example uses a Volcengine Ark endpoint:
dsh_runtime.py
python dsh_runtime.py to print the final response
provider is the user-facing provider name, defaulting to deepseek-official. openai, volcengine, volcengine-ark, ark, openai-compatible, and third-party all map to the current DSH deepseek-official adapter; unknown provider names are passed through for future DSH native adapters. When base_url and api_key are not set in the config, they fall back to Agent.model_api_base and Agent.model_api_key
When dsh_home is not configured, VeADK generates an isolated process-owned directory and passes it to the SDK instead of implicitly using ~/.dsh. Configure a persistent dsh_home when DSH sessions must survive process restarts
DSH configuration
Agent.deepseek_harness_runtime_config accepts DeepSeekHarnessRuntimeConfig or a matching dictionary
enable_skills_checklist is not enforced inside DSH’s native skill loader. Descendant DSH subagent session notifications are not yet projected as first-class child-session events. These differences from codex and piagent are listed in the compatibility table belowRuntime compatibility
Check these settings when switching tocodex, piagent, or dsh:
Agent transfer
transfer_to_agent lets the model delegate to another agent in the tree, which continues execution and returns results. ADK, Codex, PiAgent, and DSH support transfers
transfer.py
python transfer.py to obtain an announcement. Transfers require Runner; the target executes in the same invocation context, including its output_key behavior. Use the sequential workflow below when execution order must be fixed
Output persistence
output_key stores the final response in session state under ADK, Codex, PiAgent, and DSH. It does not independently persist data to disk; survival across processes depends on session storage
pipeline.py
python pipeline.py. The planner writes plan, the writer reads it through {plan}, and the article is stored in draft. The script prints the response and both state values for verification
Model callbacks
Callbacks may be synchronous or asynchronous. ADK runs them around model calls; external runtimes run before/after callbacks around the whole turn, with plugin callbacks before agent callbacks. ReturningNone continues normal handling
Mutating a request does not add support for every ADK setting. The compatibility table still applies, including restrictions on
output_schema
model_callback.py
python model_callback.py. A successful model call returns its normal answer; a model failure returns the configured message. The callback does not handle every initialization, tool, or business error
Convert PDF attachments to images
pdf_to_images_before_model_callback converts inline PDF bytes into images for models that support images but cannot read raw PDFs. PDF rendering dependencies are included in the default VeADK installation
Save a document suitable for sending to the model service as example.pdf in the current directory, select a vision-capable model, and run this script:
pdf_callback.py
2.0. To change these values, import make_pdf_to_images_callback from the same module and set max_pages and scale. More pages or a larger scale increase image volume, memory use, and model input usage
Choose where each tool runs
RuntimeProvider controls individual tool calls, typically keeping the ADK flow while sending selected work to an application service. DispatchRuntimeProvider invokes your dispatch function, while LocalRuntimeProvider uses the original tool. To customize the full policy, subclass RuntimeProvider and implement execute(tool_call)
Usage example
This runnable example simulates a service adapter locally. Dispatched stock is12; the original tool returning 0 is not executed again. It does not connect to a real remote service
dispatch.py
dispatch_task with a client configured with authentication and timeouts, correlating requests with tool_call.id. Dispatch functions may be sync or async and should return a tool-compatible result. The application defines the service contract
Alternatively, pass runtime_provider.before_tool_callback to Agent.before_tool_callback. Do not register both entry points, which would intercept twice. MCP tools retain their own connections regardless of dispatch selection
DispatchRuntimeProvider parameters
ToolCall fields
Request processing
run_processor wraps the event stream of Runner.run for authentication, timing, cleanup, or event transformation. Precedence is the current Runner.run(run_processor=...), the Runner constructor, the root agent, then the default pass-through processor. Calling run_async directly does not apply this wrapper
For authentication, pass veadk.integrations.ve_identity.AuthRequestProcessor to Agent(run_processor=...). See inbound authentication for identity setup
Custom processors
SubclassBaseRunProcessor and implement process_run(runner, message, **kwargs). This processor forwards events, records elapsed time, and closes the event stream on success, failure, or cancellation
run_processor.py
python run_processor.py to see the response and elapsed time. Before adding retries to a processor, determine whether a task has already produced external side effects to avoid repeating them