copilotkit 0.1.87__tar.gz → 0.1.88__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 (28) hide show
  1. {copilotkit-0.1.87 → copilotkit-0.1.88}/PKG-INFO +8 -5
  2. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/__init__.py +0 -2
  3. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/a2ui.py +5 -0
  4. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/copilotkit_lg_middleware.py +97 -3
  5. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/crewai/crewai_sdk.py +39 -13
  6. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/langchain.py +0 -2
  7. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/langgraph.py +17 -84
  8. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/langgraph_agui_agent.py +2 -2
  9. copilotkit-0.1.88/copilotkit/py.typed +0 -0
  10. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/sdk.py +4 -11
  11. {copilotkit-0.1.87 → copilotkit-0.1.88}/pyproject.toml +16 -6
  12. copilotkit-0.1.87/copilotkit/langgraph_agent.py +0 -840
  13. {copilotkit-0.1.87 → copilotkit-0.1.88}/README.md +0 -0
  14. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/action.py +0 -0
  15. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/agent.py +0 -0
  16. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/crewai/__init__.py +0 -0
  17. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/crewai/copilotkit_integration.py +0 -0
  18. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/crewai/crewai_agent.py +0 -0
  19. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/exc.py +0 -0
  20. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/html.py +0 -0
  21. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/integrations/__init__.py +0 -0
  22. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/integrations/fastapi.py +0 -0
  23. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/logging.py +0 -0
  24. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/parameter.py +0 -0
  25. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/protocol.py +0 -0
  26. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/runloop.py +0 -0
  27. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/types.py +0 -0
  28. {copilotkit-0.1.87 → copilotkit-0.1.88}/copilotkit/utils.py +0 -0
@@ -1,25 +1,28 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: copilotkit
3
- Version: 0.1.87
3
+ Version: 0.1.88
4
4
  Summary: CopilotKit python SDK
5
5
  License: MIT
6
6
  Keywords: copilot,copilotkit,langgraph,langchain,ai,langsmith,langserve
7
7
  Author: Markus Ecker
8
8
  Author-email: markus.ecker@gmail.com
9
- Requires-Python: >=3.10,<3.13
9
+ Requires-Python: >=3.10,<3.15
10
10
  Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
15
17
  Provides-Extra: crewai
16
- Requires-Dist: ag-ui-langgraph[fastapi] (>=0.0.29)
17
- Requires-Dist: ag-ui-protocol (>=0.1.11)
18
- Requires-Dist: crewai (==0.118.0) ; extra == "crewai"
18
+ Requires-Dist: ag-ui-langgraph[fastapi] (>=0.0.35)
19
+ Requires-Dist: ag-ui-protocol (>=0.1.15)
20
+ Requires-Dist: crewai (>=0.118.0) ; (python_version >= "3.10" and python_version < "3.14") and (extra == "crewai")
19
21
  Requires-Dist: fastapi (>=0.115.0,<1.0.0)
20
22
  Requires-Dist: langchain (>=0.3.0)
21
23
  Requires-Dist: langgraph (>=0.3.25,<2)
22
24
  Requires-Dist: partialjson (>=0.0.8,<0.0.9)
25
+ Requires-Dist: pydantic-core (>=2.35.0)
23
26
  Requires-Dist: toml (>=0.10.2,<0.11.0)
24
27
  Project-URL: Homepage, https://copilotkit.ai
25
28
  Project-URL: Repository, https://github.com/CopilotKit/CopilotKit/tree/main/sdk-python
@@ -4,7 +4,6 @@ from .action import Action
4
4
  from .langgraph import CopilotKitState
5
5
  from .parameter import Parameter
6
6
  from .agent import Agent
7
- from .langgraph_agent import LangGraphAgent
8
7
  from .langgraph_agui_agent import LangGraphAGUIAgent
9
8
  from .copilotkit_lg_middleware import CopilotKitMiddleware
10
9
  from ag_ui_langgraph.middlewares.state_streaming import StateStreamingMiddleware, StateItem
