copilotkit 0.1.86a0__tar.gz → 0.1.87__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/PKG-INFO +2 -2
  2. copilotkit-0.1.87/copilotkit/a2ui.py +237 -0
  3. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/copilotkit_lg_middleware.py +18 -4
  4. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/langgraph.py +12 -2
  5. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/langgraph_agent.py +58 -6
  6. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/langgraph_agui_agent.py +2 -2
  7. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/pyproject.toml +4 -2
  8. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/README.md +0 -0
  9. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/__init__.py +0 -0
  10. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/action.py +0 -0
  11. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/agent.py +0 -0
  12. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/crewai/__init__.py +0 -0
  13. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/crewai/copilotkit_integration.py +0 -0
  14. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/crewai/crewai_agent.py +0 -0
  15. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/crewai/crewai_sdk.py +0 -0
  16. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/exc.py +0 -0
  17. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/html.py +0 -0
  18. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/integrations/__init__.py +0 -0
  19. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/integrations/fastapi.py +0 -0
  20. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/langchain.py +0 -0
  21. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/logging.py +0 -0
  22. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/parameter.py +0 -0
  23. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/protocol.py +0 -0
  24. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/runloop.py +0 -0
  25. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/sdk.py +0 -0
  26. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/types.py +0 -0
  27. {copilotkit-0.1.86a0 → copilotkit-0.1.87}/copilotkit/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: copilotkit
3
- Version: 0.1.86a0
3
+ Version: 0.1.87
4
4
  Summary: CopilotKit python SDK
5
5
  License: MIT
6
6
  Keywords: copilot,copilotkit,langgraph,langchain,ai,langsmith,langserve
@@ -13,7 +13,7 @@ Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Programming Language :: Python :: 3.12
15
15
  Provides-Extra: crewai
16
- Requires-Dist: ag-ui-langgraph[fastapi] (>=0.0.30)
16
+ Requires-Dist: ag-ui-langgraph[fastapi] (>=0.0.29)
17
17
  Requires-Dist: ag-ui-protocol (>=0.1.11)
18
18
  Requires-Dist: crewai (==0.118.0) ; extra == "crewai"
19
19
  Requires-Dist: fastapi (>=0.115.0,<1.0.0)
