## Summary
- `copilotkit_interrupt` now handles string and dict resume values from
LangGraph 1.x's `interrupt()`
- Previously crashed with `AttributeError: 'str' object has no attribute
'content'` or `KeyError: -1`
- Type-checks response: str returned directly, dict JSON-serialized,
list uses existing `[-1].content` path
## Test plan
- [x] Red-green test: string resume value returns without crash
- [x] Red-green test: dict resume value returns JSON string
- [x] Test: list resume value still works (existing behavior)
- [x] Full test suite passes (15/15)
Closes#3096
## Summary
- `LangGraphAGUIAgent.langgraph_default_merge_state` now calls
`model_dump()` on Pydantic Context objects before storing in copilotkit
state
- Previously stored raw Pydantic objects, causing JSON serialization
failures downstream
- Handles mixed types: Pydantic objects get `model_dump()`, plain dicts
pass through unchanged
- Matches the existing pattern already used in `CopilotKitMiddleware`
## Test plan
- [x] Red-green test: AG-UI Context objects stored as plain dicts, not
Pydantic
- [x] Red-green test: mixed Pydantic + dict context items all
serializable
- [x] Full test suite passes (15/15)
Closes#3690
- Keep deletion of sdk-python/copilotkit/langgraph_agent.py (deprecated LangGraphAgent
removed in this PR; main's unrelated bug fixes are superseded by our removal)
- Resolve poetry.lock conflict by taking main's ag_ui_langgraph 0.0.33
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace custom { name, props } schema format with spec-aligned inline
catalog format (allOf + properties) so the LLM sees the same flat
structure it must produce — eliminates "props" nesting confusion.
- Restructure generation prompts: inline literal values are the default,
path binding is a narrow schema-driven exception for form inputs.
- Export InlineCatalogSchema type from a2ui-renderer.
LLMs sometimes use path bindings (e.g. {"path": "/chartData"}) on
component properties that only accept literal values, causing silent
render failures. The new guideline tells the LLM to check the schema's
anyOf type before using path bindings.
## Summary
- During frontend state merge in LangGraph agent, preserve keys that are
owned by the graph (not set by the frontend)
- Prevents graph-computed state from being overwritten when frontend
state is merged back
Closes#2893
---
*Split from #3847*
## Summary
- Add sanitization pass over LangGraph agent state to replace NaN and
Infinity with null before JSON serialization
- Prevents `ValueError: Out of range float values are not JSON
compliant` in Python SDK
Closes#1955
---
*Split from #3847*
Partially addresses #1748
When Anthropic models return multi-part content lists, only the first
element was used and the rest discarded. Now iterates all parts and
concatenates text blocks, preserving the full message content.
Split from #3838.
## Summary
Fixes#2158
Pydantic `BaseModel` instances in LangGraph agent state are not
serializable by `langchain_dumps`. This adds a recursive
`_serialize_state` helper that converts `BaseModel` instances to dicts
before serialization, preventing crashes when state contains Pydantic
models.
**Additional fixes (second commit):**
- Also applies `_serialize_state` to the `get_state()` code path, which
was missed in the original fix but has the same bug
- Fixes `filter_state_on_schema_keys` returning `None` implicitly when
schema keys are not set (the `except` branch returned `state` but the
non-matching `if` branch did not)
- Adds 17 tests covering `_serialize_state`, `_emit_state_sync_event`,
and `get_state` with Pydantic models
## Merge order note
This PR and #3851 both modify
`sdk-python/copilotkit/langgraph_agent.py`. Both add a helper function
at module level and call it from `_emit_state_sync_event` and
`get_state`. Whichever merges second will need a trivial rebase. No
semantic conflict — the fixes are complementary (this one handles
Pydantic models, #3851 handles NaN/Infinity).
## Test plan
- [x] 17 unit tests covering both code paths
- [x] Red-green verified: `get_state` tests fail without fix, pass with
it
- [x] Existing test suite (test_emit_filtering) still passes
- [x] Verify LangGraph agent with Pydantic BaseModel state serializes
correctly
- [x] Verify non-Pydantic state is unaffected
Use defensive .get() for tool_call id/name/arguments to prevent KeyError
on malformed input. Standardize content null-handling to `is not None`
(matching langgraph.py) instead of `or ""` which silently coerces falsy values.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add test suite for crewai_flow_messages_to_copilotkit covering the same
parentMessageId orphan scenarios as the langgraph tests: function-style and
direct-style tool calls with empty/missing content, orphan detection, and
plain messages. Also fix pre-existing KeyError in the name extraction loop
which only handled function-style tool calls.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Apply the same parentMessageId orphan fix to crewai_flow_messages_to_copilotkit
where the elif chain meant tool-call messages never emitted the parent assistant
message. Also refine langgraph fix: use explicit None check instead of truthiness,
add inline comments explaining the invariant, remove unused pytest import,
replace fragile commit hash in docstring, and add test for list-type content.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reverts the `if content:` guard from c5ec0f8512 that skipped emitting
the assistant message when content was empty. OpenAI models commonly
send empty content for tool-call-only responses, but the assistant
message is structurally required as the anchor for tool call grouping
via parentMessageId. Without it, tool calls become orphaned and the
frontend can't reconstruct tool call rendering on thread reconnect.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The _merge_emit_state call merges emitted state with current_graph_state
into manually_emitted_state, but the `continue` statement skips the
later current_graph_state.update(updated_state). Without this update,
the next manual emit merges against stale current_graph_state and loses
keys from the previous emit.
Also guards the update call against non-dict values from _merge_emit_state
to prevent TypeError on edge-case non-dict emits.
The original fix only covered _emit_state_sync_event but missed the
get_state() code path, which also returns state containing Pydantic
BaseModel instances to callers that will JSON-serialize downstream.
Also fixes filter_state_on_schema_keys returning None implicitly when
schema keys are not set (the except branch returned state but the
non-matching if branch did not).
Adds 17 tests covering _serialize_state, _emit_state_sync_event, and
get_state with Pydantic models (nested, lists, plain dicts, empty).
The _sanitize_for_json function was applied to state sync events and
get_state responses but not to the raw event stream yielded at line 507.
Events with NaN/Infinity float values in their data payloads would
produce invalid JSON (literal NaN) that downstream parsers cannot handle.
When Anthropic models return multi-part content lists, only the first
element was used and the rest discarded. Now iterates all parts and
concatenates text blocks, preserving the full message content.
LangGraph 1.x can return string or dict resume values from interrupt(),
not just lists. The code now type-checks the response: strings are
returned directly, dicts are JSON-serialized, and lists use the
existing [-1].content path.
Sequential emit_state calls with different keys now preserve all keys
in the snapshot. Previously, each call would replace the entire
manually_emitted_state, losing keys from earlier calls.
No longer needed now that LangGraphAgent is gone — the only caller was
the deprecated agent class. Also removed from langchain.py re-exports.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ran confirmed LangGraphAGUIAgent is the only relevant path going forward.
Removes LangGraphAgent, _StreamingStateExtractor, CopilotKitConfig, and
langgraph_default_merge_state from sdk-python (the AG-UI base class
provides this now). Also removes the use_function_call=True code path
from copilotkit_messages_to_langchain (no callers, function_call format
is obsolete).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add copilotkit.a2ui module with operation builders and prompt helpers,
fix SystemMessage ID reuse in before_agent to prevent LangGraph duplication,
add Pydantic model_dump serialization for app_context.
- Python SDK: 18 new tests for LangGraphAGUIAgent (custom event handling,
emit filtering, state merging, copilotkit namespace)
- TypeScript SDK: 25 new tests for copilotkitCustomizeConfig and
convertActionsToDynamicStructuredTools
- TypeScript Runtime: 27 new tests for event-source helpers
(shouldEmitToolCall, getCurrentMessageId, getCurrentContent, etc.)
- TypeScript Runtime: expanded dispatch-event-filtering tests with
custom event dispatch (manually_emit_message/tool_call/state, exit)
and langGraphDefaultMergeState tests
- Dead code annotations: LangGraphAgent class and use_function_call=True
branch annotated with TODO(ran-review) for Ran to verify
https://claude.ai/code/session_01BPMn7zhadhapfyyD8kYeAH
The re module was used in _fix_messages_for_bedrock (re.compile) but
never imported, causing NameError at runtime when the middleware is
invoked. Bumps version to 0.1.83.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add _fix_messages_for_bedrock() to handle three issues caused by
after_agent restoring frontend tool_calls to the checkpoint:
1. Strip unanswered tool_calls without matching ToolMessages (Bedrock
rejects toolUse without a corresponding toolResult)
2. Sync msg.content tool_use blocks with msg.tool_calls
3. Fix tool_use/tool_call blocks with string input/args (must be dict)
Avoid emitting spurious empty assistant messages for tool-call-only
AIMessages (common with OpenAI models where content is empty string).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
langchain_messages_to_copilotkit previously skipped creating the assistant
message when an AIMessage had tool_calls, causing missing context in chat
history on reconnect. Also adds ActionExecutionMessage type guard in
copilotkit_messages_to_langchain to prevent KeyError on non-tool messages
with parentMessageId.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes#2066
## Problem
`copilotkit_customize_config(config, emit_messages=False)` does not work -
TEXT_MESSAGE_* events are still emitted despite the configuration.
## Root Causes
1. **Metadata Reading Bug**: In `langgraph_agui_agent.py`, the code uses
`getattr(raw_event, 'metadata', {})` to read metadata, but `raw_event`
is a dict, not an object. This should use `.get()` instead.
2. **Encoder Crash Bug**: When the filtering logic works (after fixing bug #1),
it returns `""` (empty string) to skip dispatching events. However, this
empty string gets yielded to the event encoder which expects event objects
with `model_dump_json()` method, causing an `AttributeError`.
## Fixes
1. Changed metadata reading to handle both dict and object cases.
2. Changed return value from `""` to `None` for filtered events.
3. Added `run()` method override to filter out `None` values before yielding
to encoder.
Adds missing dict_repr() method to LangGraphAGUIAgent class to fix
AttributeError when SDK tries to serialize agent info.
The method was present in the parent LangGraphAgent class but not
overridden in LangGraphAGUIAgent, causing failures when calling
sdk.info() with AGUI agents in FastAPI integrations.
Fixes#2667