@@ -21,7 +20,6 @@ __all__ = [
21
20
  'CopilotKitContext',
22
21
  'CopilotKitSDKContext',
23
22
  'CrewAIAgent', # pyright: ignore[reportUnsupportedDunderAll] pylint: disable=undefined-all-variable
24
- 'LangGraphAgent', # pyright: ignore[reportUnsupportedDunderAll] pylint: disable=undefined-all-variable
25
23
  "LangGraphAGUIAgent",
26
24
  "CopilotKitMiddleware",
27
25
  "StateStreamingMiddleware",
@@ -117,6 +117,11 @@ CRITICAL: You MUST call the render_a2ui tool with ALL of these arguments:
117
117
  - every component must have the "component" field specifying the component type (e.g. "Text", "Image", "Row", "Column", "List", "Button", etc.)
118
118
 
119
119
  COMPONENT ID RULES:
120
+ - Exactly one component MUST have id="root". This is the surface's entry
121
+ point — the renderer begins at "root" and walks the child/children tree
122
+ from there. Every other component must be reachable from "root". If no
123
+ component has id="root", the surface renders an empty loading placeholder
124
+ and none of your components will be shown.
120
125
  - Every component ID must be unique within the surface.
121
126
  - A component MUST NOT reference itself as child/children. This causes a
122
127
  circular dependency error. For example, if a component has id="avatar",
@@ -16,7 +16,7 @@ Example:
16
16
 
17
17
  import json
18
18
  import re
19
- from typing import Any, Callable, Awaitable, ClassVar, List
19
+ from typing import Any, Callable, Awaitable, ClassVar, Iterable, List, Union
20
20
 
21
21
  from langchain_core.messages import AIMessage, SystemMessage, ToolMessage
22
22
  from langchain.agents.middleware import (
@@ -33,25 +33,118 @@ class StateSchema(AgentState):
33
33
  copilotkit: CopilotKitProperties
34
34
 
35
35
 
36
+ # Internal/framework keys that should never be surfaced to the LLM as
37
+ # user-facing state. These are either reducer-managed message buckets,
38
+ # CopilotKit/AG-UI plumbing, or graph-internal scaffolding.
39
+ _RESERVED_STATE_KEYS = frozenset({
40
+ "messages",
41
+ "copilotkit",
42
+ "ag-ui",
43
+ "tools",
44
+ "structured_response",
45
+ "thread_id",
46
+ "remaining_steps",
47
+ })
48
+
49
+
36
50
  class CopilotKitMiddleware(AgentMiddleware[StateSchema, Any]):
37
51
  """CopilotKit Middleware for LangGraph agents.
38
52
 
39
- Handles frontend tool injection and interception for CopilotKit.
53
+ Handles frontend tool injection, interception for CopilotKit, and
54
+ automatic exposure of agent state to the LLM so values written via
55
+ ``agent.setState`` on the frontend (or via ``Command(update=...)`` in a
56
+ tool) are visible in the next model call without needing a custom
57
+ ``get_state`` tool.
58
+
59
+ Args:
60
+ expose_state: Controls how user-defined state keys are surfaced into
61
+ ``request.system_message`` on every model call. Off by default
62
+ to avoid leaking arbitrary state into prompts; opt in explicitly.
63
+
64
+ - ``False`` (default) — never surface state.
65
+ - ``True`` — every state key that is not in the reserved
66
+ internal set and does not start with an underscore is
67
+ JSON-serialized into a "Current agent state:" note appended
68
+ to the system message.
69
+ - ``list``/``tuple``/``set[str]`` — only surface the named keys.
70
+ Use this when you want explicit control over what the LLM
71
+ sees (e.g. ``["liked", "todos"]``).
40
72
  """
41
73
 
42
74
  state_schema = StateSchema
43
75
  tools: ClassVar[list] = []
44
76
 
77
+ def __init__(
78
+ self,
79
+ *,
80
+ expose_state: Union[bool, Iterable[str]] = False,
81
+ ):
82
+ super().__init__()
83
+ if isinstance(expose_state, bool):
84
+ self._expose_state: Union[bool, frozenset[str]] = expose_state
85
+ else:
86
+ self._expose_state = frozenset(expose_state)
87
+
45
88
  @property
46
89
  def name(self) -> str:
47
90
  return "CopilotKitMiddleware"
48
91
 
49
- # Inject frontend tools before model call
92
+ # ------------------------------------------------------------------
93
+ # State-to-prompt surfacing
94
+ # ------------------------------------------------------------------
95
+
96
+ def _build_state_note(self, state: dict) -> str | None:
97
+ """Serialize a snapshot of user state into a system-prompt note.
98
+
99
+ Returns ``None`` when nothing should be appended (feature disabled
100
+ or no non-empty user keys present).
101
+ """
102
+ if self._expose_state is False:
103
+ return None
104
+ if isinstance(self._expose_state, frozenset):
105
+ keys: list[str] = [k for k in self._expose_state if k in state]
106
+ else:
107
+ keys = [
108
+ k for k in state
109
+ if k not in _RESERVED_STATE_KEYS and not str(k).startswith("_")
110
+ ]
111
+
112
+ snapshot: dict[str, Any] = {}
113
+ for k in keys:
114
+ v = state.get(k)
115
+ # Skip empty / no-op values to keep the note tight.
116
+ if v in (None, "", [], {}):
117
+ continue
118
+ snapshot[k] = v
119
+
120
+ if not snapshot:
121
+ return None
122
+
123
+ try:
124
+ body = json.dumps(snapshot, default=str, ensure_ascii=False, indent=2)
125
+ except (TypeError, ValueError):
126
+ body = str(snapshot)
127
+ return f"Current agent state:\n{body}"
128
+
129
+ def _apply_state_note(self, request: ModelRequest) -> ModelRequest:
130
+ note = self._build_state_note(request.state or {})
131
+ if not note:
132
+ return request
133
+ existing = request.system_message
134
+ if existing is None:
135
+ return request.override(system_message=SystemMessage(content=note))
136
+ base = existing.content if isinstance(existing.content, str) else str(existing.content)
137
+ return request.override(
138
+ system_message=SystemMessage(content=f"{base}\n\n{note}")
139
+ )
140
+
141
+ # Inject frontend tools and surface user state before model call
50
142
  def wrap_model_call(
51
143
  self,
52
144
  request: ModelRequest,
53
145
  handler: Callable[[ModelRequest], ModelResponse],
54
146
  ) -> ModelResponse:
147
+ request = self._apply_state_note(request)
55
148
  frontend_tools = request.state.get("copilotkit", {}).get("actions", [])
56
149
 
57
150
  if not frontend_tools:
@@ -224,6 +317,7 @@ class CopilotKitMiddleware(AgentMiddleware[StateSchema, Any]):
224
317
  handler: Callable[[ModelRequest], Awaitable[ModelResponse]],
225
318
  ) -> ModelResponse:
226
319
  self._fix_messages_for_bedrock(request.messages)
320
+ request = self._apply_state_note(request)
227
321
 
228
322
  frontend_tools = request.state.get("copilotkit", {}).get("actions", [])
229
323
 
@@ -16,13 +16,22 @@ from litellm.types.utils import (
16
16
  )
17
17
  from litellm.litellm_core_utils.streaming_handler import CustomStreamWrapper
18
18
  from crewai.flow.flow import FlowState, Flow
19
- from crewai.utilities.events.flow_events import (
20
- FlowEvent as CrewAIFlowEvent,
21
- FlowStartedEvent,
22
- MethodExecutionStartedEvent,
23
- MethodExecutionFinishedEvent,
24
- FlowFinishedEvent,
25
- )
19
+ try:
20
+ from crewai.utilities.events.flow_events import (
21
+ FlowEvent as CrewAIFlowEvent,
22
+ FlowStartedEvent,
23
+ MethodExecutionStartedEvent,
24
+ MethodExecutionFinishedEvent,
25
+ FlowFinishedEvent,
26
+ )
27
+ except ImportError:
28
+ from crewai.events.types.flow_events import ( # type: ignore[no-redef]
29
+ FlowEvent as CrewAIFlowEvent,
30
+ FlowStartedEvent,
31
+ MethodExecutionStartedEvent,
32
+ MethodExecutionFinishedEvent,
33
+ FlowFinishedEvent,
34
+ )
26
35
  from crewai.utilities.events import crewai_event_bus as _crewai_event_bus
27
36
 
28
37
  from copilotkit.types import Message
@@ -552,7 +561,13 @@ def crewai_flow_messages_to_copilotkit(messages: List[Dict]) -> List[Message]: #
552
561
  if "content" in message and message.get("role") == "assistant":
553
562
  if message.get("tool_calls"):
554
563
  for tool_call in message["tool_calls"]:
555
- tool_call_names[tool_call["id"]] = tool_call["function"]["name"]
564
+ tc_id = tool_call.get("id")
565
+ if tc_id is None:
566
+ continue
567
+ if tool_call.get("function"):
568
+ tool_call_names[tc_id] = tool_call["function"].get("name", "")
569
+ else:
570
+ tool_call_names[tc_id] = tool_call.get("name", "")
556
571
 
557
572
  for message in messages:
558
573
  message_id = message_ids[id(message)]
@@ -565,19 +580,30 @@ def crewai_flow_messages_to_copilotkit(messages: List[Dict]) -> List[Message]: #
565
580
  "id": message_id,
566
581
  })