@@ -0,0 +1,237 @@
1
+ """
2
+ A2UI helpers — build v0.9 A2UI operations from schema + data.
3
+
4
+ Usage:
5
+ from copilotkit import a2ui
6
+
7
+ schema = a2ui.load_schema("flight_card.json")
8
+
9
+ @tool
10
+ def search_flights(flights: list[Flight]) -> str:
11
+ return a2ui.render([
12
+ a2ui.create_surface("my-surface"),
13
+ a2ui.update_components("my-surface", schema),
14
+ a2ui.update_data_model("my-surface", {"flights": flights}),
15
+ ])
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import json
21
+ from pathlib import Path
22
+ from typing import Any
23
+
24
+
25
+ def load_schema(path: str | Path) -> list[dict[str, Any]]:
26
+ """Load an A2UI component schema from a JSON file."""
27
+ with open(path) as f:
28
+ return json.load(f)
29
+
30
+
31
+ def update_components(
32
+ surface_id: str,
33
+ components: list[dict[str, Any]],
34
+ ) -> dict[str, Any]:
35
+ """Build a v0.9 updateComponents operation."""
36
+ return {
37
+ "version": "v0.9",
38
+ "updateComponents": {
39
+ "surfaceId": surface_id,
40
+ "components": components,
41
+ }
42
+ }
43
+
44
+
45
+ def update_data_model(
46
+ surface_id: str,
47
+ data: Any,
48
+ path: str = "/",
49
+ ) -> dict[str, Any]:
50
+ """Build a v0.9 updateDataModel operation with plain JSON value."""
51
+ return {
52
+ "version": "v0.9",
53
+ "updateDataModel": {
54
+ "surfaceId": surface_id,
55
+ "path": path,
56
+ "value": data,
57
+ }
58
+ }
59
+
60
+
61
+ BASIC_CATALOG_ID = "https://a2ui.org/specification/v0_9/basic_catalog.json"
62
+ """The catalog ID for the standard v0.9 basic catalog."""
63
+
64
+
65
+ def create_surface(
66
+ surface_id: str,
67
+ catalog_id: str = BASIC_CATALOG_ID,
68
+ ) -> dict[str, Any]:
69
+ """Build a v0.9 createSurface operation."""
70
+ return {
71
+ "version": "v0.9",
72
+ "createSurface": {
73
+ "surfaceId": surface_id,
74
+ "catalogId": catalog_id,
75
+ }
76
+ }
77
+
78
+
79
+ A2UI_OPERATIONS_KEY = "a2ui_operations"
80
+ """The container key used to wrap A2UI operations for explicit detection."""
81
+
82
+
83
+ def render(
84
+ operations: list[dict[str, Any]]
85
+ ) -> str:
86
+ """Wrap operations in the a2ui_operations container and serialize to JSON.
87
+
88
+ Args:
89
+ operations: The A2UI v0.9 operations (createSurface, updateComponents, updateDataModel).
90
+
91
+ Example::
92
+ render(
93
+ operations=[...],
94
+ )
95
+ """
96
+ result: dict[str, Any] = {A2UI_OPERATIONS_KEY: operations}
97
+ return json.dumps(result)
98
+
99
+
100
+ # ---------------------------------------------------------------------------
101
+ # Dynamic A2UI prompt builder
102
+ # ---------------------------------------------------------------------------
103
+
104
+ DEFAULT_GENERATION_GUIDELINES = """\
105
+ Generate A2UI v0.9 JSON.
106
+
107
+ ## A2UI Protocol Instructions
108
+
109
+ A2UI (Agent to UI) is a protocol for rendering rich UI surfaces from agent responses.
110
+
111
+ CRITICAL: You MUST call the render_a2ui tool with ALL of these arguments:
112
+ - surfaceId: A unique ID for the surface (e.g. "product-comparison")
113
+ - components: REQUIRED — the A2UI component array. NEVER omit this. Use a List with
114
+ children: { componentId: "card-id", path: "/items" } for repeating cards.
115
+ - data: OPTIONAL — a JSON object written to the root of the surface data model.
116
+ Use for pre-filling form values or providing data for path-bound components.
117
+ - every component must have the "component" field specifying the component type (e.g. "Text", "Image", "Row", "Column", "List", "Button", etc.)
118
+
119
+ COMPONENT ID RULES:
120
+ - Every component ID must be unique within the surface.
121
+ - A component MUST NOT reference itself as child/children. This causes a
122
+ circular dependency error. For example, if a component has id="avatar",
123
+ its child must be a DIFFERENT id (e.g. "avatar-img"), never "avatar".
124
+ - The child/children tree must be a DAG — no cycles allowed.
125
+
126
+ PATH RULES FOR TEMPLATES:
127
+ Components inside a repeating List use RELATIVE paths (no leading slash).
128
+ The path is resolved relative to each array item automatically.
129
+ If List has children: { componentId: "card", path: "/items" } and item has key "name",
130
+ use { "path": "name" } (NO leading slash — relative to item).
131
+ CRITICAL: Do NOT use "/name" (absolute) inside templates — use "name" (relative).
132
+ The List's own path ("/items") uses a leading slash (absolute), but all
133
+ components INSIDE the template card use paths WITHOUT leading slash.
134
+ Do NOT use "/items/0/name" or "/items/{@key}/name" — just "name".
135
+
136
+ COMPONENT VALUES — DEFAULT RULE:
137
+ Use inline literal values for ALL component properties. Pass strings, numbers,
138
+ arrays, and objects directly on the component. Do NOT use { "path": "..." }
139
+ objects unless the property's schema explicitly allows it (see exception below).
140
+ CRITICAL: USING { "path": "..." } ON A PROPERTY THAT DOES NOT DECLARE PATH
141
+ SUPPORT IN ITS SCHEMA WILL CAUSE A RUNTIME CRASH AND BREAK THE ENTIRE UI.
142
+ ALWAYS CHECK THE COMPONENT SCHEMA FIRST — IF THE PROPERTY ONLY ACCEPTS A
143
+ PLAIN TYPE, YOU MUST USE A LITERAL VALUE.
144
+ VERY IMPORTANT: THE APPLICATION WILL BREAK IF YOU DO NOT FOLLOW THIS RULE!
145
+
146
+ For example, a chart's "data" must always be an inline array:
147
+ "data": [{"label": "Jan", "value": 100}, {"label": "Feb", "value": 200}]
148
+ A metric's "value" must always be an inline string:
149
+ "value": "$1,200"
150
+
151
+ PATH BINDING EXCEPTION — SCHEMA-DRIVEN:
152
+ A few properties accept { "path": "/some/path" } as an alternative to a literal
153
+ value. You can identify these in the Available Components schema: the property
154
+ will list BOTH a literal type AND an object-with-path option. If a property only
155
+ shows a single type (string, number, array, etc.), it does NOT support path
156
+ binding — use a literal value only.
157
+
158
+ Path binding is typically used for editable form inputs so the client can write
159
+ user input back to the data model. When building forms:
160
+ - Bind input "value" to a data model path: "value": { "path": "/form/name" }
161
+ - Pre-fill via the "data" tool argument: "data": { "form": { "name": "Alice" } }
162
+ - Capture values on submit via button action context:
163
+ "action": { "event": { "name": "submit", "context": { "name": { "path": "/form/name" } } } }
164
+
165
+ REPEATING CONTENT uses a structural children format (not the same as value binding):
166
+ children: { componentId: "card-id", path: "/items" }
167
+ Components inside templates use RELATIVE paths (no leading slash): { "path": "name" }."""
168
+
169
+ DEFAULT_DESIGN_GUIDELINES = """\
170
+ Create polished, visually appealing interfaces:
171
+ - Always include a title heading (h2) for the surface, outside the List.
172
+ Wrap in a Column: [title, list] as root.
173
+ - For card templates, create clear visual hierarchy:
174
+ - h3 for primary text (names, titles)
175
+ - h2 for featured numbers (prices, scores) — makes them stand out
176
+ - caption for secondary info (ratings, categories, metadata)
177
+ - body for descriptions
178
+ - Use Divider between logical sections within cards.
179
+ - Use Row with justify="spaceBetween" for label-value pairs
180
+ (e.g. "Rating" on left, "4.5/5" on right).
181
+ - Include images when relevant (logos, icons, product photos):
182
+ - Use Image component with variant="smallFeature" or "avatar"
183
+ - Prefer company logos for branded products — Google favicons are reliable:
184
+ https://www.google.com/s2/favicons?domain=sony.com&sz=128
185
+ https://www.google.com/s2/favicons?domain=bose.com&sz=128
186
+ - For generic icons: https://placehold.co/128x128/EEE/999?text=🎧
187
+ - Do NOT invent Unsplash photo-IDs — they will 404. Only use real, known URLs.
188
+ - Use horizontal List direction for side-by-side comparison cards.
189
+ - Keep cards clean — avoid clutter. Whitespace is good.
190
+ - Use consistent surfaceIds (lowercase, hyphenated).
191
+ - NEVER use the same ID for a component and its child — this creates a
192
+ circular dependency. E.g. if id="avatar", child must NOT be "avatar".
193
+ - Both Row and Column support "justify" and "align".
194
+ - Add Button for interactivity. Button needs child (Text ID) + action.
195
+ Action MUST use this exact nested format:
196
+ "action": { "event": { "name": "myAction", "context": { "key": "value" } } }
197
+ The "event" key holds an OBJECT with "name" (required) and "context" (optional).
198
+ Do NOT use a flat format like {"event": "name"} — "event" must be an object.
199
+ Use variant="primary" for main action buttons, variant="borderless" for links.
200
+ - For forms: wrap fields in a Card with a Column. Place the submit button in a
201
+ Row with justify="end". Every input MUST use path binding on the "value" property
202
+ (e.g. "value": { "path": "/form/name" }) to be editable. The submit button's action
203
+ context MUST reference the same paths to capture the user's input.
204
+
205
+ Use the SAME surfaceId as the main surface. Match action names to Button action event names."""
206
+
207
+
208
+ def a2ui_prompt(
209
+ component_schema: str,
210
+ generation_guidelines: str = DEFAULT_GENERATION_GUIDELINES,
211
+ design_guidelines: str = DEFAULT_DESIGN_GUIDELINES,
212
+ ) -> str:
213
+ """Build the system prompt for dynamic A2UI generation.
214
+
215
+ Args:
216
+ component_schema: JSON string of available components and their props.
217
+ Read from state["ag-ui"]["a2ui_schema"].
218
+ generation_guidelines: Instructions for how to call the render_a2ui
219
+ tool, path rules, and data format.
220
+ design_guidelines: Visual design rules, component hierarchy tips,
221
+ and action handler patterns.
222
+
223
+ Returns:
224
+ Complete system prompt string.
225
+ """
226
+ return f"""\
227
+ {generation_guidelines}
228
+
229
+ ## DESIGN GUIDELINES:
230
+ {design_guidelines}
231
+
232
+ ## AVAILABLE COMPONENTS:
233
+ The following components are available for building UI surfaces.
234
+ Use ONLY these components with the specified props.
235
+
236
+ {component_schema}
237
+ """
@@ -26,7 +26,6 @@ from langchain.agents.middleware import (
26
26
  ModelResponse,
27
27
  )
28
28
  from langgraph.runtime import Runtime
29
- from ag_ui_langgraph import make_json_safe
30
29
 
31
30
  from .langgraph import CopilotKitProperties
32
31
 
@@ -263,7 +262,15 @@ class CopilotKitMiddleware(AgentMiddleware[StateSchema, Any]):
263
262
  if isinstance(app_context, str):
264
263
  context_content = app_context
265
264
  else:
266
- context_content = json.dumps(make_json_safe(app_context), indent=2)
265
+ # Handle Pydantic models (e.g. ag_ui Context)
266
+ if hasattr(app_context, "model_dump"):
267
+ app_context = app_context.model_dump()
268
+ elif isinstance(app_context, list):
269
+ app_context = [
270
+ item.model_dump() if hasattr(item, "model_dump") else item
271
+ for item in app_context
272
+ ]
273
+ context_content = json.dumps(app_context, indent=2)
267
274
 
268
275
  context_message_content = f"App Context:\n{context_content}"
269
276
  context_message_prefix = "App Context:\n"
@@ -301,8 +308,15 @@ class CopilotKitMiddleware(AgentMiddleware[StateSchema, Any]):
301
308
  existing_context_index = i
302
309
  break
303
310
 
304
- # Create the context message
305
- context_message = SystemMessage(content=context_message_content)
311
+ # Create the context message.
312
+ # When replacing an existing context message, reuse its ID so the
313
+ # add_messages reducer updates in-place instead of appending a
314
+ # duplicate at the end of the message list.
315
+ if existing_context_index != -1:
316
+ existing_id = getattr(messages[existing_context_index], "id", None)
317
+ context_message = SystemMessage(content=context_message_content, id=existing_id)
318
+ else:
319
+ context_message = SystemMessage(content=context_message_content)
306
320
 
307
321
  if existing_context_index != -1:
308
322
  # Replace existing context message
@@ -142,9 +142,19 @@ def langchain_messages_to_copilotkit(
142
142
  if hasattr(message, "content"):
143
143
  content = message.content
144
144
 
145
- # Check if content is a list and use the first element
145
+ # Content can be a list of content blocks (e.g. Anthropic models).
146
+ # Extract and concatenate all text parts instead of only taking
147
+ # the first element.
146
148
  if isinstance(content, list):
147
- content = content[0] if content else ""
149
+ text_parts = []
150
+ for part in content:
151
+ if isinstance(part, str):
152
+ text_parts.append(part)
153
+ elif isinstance(part, dict) and part.get("type") == "text":
154
+ text_parts.append(part.get("text", ""))
155
+ elif isinstance(part, dict) and "text" in part:
156
+ text_parts.append(part.get("text", ""))
157
+ content = "".join(text_parts)
148
158
 
149
159
  # Anthropic models return a dict with a "text" key
150
160
  if isinstance(content, dict):
@@ -2,6 +2,7 @@
2
2
 
3
3
  import uuid
4
4
  import json
5
+ import math
5
6
  from typing import Optional, List, Callable, Any, cast, Union, TypedDict, Literal
6
7
 
7
8
  from langgraph.graph.state import CompiledStateGraph
@@ -18,6 +19,22 @@ except ImportError:
18
19
  from langchain_core.messages import BaseMessage, SystemMessage
19
20
 
20
21
  from langchain_core.runnables import RunnableConfig, ensure_config
22
+
23
+ def _serialize_state(state):
24
+ """Recursively convert Pydantic BaseModel instances to dicts for serialization."""
25
+ try:
26
+ from pydantic import BaseModel as PydanticBaseModel
27
+ except ImportError:
28
+ return state
29
+
30
+ if isinstance(state, PydanticBaseModel):
31
+ return state.model_dump()
32
+ elif isinstance(state, dict):
33
+ return {k: _serialize_state(v) for k, v in state.items()}
34
+ elif isinstance(state, (list, tuple)):
35
+ return type(state)(_serialize_state(item) for item in state)
36
+ return state
37
+
21
38
  from langchain_core.messages import HumanMessage
22
39
 
23
40
  from partialjson.json_parser import JSONParser
@@ -31,6 +48,20 @@ from .logging import get_logger
31
48
 
32
49
  logger = get_logger(__name__)
33
50
 
51
+
52
+ def _sanitize_for_json(obj):
53
+ """Replace NaN and Infinity float values with None for valid JSON serialization."""
54
+ if isinstance(obj, float):
55
+ if math.isnan(obj) or math.isinf(obj):
56
+ return None
57
+ return obj
58
+ if isinstance(obj, dict):
59
+ return {k: _sanitize_for_json(v) for k, v in obj.items()}
60
+ if isinstance(obj, (list, tuple)):
61
+ return [_sanitize_for_json(item) for item in obj]
62
+ return obj
63
+
64
+
34
65
  class CopilotKitConfig(TypedDict):
35
66
  """
