Commit Graph

263 Commits

Author SHA1 Message Date
copilotkit-qa-bot[bot] 4c5402a0aa chore(sdk-python): release 0.1.96 2026-08-26 15:12:51 -07:00
github-actions[bot] f2ce7fa52d style: auto-fix formatting 2026-08-19 15:06:19 +00:00
Benjamin Taylor d37fe9cbd2 test(sdk-python): revalidate the poetry lock and cover the partialjson parse path
Widening the partialjson constraint in #6123 invalidated poetry.lock's
content-hash, so a bare `poetry install` in sdk-python fails with
"pyproject.toml changed significantly since poetry.lock was last generated".
CI never saw it because test_unit-python-sdk.yml runs `poetry lock` first, and
publishing is unaffected because `poetry build` ignores the lock — but local
dev is blocked until someone relocks. Refreshed with Poetry 2.1.3 (the lock's
own generator) so the diff stays limited to the hash, and moved partialjson to
1.1.0 so CI exercises the version a fresh install now resolves. ag-ui-langgraph
is deliberately left at 0.0.42: a from-scratch resolve pulls 0.0.43, which
fails four intercepted-tool-call tests on a missing `emit_raw_events`.

The parse path had no coverage at all — disabling JSONParser.parse outright
left all 225 tests passing, because every partialjson failure mode degrades to
"no predicted state was emitted" behind the bare `except` in predict_state().
These tests pin the guarantees the `>=0.0.8,<2.0.0` range must keep, and are
version-agnostic about intermediate frames, which legitimately differ (1.1.0
preserves trailing whitespace mid-string where 0.0.8 dropped it).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 10:04:11 -05:00
Ben Taylor e0a2f00cca fix: relax partialjson version constraint in sdk-python (#6123)
## What does this PR do?

Relaxes the over-strict `partialjson` version constraint in
`sdk-python/pyproject.toml` so the CopilotKit Python SDK can consume
newer compatible `partialjson` releases.

**Change:** `sdk-python/pyproject.toml`
- Before: `partialjson = "^0.0.8"`
- After:  `partialjson = ">=0.0.8,<2.0.0"`

## Why

Poetry interprets `^0.0.8` on a `0.0.x` version as `>=0.0.8,<0.0.9`,
which effectively pins the SDK to **exactly** `0.0.8`. This blocks users
from receiving newer, compatible `partialjson` releases (currently
published: `0.0.9`, `0.1.0`, `1.0.0`, `1.1.0`). Relaxing the constraint
lets the SDK pick up fixes and features while still treating the next
major (`2.0.0`) as the breaking-change boundary.

I verified the API the SDK actually uses — `from partialjson.json_parser
import JSONParser` and `JSONParser().parse(...)` — is unchanged across
all published versions up to `1.1.0` (the `JSONParser.__init__` /
`parse` signatures are backward compatible, and the SDK call passes no
arguments). There is no breaking-API risk within the chosen range.

## Test plan

- [x] Validated `sdk-python/pyproject.toml` is well-formed:
`python3 -c "import tomllib,pathlib;
tomllib.load(pathlib.Path('sdk-python/pyproject.toml').open('rb'))"`
- [x] Grepped `sdk-python` for `partialjson` usages — only
`copilotkit/runloop.py` imports/uses it; API is stable across the
allowed range.
- [ ] `poetry lock` / `poetry install` in `sdk-python` resolves a
`partialjson` version within `>=0.0.8,<2.0.0`.

## Changed files

- `sdk-python/pyproject.toml` — single line changed (dependency range
only). No source, example, or lockfile changes.

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] No documentation change required (dependency range only)
- [x] "Allow edits by maintainers" is checked

