## 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>
The test_empty_list_returns_empty_content test expected empty-content
AIMessages to be filtered out, but the fix now always emits assistant
messages so tool calls can reference them via parentMessageId.
Co-Authored-By: Claude Opus 4.6 (1M context) <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
Extract _convert_and_split() helper in both test files to eliminate
duplicated filtering logic across tests. Add test for tool_call
without id being silently skipped (crewai tc_id is None guard).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Verifies that langchain_messages_to_copilotkit always emits the
assistant message even when content is empty (OpenAI-style tool-call-only
responses), ensuring no orphaned parentMessageId references.
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 original test only tested the _merge_emit_state helper in isolation
using mocks. Replace with integration tests that simulate the actual
state-tracking loop from _stream_events, including:
- Sequential emits with different keys preserve all keys (core bug)
- Proof that the bug manifests without current_graph_state.update
- Three sequential emits accumulate correctly
- Same key emitted twice uses latest value
- Non-dict emit does not corrupt current_graph_state
- Initial state reference is not mutated
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.
Verifies langchain_messages_to_copilotkit correctly concatenates all text
parts from list-style content blocks (Anthropic models). Includes
regression tests for the exact scenario from #1748 (text + image blocks)
and tests for string lists, mixed content, empty lists, and edge cases.
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.
- Add TestAGUIStyleEventIntegration to test_agui_agent.py: proves that AG-UI-style
unprefixed events (manually_emit_message, manually_emit_tool_call, exit) flow
correctly through CopilotKit's _handle_single_event to the AG-UI base handler
without being suppressed or double-converted by the CopilotKit layer.
- Replace LangGraphAgent with LangGraphAGUIAgent in both example files
(canvas/gemini and v1/_legacy/saas-dynamic-dashboards) to close the concern
about removed APIs still being referenced externally.
- Update poetry.lock to reflect ag_ui_langgraph 0.0.32 and dependency updates.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Expands test suite for LangGraphAGUIAgent:
- TestLanggraphDefaultMergeState: verify no duplicates in copilotkit.actions
and that tool ordering is preserved in the merged result
- TestReasoningContentPreservation: verify unknown custom events pass through,
empty-string messages still emit the full TEXT_MESSAGE_* sequence, and
empty-args tool calls still emit the full TOOL_CALL_* sequence
Expands test suite for emit filtering:
- TestMissingOrNoneRawEvent: events with rawEvent=None should pass through
to super() without crashing
- TestNoneEmitMetadataValues: None and 0 values for emit metadata keys
should NOT filter (only exactly False suppresses events)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.