36
67
  CopilotKit config for LangGraphAgent
@@ -236,7 +267,12 @@ class LangGraphAgent(Agent):
236
267
  actions=actions,
237
268
  agent_name=self.name
238
269
  )
239
- current_graph_state.update(state)
270
+ # Only update graph state with keys that merge_state explicitly produced,
271
+ # not keys that were simply passed through from state_input unchanged.
272
+ # This preserves graph-owned state keys that the frontend may have sent stale values for.
273
+ for key, value in state.items():
274
+ if key not in state_input or value is not state_input.get(key):
275
+ current_graph_state[key] = value
240
276
  lg_interrupt_meta_event = next((ev for ev in (meta_events or []) if ev.get("name") == "LangGraphInterruptEvent"), None)
241
277
  has_active_interrupts = active_interrupts is not None and len(active_interrupts) > 0
242
278
 
@@ -435,7 +471,9 @@ class LangGraphAgent(Agent):
435
471
  manually_emitted_state = None
436
472
 
437
473
  if manually_emit_intermediate_state:
438
- manually_emitted_state = cast(Any, event["data"])
474
+ manually_emitted_state = _merge_emit_state(current_graph_state, cast(Any, event["data"]))
475
+ if isinstance(manually_emitted_state, dict):
476
+ current_graph_state.update(manually_emitted_state)
439
477
  yield self._emit_state_sync_event(
440
478
  thread_id=thread_id,
441
479
  run_id=run_id,
@@ -489,7 +527,7 @@ class LangGraphAgent(Agent):
489
527
  active=not exiting_node
490
528
  ) + "\n"