567
582
  elif message.get("tool_calls"):
583
+ # Always emit the assistant message, even with empty content.
584
+ # Tool call entries reference it via parentMessageId; omitting it
585
+ # orphans tool calls and breaks frontend thread reconstruction.
586
+ result.append({
587
+ "role": message["role"],
588
+ "content": message.get("content") if message.get("content") is not None else "",
589
+ "id": message_id,
590
+ })
568
591
  for tool_call in message["tool_calls"]:
592
+ tc_id = tool_call.get("id")
593
+ if tc_id is None:
594
+ continue
569
595
  if tool_call.get("function"):
570
596
  result.append({
571
- "id": tool_call["id"],
572
- "name": tool_call["function"]["name"],
597
+ "id": tc_id,
598
+ "name": tool_call["function"].get("name", ""),
573
599
  "arguments": json.loads(tool_call["function"]["arguments"]),
574
600
  "parentMessageId": message_id,
575
601
  })
576
602
  else:
577
603
  result.append({
578
- "id": tool_call["id"],
579
- "name": tool_call["name"],
580
- "arguments": tool_call["arguments"],
604
+ "id": tc_id,
605
+ "name": tool_call.get("name", ""),
606
+ "arguments": tool_call.get("arguments", {}),
581
607
  "parentMessageId": message_id,
582
608
  })
