Configuration and Environment Variables¶
This page documents environment variables used in src/.
.env is read once per process
The toolkit loads .env on the first client initialization and never re-reads it. Editing
.env — including rotating an API key — has no effect on an already-running process; restart
it. To use a different key without restarting, pass api_key=... to Agent(...) or to
initialize_clients / initialize_async_clients, which overrides the environment variable for
that client. See Async Clients.
API and provider credentials¶
GOOGLE_GEMINI_API_KEY¶
- Required: optional globally; required to initialize Google clients
- Format: string
- Example:
AIza... - Controls: Google GenAI client initialization
- Default behavior if missing: Google client is not initialized
ANTHROPIC_API_KEY¶
- Required: optional globally; required to initialize Anthropic clients
- Format: string
- Example:
sk-ant-... - Controls: Anthropic sync/async client initialization
- Default behavior if missing: Anthropic client is not initialized
OPENAI_API_KEY¶
- Required: optional globally; required to initialize OpenAI clients
- Format: string
- Example:
sk-... - Controls: OpenAI sync/async client initialization
- Default behavior if missing: OpenAI client is not initialized
FIREWORKS_API_KEY¶
- Required: optional globally; required to initialize Fireworks clients
- Format: string
- Example:
fwk-... - Controls: Fireworks sync/async client initialization
- Default behavior if missing: Fireworks client is not initialized
LLMWHISPERER_API_KEY¶
- Required: optional globally; required to initialize LLM Whisperer client
- Format: string
- Example:
lw-... - Controls: OCR client initialization used by PDF processing utilities
- Default behavior if missing: LLM Whisperer client is not initialized
LLMWHISPERER_BASE_URL_V2¶
- Required: optional
- Format: string URL
- Example:
https://llmwhisperer-api.us-central.unstract.com/api/v2 - Controls: LLM Whisperer base endpoint
- Default behavior if missing: defaults to US-Central endpoint
Agent runtime and logging¶
AGENT_LOG_LEVEL¶
- Required: optional
- Format: string enum (
DEBUG|INFO|WARNING|ERROR|CRITICAL|NOTSET) - Example:
INFO - Controls: default logger level in
agent/core.py - Default behavior if missing: call-level log level argument is used
Where the default log file goes
With no logger= and no log_file=, an Agent writes to
./logs/<logger-name>_<timestamp>.log. The timestamp is resolved once per process, so
building many Agents in one process appends to one file per logger name rather than opening a
new file per Agent — tens of thousands had accumulated in long-lived working trees. A handler
already open on that path is reused rather than closed and reopened, and a handler on a
different path is still removed, so sub-agent logs cannot cross-contaminate.
Pass log_file= to choose the path, or pass your own logger= to take over logging entirely;
in that case the toolkit never creates ./logs/ at all, and importing the package neither
stamps a timestamp nor creates the directory. AGENT_LOG_LEVEL sets the level for Agent, and
SUB_AGENT_LOG_LEVEL does the same for the sub-agents the agent-factory MCP server creates.
DPT_AGENT_LOG_TO_FILE¶
- Required: optional
- Format:
0,false,nooroff(case-insensitive) to disable;1,true,yesoronto enable - Example:
0 - Default behavior if missing: on — the default log file described above is written
- Controls: whether the logger an
Agentbuilds for itself (nologger=passed), and the logger of each agent-factory sub-agent, writes a log file. Off gives a console-only logger: no file is opened,./logs/is never created, an explicitlog_file=is ignored with a warning, and the agent-factory tools reportlog_file: null.Agent(log_to_file=False)does the same for one Agent. An unrecognised value is ignored with a single warning per process and leaves file logging on. - When to use it: server and container deployments where a library writing files into the working directory is unwanted, or where logs are collected from the console.
DEBUG_API_RESPONSES¶
- Required: optional
- Format: bool-like string (
"true"to enable) - Example:
true - Controls: verbose API response logging path in utils. It is read once, when
doc_processing_toolkit.utilsis first imported and before.envis loaded, so set it in the process environment rather than in.env. - Default behavior if missing: verbose response logging disabled
DEBUG_AGENT_STREAMING¶
- Required: optional
- Format: bool-like string (
"true"to enable) - Example:
true - Controls: grouped debug logging for
Agent.stream()/AgentSession.stream()text and thinking deltas - Default behavior if missing: grouped stream debug logging disabled
DOC_TOOLKIT_SESSION_HISTORY_MODE¶
- Required: optional
- Format: string enum (
full|messages_only|no_tool_results|no_reasoning|compact_tool_results|text_summary) - Example:
no_reasoning - Controls: default session history mode, below per-session and Agent-level overrides (see Sessions)
- Default behavior if missing:
full - The value must be one of the six mode names exactly (lowercase). Any other value is ignored with
one WARNING per process, and
fullapplies (it used to be ignored silently). - Behaviour change: on Google,
messages_onlyused to delete every message of a session, not only its tool traffic, so a Gemini session pinned to this mode through the environment variable ran with no memory at all. It now replays every earlier question and answer, which raises those sessions' input-token cost. See Sessions.
DPTTOOLS_SKILLS_DIR¶
- Required: optional
- Format: filesystem path
- Example:
/path/to/.agent_skills - Controls: the directory the skills MCP server reads skill definitions from (see Skills).
It is read by the server, in the server's own process: set it in that process's environment — for
an
mcp_command/mcp_scriptserver through the config'senv(a subprocess does not inherit this process's variables), for a server you start yourself through itsStdioServerParameters(env=...), for an in-process server in this process. EveryDPTTOOLS_*variable is documented on the std-tools configuration page. - Default behavior if missing: the server uses its own default (
./.agent_skillsin its working directory) and reports an empty catalog when that does not exist; the toolkit then hides the activation tools. Since 0.5.3 the toolkit itself does not read this variable.
Provider behaviour kill switches¶
These switches exist so a behaviour change that landed in this release can be turned off without a
downgrade. The three Google switches accept on (the default, also for an unset or empty variable)
and off (case-insensitive, surrounding whitespace ignored).
DPT_GOOGLE_EMPTY_TURN_RETRY additionally accepts identical. DPT_ANTHROPIC_BLOCK_BINDING takes
three named values; DPT_ANTHROPIC_STRICT_BUDGET and DPT_COMPACTION_PROTECT_CURRENT_TURN take on
or off; and DPT_SDK_RETRIES takes default or an integer. Every switch in this section,
and the opt-in DPT_PREFIX_AUDIT flag, logs one warning per distinct value it does not recognise and
then applies its default (DPT_PREFIX_AUDIT stays off).
DPT_GOOGLE_RETAIN_FINAL_TURN¶
- Required: optional
- Format:
offto disable; anything else (or unset) enables - Default behavior if missing: on
- Controls: whether a Gemini session stores the final answer as the model response object the API
returned, so its text
thought_signaturesurvives into the next turn.offstores the answer as plain text instead, which drops the signature — the behaviour before this release. - When to use it: leaving it on adds roughly 200–1,700 input tokens per stored turn when thinking is enabled, because each stored answer replays its thought summary, and that compounds across a session and brings compaction forward. Turn it off if that cost outweighs the reasoning quality Google's guidance attributes to returning the signature. See Sessions.
DPT_ANTHROPIC_RETAIN_FINAL_TURN¶
- Required: optional
- Format:
onoroff(case-insensitive); any other value is ignored with one WARNING and leaves the default - Default behavior if missing: on
- Controls: whether an Anthropic session stores the final answer of each turn as the block list the
model returned — its signed
thinking/redacted_thinkingblocks plus the text — so the next request replays that thinking. It applies only on the rows whose registry entry setsproduces_preserved_thinking_blocks(claude-fable-5-1,claude-opus-5-5,claude-sonnet-5-5,claude-haiku-5-5) and only to a turn that carried a thinking block; every other row stores the answer as plain text exactly as before. It covers the tool loop and the no-tools path throughAgentSession.run(), and a streamed turn withoutput_schema(stored exactly asrun()stores it); a plain-textAgentSession.stream()turn keeps its plain-text record.offstores the answer as plain text on the gated rows too — the behaviour before this release. - When to use it: leaving it on raises input tokens on the gated rows by the replayed thinking of every stored answer, which compounds across a session and brings compaction forward. Turn it off if that cost outweighs the reasoning continuity Anthropic's preserved-thinking guidance attributes to sending the thinking back. A session later moved to a model that cannot read the blocks still replays them; the API drops them. See Extended Thinking.
DPT_GOOGLE_EMPTY_TURN_RETRY¶
- Required: optional
- Format:
offto disable,identicalfor the byte-for-byte re-send; anything else (or unset) gives the default nudge behaviour - Default behavior if missing: on (nudge)
- Controls: the single bounded re-request after an empty Google model turn. Gemini occasionally
returns a model turn with
finish_reason=STOP, no usable content and zero output tokens after a tool result. The toolkit re-requests once — that request is billed and appears as its own cost record — and fails the run with an explicit "Empty model response" error if the turn is still empty. The re-request appends one short synthetic user turn (providers.google.GOOGLE_EMPTY_TURN_NUDGE_TEXT, "Use the tool results above to answer the question.") that is sent on the wire only and never stored in history — not even when compaction fires later in the same run, which used to copy it into the session's stored messages through the post-compaction resync: live, an identical re-send came back empty again in 8 of 14 occurrences, while the nudged form recovered 44 of 44. - When to use it:
identicalkeeps the re-request byte-for-byte identical to the original — choose it if a synthetic turn would disturb your own transcript accounting, accepting that it is a re-roll rather than a recovery.offrestores the earlier behaviour, in which such a run returned an empty answer with no error at all; choose it only if an empty answer is more useful to your application than a loud failure. A final turn carrying only a thought summary is never treated as empty. See Providers.
DPT_GOOGLE_BILL_TOOL_USE_TOKENS¶
- Required: optional
- Format:
onoroff(case-insensitive, surrounding whitespace ignored) - Default behavior if missing: on
- Controls: whether a Gemini request's
usage_metadata.tool_use_prompt_token_countis billed as input (0.5.1). Gemini reports the server-side tool output it feeds the model there, outsideprompt_token_count: the pages a URL-context call retrieves, and code execution's generated code and results. Google bills both as input, andtotal_token_countincludes them. When on, both Google paths (the tool loop and the structured / text call) add them toinput_tokensand to the input modality breakdown on a response that shows URL context or code execution ran, so each request's cost record and the run'scostinclude them. A search-only response is unchanged (search is priced per query), and the in-loop compaction check still reads the prompt alone.offrestores the earlier billing exactly: onlyprompt_token_countwas billed, so a run with URL context or code execution was under-reported (one URL: 17 prompt tokens against 1,323 tool-use tokens, measured 2026-09-28). Google Search reports no tool-use tokens; it is priced per query, as before. - When to use it: leave it on. Turn it off only to compare a run's cost with a figure computed before 0.5.1. See Cost Tracking.
DPT_ANTHROPIC_REFUSAL_BILLING¶
- Required: optional
- Format:
categoryorlegacy(case-insensitive, surrounding whitespace ignored); any other value is ignored with one WARNING and leaves the default - Default behavior if missing: category
- Controls: how a Claude refusal that arrives before any output is priced (0.5.2). With
category, such a refusal is billed only when its category is one Anthropic bills (bio,frontier_llm,reasoning_extractiontoday, read frompricing.anthropic.refusal_billingin the registry), and the same rule applies to each attempt of a server-side fallback: a declined attempt in a billed category is added tocostat the declining model's rates, and to the request's token counts.legacyrestores the pricing before 0.5.2 exactly: a fallback attempt that declined before any output is never billed, an output-less last step of an attempt that declined partway is not billed either, a refusal with no fallback is always billed from its usage, whatever its category, and a request's tokens are the top-levelusagealone. - When to use it: leave it on
category, which follows Anthropic's billing. Uselegacyonly to compare a run's cost with a figure computed before 0.5.2. See Cost Tracking.
DPT_ANTHROPIC_BLOCK_BINDING¶
- Required: optional
- Format:
off,drop_blockorerror(case-insensitive) - Default behavior if missing: the model's registry row decides. Four rows opt in today
(
claude-fable-5-1,claude-opus-5-5,claude-sonnet-5-5andclaude-haiku-5-5, which setenforces_thinking_prefix_check): every thinking request on them carriesdrop_blockand the beta header. Every other row sends nothing. - Controls: whether an Anthropic request carries
thinking.block_binding.prefix_mismatch_behaviorand thethinking-binding-controls-2026-08-01beta header.offsuppresses both on every model, including one whose registry row enables them — it is the kill switch.drop_blockanderroropt in process-wide. An unrecognised value is ignored with a single warning per process. - When to use it:
offif your account or gateway rejects the beta header;errorin CI, where a replayed thinking block that no longer matches its conversation prefix should fail loudly rather than be dropped. Precedence is athinking={"block_binding": ...}you pass yourself, thenAgent(anthropic_block_binding=...), thenAgent(strict_prefix_binding=True), then this variable, then the registry. See Thinking. - With thinking turned off: an off value cannot carry the field, so nothing is sent whatever this
variable says.
errorstill has one effect there: on a model that checks the prefix, a prefix-mismatch 400 is raised with its cause instead of being retried with the thinking blocks removed. See Thinking off on a model that checks the thinking prefix.
DPT_ANTHROPIC_STRICT_BUDGET¶
- Required: optional
- Format:
onoroff(case-insensitive, surrounding whitespace ignored) - Default behavior if missing: on
- Controls: how
strict_tools=True(theAgentdefault) picks the Anthropic tools it makes strict. Anthropic refuses a request whose strict schemas exceed any of three limits in total: 20 strict tools, 24 optional properties and 16 union-typed parameters. The counts cover every strict tool, nested objects included, and a structured run's response schema counts toward the property and union limits. The 400 reads, for example, "Schemas contains too many optional parameters (40) ... (limit: 24)". When on (0.5.1), the tools with the fewest optional properties are made strict first (then the fewest union-typed parameters, then by name) while all three budgets last, the response schema's share reserved first. A tool that does not fit is sent without strict mode, one WARNING names every such tool, and every tool is still offered.offrestores the earlier check, which looked only at each tool's own optional properties (a tool over 24 is sent without strict mode). Under that check nine small tools (the agent-factory server's, 40 optional properties in all) drew the 400, and so did any agent with more than 20 tools. The optional-property limit isSCHEMA_OPTIONAL_COUNT_ANTHROPIC. - When to use it: leave it on. Turn it off only if a gateway in front of Anthropic accepts more and you want strict mode on every tool that passes the per-tool check. See Strict tool schemas on Anthropic.
DPT_PREFIX_AUDIT¶
- Required: optional
- Format:
1,true,yesoronto enable (case-insensitive); any other value leaves the audit off, without a warning - Default behavior if missing: off
- Controls: the shadow prefix audit for every Anthropic request in the process. It fingerprints the
system prompt, the tools array and each message before a request and logs what changed since the
previous one as a
prefix_driftrecord, without altering a single request byte — proved byte-identical across every compaction strategy and history mode in the golden harness. - When to use it: to find out what is moving your cached or bound prefix — a rebuilt instruction
string, an MCP server whose tool list changed mid-session, a compaction strategy rewriting more
than you expected. It works on every model and needs no beta header. Per agent instead of per
process:
Agent(prefix_audit=True).
DPT_SDK_RETRIES¶
- Required: optional
- Format:
default, or a non-negative integer - Example:
default - Default behavior if missing: the Anthropic and OpenAI SDK clients are built with
max_retries=0 - Controls: the SDKs' own retry layer underneath tenacity. By default tenacity is the only retry
layer: each of its five attempts is exactly one HTTP request, and every retry is logged and billed
in
retry_count/retry_cost.defaultrestores the SDKs' own default of two retries per attempt — the behaviour before this release, worst case 15 requests with 10 of them invisible. An integer is passed through asmax_retries. Anything else is ignored with a single warning per process (clients keepmax_retries=0). Read when a client is built; clients are process-cached, so set it before the first request. Fireworks clients always usemax_retries=0andgoogle-genaihas no SDK retry layer, so neither is affected. - When to use it:
defaultif you relied on the SDKs' fast (0.5 s) first retry; with the SDK layer off, a transient failure waits tenacity's back-off (a serverretry-after, else 4 s). See Cost Tracking.
DPT_OPENAI_REALTIME_REASONING¶
- Required: optional
- Format:
onoroff(case-insensitive); any other value is ignored with one WARNING per process and leaves the default - Default behavior if missing: on
- Controls: whether
thinking_level=on an OpenAI realtime session is sent assession.reasoning.effort. It applies only to the rows whose registry entry publishessupported_thinking_levels(gpt-realtime-2,gpt-realtime-2.1,gpt-realtime-2.1-mini:minimal,low,medium,high,xhigh); on every other OpenAI realtime row the value is dropped with a WARNING, as before.offdrops it with a WARNING on the reasoning rows too — the behaviour before this release. - When to use it: higher reasoning effort can raise latency and output tokens on a voice turn. Turn
it off if a deployed voice agent already passes
thinking_level(it used to be ignored) and you need the old latency back before you can change the call. See the OpenAI provider notes on Realtime Voice.
DPT_MERGE_REASONING_INTO_SCHEMA¶
- Required: optional
- Format:
on/off(also1/0,true/false,yes/no; case-insensitive, surrounding whitespace ignored); any other value is ignored with one WARNING per distinct value and leaves the default - Default behavior if missing: on
- Controls: whether the model's reasoning text is appended to a schema field named
internal_reasoning,thoughtsorreasoning(case-insensitive) of a structured answer, under a***Internal Reasoning:***heading. On is today's behaviour on all four providers;offleaves the field exactly as the model wrote it. The reasoning is onAgentResult.reasoning_contenteither way — that is the canonical location. One shared predicate (utils.merge_reasoning_into_schema_enabled) gates every merge site, and it reads the variable on each structured answer, so a change takes effect without a restart. - When to use it:
offif your schema has such a field for the model's own words and the appended copy duplicatesreasoning_content. The merge is scheduled to be retired in 0.5 (the default becomesoff, as a breaking change). See Agent.
DPT_OPENAI_WEB_SEARCH_SOURCES¶
- Required: optional
- Format:
onoroff(0,false,noalso mean off) - Default behavior if missing: on
- Controls: whether OpenAI requests that carry
web_searchalso sendinclude=["web_search_call.action.sources"](merged into anyincludealready present) and whetherdistinct_urlsadds the URLs each search visited to the ones the answer cites — which is what fills the field on JSON-mode andoutput_schemaanswers, where no citation annotations exist.offrestores the previous request (noincludeentry) and the citations-only list. Any other value is ignored with one warning per process. - When to use it: if a proxy rejects the
includefield, or if you depend ondistinct_urlslisting only cited pages. See Built-in tools recipe.
DPT_OPENAI_RETAIN_ENCRYPTED_REASONING¶
- Required: optional
- Format:
onoroff(case-insensitive); any other value is ignored with one WARNING and leaves the default - Default behavior if missing: on
- Controls: whether OpenAI requests on a reasoning row (a registry entry with
supported_reasoning_values) sendinclude=["reasoning.encrypted_content"](merged into anyincludealready present) and whether anAgentSessionkeeps the returnedencrypted_contenton the reasoning items it stores. With both, a stored OpenAI conversation replays its reasoning on its own: it no longer depends on OpenAI's 30-day response store and works under zero-data retention.offrestores the previous request (noincludeentry on a reasoning row) and the previous storage (the payload stripped; replay byrs_id through the store). Thestatusfield is dropped from stored reasoning items either way. - When to use it: leave it on. Turn it off if a proxy rejects the
includefield, or if envelope size matters more than store-independent replay (an encrypted payload is a few KB per reasoning item). See History and Checkpointing.
DPT_COMPACTION_PROTECT_CURRENT_TURN¶
- Required: optional
- Format:
offto disable;on, empty or unset enables. Case-insensitive, surrounding whitespace ignored. Any other value is ignored with a single warning per process, and the default applies. - Default behavior if missing: on
- Controls: whether in-loop (Level 2) compaction protects the turn in progress, on all four
providers. When it is on,
summarize, the summarise step ofcombinedandsliding_windowcompact only the messages before the current turn's user message. The request, the draft the model has already written and the open tool round are kept verbatim. When nothing precedes the turn, the in-loop compaction falls back totool_result_clearingon the turn.offrestores the earlier behaviour byte for byte: the turn is folded into the summary, followed by the synthetic "Continue the task" turn. Pre-call (Level 1) compaction andtool_result_clearingon its own are not affected either way. - When to use it: leave it on. Folding the turn is what made models answer "the previous task has already been completed" instead of finishing the task. Turn it off only if you depend on the exact pre-change request shape, or if a single turn's own tool loop regularly fills the context window and you prefer the old aggressive summary to a larger context. See Compaction.
OpenAI transport¶
DPT_OPENAI_TRANSPORT¶
- Required: optional
- Format:
httporwebsocket(case-insensitive, surrounding whitespace ignored); any other value is ignored with one WARNING and leaves the default - Default behavior if missing: http
- Controls: the process default for the transport of the OpenAI tool loop (0.5.2). With
websocket, a run with local tools sends its requests over one WebSocket to the Responses API instead of one HTTP request each.Agent(openai_transport=...)wins over it for that agent. Read on every run, so it takes effect without a restart. If thewebsocketspackage cannot be imported, the run logs one WARNING and uses HTTP. - When to use it: for agents that call tools in a loop, where each request saves connection overhead; leave it unset for single-request workloads, which are slower over a socket. Results, billing and retries are the same on both transports. See OpenAI WebSocket Transport.
Session kill switches¶
DPT_SESSION_INTERRUPTED_TURN¶
- Required: optional
- Format:
onoroff(case-insensitive); any other value is ignored with one WARNING and leaves the default - Default behavior if missing: on
- Controls: what an
AgentSessionkeeps of a turn that did not complete. On, a turn that raises, or returns an error-shaped result with no transcript and no answer, has its user message removed from the stored history and recorded onmetadata["interrupted_turn"](message,files_attached,error,error_type,failed_at,run,undelivered_injections), which survives the envelope and is cleared when the next turn starts;session.interrupted_turnreads it.offrestores the previous behaviour exactly: the unanswered question stays inmessagesas a dangling user turn and nothing is recorded. See History and Checkpointing. - When to use it: leave it on. Turn it off only if a host parses the stored history for the failed question itself and cannot yet read the record.
DPT_GOOGLE_MERGE_TEXT_PARTS¶
- Required: optional
- Format:
onoroff(case-insensitive); any other value is ignored with one WARNING and leaves the default - Default behavior if missing: on
- Controls: how an
AgentSessionstores a Gemini answer that arrived as several plain text parts (one per streamed chunk). On, adjacent plain text parts of a stored Google message are merged into one part when the turn is stored, so the history replays as one text block per run of text; a part carrying athought_signature, a thought, a function call or media is never merged and stays exactly as returned.offrestores the previous storage: one part per chunk. - When to use it: leave it on. Turn it off only if a host reads the stored
partsand depends on the per-chunk split.
DPT_SESSION_CROSS_PROVIDER_CONVERSION¶
- Required: optional
- Format:
onoroff(case-insensitive); any other value is ignored with one WARNING and leaves the default - Default behavior if missing: on
- Controls: what
AgentSession.from_dict/from_json/rebinddo with a stored history written in one provider's dialect when the agent is on another provider. On, the history is converted through the neutral representation — tool rounds and attachments cross, what cannot cross is dropped and recorded onconversion_report— and only a history that cannot be converted raisesSessionCompatibilityError.offrestores the previous behaviour: every provider-native history is refused withSessionCompatibilityError, and the host restores it under the provider that wrote it or truncates it to its plain-text turns first. See Continuing on another provider. - When to use it: leave it on. Turn it off only if a host must never continue a conversation on another provider without its own review of what a conversion would drop.
Streaming kill switches¶
Two switches cover the Agent.stream() changes described in
Stopping a stream early. Both accept on or off
(case-insensitive, surrounding whitespace ignored). Unset means on. off restores the previous
behaviour exactly. Any other value is ignored with one warning per process and leaves the behaviour
on.
DPT_STREAM_CANCEL_ON_CLOSE¶
- Required: optional
- Format:
onoroff - Default behavior if missing: on
- Controls: what happens when the consumer closes
Agent.stream()orAgentSession.stream()before thecompleteevent, whether throughaclose(),contextlib.aclosingor the interpreter finalizing an abandoned generator. When on, the run is cancelled and the toolkit waits at mostSTREAM_CANCEL_WAIT_SECONDS(5 s) for it to stop. It then reports what the run had already spent as acomplete-style record withmetadata["abandoned"] = True. The record goes toAgent.last_abandoned_stream, to the session's totals andlast_turn_results, and tohooks.AgentSession.stream()also closes its inner generators at once, so a consumer that stops at thecompleteevent still has that turn banked.offrestores the previous handler: an unbounded wait for the cancelled run, and no record. - When to use it: turn it off only if a hook or a session-metadata consumer cannot handle the extra
completeevent or the extra banked run.
DPT_STREAM_STRUCTURED_OFFLOOP¶
- Required: optional
- Format:
onoroff - Default behavior if missing: on
- Controls: where a streamed run without local tools makes its provider call. Such a run takes the
provider's synchronous structured-output function. When on, that function runs in a worker thread
(
asyncio.to_thread) and every delta is handed back to the event loop as it arrives, so the stream is real-time and other tasks on the loop keep running. A closed stream also stops the request at the next chunk.offcalls the function on the event-loop thread again: the loop is blocked for the whole request, and every delta arrives after the response has returned.Agent.run()is not affected either way. - When to use it: turn it off if your own code relies on the provider call running on the event-loop
thread, for example a
logginghandler or a patched SDK client that is not thread-safe.
In-process tool kill switches¶
Two switches cover the 0.5.3 changes to how a tool of an in-process FastMCP server (tool format 4 —
the SDK-bundled mcp.server.fastmcp.FastMCP or standalone fastmcp<3) is called. Neither touches
fastmcp>=3 servers, which are called through their own public call_tool. Each takes on (the
default, also for an unset or empty variable) or off; an unrecognised value is ignored with one
warning per distinct unrecognised value and on applies.
DPT_FASTMCP_VALIDATED_CALL¶
- Required: optional
- Format:
onoroff - Default behavior if missing: on
- Controls: whether a registry tool is called through its own
run(since 0.5.3) rather than asfn(**arguments)with the arguments as the model sent them. On the SDK-bundled FastMCP,runvalidates and coerces the arguments as the server does over stdio: an unknown argument is dropped (before, it raised), a string is coerced to an annotatedint, a tool that asks for aContextreceives one bound to the server (onlyctx.fastmcpworks; its log and progress methods raise and the call becomes an error result) — and, which a tool written against the old direct call can notice, a pydantic-model parameter arrives as the model instance rather than a dict,date/Path/Enumparameters arrive converted, and a value the schema rejects (2.5for anint,nullfor a parameter that is notOptional) is an error result instead of being passed through. Afastmcp<3tool'srunvalidates by its own rules (it rejects an unknown argument, asfastmcp>=3does) and gets no toolkit-made context.offrestores the direct call exactly. - When to use it: turn it off if an in-process tool relied on receiving raw values — a dict where a
model is annotated, a string where a
dateis — until the tool is adjusted.
DPT_FASTMCP_SYNC_TOOLS_OFFLOOP¶
- Required: optional
- Format:
onoroff - Default behavior if missing: on
- Controls: where a synchronous tool function of an in-process FastMCP server runs. When on, it
runs on a worker thread (
anyio.to_thread.run_sync), so the event loop keeps serving stream events, hooks and its other tasks (a concurrent run, another agent's turn) while the tool works (since 0.5.3). It does not run a round's tool calls in parallel: every provider awaits them one after another.offcalls the function on the event-loop thread again: one slow tool stalls everything else on the loop for its duration — the behaviour before 0.5.3. Async tools, and a tool whoserunwraps no function of its own (afastmcp<3proxy or OpenAPI tool), stay on the loop either way. - When to use it: turn it off if a synchronous tool touches thread-affine state — an SQLite connection opened on the loop thread, a client that is not thread-safe. See MCP.
Text-to-speech kill switches¶
Two switches cover the 0.5.1 changes to synthesize_speech described in
Multimedia. Both accept on or off (case-insensitive, surrounding
whitespace ignored). Unset means on. off restores the behaviour before 0.5.1 exactly. Any other
value is ignored with one warning per process and leaves the behaviour on.
DPT_GOOGLE_TTS_RAW_PCM¶
- Required: optional
- Format:
onoroff - Default behavior if missing: on
- Controls: what
output_format="pcm"returns on a Google TTS row. When on, the raw 16-bit samples, withmime_typeaudio/l16;rate=24000.offreturns the WAV container instead, which is what"pcm"returned before 0.5.1."wav"and the default are unaffected either way, and so are the streaming functions, which always stream raw PCM on Google. The reader ismultimedia.google_tts_raw_pcm_enabled(). - When to use it: turn it off if your code passed
output_format="pcm"to a Google row and relies on getting a WAV back.
DPT_OPENAI_TTS_SSE_USAGE¶
- Required: optional
- Format:
onoroff - Default behavior if missing: on
- Controls: how
synthesize_speechcalls an OpenAI TTS row withsupports_sse_usage: true(gpt-4o-mini-tts). When on, it asks for the SSE form of the response, assembles the audio from its delta events, and bills the provider's owninput_tokensandoutput_tokens, socost_estimatedisFalse.offsends the raw-audio request used before 0.5.1 and estimates the cost from the text and the clip's duration. The streaming functions (synthesize_speech_stream,asynthesize_speech_stream) follow the same switch: on, they stream the SSE form and bill its usage; off, they stream raw audio and estimate. The reader ismultimedia.openai_tts_sse_usage_enabled(). - When to use it: turn it off if a proxy between you and OpenAI cannot pass an event stream, or if
you need the exact request used before 0.5.1. A single call can keep the raw request with
provider_options={"stream_format": "audio"}.
Tool-reported spend¶
DPT_AGENT_TOOL_REPORTED_COST¶
- Required: optional
- Format:
onoroff(case-insensitive, surrounding whitespace ignored). Any other value logs one WARNING and is treated ason. - Default behavior if missing: on
- Controls: whether an
Agentadds the spend its tools report (the_dpt_spendmarker the multimedia MCP tools, the agent factory'screate_agentandsuggest_agent_config, and your own tools can return) to itscost,tool_reported_costandtool_reported_costs.offrestores the totals from before 0.5.1 exactly: nothing is read, the new fields stay at0.0and[], andtool_call_resultevents carry notool_reported_cost.utils.tool_reported_cost_enabled()reads the variable on every call. - When to use it: turn it off if your app already adds these tools' costs to the agent's cost itself, until it stops doing so. See Cost Tracking.
Multimedia MCP server output¶
DPT_MULTIMEDIA_OUTPUT_DIR¶
- Required: optional
- Format: a folder path (surrounding whitespace ignored; blank is the same as unset)
- Default behavior if missing:
<system temp>/doc_processing_toolkit/multimedia, created on first use. The system temp folder followsTMPDIR/TEMP/TMP, so inside a sandbox it is the sandbox's own. On Linux and macOS, where/tmpis shared, the folder isdoc_processing_toolkit-<uid>/multimedia, created private to the user (mode 700). Each of its two components is refused when it exists as a symlink, belongs to another user, or can be written to by the group or other users, so nobody else can redirect the files. - Controls: where the multimedia MCP tools (
synthesize_speech,generate_image,edit_image, and the transcript copytranscribe_audiosaves) write their files when the call gives nooutput_dir. The tools never return base64 (0.5.1), so the file is the output: a call'soutput_dirwins, then this variable, then the default. A folder that cannot be created or written to is an error result naming the folder and this variable, before any provider call is billed. The toolkit never deletes these files, and never overwrites one: a clashing name gains a_2suffix. - When to use it: when the app reads the files from another container (point it at a shared volume), when the process cannot write to its temp folder, or to keep the files somewhere you clean up. See MCP Integration.
Google Files API upload wait¶
A Google request carrying a file uploads it through the Files API and waits for it to reach ACTIVE.
These three variables set the process-wide default for that wait; each is overridden per call by the
matching Agent(...) provider keyword (file_active_timeout_s, file_poll_interval_s,
file_poll_backoff). Pass nothing and set nothing and the wait behaves exactly as it did before this
release. An out-of-range parameter raises before the first state check, because clamping would
hide the typo the caller was trying to escape; an out-of-range or unparseable environment value
logs one warning and falls back to the built-in default, because an operator typo must not brick a
process. Empty or whitespace-only counts as unset, not as bad.
DPT_GOOGLE_FILE_ACTIVE_TIMEOUT_S¶
- Required: optional
- Format: number of seconds, 1–3600
- Default behavior if missing: 60 s for images and 120 s for video and audio. A document waits 60 s on the synchronous path and 120 s on the async path.
- Controls: how long a request waits for an uploaded file to become
ACTIVE. A long video exceeds the built-in ceiling and used to fail with a timeout a caller had no way to raise. - When to use it: raise it for long video or audio. The timeout error names the file, the state it was last seen in, how many checks were made and which knob to turn.
DPT_GOOGLE_FILE_POLL_INTERVAL_S¶
- Required: optional
- Format: number of seconds, 0.05–60; must not exceed the timeout
- Default behavior if missing: 1 s for images, 2 s otherwise
- Controls: the gap between file-state checks while waiting.
DPT_GOOGLE_FILE_POLL_BACKOFF¶
- Required: optional
- Format: multiplier, 1–10
- Default behavior if missing:
1.0— the fixed cadence used before this release - Controls: the factor the poll interval is multiplied by after each check. The interval is clamped to the remaining budget on the last sleep, so a backoff can never overrun its own timeout.
Audio conversion¶
doc_processing_toolkit.audio_convert transcodes a recording whose container the target
speech-to-text model does not accept. The converter is discovered, never required — see
Multimedia — and with none installed an unaccepted
container raises a named error instead of reaching the provider as a 400.
DPT_FFMPEG_PATH¶
- Required: optional
- Format: filesystem path to an ffmpeg executable
- Default behavior if missing:
ffmpegonPATH, then the binaryimageio-ffmpegbundles, then PyAV, then no converter - Controls: which ffmpeg the converter uses. Checked first, so it always wins. A path that is not a
file logs one warning and discovery continues with
PATH. - When to use it: a pinned or sandboxed ffmpeg build, or a machine where
PATHis not the one the process inherits. The discovery verdict is cached for the process;audio_convert.reset_converter_cache()clears it in a test.
DPT_AUDIO_CONVERT_TIMEOUT¶
- Required: optional
- Format: number of seconds, greater than 0. A value that is not a positive number logs a warning and the default applies.
- Default behavior if missing:
60 - Controls: the wall clock allowed for one ffmpeg subprocess. A transcode that outruns it is killed
and raises
AudioConversionError.
DPT_AUDIO_CONVERT_MAX_MB¶
- Required: optional
- Format: number of megabytes, greater than 0. A value that is not a positive number logs a warning and the default applies.
- Default behavior if missing:
200 - Controls: the largest input accepted for conversion, so a multi-gigabyte recording is refused locally rather than handed to a subprocess and buffered in memory.
Agent factory server env vars¶
SUB_AGENT_LOG_LEVEL¶
- Required: optional
- Format: string log level
- Example:
DEBUG - Controls: sub-agent logger level in agent factory flows
- Default behavior if missing:
DEBUG
SUB_AGENT_DEFAULT_TOOLS¶
- Required: optional
- Format: JSON list string
- Example:
["tools/my_tools.py", {"type":"mcp", "server_url":"https://..."}] - Controls: fallback tool list when no explicit tools are passed to factory flows
- Default behavior if missing: no default tools injected
AGENT_FACTORY_SUGGESTION_MODEL¶
- Required: optional
- Format: string model name
- Example:
claude-sonnet-4-5 - Controls: model used by suggestion sub-agent in
suggest_agent_config - Default behavior if missing:
claude-sonnet-4-5 - Any provider works, Gemini included.
recommended_config, each alternative'sconfigandestimated_costare typed objects (SuggestedAgentConfig,CostRange). They were property-less{"type": "object"}fields, which gave a constrained decoder nothing to emit: ongemini-3-flash-previewthe sub-agent emitted whitespace until its 65k-token limit, twice per call. The sub-agent also runs with an output ceiling (max_tokens=16384, clamped to the model's own maximum), so a runaway is a fast parse failure handled by the existing single retry rather than a $0.20, 12-minute one. Optional parameters the model leaves unset are removed from the returned dicts, so the result shape is unchanged: only what differs from thecreate_agentdefaults. - The catalogue of registry rows this sub-agent is shown has editor-facing provenance keys stripped
out first, so a
_sourceor_verifiednote can never reach the suggestion prompt.
AGENT_FACTORY_SUGGESTION_MODEL_USE_WEB_SEARCH¶
- Required: optional
- Format: bool-like string (
"true"/"false") - Example:
true - Controls: whether suggestion sub-agent enables web search
- Default behavior if missing:
false
Provider tuning env vars¶
SCHEMA_OPTIONAL_COUNT_ANTHROPIC¶
- Required: optional
- Format: int
- Example:
24 - Controls: the most optional properties Anthropic's strict mode accepts. A tool with more is sent
without strict mode; since 0.5.1 the same number is also the budget for all the strict schemas of
one request (see
DPT_ANTHROPIC_STRICT_BUDGET) - Default behavior if missing:
24
SCHEMA_OPTIONAL_COUNT_OPENAI¶
- Required: optional
- Format: int
- Example:
24 - Controls: OpenAI schema optional field behavior in provider implementation
- Default behavior if missing:
24
Notes¶
- Variables are read via
os.getenv(...)andos.environ.get(...)in source files. - For local development, define values in
.envat repository root. - For CI, prefer secret-injected environment variables over committed files.
- A missing provider key is reported by name: client initialization raises
RuntimeError: <provider> API key not configured (<ENV_VAR> is unset).Any other construction failure chains its real cause instead of blaming the key. See Async Clients.