copilotkit 0.1.80__tar.gz → 0.1.82__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.
- {copilotkit-0.1.80 → copilotkit-0.1.82}/PKG-INFO +2 -1
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/copilotkit_lg_middleware.py +83 -10
- {copilotkit-0.1.80 → copilotkit-0.1.82}/pyproject.toml +2 -1
- {copilotkit-0.1.80 → copilotkit-0.1.82}/README.md +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/__init__.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/action.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/agent.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/crewai/__init__.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/crewai/copilotkit_integration.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/crewai/crewai_agent.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/crewai/crewai_sdk.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/exc.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/html.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/integrations/__init__.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/integrations/fastapi.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/langchain.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/langgraph.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/langgraph_agent.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/langgraph_agui_agent.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/logging.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/parameter.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/protocol.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/runloop.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/sdk.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/types.py +0 -0
- {copilotkit-0.1.80 → copilotkit-0.1.82}/copilotkit/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: copilotkit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.82
|
|
4
4
|
Summary: CopilotKit python SDK
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: copilot,copilotkit,langgraph,langchain,ai,langsmith,langserve
|
|
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
15
|
Provides-Extra: crewai
|
|
16
16
|
Requires-Dist: ag-ui-langgraph[fastapi] (>=0.0.27)
|
|
17
|
+
Requires-Dist: ag-ui-protocol (>=0.1.11)
|
|
17
18
|
Requires-Dist: crewai (==0.118.0) ; extra == "crewai"
|
|
18
19
|
Requires-Dist: fastapi (>=0.115.0,<1.0.0)
|
|
19
20
|
Requires-Dist: langchain (>=0.3.0)
|
|
@@ -65,20 +65,65 @@ class CopilotKitMiddleware(AgentMiddleware[StateSchema, Any]):
|
|
|
65
65
|
def _fix_messages_for_bedrock(messages: list) -> list:
|
|
66
66
|
"""Fix messages loaded from checkpoint before sending to Bedrock.
|
|
67
67
|
|
|
68
|
-
Handles
|
|
68
|
+
Handles four issues caused by CopilotKit's after_agent restoring
|
|
69
69
|
frontend tool_calls to the checkpoint:
|
|
70
70
|
1. Strip unanswered tool_calls (no matching ToolMessage) — Bedrock
|
|
71
71
|
rejects toolUse without a corresponding toolResult.
|
|
72
72
|
2. Sync msg.content tool_use blocks with msg.tool_calls.
|
|
73
73
|
3. Fix tool_use content blocks with string input (must be dict).
|
|
74
|
+
4. Deduplicate ToolMessages by tool_call_id — patch_orphan_tool_calls
|
|
75
|
+
injects a placeholder with a new random ID on every checkpoint load;
|
|
76
|
+
when the real result is later appended alongside it, Bedrock rejects
|
|
77
|
+
the duplicate toolResult IDs. We keep the real result (non-interrupted)
|
|
78
|
+
over the placeholder, falling back to the last occurrence if both look
|
|
79
|
+
real.
|
|
74
80
|
"""
|
|
75
|
-
#
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
# 4. Deduplicate ToolMessages by tool_call_id before all other processing.
|
|
82
|
+
# patch_orphan_tool_calls adds "…was interrupted before completion."
|
|
83
|
+
# placeholders with fresh random IDs on every checkpoint load. The real
|
|
84
|
+
# result comes in as a separate message with a different ID, so both end
|
|
85
|
+
# up in the list. Keep the real (non-interrupted) one; if multiple real
|
|
86
|
+
# ones exist, keep the last.
|
|
87
|
+
_INTERRUPTED_PAT = re.compile(
|
|
88
|
+
r"^Tool call '.+' with id '.+' was interrupted before completion\.$"
|
|
89
|
+
)
|
|
90
|
+
# Group ToolMessages by tool_call_id, preserving position
|
|
91
|
+
tc_groups: dict[str, list] = {}
|
|
92
|
+
for i, msg in enumerate(messages):
|
|
93
|
+
if isinstance(msg, ToolMessage):
|
|
94
|
+
tc_id = getattr(msg, 'tool_call_id', None)
|
|
95
|
+
if tc_id:
|
|
96
|
+
tc_groups.setdefault(tc_id, []).append(i)
|
|
97
|
+
|
|
98
|
+
drop_indices: set = set()
|
|
99
|
+
for tc_id, indices in tc_groups.items():
|
|
100
|
+
if len(indices) <= 1:
|
|
101
|
+
continue
|
|
102
|
+
# Separate interrupted placeholders from real results
|
|
103
|
+
real_indices = [
|
|
104
|
+
i for i in indices
|
|
105
|
+
if not (isinstance(messages[i].content, str)
|
|
106
|
+
and _INTERRUPTED_PAT.match(messages[i].content))
|
|
107
|
+
]
|
|
108
|
+
interrupted_indices = [i for i in indices if i not in real_indices]
|
|
109
|
+
if real_indices and interrupted_indices:
|
|
110
|
+
# Replace the first placeholder (correct position, adjacent to AI
|
|
111
|
+
# message) with the last real result (likely appended at the end).
|
|
112
|
+
# This keeps the tool result in the right position for Bedrock.
|
|
113
|
+
messages[interrupted_indices[0]] = messages[real_indices[-1]]
|
|
114
|
+
drop_indices.update(interrupted_indices[1:])
|
|
115
|
+
drop_indices.update(real_indices) # drop all originals (we moved one)
|
|
116
|
+
elif real_indices:
|
|
117
|
+
# No placeholders, multiple real — keep only the last
|
|
118
|
+
drop_indices.update(real_indices[:-1])
|
|
119
|
+
else:
|
|
120
|
+
# All interrupted — keep only the last
|
|
121
|
+
drop_indices.update(interrupted_indices[:-1])
|
|
80
122
|
|
|
81
|
-
|
|
123
|
+
if drop_indices:
|
|
124
|
+
messages[:] = [msg for i, msg in enumerate(messages) if i not in drop_indices]
|
|
125
|
+
|
|
126
|
+
for idx, msg in enumerate(messages):
|
|
82
127
|
if not isinstance(msg, AIMessage):
|
|
83
128
|
continue
|
|
84
129
|
|
|
@@ -106,11 +151,23 @@ class CopilotKitMiddleware(AgentMiddleware[StateSchema, Any]):
|
|
|
106
151
|
if not tool_calls:
|
|
107
152
|
continue
|
|
108
153
|
|
|
109
|
-
# 2. Strip unanswered tool_calls
|
|
110
|
-
|
|
154
|
+
# 2. Strip unanswered tool_calls — only consider ToolMessages that
|
|
155
|
+
# are ADJACENT (immediately following this AIMessage, before the
|
|
156
|
+
# next non-ToolMessage). A ToolMessage at the wrong position
|
|
157
|
+
# won't satisfy Bedrock's Converse API requirement that toolResult
|
|
158
|
+
# blocks appear in the user turn right after the assistant turn.
|
|
159
|
+
adjacent_tc_ids: set = set()
|
|
160
|
+
j = idx + 1
|
|
161
|
+
while j < len(messages) and isinstance(messages[j], ToolMessage):
|
|
162
|
+
tc_id = getattr(messages[j], 'tool_call_id', None)
|
|
163
|
+
if tc_id:
|
|
164
|
+
adjacent_tc_ids.add(tc_id)
|
|
165
|
+
j += 1
|
|
166
|
+
|
|
167
|
+
unanswered = [tc for tc in tool_calls if tc.get('id') not in adjacent_tc_ids]
|
|
111
168
|
if unanswered:
|
|
112
169
|
unanswered_ids = {tc['id'] for tc in unanswered}
|
|
113
|
-
msg.tool_calls = [tc for tc in tool_calls if tc.get('id') in
|
|
170
|
+
msg.tool_calls = [tc for tc in tool_calls if tc.get('id') in adjacent_tc_ids]
|
|
114
171
|
|
|
115
172
|
# Also strip matching content blocks
|
|
116
173
|
if isinstance(msg.content, list):
|
|
@@ -142,6 +199,22 @@ class CopilotKitMiddleware(AgentMiddleware[StateSchema, Any]):
|
|
|
142
199
|
elif inp is None:
|
|
143
200
|
block['input'] = {}
|
|
144
201
|
|
|
202
|
+
# 5. Remove orphan ToolMessages whose tool_call_id no longer matches
|
|
203
|
+
# any remaining tool_call in any AIMessage. These can be left over
|
|
204
|
+
# after stripping unanswered tool_calls above.
|
|
205
|
+
remaining_tc_ids: set = set()
|
|
206
|
+
for msg in messages:
|
|
207
|
+
if isinstance(msg, AIMessage):
|
|
208
|
+
for tc in (getattr(msg, 'tool_calls', None) or []):
|
|
209
|
+
tc_id = tc.get('id')
|
|
210
|
+
if tc_id:
|
|
211
|
+
remaining_tc_ids.add(tc_id)
|
|
212
|
+
messages[:] = [
|
|
213
|
+
msg for msg in messages
|
|
214
|
+
if not isinstance(msg, ToolMessage)
|
|
215
|
+
or getattr(msg, 'tool_call_id', None) in remaining_tc_ids
|
|
216
|
+
]
|
|
217
|
+
|
|
145
218
|
return messages
|
|
146
219
|
|
|
147
220
|
async def awrap_model_call(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "copilotkit"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.82"
|
|
4
4
|
description = "CopilotKit python SDK"
|
|
5
5
|
authors = ["Markus Ecker <markus.ecker@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -15,6 +15,7 @@ langgraph = {version = ">=0.3.25,<1.1.0"}
|
|
|
15
15
|
langchain = {version = ">=0.3.0"}
|
|
16
16
|
crewai = { version = "0.118.0", optional = true }
|
|
17
17
|
ag-ui-langgraph = { version = ">=0.0.27", extras = ["fastapi"] }
|
|
18
|
+
ag-ui-protocol = ">=0.1.11"
|
|
18
19
|
fastapi = ">=0.115.0,<1.0.0"
|
|
19
20
|
partialjson = "^0.0.8"
|
|
20
21
|
toml = "^0.10.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|