Repository navigation
fix!: pass AI Config model parameters through to every provider handler - #107
Conversation
model.parameters is a free-form dict the LaunchDarkly UI writes for
provider tuning values, including max_turns for agent turn caps. Every
Python handler except claude-messages ignored it entirely, and
claude-messages read only max_tokens. In particular there was no way
to cap an agent's turns: claude-agents never set
ClaudeAgentOptions.max_turns and openai-agents never passed max_turns
to Runner.run.
Adds a shared launchdarkly_ai_server.model_parameters(config) helper
that returns model.parameters as a fresh dict (or {} when absent/not
a mapping), and never reads model.custom. Applies it at every provider
call site across all six handler packages, including streaming and
native-graph paths. Keys are forwarded as-is (snake_case, matching
both the UI and every Python provider SDK here); no allowlisting and
no case conversion.
Handler-owned keys (model, messages/input, system/instructions, tools,
etc., decided per call site) are always removed from the forwarded
dict before merging with the handler's own kwargs, so a config value
can never override what the handler itself sets. claude-messages keeps
its max_tokens-defaults-to-1024 behaviour exactly.
LangChain's handlers already forwarded model.parameters through a
local _model_constructor_kwargs helper; swapped that to use the new
shared helper for consistency.
Verified: uv run pytest (1332 passed, 11 skipped), uv run mypy
packages/*/src, uv run ruff check ., uv run ruff format --check .
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
bugbot run |
…ccepts model.parameters is a free-form dict the LaunchDarkly UI writes, offering keys that make sense across providers but that no single provider SDK accepts in full. Forwarding it unfiltered (added in a prior commit on this branch) raised TypeError before any request for several real configs: ClaudeAgentOptions has no temperature/top_p/top_k/max_tokens/ stop_sequences/tool_choice/metadata; the OpenAI Responses API has no max_tokens/frequency_penalty/presence_penalty/seed/n/stop/response_format/ logit_bias/logprobs/max_completion_tokens/audio/modalities/prediction; Anthropic's Messages API has no top-level effort. Adds launchdarkly_ai_server.parameter_forwarding, a shared filter used by every handler: accept-sets are derived once from the live provider type (inspect.signature for plain methods, dataclasses.fields for ClaudeAgentOptions/ModelSettings, pydantic model_fields + aliases for the LangChain chat models), never hand-listed, so an SDK's own drift is picked up automatically. transport/escape-hatch keys (extra_headers, extra_query, extra_body, timeout, extra_*) are stripped unconditionally regardless of what a signature accepts, and each call site may additionally exclude keys the API accepts but that would break the handler because the handler itself already decides that behaviour: stream/stream_options/background/ conversation/prompt for the OpenAI Responses API, stream for Anthropic Messages. Everything else the provider accepts is forwarded, including keys that are not strictly generation settings (store, user, safety_identifier, prompt_cache_key, prompt_cache_retention, include, context_management, metadata, service_tier, instructions, moderation). Two renames, applied before the accept filter: openai-messages maps max_tokens/max_completion_tokens to the Responses API's max_output_tokens (explicit max_output_tokens wins, then max_completion_tokens, then max_tokens); claude-messages maps a top-level effort into output_config.effort, unless the config already sets its own output_config.effort, which wins. Applied at every call site across all six handlers, including streaming and native-graph paths. claude-agents/openai-agents dataclass accept-sets are computed once at module load from the real SDK types, independent of each package's per-call importlib.import_module mocking in tests. LangChain's per-provider pydantic accept-sets are cached per class with functools.cache, since the constructor class is only known once a provider branch resolves langchain_openai/_anthropic/_aws. Updated the existing tests whose old assertions relied on unfiltered pass-through of a key the real SDK does not accept (tools forwarded to ChatOpenAI's constructor in two langchain-agents/messages tests) to give the mocked provider class an accurate model_fields shape and to reflect the value now being dropped. Verified: uv run pytest (1383 passed, 11 skipped), uv run mypy packages/*/src, uv run ruff check ., uv run ruff format --check .. uv.lock unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
bugbot run |
…warded-key lists Every handler decided which model.parameters keys to forward by introspecting the live provider SDK at runtime: inspect.signature for the two Anthropic/OpenAI methods, dataclasses.fields for ClaudeAgentOptions/ModelSettings, pydantic model_fields plus aliases for the three LangChain chat models. Every competitor SDK (LiteLLM, Vercel AI SDK, Braintrust, LangChain) and this SDK's own TypeScript port use a written-down list instead, so replaces the derivation with one: each handler now carries a literal frozenset of the keys it forwards, generated once from the same introspection and pasted in as a reviewable list, next to its existing handler-owned and excluded sets. Deletes accepted_parameter_keys_from_signature/_from_dataclass/_from_pydantic_model, _alias_strings, is_transport_parameter, strip_transport_parameters, and filter_forwardable_parameters from packages/client's parameter_forwarding module now that nothing calls them. In their place, select_forwarded_parameters(params, keys) is the one shared piece left: keep the params keys that are on a handler's own forwarded list, drop everything else. Reproduces every handler's existing forwarded/owned/excluded classification exactly, with one deliberate addition applied consistently: client/connection configuration (API keys, base URLs, organization ids, HTTP clients, default headers/query, proxies, timeouts, retry counts) is now always excluded, even when a provider's own accept-set would otherwise let it through. This newly excludes, per handler: - langchain-messages / langchain-agents (ChatOpenAI): api_key, openai_api_key, base_url, openai_api_base, organization, openai_organization, openai_proxy, client, async_client, root_client, root_async_client, http_client, http_async_client, http_socket_options, default_headers, default_query, max_retries, request_timeout - langchain-messages / langchain-agents (ChatAnthropic): anthropic_api_key, api_key, anthropic_api_url, base_url, anthropic_proxy, default_request_timeout, max_retries, default_headers - langchain-messages / langchain-agents (ChatBedrockConverse, opt-in dependency): bedrock_api_key, api_key, aws_access_key_id, aws_secret_access_key, aws_session_token, credentials_profile_name, endpoint_url, base_url, client, bedrock_client, config, default_headers, max_retries - openai-agents (ModelSettings): retry timeout/extra_headers/extra_query/extra_body/extra_args were already excluded by the prior transport-prefix rule on every handler that has them; they are now named explicitly in each handler's own excluded set instead of matched by a shared prefix check, since nothing here is derived at runtime any more. Adds one drift test per handler package (test_parameter_forwarding.py) that reads the real provider SDK's signature/dataclass/model_fields and asserts every parameter it accepts is classified in exactly one of forwarded, handler-owned, or excluded, so an SDK addition nobody has classified fails loudly by name, and so does a stale list entry. The LangChain ChatBedrockConverse test skips itself when langchain-aws (an opt-in dependency) is not installed, matching this package's existing Bedrock test pattern. Updates langchain-messages' TestModelParametersReachTheWire test, which relied on forwarding http_async_client/api_key through model.parameters to mock the outgoing HTTP call: it now intercepts ChatOpenAI's own default httpx client builder instead, so the mock transport is reached without any config value ever naming an HTTP client or a key. Adds TestConnectionConfigIsNeverForwarded to both langchain-messages and langchain-agents, asserting api_key/base_url from model.parameters never reach the ChatOpenAI/ChatAnthropic constructor kwargs. Verified: uv run --frozen pytest (1385 passed, 15 skipped), uv run --frozen mypy packages/*/src, uv run --frozen ruff check ., uv run --frozen ruff format --check ., uv.lock unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
bugbot run |
…eters They are the same constructor field as `model`, under its field name or its alias. The handler always sets `model` itself, so a config value for either collided with the resolved model name or overrode it. Classify both as handler-owned per model class, and have the drift tests read that set from the handler instead of a test-local copy. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
bugbot run |
jeffdupont
left a comment
There was a problem hiding this comment.
Reviewed against the 1.0 GA plan. The fix is needed: parameters set in an AI Config were being ignored, and that should be fixed before 1.0. Tests pass at d51199d (make test: 1391 passed, 15 skipped, exit 0). The branch merges cleanly onto current main (fee904a), and the merged tree passes too: 1491 passed, make typecheck clean.
Four things I think need settling before merge, because each one changes the public interface and can't be changed after 1.0:
- Claude Agents forwards host-process settings, not just model settings. A config can set
cli_path,env,cwd,add_dirs,permission_mode,settings,setting_sources,pluginsandsandbox. I reproduced it: a config withcli_path: /tmp/attacker-binarymakes the SDK launch that file as its agent process (claude_agent_sdk/_internal/transport/subprocess_cli.py:225), andenv: {ANTHROPIC_BASE_URL: ...}points the customer's API key at another host (:434). That turns "can edit an AI Config" into "can run code on the customer's server." Inline comment below. - Two LangChain keys get around the exclusion list.
model_kwargscarriesextra_headers/extra_queryinto the ChatOpenAI request payload (reproduced with_get_request_payload), though both are listed as excluded.mcp_servers(ChatAnthropic) would let a config attach a remote MCP server; I haven't tested that one. - Two new public names.
model_parametersandselect_forwarded_parametersare in the root__all__, but only the provider packages use them. Same point as on #121 withmake_graph_track_data: they belong in the internal group, or the 1.0 surface trim has to remove them again. - Python and JS don't match (js #73). The two messages handlers agree. In the agent and LangChain handlers, JS forwards the whole camelCased bag with no allowlist, so
api_keybecomesapiKey, which ChatOpenAI and ChatAnthropic use. I'll leave a note there too. The monorepo has no spec for forwarding these parameters, and open monorepo #10 says "do not rename or filter parameter keys," which contradicts both PRs. One spec inTESTING.mdwould settle it for both languages.
Smaller points, not blocking:
output_format(Claude Messages) andresponse_id/starting_after/text_format(OpenAI) are forwarded on stream but not invoke, so one config can behave differently depending on the call.- OpenAI Responses forwards
reasoningbut notreasoning_effort. If the UI writesreasoning_effort, it's dropped silently, aseffortwas before your rename. I haven't checked what the UI writes. - The loops that remove handler-owned keys never find anything, since those keys aren't on the forwarded lists. They're harmless, just dead code.
- In native graphs only the root node's
max_turnsis used.
Removing _HAS_ANTHROPIC is fine; nothing on main reads it.
|
bugbot run |
…d_parameters from the package root Only the provider packages in this repo use these helpers, and anything in __all__ becomes public API at 1.0. Callers now import them from the modules that define them: launchdarkly_ai_server.utils and launchdarkly_ai_server.parameter_forwarding.
|
bugbot run |
…rameters ClaudeAgentOptions also configures the host process the SDK launches: which binary runs, its environment, working directory, file access, permission mode, settings, plugins, sandbox, and session state. Forwarding those from a config let anyone who can edit an AI Config launch their own binary or point the API key at another host. The forwarded list is now max_turns, max_thinking_tokens, thinking, effort, max_budget_usd, fallback_model, output_format and betas. Every other field is excluded, with the reason next to it. The loops that popped handler-owned keys are removed: none of those keys is on the forwarded list, so the filter already drops them.
model_kwargs is merged straight into the request payload, so it carried extra_headers and extra_query past the exclusions. mcp_servers let a config attach a remote MCP server that then receives the conversation. region_name and inference_geo move where data is processed. Bedrock's additional_model_request_fields is another unfiltered request bag. All of those are now excluded, along with LangChain's own runtime fields (callbacks, cache, tags, metadata, streaming mode, message format, ...), which configure how LangChain runs in this process rather than the request. _model_constructor_kwargs now requires its forwarded list, and the dead pop of tools for Bedrock is removed.
output_format is only accepted by messages.stream, so a config with it behaved differently depending on the call. It is now dropped on both paths; output_config, which both accept, carries the output format. One forwarded list now serves both calls. inference_geo is excluded too: it sets the region inference runs in. The loops that popped handler-owned keys are removed, since none of those keys is on the forwarded list.
ChatAnthropic sends thinking to the Messages API as written, and the handler
checked only that it was an object, so {"budget_tokens": 1024} or
{"type": "enabled"} with no budget reached the API and was rejected there.
The JS SDK already drops a thinking without a string type.
thinking is now rebuilt from the variants the Messages API takes (enabled
with budget_tokens, adaptive, disabled), keeping only their sub-keys, the
same shapes the Claude Messages handler uses.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Data residency (inference_geo) and container state (container, reuse_last_container) are deployment settings, not generation settings, and section 1.12 lists them as never forwarded. inference_geo was filed under endpoints and the container keys were missing, so no test checked that a handler drops them. inference_geo now sits under data location and retention, and container, reuse_last_container and previous_response_id under server-side state, matching the spec's categories. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rameters The TypeSafe judge handler landed on main without a forwarded-key list, so the guard that every handler module declares one fails. system_one takes the state, the questions and the model name; its other arguments are runtime wiring, so the list is empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jeffdupont
left a comment
There was a problem hiding this comment.
Changing my approval to request changes. I found this after reading the full spec in ai-sdks-monorepo#42.
output_config is forwarded with only a mapping check (claude-messages/.../handler.py:67 and :81, and the LangChain ChatAnthropic list), so output_config.format reaches the API. That field is Anthropic's structured-output JSON schema. The handler owns structured output through outputFormat (the schema instruction at handler.py:330). So a config with outputFormat set to schema A and output_config.format set to schema B gets back JSON in the shape of B.
I've asked in mono#42 for output_config to keep only effort. Once that lands, rebuilding it here as {effort}, and flipping the tests that assert it passes through, would close this. Everything from my earlier threads still looks good.
output_config.format is Anthropic's structured-output schema. The handlers
own structured output through outputFormat, so a config that also set
output_config.format got a reply shaped by a second schema. Claude Messages
(invoke and stream) and LangChain ChatAnthropic now rebuild output_config as
{"effort": ...} and drop it when effort is missing or not a string.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed output The handler puts the config's outputFormat schema in the system prompt. A model.parameters.output_format with another schema was forwarded to ClaudeAgentOptions, so the prompt asked for one shape while the SDK enforced another. output_format is now excluded on the handler and the native graph, which share the same list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
bugbot run |
A mistyped effort or summary made Reasoning(...) raise while the Agent was built, so a config that used to run failed. It is malformed, so it is now dropped like any other malformed nested value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ChatAnthropic takes stop as the alias of stop_sequences. A config that set both passed both spellings of one field. stop is now renamed, and stop_sequences wins when both are set, as for ChatBedrockConverse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@jeffdupont fixed, following the #42 change:
Two other things changed since your approval:
Bugbot also found two crashes on the latest push, both fixed:
|
Eleven handler packages now import launchdarkly_ai_server.parameter_forwarding and utils.model_parameters, which no released client has (PyPI's latest is 0.4.0). This change is breaking, so release-please ships the client as 0.5.0, and each handler now requires it instead of a bare or >=0.3.1 range that would fail at import. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 3e11f4f. Configure here.
PyPI's latest launchdarkly-ai-server is 0.4.0, which has no parameter forwarding, so the handlers need a later release. >=0.4.1 holds whichever version release-please cuts next, the same convention as the >=0.3.1 bound added by #158. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@jeffdupont one follow-up after your approval: point 2 from your Oct 12 review (a lower bound on |
The JS and Python SDKs have to forward the same `model.parameters` keys before 1.0. Jeff's review of launchdarkly/python-ai-sdk#107 found that the lists from #73 differ from Python's. The shared lists are now in the cross-SDK spec (launchdarkly/ai-sdks-monorepo#42, TESTING.md §1.12), and Python is moving to them too. This PR makes each JS handler forward exactly those lists, minus the keys the spec marks as not supported in TypeScript. What changed: - Claude Messages: stops forwarding `container`, `inference_geo` and `metadata`. `output_config` keeps only `effort` (a top-level `effort` still moves into it, and an explicit `output_config.effort` wins). `output_config.format` is dropped, because structured output belongs to the config's `outputFormat`: forwarding it let a config ask for one schema in `outputFormat` while the API enforced another, and made `stream` return JSON. - OpenAI Messages: stops forwarding `context_management`, `include`, `instructions`, `metadata`, `moderation`, `prompt_cache_retention`, `safety_identifier`, `store`, `truncation` and `user`, on both invoke and stream. `max_tool_calls` is not forwarded: the spec marks it not supported in TypeScript, because the `openai` package's `responses.create` has no such parameter. - OpenAI Agents (handler and native graph): stops forwarding `context_management`, `prompt_cache_retention`, `store` and `truncation`. `tool_choice` is forwarded only as a string. - LiteLLM Agents (handler and native graph): stops forwarding `store`. `reasoning` keeps only `effort`, because the Chat Completions model sends it as `reasoning_effort` and has no field for `summary`. `tool_choice` is forwarded only as a string. - Claude Agents (handler and native graph): stops forwarding `output_format`, for the same reason as `output_config.format`: the handler applies the config's `outputFormat` itself. `thinking` is kept only with a type the Claude Agent SDK knows, an `enabled` thinking needs a numeric budget, and `display` is kept only as `summarized` or `omitted`. - Vercel (messages and agents): a `reasoning` object is read as its `effort`, and any other non-string `reasoning` is dropped. `tool_choice` is forwarded only as a string, since the AI SDK reads any other value as a call to a named tool. - LangChain ChatOpenAI (both LangChain packages and the LangGraph native graph's default model): stops forwarding `audio`, `disable_streaming`, `metadata`, `modalities`, `output_version`, `prompt_cache_retention`, `reasoning`, `stream_usage`, `streaming`, `tags`, `use_responses_api`, `user`, `verbose` and `zdr_enabled`. `prompt_cache_key` is still forwarded: the spec marks it not supported in Python only, where `ChatOpenAI` has no such field. - LangChain ChatAnthropic: stops forwarding `context_management`, `disable_streaming`, `inference_geo`, `metadata`, `output_version`, `stream_usage`, `streaming`, `tags` and `verbose`. It now takes `stop` as `stopSequences` and `max_tokens_to_sample` as `maxTokens`, because `ChatAnthropic` has neither field under those names. `outputConfig` keeps only `effort`, as in Claude Messages. - LangChain ChatBedrockConverse: stops forwarding `disable_streaming`, `guardrail_config`, `metadata`, `output_version`, `stream_usage`, `streaming`, `supports_tool_choice_values`, `tags` and `verbose`. - LiteLLM Messages and TypeSafe: no change. LiteLLM Messages already forwarded the spec's list, and TypeSafe forwards no `model.parameters` key. Following the spec, nested values with the wrong shape are dropped instead of sent. Examples are a `thinking` without a `type`, a `reasoning` that is not an object, a `tool_choice` of the wrong type, or an `output_config` without a string `effort`. OpenAI Messages `reasoning` keeps only `effort` and `summary`, as OpenAI Agents already did. Testing: - Each handler and native graph has a test that the forwarded keys are exactly the spec's list, and, where the handler has both, that invoke and stream forward the same keys and values. This now includes LiteLLM Messages and LiteLLM Agents. TypeSafe has a test that it forwards no `model.parameters` key at all. - Every package's never-forward bag is built from one shared key list in `packages/client/src/__tests__/never-forwarded-keys.ts`, which covers every never-forward category in the spec, including `user`, `metadata`, `inference_geo` and `container`. Each package's `__tests__/never-forwarded.ts` adds only the keys its own table leaves out, so the bags can't drift apart. - New tests cover dropping malformed nested values, `output_config.format` on invoke and stream, and `output_format` in Claude Agents. - Each changed package's tests and `tsc --noEmit`, and `biome check .`, pass locally. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > Aligns every JS provider handler with **TESTING.md §1.12** so `model.parameters` forwards the same generation-related keys as the Python SDK, and drops everything else (credentials, retention, metadata, wiring, etc.). > > **Allowlists are tightened across packages:** OpenAI Messages/Agents stop forwarding retention and attribution fields (`store`, `user`, `context_management`, …); Claude Messages keeps `output_config` to `{ effort }` only and strips `format`; Claude Agents no longer forwards `output_format` (structured output stays on config `outputFormat`); LangChain constructors lose base-model wiring keys and narrow ChatAnthropic `outputConfig`; LiteLLM/Vercel agents drop `store` and non-string `tool_choice`; Vercel maps object `reasoning` to a string `effort`. **Malformed nested values** (`thinking`, `tool_choice`, `reasoning`, typed Anthropic objects) are dropped instead of passed through. > > **Tests:** Shared `createNeverForwarded()` in `packages/client` replaces duplicated never-forward lists; each handler/native graph gets §1.12 canonical-key tests and invoke/stream parity checks where applicable. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 0dd95ab. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…host settings (#201) ## Summary This is the Python side of the core half of launchdarkly/ai-sdks-monorepo#59 (TESTING.md §2.x "Claude Agents Handler — host options and built-in tool set"). The TypeScript side is launchdarkly/js-ai-sdk#135. - **`tools=[]` when a config attaches no `NativeTool`** (§2.x.2). This reverses 50e82fd. With the key omitted, every Python run got the full Claude Code built-in set (`Read`, `Bash`, `Skill` and the rest), and the read-only tools in it need no permission. A config now turns a built-in on by attaching its sentinel, which is what TypeScript has always done. User-defined tools still go through the in-process MCP server and `allowed_tools`, whatever `tools` holds. - **`create_claude_agents_handler(*, capture_content=None, cwd=None, setting_sources=None)`** (§2.x.1). Both options are forwarded to every `query()` on the blocking and streaming paths. - `setting_sources` not given sends `[]`. The Agent SDK reads `None` as "load every source", which put the host's plugins, skills, hooks, permission rules and `CLAUDE.md` into every run (AIC-3584). - `cwd` is sent only when given. - `capture_content=None` still follows `configure()`. - A config can't set or override either option: since #107 the handler forwards only allowlisted `model.parameters`, and both keys are on the excluded list. A test pins that. - **The wrappers and the native adapter take both options too**, matching launchdarkly/js-ai-sdk#135: - `claude_agents()` and `claude_graph()` take `cwd` / `setting_sources` keyword-only and build the handler with them. Neither reaches `config()` / `graph()`. - `to_claude_agents()` reads them from `opts` and sends `_host_query_options(...)` to every node's `query()`, root and sub-agents. It already sent `tools=[]` with no native tools, and a test now pins that too. - All four entry points default to `setting_sources=[]` and no `cwd`. - **The README has a migration note.** `[]` also drops the settings-file `env` (`ANTHROPIC_BASE_URL`, `CLAUDE_CODE_USE_BEDROCK` / `_VERTEX`, proxy variables), `apiKeyHelper` and `model`. A deployment that reaches Bedrock, Vertex or a gateway through `~/.claude/settings.json` needs `setting_sources=["user"]` or those variables in the process environment. ## Breaking Two changes in runtime behaviour: - A run has no Claude Code built-ins unless its config attaches them. - A run loads no host Claude Code settings unless the application passes `setting_sources`. This needs to land before the 1.0 freeze, because changing either default after 1.0 would be breaking. ## Tests - `TestBuiltinsSurviveAnEmptyToolList` is replaced. Its "omits the key" test asserted the behaviour this PR reverses. - `TestHostOptionsAndBuiltinToolSet` adds nine tests, each run on both the invoke and stream paths. - `TestToClaudeAgentsHostIsolation` adds four, including root and leaf for the default and for given values. - `TestConvenienceExportHostOptions` and `TestClaudeGraphHostOptions` add two each: the default, and given values reaching `query()` but not `config()` / `graph()`. - The API report is regenerated for the three new signatures. Checks: - `uv run pytest`: every claude-agents test passes. The one failure in the full suite is `opencode-agents` `test_a_fake_opencode_is_started_spoken_to_and_stopped`, a macOS `/private/var` vs `/var` temp-path mismatch already on `main`. - `make lint`, `make format-check` and `make typecheck` are clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > **Breaking:** Python Claude agent runs no longer inherit the Agent SDK’s implicit defaults for built-in tools or host Claude Code settings. > > Runs now pass **`tools=[]`** when the flag config attaches no `NativeTool` sentinel, which turns off Claude Code built-ins (`Read`, `Bash`, etc.) unless you explicitly enable them via `ClaudeRead`, `ClaudeBash`, and similar in `tool_handlers`. User-defined tools still use the in-process MCP server unchanged. > > **Host isolation:** `create_claude_agents_handler`, `claude_agents`, `claude_graph`, and `to_claude_agents` gain app-only **`cwd`** and **`setting_sources`** (via `_host_query_options`), forwarded on every `query()` (invoke, stream, and graph nodes). Default **`setting_sources=[]`** stops loading `~/.claude/settings.json`, project `CLAUDE.md`, plugins, hooks, and settings-file `env` (Bedrock/Vertex gateways may break until you opt in or set process env). Flag **`model.parameters` cannot override** these options. > > README/agents.md document migration; tests replace the old “omit `tools`” expectation and add coverage for host options and built-in tool sets across invoke/stream/graph paths. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit bcbde98. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

Every time I ask "how would I use the turnkey SDK here?", I hear about how model params aren't currently being passed to models. This PR fixes that.
Not entirely sure about the implementation though: we ended up definiting the list of params supported by each of the underlying SDKs, because I couldn't find a better export from those packages, and iterating on types felt harder than the explicit list. The downside is that if/when things change, we'd need to update the code. But presumably we already need to do that for our model parameter schemas in Gonfalon.
Model parameters set in an AI Config were silently ignored by most handlers. Every handler now passes them through to the provider.
extra_*,model_kwargs), remote MCP servers, the Claude Agent SDK's host-process settings (cli_path,env,cwd,add_dirs,permission_mode, sessions, hooks, callbacks), and keys that set data location or retention, server-side state, identity, runtime wiring or API shape, extra prompt content, or safety configuration (for examplestore,truncation,metadata,user,include,system,guardrails).invokeandstreamforward the same keys, and so do each handler and its native graph.thinking,reasoning,text, ...) is dropped when the config sets it to anything else.thinkingneeds a knowntype(andbudget_tokensforenabled), Anthropictool_choiceandcache_controlneed a knowntype, OpenAIreasoningkeepseffortandsummary, OpenAI Agentstool_choiceis a string only, and Anthropicoutput_configkeeps onlyeffort.outputFormat:output_config.formatand Claude Agentsoutput_formatare not forwarded, so a config cannot ask the provider to enforce a second schema.packages/and fails if one has no forwarded-keys list, so a new handler package must declare one.max_turns,top_p), the providers' own names and what the UI writes.Release
This is a breaking change. A setting in an AI Config's
model.parametersthat every handler used to ignore now reaches the provider, so a config that already sets, say,temperaturebehaves differently after upgrading. The API check also flags two removed handler-module names (OWNED_PARAMETERSin the Vercel handlers) and a new required argument on OpenCodesession_events. Before 1.0 this ships as a minor release.BREAKING CHANGE: handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect.
🤖 Generated with Claude Code
Note
Overview
Breaking: AI Config
model.parametersnow reach provider SDKs instead of being ignored. Handlers filter through per-provider allowlists (cross-SDK TESTING.md §1.12), sharedmodel_parameters()/select_forwarded_parameters()inlaunchdarkly-ai-server, and rebuild or drop nested shapes (thinking, Anthropictool_choice/output_config, LiteLLMreasoning, etc.).Security: Credentials, endpoints, request-injection bags (
extra_*,model_kwargs), Claude Agent host settings (cli_path,env,permission_mode, …), and similar keys are explicitly excluded; drift and probe tests lock the classification to each SDK’s fields.Coverage: Claude Agents/Messages, LangChain Agents/Messages (OpenAI/Anthropic/Bedrock), and LiteLLM Agents—including native graph paths—merge filtered params into
ClaudeAgentOptions, Messages API calls, LangChain constructors, andModelSettings. Package deps bump tolaunchdarkly-ai-server>=0.4.1.Reviewed by Cursor Bugbot for commit 718594e. Bugbot is set up for automated code reviews on this repo. Configure here.