491
529
 
492
- yield langchain_dumps(event) + "\n"
530
+ yield langchain_dumps(_sanitize_for_json(event)) + "\n"
493
531
  except Exception as error:
494
532
  # Emit error information through streaming protocol before terminating
495
533
  # This preserves the semantic error details that would otherwise be lost
@@ -591,6 +629,9 @@ class LangGraphAgent(Agent):
591
629
  # Filter by schema keys if available
592
630
  state = self.filter_state_on_schema_keys(state, 'output')
593
631
 
632
+ # Convert Pydantic BaseModel instances to dicts for serialization
633
+ state = _serialize_state(state)
634
+
594
635
  return langchain_dumps({
595
636
  "event": "on_copilotkit_state_sync",
596
637
  "thread_id": thread_id,
@@ -598,7 +639,7 @@ class LangGraphAgent(Agent):
598
639
  "agent_name": self.name,
599
640
  "node_name": node_name,
600
641
  "active": active,
601
- "state": state,
642
+ "state": _sanitize_for_json(state),
602
643
  "running": running,
603
644
  "role": "assistant"
604
645
  })
@@ -636,10 +677,13 @@ class LangGraphAgent(Agent):
636
677
  state_copy = state.copy()
637
678
  state_copy.pop("messages", None)
638
679
 