583
609
  elif message.get("content"):
@@ -4,7 +4,6 @@ copilotkit.langchain is deprecated. Use copilotkit.langgraph instead.
4
4
  import warnings
5
5
  from copilotkit.langgraph import (
6
6
  langchain_messages_to_copilotkit,
7
- copilotkit_messages_to_langchain,
8
7
  copilotkit_customize_config,
9
8
  copilotkit_exit,
10
9
  copilotkit_emit_state,
@@ -21,7 +20,6 @@ warnings.warn(
21
20
 
22
21
  __all__ = [
23
22
  "langchain_messages_to_copilotkit",
24
- "copilotkit_messages_to_langchain",
25
23
  "copilotkit_customize_config",
26
24
  "copilotkit_exit",
27
25
  "copilotkit_emit_state",
@@ -6,7 +6,7 @@ import uuid
6
6
  import json
7
7
  import warnings
8
8
  import asyncio
9
- from typing import List, Optional, Any, Union, Dict, Callable, cast
9
+ from typing import List, Optional, Any, Union, Dict
10
10
  from typing_extensions import TypedDict
11
11
  from langgraph.graph import MessagesState
12
12
 
@@ -46,82 +46,6 @@ class CopilotKitState(MessagesState):
46
46
  copilotkit: CopilotKitProperties
47
47
 
48
48
 
49
- def copilotkit_messages_to_langchain(
50
- use_function_call: bool = False
51
- ) -> Callable[[List[Message]], List[BaseMessage]]:
52
- """
53
- Convert CopilotKit messages to LangChain messages
54
- """
55
- def _copilotkit_messages_to_langchain(messages: List[Message]) -> List[BaseMessage]:
56
- result = []
57
- processed_action_executions = set()
58
- for message in cast(Any, messages):
59
- if message["type"] == "TextMessage":
60
- if message["role"] == "user":
61
- result.append(HumanMessage(content=message["content"], id=message["id"]))
62
- elif message["role"] == "system":
63
- result.append(SystemMessage(content=message["content"], id=message["id"]))
64
- elif message["role"] == "assistant":
65
- result.append(AIMessage(content=message["content"], id=message["id"]))
66
- elif message["type"] == "ActionExecutionMessage":
67
- if use_function_call:
68
- result.append(AIMessage(
69
- id=message["id"],
70
- content="",
71
- additional_kwargs={
72
- 'function_call':{
73
- 'name': message["name"],
74
- 'arguments': json.dumps(message["arguments"]),
75
- }
76
- }
77
- ))
78
- else:
79
- # convert multiple tool calls to a single message
80
- message_id = message.get("parentMessageId")
81
- if message_id is None:
82
- message_id = message["id"]
83
-
84
- if message_id in processed_action_executions:
85
- continue
86
-
87
- processed_action_executions.add(message_id)
88
-
89
- all_tool_calls = []
90
-
91
- # Find all tool calls for this message (only ActionExecutionMessage type)
92
- for msg in messages:
93
- if msg.get("type") != "ActionExecutionMessage":
94
- continue
95
- if msg.get("parentMessageId", None) == message_id or msg["id"] == message_id:
96
- all_tool_calls.append(msg)
97
-
98
- tool_calls = [{
99
- "name": t["name"],
100
- "args": t["arguments"],
101
- "id": t["id"],
102
- } for t in all_tool_calls]
103
-
104
- result.append(
105
- AIMessage(
106
- id=message_id,
107
- content="",
108
- tool_calls=tool_calls
109
- )
110
- )
111
-
112
- elif message["type"] == "ResultMessage":
113
- result.append(ToolMessage(
114
- id=message["id"],
115
- content=message["result"],
116
- name=message["actionName"],
117
- tool_call_id=message["actionExecutionId"]
118
- ))
119
-
120
- return result
121
-
122
- return _copilotkit_messages_to_langchain
123
-
124
-
125
49
  def langchain_messages_to_copilotkit(
126
50
  messages: List[BaseMessage]
127
51
  ) -> List[Message]:
@@ -173,12 +97,14 @@ def langchain_messages_to_copilotkit(
173
97
  "id": message.id,
174
98
  })
175
99
  elif isinstance(message, AIMessage):
176
- if content:
177
- result.append({
178
- "role": "assistant",
179
- "content": content,
180
- "id": message.id,
181
- })
100
+ # Always emit the assistant message, even with empty content.
101
+ # Tool call entries reference it via parentMessageId; omitting it
102
+ # orphans tool calls and breaks frontend thread reconstruction.
103
+ result.append({
104
+ "role": "assistant",
105
+ "content": content if content is not None else "",
106
+ "id": message.id,
107
+ })
182
108
  if message.tool_calls:
183
109
  for tool_call in message.tool_calls:
184
110
  result.append({
@@ -507,6 +433,13 @@ def copilotkit_interrupt(
507
433
  "__copilotkit_interrupt_value__": interrupt_values,
508
434
  "__copilotkit_messages__": [interrupt_message]
509
435
  })
510
- answer = response[-1].content
436
+ if isinstance(response, str):
437
+ answer = response
438
+ elif isinstance(response, dict):
439
+ answer = json.dumps(response)
440
+ elif isinstance(response, list):
441
+ answer = response[-1].content
442
+ else:
443
+ answer = str(response)
511
444
 
512
445
  return answer, response
@@ -198,8 +198,8 @@ class LangGraphAGUIAgent(LangGraphAgent):
198
198
  return {
199
199
  **merged_state,
200
200
  'copilotkit': {
201
- 'actions': agui_properties.get('tools', []),
202
- 'context': agui_properties.get('context', [])
201
+ 'actions': [a.model_dump() if hasattr(a, 'model_dump') else a for a in agui_properties.get('tools', [])],
202
+ 'context': [c.model_dump() if hasattr(c, 'model_dump') else c for c in agui_properties.get('context', [])]
203
203
  },
204
204
  }
205
205
 
File without changes
@@ -130,12 +130,12 @@ class CopilotKitRemoteEndpoint:
130
130
  Serving agents works in a similar way to serving actions:
131
131
 
132
132
  ```python
133
- from copilotkit import CopilotKitRemoteEndpoint, LangGraphAgent
133
+ from copilotkit import CopilotKitRemoteEndpoint, LangGraphAGUIAgent
134
134
  from my_agent.agent import graph
135
135
 
136
136
  sdk = CopilotKitRemoteEndpoint(
137
137
  agents=[
138
- LangGraphAgent(
138
+ LangGraphAGUIAgent(
139
139
  name="email_agent",
140
140
  description="This agent sends emails",
141
141
  graph=graph,
@@ -147,12 +147,12 @@ class CopilotKitRemoteEndpoint:
147
147
  To dynamically build agents, provide a callable that returns a list of agents:
148
148
 
149
149
  ```python
150
- from copilotkit import CopilotKitRemoteEndpoint, LangGraphAgent
150
+ from copilotkit import CopilotKitRemoteEndpoint, LangGraphAGUIAgent
151
151
  from my_agent.agent import graph
152
152
 
153
153
  sdk = CopilotKitRemoteEndpoint(
154
154
  agents=lambda context: [
155
- LangGraphAgent(
155
+ LangGraphAGUIAgent(
156
156
  name="email_agent",
157
157
  description="This agent sends emails",
158
158
  graph=graph,
@@ -226,13 +226,6 @@ class CopilotKitRemoteEndpoint:
226
226
  self.agents = agents or []
227
227
  self.actions = actions or []
228
228
 
229
- if isinstance(agents, list):
230
- from .langgraph_agent import LangGraphAgent
231
- for agent in agents:
232
- if isinstance(agent, LangGraphAgent):
233
- raise ValueError(
234
- "LangGraphAgent should be instantiated using LangGraphAGUIAgent. Refer to https://docs.copilotkit.ai/langgraph for more information.")
235
-
236
229
 
237
230
  def info(
238
231
  self,
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "copilotkit"
3
- version = "0.1.87"
3
+ version = "0.1.88"
4
4
  description = "CopilotKit python SDK"
5
5
  authors = ["Markus Ecker <markus.ecker@gmail.com>"]
6
6
  license = "MIT"
@@ -16,19 +16,29 @@ keywords = [
16
16
  "langsmith",
17
17
  "langserve",
18
18
  ]
19
+ classifiers = [
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Programming Language :: Python :: 3.14",
25
+ ]
19
26
  packages = [{ include = "copilotkit" }]
20
- include = ["copilotkit/*.json"]
27
+ include = ["copilotkit/*.json", "copilotkit/py.typed"]
21
28
 
22
29
  [tool.poetry.dependencies]
23
- python = ">=3.10,<3.13"
30
+ python = ">=3.10,<3.15"
24
31
  langgraph = { version = ">=0.3.25,<2" }
25
32
  langchain = { version = ">=0.3.0" }
26
- crewai = { version = "0.118.0", optional = true }
27
- ag-ui-langgraph = { version = ">=0.0.29", extras = ["fastapi"] }
28
- ag-ui-protocol = ">=0.1.11"
33
+ crewai = { version = ">=0.118.0", optional = true, python = ">=3.10,<3.14" }
34
+ ag-ui-langgraph = { version = ">=0.0.35", extras = ["fastapi"] }
35
+ ag-ui-protocol = ">=0.1.15"
29
36
  fastapi = ">=0.115.0,<1.0.0"
30
37
  partialjson = "^0.0.8"
31
38
  toml = "^0.10.2"
39
+ # Pin transitive: cp314 wheels first appear in 2.35.0; older versions
40
+ # would force a Rust source build that fails on PyO3 0.24 (capped at 3.13).
41
+ pydantic-core = ">=2.35.0"
32
42
 
33
43
  [tool.poetry.extras]
34
44
  crewai = ["crewai"]