Relates to #4131
2026-08-18 09:56:22 -05:00
Ben Taylor e6d3629445 fix(sdk-python): emit tool call events for middleware-intercepted SDK Actions (#5372)
## Summary

Frontend SDK Actions intercepted by `CopilotKitMiddleware` could
disappear before the client received a tool-call lifecycle, leaving the
handler uncalled and the conversation without a tool result or follow-up
answer. The bridge now consumes the middleware's authoritative
intercepted-call state and publishes each call through the AG-UI adapter
at most once. The focused intercepted file has 8 tests and the manual
AG-UI file has 30 tests; the combined run is 38 tests, with the
supported-version matrix remaining CI-owned.

## Root cause

`CopilotKitMiddleware.after_model()` removes frontend calls before
LangChain's backend tool node and records those exact calls for
final-message restoration. The previous adapter fallback ignored that
state and reconstructed frontend ownership from `on_chat_model_end` plus
the run's action catalog. That duplicated classification, failed when an
action carried `function: None`, and never ran on Python 3.10 because
the async LangChain path emitted no model callbacks there.

## Changes

- `sdk-python/copilotkit/langgraph_agui_agent.py`
- publish middleware-classified calls from the existing chain-state
event path
- reuse one lifecycle materializer and the parent adapter's per-run ID
ledger
- remove raw model-end/action-schema reconstruction and the duplicate
logger assignment
- `sdk-python/tests/test_intercepted_tool_call_events.py`
- exercise the real `create_agent(..., tools=[])` middleware and AG-UI
path in streaming and non-streaming modes
- cover exactly-once identity, Python 3.10 restoration, multiple IDs,
metadata opt-out, backend negative space, and malformed action/state
shapes

## What is not changed

- backend tools still use LangChain's normal tool node
- `after_agent` still restores intercepted calls into the final
assistant snapshot
- tool-call emission metadata still controls streamed lifecycle
visibility
- explicit `copilotkit_emit_tool_call()` calls keep their existing
caller-controlled semantics

## Related PRs and Issues

Closes #4342.

## Test plan

- [x] Focused intercepted SDK Action regression file, 8 passed on Python
3.12. Covers the real `create_agent(..., tools=[])` path, streaming,
restoration, mixed ownership, chain metadata suppression, malformed
state, and action-shape isolation.
- [x] Existing manual tool-call materialization file, 30 passed on
Python 3.12.
- [x] Combined focused selection, 38 passed on Python 3.12, counted once
rather than adding a second 38-test total.
- [x] Ruff check and format check on both changed files, both passed.
- [ ] Python SDK CI matrix green on Python 3.10, 3.11, 3.12, 3.13, and
3.14
2026-08-17 12:58:37 -05:00
Ben Taylor cd0d38a073 fix(sdk-python): restore frontend tool history faithfully in LangGraph middleware (#5308)
## Description

Fixes the LangGraph middleware "lying" to the agent about frontend tool
execution (#4759). When a model turn calls a `useFrontendTool` handler,
the middleware strips the FE tool calls so the backend `ToolNode` only
runs calls it actually made, then rehydrates them afterward. Previously
the rehydration could leave the restored AI message and its tool history
inconsistent, so providers that require every tool call to have a
matching result would break, and the agent saw an incorrect picture of
what executed.

## Changes

- `_restore_intercepted_tool_call_history` rebuilds the intercepted FE
tool calls with synthetic `ToolMessage` results only for the restored
model history (so providers that require every `tool_call` to have a
result stay valid), and preserves the original AI message's `name`,
`additional_kwargs`, and `response_metadata` on restore.
- Tracks `original_tool_calls` in the CopilotKit private state so the
original call set is restored exactly.

## Testing

`uv run pytest tests/test_copilotkit_lg_middleware.py` — 68 passed,
including new cases asserting the restored AI message keeps its
tool-call ids, name, `additional_kwargs`, and `response_metadata`, and
that tool results line up with their calls.

Closes #4759

AI was used for assistance.
2026-08-17 12:54:26 -05:00
Cursor Agent 49bf4535a9 test(sdk-python): add checkpoint roundtrip coverage for orphan-handoff contract
Adds tests that exercise the full checkpoint roundtrip path:
- test_checkpoint_roundtrip_placeholder_replaced_by_real_result: Verifies
  _fix_messages_for_bedrock correctly replaces patch_orphan_tool_calls
  placeholder with real FE result
- test_checkpoint_roundtrip_multiple_fe_calls_with_placeholders: Multiple
  FE calls with placeholders all correctly resolved
- test_after_agent_leaves_fe_call_orphaned_for_checkpoint: Confirms
  after_agent does not persist synthetic ToolMessages, leaving FE calls
  as orphans for the real result to fill

These tests address reviewer feedback requesting coverage for the checkpoint
persist → add_messages merge → patch_orphan_tool_calls → real FE result path.

Co-authored-by: Matt Van Horn <mvanhorn@users.noreply.github.com>
2026-08-16 18:12:11 +00:00
mvanhorn 467d1c5337 fix: implemented the remaining review fix:
Implemented the remaining review fix:
- Sync and async model restoration now operate on copied message lists, preventing synthetic results from entering checkpoint state.
- Removed the obsolete synthesis flag and ineffective result-ID filter.
- `after_agent` restores the frontend call as an orphan.
- Added sync/async regression coverage for request-scoped restoration.
[Middleware changes](/Users/mvanhorn/.osc/workspaces/CopilotKit-CopilotKit-pr5308/sdk-python/copilotkit/copilotkit_lg_middleware.py:450)
[Regression tests](/Users/mvanhorn/.osc/workspaces/CopilotKit-CopilotKit-pr5308/sdk-python/tests/test_copilotkit_lg_middleware.py:618)
```text
2026-08-13 09:39:14 -07:00
Rod Boev d9667bc5f1 test(sdk-python): cover intercepted event delivery boundaries 2026-08-12 12:39:39 -04:00
Rod Boev ca2818f6dc fix(sdk-python): deliver intercepted action events through the adapter 2026-08-12 12:22:12 -04:00
Rod Boev 9f77123df4 fix(sdk-python): route intercepted action events through adapter filtering 2026-08-12 12:10:16 -04:00
Benjamin Taylor 07855f3fc9 chore: release python sdk 0.1.95
Ships the four sdk-python fixes merged since 0.1.94 (2026-06-04), none of
which are in any published artifact — the newest upload of any kind is the
0.1.95a4 prerelease from 2026-06-19, which predates all of them.

Closes #6231

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 11:58:08 -05:00
Rod Boev 26bcbc8043 fix(sdk-python): keep intercepted tool call emission on the AG-UI adapter (#4342) 2026-07-27 07:33:18 -04:00
Rod Boev 1340fd806d fix(sdk-python): suppress duplicate streamed tool lifecycle events (#4342) 2026-07-27 07:28:19 -04:00
Rod Boev 3338c7f1e4 test(sdk-python): prove intercepted sdk action event emission (#4342) 2026-07-27 07:24:12 -04:00
Rod Boev 9fcaf34225 fix(sdk-python): emit tool call events for middleware-intercepted SDK Actions 2026-07-27 07:24:12 -04:00
godququ5-code bf5b1a0be4 fix: relax partialjson version constraint in sdk-python
Poetry's ^0.0.8 on a 0.0.x version resolves to >=0.0.8,<0.0.9,
pinning users to exactly 0.0.8 and blocking newer compatible
releases (0.0.9, 0.1.0, 1.0.0, 1.1.0). Relax to >=0.0.8,<2.0.0
so the SDK can pick up fixes/features while still gating on the
next major (2.0.0) as the breaking boundary.

The only SDK usage is partialjson.json_parser.JSONParser().parse(),
which is unchanged across all published versions up to 1.1.0.

Relates to #4131
2026-07-27 10:10:50 +03:00
Rod Boev a76d59ae0b fix(sdk-python): capture subgraph context from run input (#3886) 2026-07-26 16:42:46 -04:00
Matt Van Horn 9f847a2e47 fix(sdk-python): keep the frontend tool call orphaned in after_agent
Addresses both review concerns on the after_agent half.

after_agent no longer synthesizes a forwarded_to_frontend ToolMessage. That
synthetic result did not match _INTERRUPTED_PAT, so _fix_messages_for_bedrock
and langchain's patch_orphan_tool_calls treated it as a real second result and
skipped the reposition path, which on the Bedrock path could leave the real
frontend result non-adjacent and then stripped as unanswered. The restore helper
now takes synthesize_frontend_tool_results, which after_agent sets to False so
the call stays an orphan for the real result to fill, and any synthetic result
already in history is dropped by id.

The sync wrap_model_call now also runs _fix_messages_for_bedrock, matching
awrap_model_call, so the messages it injects go through the same dedup instead
of risking a duplicate tool_call id on the sync path.
2026-07-25 13:32:24 -07:00
Rod Boev fee7ec237c fix(sdk-python): bridge copilotkit context into LangGraph subgraphs 2026-07-24 16:19:13 -04:00
Rod Boev bb32138e15 fix(sdk-python): read copilotkit context from config when state is empty (subgraph support) 2026-07-24 16:13:05 -04:00
godququ5-code 44c43e4770 fix(python-sdk): fold app context into system prompt 2026-07-03 01:06:09 +03:00
Ran Shem Tov 186b007091 chore(sdk-python): require ag-ui-langgraph >=0.0.42
0.0.42 ships the single-arg A2UIToolParams API the middleware's a2ui_params
override relies on; pulls ag-ui-a2ui-toolkit 0.0.4 transitively. uv.lock is
unchanged — it locks only the dev/test toolchain, not the runtime deps.
2026-06-19 17:51:02 +02:00
Ran Shem Tov f13865a21f fix(sdk-python): declare ag-ui state channel and add a2ui_params host override
The middleware reads state["ag-ui"]["inject_a2ui_tool"] and
state["ag-ui"]["a2ui_schema"], but "ag-ui" was never declared on the
StateSchema, so create_agent's StateGraph dropped both keys before the
middleware ran — the generate_a2ui tool never injected and the catalog was
lost. Declare the "ag-ui" channel via StateSchema.__annotations__ so the flag
and catalog survive.

Also add an a2ui_params kwarg so a host can steer the auto-injected
generate_a2ui subagent (design/generation guidelines, catalog id, ...). The
middleware still injects the bound model and folds the registered catalog in,
but host-set values win.
2026-06-19 17:50:26 +02:00
Ran Shem Tov 6e9240accc chore(deps): revert @ag-ui/core,client to 0.0.53 (decouple from langgraph 0.0.41 bump)
@ag-ui/client 0.0.56 changed runHttpRequest to a thunk signature, breaking
@copilotkit/core's ProxiedCopilotRuntimeAgent. OSS-248 only needs
@ag-ui/langgraph 0.0.41; keep that, revert the unrelated core/client/protocol
'latest' bump. Adopting client 0.0.56 is a separate migration.
2026-06-09 16:59:50 +02:00
Ran Shem Tov cf7bcd67e5 chore(deps): use latest @ag-ui packages and langgraph integration
- @ag-ui/core, @ag-ui/client: 0.0.53 -> 0.0.56 across all packages
  (react-core, core, react-native, vue, runtime, angular, shared,
  web-inspector, agentcore-runner, demo-agents, sqlite-runner) + root
  pnpm override; shared's @ag-ui/core range floor -> >=0.0.56.
- @ag-ui/langgraph (runtime): 0.0.39 -> 0.0.40.
- ag-ui-protocol (sdk-python): >=0.1.15 -> >=0.1.19.
- .npmrc: exclude first-party @ag-ui/{core,client,encoder,proto} from the
  minimum-release-age gate so the freshly-published 0.0.56 set installs.
- Regenerate pnpm-lock.yaml + sdk-python/poetry.lock.
2026-06-09 12:14:12 +02:00
Ran Shem Tov 5bb32b9dd5 feat(a2ui): use new ag-ui single-arg A2UIToolParams API (OSS-248)
ag-ui-langgraph's tool factory now takes one A2UIToolParams object (model
inside) and folds composition_guide into the guidelines bag.

- sdk-js middleware: getA2UITools(params) + type A2UIToolParams import;
  pin @ag-ui/langgraph 0.0.40 (the JS release carrying the new API).
- sdk-python middleware: get_a2ui_tools(params) + guarded A2UIToolParams
  import; pin ag-ui-langgraph >=0.0.41 (the py release with the new API).
- Update the a2ui injection test skip-reason wording.
2026-06-09 12:13:51 +02:00
Ran Shem Tov c85c140f05 feat: update all dependencies to use latest a2ui implementation features 2026-06-08 12:09:20 +02:00
Matt Van Horn 850c74e4b8 fix(sdk-python): keep LangGraph middleware honest about frontend tool execution
The middleware strips FE tool calls so the backend ToolNode only executes
its own calls, then restores them. Restoration now rebuilds the intercepted
calls with synthetic ToolMessage results scoped to the restored model
history (providers that require a result per tool_call stay valid) and
preserves the original AI message's name, additional_kwargs, and
response_metadata. Tracks original_tool_calls in private state so the
original call set is restored exactly.

Closes #4759

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 09:02:34 -07:00
Ran Shem Tov 2b5d2e0113 chore: release python sdk 0.1.94 2026-06-04 19:39:44 +02:00
Ran Shem Tov 02ae7895e9 chore: restore copilotkit py version 2026-06-04 18:37:52 +02:00
Ran Shem Tov 3ca0f194b8 feat(sdk): gate auto-A2UI injection on injectA2UITool (opt-in)
The A2UI middleware (@ag-ui/a2ui-middleware) forwards injectA2UITool on
forwardedProps; ag-ui-langgraph surfaces it into agent state at
state["ag-ui"]["inject_a2ui_tool"]. The CopilotKit LangGraph middleware
(py + js) now reads that flag and only injects generate_a2ui when it is
truthy (opt-in), drops the runtime's render_a2ui so the model sees one
A2UI tool, and skips if the agent already defines generate_a2ui. The
catalog only binds surfaces; it is no longer the gate.

Reverts the earlier runtime-forward + context-channel approach.

Deps: ag-ui-langgraph>=0.0.38 (py), @ag-ui/langgraph 0.0.37 (sdk-js),
@ag-ui/a2ui-middleware 0.0.6 + @ag-ui/langgraph 0.0.37 (runtime);
.npmrc min-release-age exclude for @ag-ui/a2ui-middleware. Showcase
langgraph pins bumped to copilotkit==0.1.94a3 / sdk-js 1.59.3-alpha.3 /
@ag-ui/langgraph 0.0.37.
2026-06-04 18:37:03 +02:00
Ran Shem Tov 1bd944bd43 feat(sdk): source A2UI catalog wherever the frontend registered it
The auto-injected generate_a2ui tool now resolves the A2UI catalog from
both delivery paths: the CopilotKit runtime proxy (a copilotkit.context
entry) and the AG-UI native endpoint (state["ag-ui"].a2ui_schema). The
registered catalog id is extracted and bound to generated surfaces so
BYOC custom catalogs render their own components instead of the basic
catalog. Covers @copilotkit/sdk-js and the copilotkit Python SDK.
2026-06-04 18:36:12 +02:00
Ran Shem Tov ce4e4142b2 feat(sdk): auto-inject A2UI tool in CopilotKitMiddleware
Prebuilt agents get dynamic A2UI with no extra wiring — adding the
middleware is enough. When the frontend registers an A2UI catalog
(surfaced by the runtime into state["ag-ui"].a2ui_schema), the
middleware infers the agent's own model, advertises the generate_a2ui
tool in the model-call hook, and executes it in the tool-call hook.
No catalog → the tool is never advertised.

Covers both @copilotkit/sdk-js and the copilotkit Python SDK. Bumps
the A2UI tool-factory dependency to where get_a2ui_tools ships
(@ag-ui/langgraph 0.0.35, ag-ui-langgraph >=0.0.37).
2026-06-04 18:36:12 +02:00
Jordan Ritter ea5ea897c1 style(sdk-python): apply ruff format to test_agui_agent.py
main's static/quality format gate was red because this file was not
ruff-formatted (introduced by an earlier unrelated python commit).
Apply ruff 0.15.13 format to restore the gate.
2026-05-29 12:54:47 -07:00
Jordan Ritter e77856e6b7 chore(sdk-python): release v0.1.93 2026-05-28 21:15:00 -07:00
github-actions[bot] b2f10f6753 style: auto-fix formatting 2026-05-29 03:58:54 +00:00
Jordan Ritter 59eff59b7f fix(sdk-python): strip copilotkit_forwarded_headers from LLM prompt (App Context + expose_state)
langgraph-api auto-copies the entire config.configurable dict into runtime.context. That dict
carries the CopilotKit-internal transport key `copilotkit_forwarded_headers`, populated solely
to drive the httpx header-forwarding hook (it is not user-visible state). The middleware's
before_agent step then rendered runtime.context into the LLM prompt as an "App Context" system
message, and the expose_state path could surface the same key via the state note — either path
leaks transport headers into the prompt body and, under strict fixture matching (D6/aimock),
changes the request payload and breaks the match.

Fix: hard-exclude `copilotkit_forwarded_headers` from both render paths. The App Context
renderer strips the reserved key before serialization, and the expose_state allowlist applies
the same exclusion so an explicit allow cannot reintroduce the leak. The httpx hook is
unchanged, so the key still reaches aimock as an HTTP header — pure conveyance, no body
pollution.

Adds red-green unit coverage for both paths (App Context strip, expose_state default + allowlist).
2026-05-28 20:57:37 -07:00
Jordan Ritter 3110cf9b00 docs(sdk-python): document header_propagation module purpose and scope
Adds a module-level docstring at the top of header_propagation.py that explains in plain
English what the module does (forwards CopilotKit request-context x-* headers onto
outbound LLM/provider HTTP calls so downstream services like the aimock test server and
proxies can correlate the outbound call with the original inbound request), the precise
scope (only headers the application itself set via set_forwarded_headers; no request
bodies, cookies, user data, credentials, or telemetry), and the mechanics (walks the
._client chain to find the httpx client and attaches an async hook for AsyncClient or a
sync hook for Client). Also softens a few comments and docstrings whose wording could
read as surveillance language ("inject" / "installing an async hook ... silently dropped")
to neutral engineering phrasing ("attach" / "the forwarded headers would not be attached
to the outbound request") without losing any technical meaning or warnings. No executable
logic, signatures, conditionals, or string literals in code paths were changed; the diff
is comments and docstrings only.

In response to review feedback on PR #5088.
2026-05-28 14:39:07 -07:00
Jordan Ritter 6b6e08fb8a chore(sdk-python): release v0.1.92 2026-05-28 14:39:07 -07:00
Jordan Ritter 3e6fc5ca32 fix(sdk-python): walk _client chain and install async hook on AsyncClient
Forwarded headers (e.g. X-AIMock-Context) were dropped before reaching the
wire in two cases. First, install_httpx_hook only attached to the immediate
client, missing an httpx client nested one or more ._client hops deep; it now
walks the ._client chain (bounded) to find the object that carries
event_hooks. Second, a sync def hook installed on an httpx.AsyncClient was
invoked as a coroutine and never awaited, silently dropping headers; the hook
is now async def for AsyncClient and sync def for Client. Async detection
prefers isinstance against the real httpx classes and falls back to an EXACT
"AsyncClient" MRO class-name match (not startswith("Async"), which would
misclassify a sync client whose MRO includes an Async*-named base).
2026-05-28 14:39:07 -07:00
Ran Shemtov 9158241c10 Merge branch 'main' into wt-4995-29229-6154 2026-05-27 17:39:28 +02:00
Maxim dff634d462 Merge branch 'main' into feature/emit-tool-call-optional-id 2026-05-27 15:12:10 +02:00
ryker 8e5901133c fix(python): serialize AGUI agent metadata safely 2026-05-27 14:10:12 +08:00
Jordan Ritter 5f679863c6 chore(sdk-python): release v0.1.91
Cuts copilotkit 0.1.91 with forwarded-header extraction landed in
#4984 (_extract_forwarded_headers_from_config + lowercase-at-insertion
precedence + exception-safe ContextVar clearing).
2026-05-26 08:58:31 -07:00
Jordan Ritter e71ded2591 test(sdk-python): coverage for forwarded-header extraction and precedence
Add integration and edge-case coverage for the new
_extract_forwarded_headers_from_config flow: wrapper-dict and raw x-*
sources, context > configurable precedence, mixed-case key normalization,
RuntimeError early-return clears stale ContextVar, exception path clears
stale ContextVar, None and empty-config fallbacks, and a sync/async
parity check that both call paths run the extraction.
2026-05-22 14:36:43 -07:00
Jordan Ritter 7b2f1e9be6 fix(sdk-python): forwarded-header extraction with precedence, normalization, and exception-safe clearing
Extract incoming x-* headers from LangGraph's runtime config and republish
them via the forwarded-headers ContextVar so the httpx hook can attach
them to outbound provider requests. Apply documented precedence
(context > configurable, wrapper-dict > raw x-*) by processing sources
in order with first-write-wins and lowercasing keys at insertion so
mixed-case headers do not silently overwrite each other downstream.

Always clear the ContextVar on early exits so stale headers from a prior
request never leak into the next: explicit set_forwarded_headers({}) on
the RuntimeError no-active-runnable path and on the generic exception
fallback. The happy path already overwrites the ContextVar
unconditionally, even with an empty dict.

The change also installs the httpx event hook once per chat-model client
via a module-level set keyed by id(client), so models reused across
requests pick up fresh per-request headers without re-hooking.
2026-05-22 14:36:43 -07:00
Maxim cc69fd04ef fix(sdk): add pytest-asyncio dev dependency for async test support
The new test_emit_tool_call_optional_id.py uses async test methods
decorated with @pytest.mark.asyncio, but pytest-asyncio was missing
from dev dependencies — causing all 11 async tests to fail in CI
across all Python versions (3.10–3.14).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-21 23:36:29 +02:00
Ran Shem Tov 6f3080ce05 chore: release sdk python 0.1.90 2026-05-21 10:24:13 -05:00
Jordan Ritter 9107e00a9a fix(runtime): close header propagation gap for LangGraphAgent
The if (agent.headers) guard in configureAgentForRequest silently
skipped header forwarding when agent.headers was undefined (the
default for LangGraphAgent). This meant x-aimock-context, x-test-id,
and other x-* headers were never forwarded to agent backends.

Also wires install_httpx_hook in the Python SDK middleware so
forwarded headers propagate to outgoing LLM API calls.

Closes the gap documented in PR #4773 spec as out-of-scope.
2026-05-21 01:39:28 -07:00