680
+ # Convert Pydantic BaseModel instances to dicts for serialization
681
+ state_copy = _serialize_state(state_copy)
682
+
639
683
  return {
640
684
  "threadId": thread_id,
641
685
  "threadExists": True,
642
- "state": state_copy,
686
+ "state": _sanitize_for_json(state_copy),
643
687
  "messages": messages
644
688
  }
645
689
 
@@ -687,7 +731,8 @@ class LangGraphAgent(Agent):
687
731
  if hasattr(self, schema_keys_name) and getattr(self, schema_keys_name):
688
732
  return filter_by_schema_keys(state, getattr(self, schema_keys_name))
689
733
  except Exception:
690
- return state
734
+ pass
735
+ return state
691
736
 
692
737
  def get_interrupt_event(self, value):
693
738
  if not isinstance(value, str) and "__copilotkit_interrupt_value__" in value:
@@ -724,6 +769,13 @@ class LangGraphAgent(Agent):
724
769
 
725
770
  raise ValueError("Message ID not found in history")
726
771
 
772
+
773
+ def _merge_emit_state(current_state: dict, emitted_state: Any) -> dict:
774
+ """Merge emitted state on top of current graph state instead of replacing it."""
775
+ if isinstance(emitted_state, dict):
776
+ return {**current_state, **emitted_state}
777
+ return cast(Any, emitted_state)
778
+
727
779
  class _StreamingStateExtractor:
