274 Commits

Author SHA1 Message Date
Alem Tuzlak ae1219455a Merge branch 'main' into fix/crewai-import-compat-3268 2026-04-22 12:58:24 +02:00
Alem Tuzlak 2a800f99a7 Merge branch 'main' into fix/issue-3156 2026-04-22 12:57:58 +02:00
Alem Tuzlak b8349d2067 fix(sdk-python): copilotkit_interrupt handles non-list resume values (#3096) (#3784)
## 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
2026-04-22 12:33:27 +02:00
Alem Tuzlak d6ff5772ed fix(sdk-python): LangGraphAGUIAgent serializes Context objects to dicts (#3690) (#3787)
## 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
2026-04-22 12:33:14 +02:00
Alem Tuzlak 95fe4889b6 Merge remote-tracking branch 'origin/main' into fix/crewai-import-compat-3268
# Conflicts:
#	sdk-python/pyproject.toml
2026-04-22 12:26:49 +02:00
Alem Tuzlak 51b0513eab Merge remote-tracking branch 'origin/main' into fix/issue-3156
# Conflicts:
#	sdk-python/pyproject.toml
2026-04-22 12:13:45 +02:00
Martha Schumann 0e77affe14 Merge main: remove langgraph_agent.py (deprecated), resolve lock conflict
- 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>
2026-04-20 12:10:19 -07:00
Maxim 45b28dd3b3 fix(sdk-python): update test to match always-emit assistant message behavior
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>
2026-04-17 15:02:31 +02:00
Maxim 85dbf25c74 Merge branch 'main' into fix/always-emit-assistant-message 2026-04-17 14:42:22 +02:00
Ran Shem Tov 6986a1c120 fix: use latest agui langgraph packages 2026-04-15 18:11:17 +02:00
Markus Ecker 0f02aae198 fix: align A2UI schema format with v0.9 spec and improve path binding prompts
- 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.
2026-04-15 12:28:13 +02:00
Markus Ecker abd0d5c11c fix: add path binding schema rule to A2UI generation prompts
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.
2026-04-15 11:51:05 +02:00
Jordan Ritter 19798bbbd3 fix(sdk-python): support both old and new crewai import paths (#3268) 2026-04-14 16:24:09 -07:00
Jordan Ritter e01d510a7a fix(sdk-python): serialize Pydantic Context and Tool objects to dicts in LangGraphAGUIAgent (#3690) 2026-04-14 16:23:52 -07:00
Jordan Ritter bc4a173751 fix: preserve graph-owned state keys during frontend state merge (#3852)
## 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*
2026-04-14 14:06:10 -07:00
Jordan Ritter de78dfdb68 fix: sanitize NaN/Infinity values in LangGraph state before JSON serialization (#3851)
## 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*
2026-04-14 14:06:07 -07:00
Jordan Ritter fe365d3f7f fix: extract all text parts when AIMessage content is a list (#3844)
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.
2026-04-14 14:06:04 -07:00
Jordan Ritter 76b1d73bb8 fix: convert Pydantic BaseModel instances to dicts before serialization (#2158) (#3816)
## 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
2026-04-14 14:06:00 -07:00
Maxim b82e13d134 test(sdk-python): extract shared helpers and add missing edge case test
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>
2026-04-14 20:18:11 +02:00
Maxim 2f84ea9ac0 fix(sdk-python): harden crewai tool_call dict access and normalize content handling
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>
2026-04-14 19:55:38 +02:00
Maxim 25cc48be27 test(sdk-python): add tests for crewai assistant message emission
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>
2026-04-14 19:45:23 +02:00
Maxim 54eed60971 fix(sdk-python): emit assistant message in crewai + review improvements
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>
2026-04-14 19:34:39 +02:00
Maxim d99afc3759 test(sdk-python): add tests for assistant message emission with tool calls
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>
2026-04-14 17:40:33 +02:00
Maxim 640b1ce8ed fix(sdk-python): always emit assistant message for AIMessages with tool_calls
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>
2026-04-14 17:31:45 +02:00
Jordan Ritter 6d9efa00a9 test: add comprehensive tests for _sanitize_for_json
27 tests covering:
- Passthrough of valid types (floats, ints, strings, booleans, None)
- NaN and +/-Infinity replacement with None
- Recursive sanitization through dicts, lists, and tuples
- Real-world event structures (on_chain_end, streaming chunks, state sync)
2026-04-13 08:30:07 -07:00
Jordan Ritter e76b7f435e test(sdk-python): replace mock test with integration tests for emit_state merge
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
2026-04-13 08:29:15 -07:00
Jordan Ritter 35705db3c7 fix(sdk-python): update current_graph_state after manual emit to persist across iterations
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.
2026-04-13 08:29:07 -07:00
Jordan Ritter a559406bee fix: also serialize Pydantic state in get_state() and fix implicit None return
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).
2026-04-13 08:24:10 -07:00
Jordan Ritter d0b7b6981c fix: sanitize raw event stream to prevent NaN/Infinity in JSON output
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.
2026-04-13 08:19:35 -07:00
Jordan Ritter 2e92fe1bff test: add tests for multi-part AIMessage content extraction
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.
2026-04-13 08:19:29 -07:00
Jordan Ritter aa8a9e0688 fix: cap Python at <3.14 to match ag-ui-langgraph constraint 2026-04-12 15:43:12 -07:00
Jordan Ritter 906ab8d6e2 fix: add Python version constraint for crewai dependency
crewai requires Python <3.13, so add a python marker to prevent
resolution failures when the project supports >=3.10,<4.
2026-04-12 15:38:49 -07:00
Jordan Ritter 35ee689652 fix: preserve graph-owned state keys during frontend state merge (#2893) 2026-04-12 15:25:10 -07:00
Jordan Ritter 0e57499664 fix: sanitize NaN/Infinity values in LangGraph state before JSON serialization (#1955) 2026-04-12 15:25:03 -07:00
Jordan Ritter 2f3938036e fix: extract all text parts when AIMessage content is a list (#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.
2026-04-12 15:16:36 -07:00
Jordan Ritter 9483051492 fix: allow Python 3.13+ in SDK version constraint (#3156)
Widen the python dependency from >=3.10,<3.13 to >=3.10,<4 and add
Python 3.13 classifier to enable installation on newer Python versions.
2026-04-12 15:09:38 -07:00
Jordan Ritter 9fa923e421 fix: add py.typed marker to Python SDK for PEP 561 compliance (#2921)
Creates the empty py.typed file and adds it to pyproject.toml's
include list so type checkers recognize the package as typed.
2026-04-12 15:05:13 -07:00
Jordan Ritter eb5b98f296 fix: convert Pydantic BaseModel instances to dicts before langchain_dumps serialization (#2158) 2026-04-12 14:59:36 -07:00
Jordan Ritter fee1a888b2 fix(sdk-python): copilotkit_interrupt handles non-list resume values (#3096)
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.
2026-04-12 13:22:28 -07:00
Jordan Ritter ee9e9c37b8 fix(sdk-python): copilotkit_emit_state merges state instead of replacing (#3138)
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.
2026-04-12 13:21:53 -07:00
Martha Schumann 21d2051b33 Close three PR review blockers: cross-layer event tests, API removals verified
- 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>
2026-04-10 17:18:42 -07:00
Martha Schumann a90dbce49d test(sdk-python): add edge case coverage for emit filtering and merge state
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>
2026-04-10 13:11:41 -07:00
Jordan Ritter f5462915c3 ci: fix static_danger matrix bug, python-sdk concurrency and dep pin
- static_danger: removed undefined matrix.node-version, upgraded to
  v4, added concurrency and timeout
- python-sdk: added concurrency and timeout, pinned ag-ui-langgraph
  <0.0.32 (0.0.32 imports non-existent ImageInputContent from ag_ui.core)
2026-04-10 10:37:42 -07:00
Martha Schumann 506a1e8e9d chore: remove copilotkit_messages_to_langchain
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>
2026-04-10 07:54:06 -07:00
Martha Schumann be121cf178 chore: remove deprecated LangGraphAgent class and use_function_call branch
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>
2026-04-09 18:38:13 -07:00
Martha Kelly Schumann 1a4ed9d639 Merge branch 'main' into claude/langgraph-tests-cleanup-1jVZ3 2026-04-09 07:27:59 -07:00
Markus Ecker 0a11a05772 feat(sdk-python): A2UI helpers and before_agent ID reuse fix
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.
2026-04-08 12:50:28 -07:00
Martha Kelly Schumann 04d73d1dca Merge branch 'main' into claude/langgraph-tests-cleanup-1jVZ3 2026-04-08 08:52:43 -07:00
Ran Shem Tov a7e3e131d5 chore: loosen up langgraph dependencies on copilotkit and release 2026-04-07 13:14:00 +02:00
Claude 2171c7b855 test: address review feedback on LangGraph tests
- Python: extract track_parent_dispatches() context manager, eliminating
  repeated boilerplate; import AGUIBase directly instead of __bases__[0]
- Python: use named constants for "copilotkit_exit" and "Exit" strings
  with comments explaining why they're literals (source uses them too)
- TS runtime: extract withMockedParentMerge() helper using vi.spyOn for
  automatic cleanup instead of manual try/finally prototype patching
- TS runtime: add makeCustomEvent() factory to centralise as-any casts
- TS runtime: use CustomEventNames constants for all custom event tests
- TS runtime: fix misleading comment on empty string content test
- TS runtime: add callPrivate() docstring noting the any trade-off

https://claude.ai/code/session_01BPMn7zhadhapfyyD8kYeAH
2026-04-03 23:12:52 +00:00