728
780
  def __init__(self, emit_intermediate_state: List[dict]):
729
781
  self.emit_intermediate_state = emit_intermediate_state
@@ -1,7 +1,7 @@
1
1
  import json
2
2
  from typing import Dict, Any, List, Optional, Union, AsyncGenerator
3
3
  from enum import Enum
4
- from ag_ui_langgraph import LangGraphAgent, make_json_safe
4
+ from ag_ui_langgraph import LangGraphAgent
5
5
  from ag_ui.core import (
6
6
  EventType,
7
7
  CustomEvent,
@@ -108,7 +108,7 @@ class LangGraphAGUIAgent(LangGraphAgent):
108
108
  type=EventType.TOOL_CALL_ARGS,
109
109
  tool_call_id=custom_event.value["id"],
110
110
  delta=custom_event.value["args"] if isinstance(custom_event.value["args"], str) else json.dumps(
111
- make_json_safe(custom_event.value["args"])),
111
+ custom_event.value["args"]),
112
112
  raw_event=event,
113
113
  )
114
114
  )
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "copilotkit"
3
- version = "0.1.86-alpha.0"
3
+ version = "0.1.87"
4
4
  description = "CopilotKit python SDK"
5
5
  authors = ["Markus Ecker <markus.ecker@gmail.com>"]
6
6
  license = "MIT"
@@ -16,13 +16,15 @@ keywords = [
16
16
  "langsmith",
17
17
  "langserve",
18
18
  ]
19
+ packages = [{ include = "copilotkit" }]
20
+ include = ["copilotkit/*.json"]
19
21
 
20
22
  [tool.poetry.dependencies]
21
23
  python = ">=3.10,<3.13"
22
24
  langgraph = { version = ">=0.3.25,<2" }
23
25
  langchain = { version = ">=0.3.0" }
24
26
  crewai = { version = "0.118.0", optional = true }
25
- ag-ui-langgraph = { version = ">=0.0.30", extras = ["fastapi"] }
27
+ ag-ui-langgraph = { version = ">=0.0.29", extras = ["fastapi"] }
26
28
  ag-ui-protocol = ">=0.1.11"
27
29
  fastapi = ">=0.115.0,<1.0.0"
28
30
  partialjson = "^0.0.8"
File without changes