agentcrew-ai 0.8.4__py3-none-any.whl → 0.8.5__py3-none-any.whl
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.
- AgentCrew/__init__.py +1 -1
- AgentCrew/modules/a2a/task_manager.py +1 -1
- AgentCrew/modules/agents/local_agent.py +1 -1
- AgentCrew/modules/browser_automation/element_extractor.py +1 -1
- AgentCrew/modules/browser_automation/service.py +17 -7
- AgentCrew/modules/chat/message/command_processor.py +4 -2
- AgentCrew/modules/chat/message/conversation.py +1 -0
- AgentCrew/modules/chat/message/handler.py +3 -6
- AgentCrew/modules/console/command_handlers.py +2 -2
- AgentCrew/modules/console/confirmation_handler.py +83 -38
- AgentCrew/modules/console/console_ui.py +27 -23
- AgentCrew/modules/console/diff_display.py +203 -0
- AgentCrew/modules/console/display_handlers.py +3 -0
- AgentCrew/modules/console/input_handler.py +1 -1
- AgentCrew/modules/console/tool_display.py +35 -4
- AgentCrew/modules/console/ui_effects.py +30 -14
- AgentCrew/modules/custom_llm/deepinfra_service.py +20 -19
- AgentCrew/modules/custom_llm/github_copilot_service.py +157 -2
- AgentCrew/modules/custom_llm/service.py +1 -9
- {agentcrew_ai-0.8.4.dist-info → agentcrew_ai-0.8.5.dist-info}/METADATA +2 -2
- {agentcrew_ai-0.8.4.dist-info → agentcrew_ai-0.8.5.dist-info}/RECORD +25 -24
- {agentcrew_ai-0.8.4.dist-info → agentcrew_ai-0.8.5.dist-info}/WHEEL +0 -0
- {agentcrew_ai-0.8.4.dist-info → agentcrew_ai-0.8.5.dist-info}/entry_points.txt +0 -0
- {agentcrew_ai-0.8.4.dist-info → agentcrew_ai-0.8.5.dist-info}/licenses/LICENSE +0 -0
- {agentcrew_ai-0.8.4.dist-info → agentcrew_ai-0.8.5.dist-info}/top_level.txt +0 -0
AgentCrew/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.8.
|
|
1
|
+
__version__ = "0.8.5"
|
|
@@ -642,7 +642,7 @@ Check if `when` condition in <Global_Behavior> or <Project_Behavior> matches, up
|
|
|
642
642
|
adaptive_messages["content"].append(
|
|
643
643
|
{
|
|
644
644
|
"type": "text",
|
|
645
|
-
"text": f"Here are conversations that we have discussed:\n- {'\n- '.join(memory_headers)}",
|
|
645
|
+
"text": f"Here are conversations that we have discussed from oldest to latest:\n - {'\n - '.join(memory_headers)}",
|
|
646
646
|
}
|
|
647
647
|
)
|
|
648
648
|
if len(adaptive_messages["content"]) > 0:
|
|
@@ -68,7 +68,7 @@ def clean_markdown_images(markdown_content: str) -> str:
|
|
|
68
68
|
Cleaned markdown content
|
|
69
69
|
"""
|
|
70
70
|
# Pattern for markdown images: 
|
|
71
|
-
markdown_img_pattern = r"
|
|
71
|
+
markdown_img_pattern = r"!?\[([^\]]*)\]\(([^)]+)\)"
|
|
72
72
|
|
|
73
73
|
def replace_markdown_img(match):
|
|
74
74
|
alt_text = match.group(1)
|
|
@@ -7,7 +7,7 @@ scroll content, and extract page information using Chrome DevTools Protocol.
|
|
|
7
7
|
|
|
8
8
|
import time
|
|
9
9
|
from typing import Dict, Any, Optional, List
|
|
10
|
-
from html_to_markdown import
|
|
10
|
+
from html_to_markdown import convert, ConversionOptions, PreprocessingOptions
|
|
11
11
|
import urllib.parse
|
|
12
12
|
|
|
13
13
|
from html.parser import HTMLParser
|
|
@@ -344,13 +344,23 @@ class BrowserAutomationService:
|
|
|
344
344
|
filtered_html = self._filter_hidden_elements(raw_html)
|
|
345
345
|
|
|
346
346
|
# Convert HTML to markdown
|
|
347
|
-
raw_markdown_content = convert_to_markdown(
|
|
347
|
+
# raw_markdown_content = convert_to_markdown(
|
|
348
|
+
# filtered_html,
|
|
349
|
+
# source_encoding="utf-8",
|
|
350
|
+
# strip_newlines=True,
|
|
351
|
+
# extract_metadata=False,
|
|
352
|
+
# remove_forms=False,
|
|
353
|
+
# remove_navigation=False,
|
|
354
|
+
# )
|
|
355
|
+
raw_markdown_content = convert(
|
|
348
356
|
filtered_html,
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
357
|
+
ConversionOptions(
|
|
358
|
+
strip_newlines=True,
|
|
359
|
+
extract_metadata=False,
|
|
360
|
+
),
|
|
361
|
+
PreprocessingOptions(
|
|
362
|
+
remove_navigation=False, remove_forms=False, preset="minimal"
|
|
363
|
+
),
|
|
354
364
|
)
|
|
355
365
|
if not raw_markdown_content:
|
|
356
366
|
return {"success": False, "error": "Could not convert HTML to markdown"}
|
|
@@ -101,6 +101,9 @@ class CommandProcessor:
|
|
|
101
101
|
|
|
102
102
|
async def _handle_copy_command(self, user_input: str) -> CommandResult:
|
|
103
103
|
copy_idx = user_input[5:].strip() or 1
|
|
104
|
+
user_input_idxs = [
|
|
105
|
+
turn.message_index for turn in self.message_handler.conversation_turns
|
|
106
|
+
]
|
|
104
107
|
|
|
105
108
|
asssistant_messages_iterator = reversed(
|
|
106
109
|
[
|
|
@@ -108,8 +111,7 @@ class CommandProcessor:
|
|
|
108
111
|
for i, msg in enumerate(self.message_handler.streamline_messages)
|
|
109
112
|
if msg.get("role") == "assistant"
|
|
110
113
|
and (
|
|
111
|
-
|
|
112
|
-
== "user"
|
|
114
|
+
i + 1 in user_input_idxs
|
|
113
115
|
if i + 1 < len(self.message_handler.streamline_messages)
|
|
114
116
|
else True
|
|
115
117
|
)
|
|
@@ -279,7 +279,7 @@ class MessageHandler(Observable):
|
|
|
279
279
|
# Notify about response progress
|
|
280
280
|
if not self.agent.is_streaming():
|
|
281
281
|
# Delays it a bit when using without stream
|
|
282
|
-
time.sleep(0.
|
|
282
|
+
time.sleep(0.3)
|
|
283
283
|
self._notify("response_chunk", (chunk_text, assistant_response))
|
|
284
284
|
if voice_sentence is not None:
|
|
285
285
|
if (
|
|
@@ -321,6 +321,8 @@ class MessageHandler(Observable):
|
|
|
321
321
|
.lstrip("\n")
|
|
322
322
|
)
|
|
323
323
|
|
|
324
|
+
self.stream_generator = None
|
|
325
|
+
|
|
324
326
|
# End thinking when break the response stream
|
|
325
327
|
if not end_thinking and start_thinking:
|
|
326
328
|
self._notify("thinking_completed", thinking_content)
|
|
@@ -386,11 +388,6 @@ class MessageHandler(Observable):
|
|
|
386
388
|
|
|
387
389
|
return await self.get_assistant_response()
|
|
388
390
|
|
|
389
|
-
self.stream_generator = None
|
|
390
|
-
|
|
391
|
-
if thinking_content:
|
|
392
|
-
self._notify("agent_continue", self.agent.name)
|
|
393
|
-
|
|
394
391
|
# Add assistant response to messages
|
|
395
392
|
if assistant_response.strip():
|
|
396
393
|
self._messages_append(
|
|
@@ -346,8 +346,8 @@ class CommandHandlers:
|
|
|
346
346
|
for behavior_id, behavior_text in project_behaviors.items():
|
|
347
347
|
project_table.add_row(behavior_id, behavior_text)
|
|
348
348
|
|
|
349
|
-
|
|
350
|
-
|
|
349
|
+
self.console.print(project_table)
|
|
350
|
+
self.console.print()
|
|
351
351
|
|
|
352
352
|
def handle_update_behavior_command(
|
|
353
353
|
self, behavior_id: str, behavior_text: str, scope: str = "global"
|
|
@@ -11,6 +11,8 @@ from rich.box import HORIZONTALS
|
|
|
11
11
|
from rich.console import Group
|
|
12
12
|
import time
|
|
13
13
|
|
|
14
|
+
from .diff_display import DiffDisplay
|
|
15
|
+
|
|
14
16
|
from .constants import (
|
|
15
17
|
RICH_STYLE_BLUE,
|
|
16
18
|
RICH_STYLE_BLUE_BOLD,
|
|
@@ -45,6 +47,20 @@ class ConfirmationHandler:
|
|
|
45
47
|
self._handle_ask_tool(tool_use, confirmation_id, message_handler)
|
|
46
48
|
return
|
|
47
49
|
|
|
50
|
+
# Special handling for 'write_or_edit_file' tool with search/replace blocks
|
|
51
|
+
if tool_use["name"] == "write_or_edit_file":
|
|
52
|
+
file_path = tool_use["input"].get("file_path", "")
|
|
53
|
+
text_or_blocks = tool_use["input"].get("text_or_search_replace_blocks", "")
|
|
54
|
+
|
|
55
|
+
if DiffDisplay.has_search_replace_blocks(text_or_blocks):
|
|
56
|
+
self._display_write_or_edit_file_diff(
|
|
57
|
+
tool_use, file_path, text_or_blocks
|
|
58
|
+
)
|
|
59
|
+
self._get_and_handle_tool_response(
|
|
60
|
+
tool_use, confirmation_id, message_handler
|
|
61
|
+
)
|
|
62
|
+
return
|
|
63
|
+
|
|
48
64
|
tool_texts_group = []
|
|
49
65
|
header = Text("🔧 Tool ", style=RICH_STYLE_YELLOW)
|
|
50
66
|
header.append(tool_use["name"], style=RICH_STYLE_BLUE_BOLD)
|
|
@@ -72,8 +88,71 @@ class ConfirmationHandler:
|
|
|
72
88
|
)
|
|
73
89
|
)
|
|
74
90
|
|
|
75
|
-
|
|
91
|
+
self._get_and_handle_tool_response(tool_use, confirmation_id, message_handler)
|
|
92
|
+
|
|
93
|
+
def _handle_ask_tool(self, tool_use, confirmation_id, message_handler):
|
|
94
|
+
"""Handle the ask tool - display question and guided answers."""
|
|
95
|
+
question = tool_use["input"].get("question", "")
|
|
96
|
+
guided_answers = tool_use["input"].get("guided_answers", [])
|
|
97
|
+
if isinstance(guided_answers, str):
|
|
98
|
+
guided_answers = guided_answers.strip("\n ").splitlines()
|
|
99
|
+
|
|
100
|
+
guided_answers.append("Custom your answer")
|
|
101
|
+
|
|
102
|
+
self.input_handler._stop_input_thread()
|
|
103
|
+
# Display the question
|
|
104
|
+
self.console.print(
|
|
105
|
+
Text("\n❓ Agent is asking for clarification:", style=RICH_STYLE_BLUE_BOLD)
|
|
106
|
+
)
|
|
107
|
+
response = self.input_handler.get_choice_input(f"{question}", guided_answers)
|
|
108
|
+
|
|
109
|
+
if response == "Custom your answer":
|
|
110
|
+
custom_answer = self.input_handler.get_prompt_input("Input your answer:")
|
|
111
|
+
message_handler.resolve_tool_confirmation(
|
|
112
|
+
confirmation_id, {"action": "answer", "answer": custom_answer}
|
|
113
|
+
)
|
|
114
|
+
elif response:
|
|
115
|
+
message_handler.resolve_tool_confirmation(
|
|
116
|
+
confirmation_id, {"action": "answer", "answer": response}
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
else:
|
|
120
|
+
message_handler.resolve_tool_confirmation(
|
|
121
|
+
confirmation_id, {"action": "answer", "answer": "Cancelled by user"}
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
self._ui.start_loading_animation()
|
|
125
|
+
|
|
126
|
+
self.input_handler._start_input_thread()
|
|
127
|
+
|
|
128
|
+
def _display_write_or_edit_file_diff(self, tool_use, file_path, blocks_text):
|
|
129
|
+
"""Display split diff view for write_or_edit_file tool."""
|
|
130
|
+
header = Text("📝 File Edit ", style=RICH_STYLE_YELLOW)
|
|
131
|
+
header.append(file_path, style=RICH_STYLE_BLUE_BOLD)
|
|
132
|
+
header.append(" - Search/Replace Blocks", style=RICH_STYLE_YELLOW)
|
|
133
|
+
|
|
134
|
+
self.console.print(
|
|
135
|
+
Panel(header, box=HORIZONTALS, border_style=RICH_STYLE_YELLOW)
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
blocks = DiffDisplay.parse_search_replace_blocks(blocks_text)
|
|
139
|
+
|
|
140
|
+
if not blocks:
|
|
141
|
+
self.console.print(
|
|
142
|
+
Text("No valid search/replace blocks found", style=RICH_STYLE_RED)
|
|
143
|
+
)
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
for block in blocks:
|
|
147
|
+
diff_table = DiffDisplay.create_split_diff_table(
|
|
148
|
+
block["search"], block["replace"], max_width=self.console.width - 4
|
|
149
|
+
)
|
|
150
|
+
self.console.print(diff_table)
|
|
151
|
+
|
|
152
|
+
def _get_and_handle_tool_response(self, tool_use, confirmation_id, message_handler):
|
|
153
|
+
"""Get user response for tool confirmation and handle it."""
|
|
76
154
|
self.input_handler._stop_input_thread()
|
|
155
|
+
|
|
77
156
|
choices = [
|
|
78
157
|
"yes",
|
|
79
158
|
"no",
|
|
@@ -91,11 +170,11 @@ class ConfirmationHandler:
|
|
|
91
170
|
confirmation_id, {"action": "approve"}
|
|
92
171
|
)
|
|
93
172
|
elif response == choices[1]:
|
|
94
|
-
|
|
173
|
+
deny_reason = self.input_handler.get_prompt_input(
|
|
95
174
|
"Please tell me why you are denying this tool: "
|
|
96
175
|
)
|
|
97
176
|
message_handler.resolve_tool_confirmation(
|
|
98
|
-
confirmation_id, {"action": "deny", "reason":
|
|
177
|
+
confirmation_id, {"action": "deny", "reason": deny_reason}
|
|
99
178
|
)
|
|
100
179
|
elif response == choices[2]:
|
|
101
180
|
message_handler.resolve_tool_confirmation(
|
|
@@ -120,44 +199,10 @@ class ConfirmationHandler:
|
|
|
120
199
|
style=RICH_STYLE_YELLOW,
|
|
121
200
|
)
|
|
122
201
|
self.console.print(saved_text)
|
|
123
|
-
self._ui.start_loading_animation()
|
|
124
|
-
self.input_handler._start_input_thread()
|
|
125
|
-
time.sleep(0.2) # Small delay to between tool calls
|
|
126
|
-
|
|
127
|
-
def _handle_ask_tool(self, tool_use, confirmation_id, message_handler):
|
|
128
|
-
"""Handle the ask tool - display question and guided answers."""
|
|
129
|
-
question = tool_use["input"].get("question", "")
|
|
130
|
-
guided_answers = tool_use["input"].get("guided_answers", [])
|
|
131
|
-
if isinstance(guided_answers, str):
|
|
132
|
-
guided_answers = guided_answers.strip("\n ").splitlines()
|
|
133
|
-
|
|
134
|
-
guided_answers.append("Custom your answer")
|
|
135
|
-
|
|
136
|
-
self.input_handler._stop_input_thread()
|
|
137
|
-
# Display the question
|
|
138
|
-
self.console.print(
|
|
139
|
-
Text("\n❓ Agent is asking for clarification:", style=RICH_STYLE_BLUE_BOLD)
|
|
140
|
-
)
|
|
141
|
-
response = self.input_handler.get_choice_input(f"{question}", guided_answers)
|
|
142
|
-
|
|
143
|
-
if response == "Custom your answer":
|
|
144
|
-
custom_answer = self.input_handler.get_prompt_input("Input your answer:")
|
|
145
|
-
message_handler.resolve_tool_confirmation(
|
|
146
|
-
confirmation_id, {"action": "answer", "answer": custom_answer}
|
|
147
|
-
)
|
|
148
|
-
elif response:
|
|
149
|
-
message_handler.resolve_tool_confirmation(
|
|
150
|
-
confirmation_id, {"action": "answer", "answer": response}
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
else:
|
|
154
|
-
message_handler.resolve_tool_confirmation(
|
|
155
|
-
confirmation_id, {"action": "answer", "answer": "Cancelled by user"}
|
|
156
|
-
)
|
|
157
202
|
|
|
158
203
|
self._ui.start_loading_animation()
|
|
159
|
-
|
|
160
204
|
self.input_handler._start_input_thread()
|
|
205
|
+
time.sleep(0.2)
|
|
161
206
|
|
|
162
207
|
def display_mcp_prompt_confirmation(self, prompt_data, input_queue):
|
|
163
208
|
"""Display MCP prompt confirmation request and get user response."""
|
|
@@ -20,7 +20,6 @@ from .constants import (
|
|
|
20
20
|
RICH_STYLE_GREEN,
|
|
21
21
|
RICH_STYLE_BLUE,
|
|
22
22
|
RICH_STYLE_YELLOW,
|
|
23
|
-
RICH_STYLE_GREEN_BOLD,
|
|
24
23
|
RICH_STYLE_YELLOW_BOLD,
|
|
25
24
|
PROMPT_CHAR,
|
|
26
25
|
)
|
|
@@ -83,11 +82,17 @@ class ConsoleUI(Observer):
|
|
|
83
82
|
|
|
84
83
|
if event == "thinking_started":
|
|
85
84
|
self.ui_effects.stop_loading_animation() # Stop loading on first chunk
|
|
86
|
-
self.display_handlers.display_thinking_started(data) # data is agent_name
|
|
85
|
+
# self.display_handlers.display_thinking_started(data) # data is agent_name
|
|
87
86
|
elif event == "thinking_chunk":
|
|
88
|
-
self.
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
self.ui_effects.update_live_display(data, is_thinking=True)
|
|
88
|
+
# self.display_handlers.display_thinking_chunk(
|
|
89
|
+
# data
|
|
90
|
+
# ) # data is the thinking chunk
|
|
91
|
+
#
|
|
92
|
+
elif event == "thinking_completed":
|
|
93
|
+
self.ui_effects.finish_response(
|
|
94
|
+
self.ui_effects.updated_text, is_thinking=True
|
|
95
|
+
)
|
|
91
96
|
elif event == "user_message_created":
|
|
92
97
|
pass
|
|
93
98
|
elif event == "response_chunk":
|
|
@@ -178,10 +183,6 @@ class ConsoleUI(Observer):
|
|
|
178
183
|
f"{data['agent_name'] if 'agent_name' in data else 'other'} agent"
|
|
179
184
|
)
|
|
180
185
|
self.display_handlers.display_message(transfer_text)
|
|
181
|
-
elif event == "agent_continue":
|
|
182
|
-
self.display_handlers.display_message(
|
|
183
|
-
Text(f"\n🤖 {data.upper()}:", style=RICH_STYLE_GREEN_BOLD)
|
|
184
|
-
)
|
|
185
186
|
elif event == "jump_performed":
|
|
186
187
|
jump_text = Text(
|
|
187
188
|
f"🕰️ Jumping to turn {data['turn_number']}...\n",
|
|
@@ -189,12 +190,11 @@ class ConsoleUI(Observer):
|
|
|
189
190
|
)
|
|
190
191
|
preview_text = Text("Conversation rewound to: ", style=RICH_STYLE_YELLOW)
|
|
191
192
|
preview_text.append(data["preview"])
|
|
193
|
+
self._clean_and_reprint_chat()
|
|
192
194
|
|
|
193
195
|
self.display_handlers.display_message(jump_text)
|
|
194
196
|
self.display_handlers.display_message(preview_text)
|
|
195
197
|
self.input_handler.set_current_buffer(data["message"])
|
|
196
|
-
elif event == "thinking_completed":
|
|
197
|
-
self.display_handlers.display_divider()
|
|
198
198
|
elif event == "file_processing":
|
|
199
199
|
self.ui_effects.stop_loading_animation() # Stop loading on first chunk
|
|
200
200
|
self.display_handlers.add_file(data["file_path"])
|
|
@@ -286,25 +286,14 @@ class ConsoleUI(Observer):
|
|
|
286
286
|
Signal handler for SIGWINCH.
|
|
287
287
|
This function is called when the terminal window is resized.
|
|
288
288
|
"""
|
|
289
|
-
import os
|
|
290
289
|
import time
|
|
291
290
|
|
|
292
291
|
if self.input_handler.is_message_processing or self._is_resizing:
|
|
293
292
|
return # Ignore resize during message processing
|
|
294
293
|
self._is_resizing = True
|
|
295
294
|
time.sleep(0.5) # brief pause to allow resize to complete
|
|
296
|
-
|
|
297
|
-
self.display_handlers.display_loaded_conversation(
|
|
298
|
-
self.message_handler.streamline_messages, self.message_handler.agent.name
|
|
299
|
-
)
|
|
300
|
-
self.display_handlers.print_prompt_prefix(
|
|
301
|
-
self.message_handler.agent.name,
|
|
302
|
-
self.message_handler.agent.get_model(),
|
|
303
|
-
self.message_handler.tool_manager.get_effective_yolo_mode(),
|
|
304
|
-
)
|
|
305
|
-
|
|
295
|
+
self._clean_and_reprint_chat()
|
|
306
296
|
self.display_handlers.print_divider("👤 YOU: ", with_time=True)
|
|
307
|
-
|
|
308
297
|
prompt = Text(
|
|
309
298
|
PROMPT_CHAR,
|
|
310
299
|
style=RICH_STYLE_BLUE,
|
|
@@ -318,6 +307,21 @@ class ConsoleUI(Observer):
|
|
|
318
307
|
self.console.print(prompt, end="")
|
|
319
308
|
self._is_resizing = False
|
|
320
309
|
|
|
310
|
+
def _clean_and_reprint_chat(self):
|
|
311
|
+
"""Clear and reprint the chat display."""
|
|
312
|
+
|
|
313
|
+
import os
|
|
314
|
+
|
|
315
|
+
os.system("cls" if os.name == "nt" else "printf '\033c'")
|
|
316
|
+
self.display_handlers.display_loaded_conversation(
|
|
317
|
+
self.message_handler.streamline_messages, self.message_handler.agent.name
|
|
318
|
+
)
|
|
319
|
+
self.display_handlers.print_prompt_prefix(
|
|
320
|
+
self.message_handler.agent.name,
|
|
321
|
+
self.message_handler.agent.get_model(),
|
|
322
|
+
self.message_handler.tool_manager.get_effective_yolo_mode(),
|
|
323
|
+
)
|
|
324
|
+
|
|
321
325
|
def start_streaming_response(self, agent_name: str):
|
|
322
326
|
"""Start streaming the assistant's response."""
|
|
323
327
|
self.ui_effects.start_streaming_response(agent_name)
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Diff display helper for showing file changes in console UI.
|
|
3
|
+
Provides split view diff display with syntax highlighting.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import difflib
|
|
7
|
+
from rich.text import Text
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
from rich.box import SIMPLE_HEAD
|
|
10
|
+
from .constants import (
|
|
11
|
+
RICH_STYLE_BLUE_BOLD,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DiffDisplay:
|
|
16
|
+
"""Helper class for creating split diff views."""
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def has_search_replace_blocks(text: str) -> bool:
|
|
20
|
+
"""Check if text contains search/replace blocks."""
|
|
21
|
+
return (
|
|
22
|
+
"<<<<<<< SEARCH" in text and "=======" in text and ">>>>>>> REPLACE" in text
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def parse_search_replace_blocks(blocks_text: str) -> list:
|
|
27
|
+
"""
|
|
28
|
+
Parse search/replace blocks from text.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
blocks_text: Text containing search/replace blocks
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
List of dicts with 'index', 'search', and 'replace' keys
|
|
35
|
+
"""
|
|
36
|
+
blocks = []
|
|
37
|
+
lines = blocks_text.split("\n")
|
|
38
|
+
i = 0
|
|
39
|
+
block_index = 0
|
|
40
|
+
|
|
41
|
+
while i < len(lines):
|
|
42
|
+
if lines[i].strip() == "<<<<<<< SEARCH":
|
|
43
|
+
search_lines = []
|
|
44
|
+
i += 1
|
|
45
|
+
|
|
46
|
+
while i < len(lines) and lines[i].strip() != "=======":
|
|
47
|
+
search_lines.append(lines[i])
|
|
48
|
+
i += 1
|
|
49
|
+
|
|
50
|
+
if i >= len(lines):
|
|
51
|
+
break
|
|
52
|
+
|
|
53
|
+
i += 1
|
|
54
|
+
replace_lines = []
|
|
55
|
+
|
|
56
|
+
while (
|
|
57
|
+
i < len(lines)
|
|
58
|
+
and lines[i].strip() != ">>>>>>> REPLACE"
|
|
59
|
+
and lines[i].strip() != "======="
|
|
60
|
+
):
|
|
61
|
+
replace_lines.append(lines[i])
|
|
62
|
+
i += 1
|
|
63
|
+
|
|
64
|
+
if i >= len(lines):
|
|
65
|
+
break
|
|
66
|
+
|
|
67
|
+
blocks.append(
|
|
68
|
+
{
|
|
69
|
+
"index": block_index,
|
|
70
|
+
"search": "\n".join(search_lines),
|
|
71
|
+
"replace": "\n".join(replace_lines),
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
block_index += 1
|
|
75
|
+
i += 1
|
|
76
|
+
else:
|
|
77
|
+
i += 1
|
|
78
|
+
|
|
79
|
+
return blocks
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def create_split_diff_table(
|
|
83
|
+
original: str, modified: str, max_width: int = 60
|
|
84
|
+
) -> Table:
|
|
85
|
+
"""
|
|
86
|
+
Create a split diff display table using difflib for intelligent comparison.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
original: Original text content
|
|
90
|
+
modified: Modified text content
|
|
91
|
+
max_width: Maximum width for each column
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Rich Table object with split diff view
|
|
95
|
+
"""
|
|
96
|
+
table = Table(
|
|
97
|
+
show_header=True,
|
|
98
|
+
header_style=RICH_STYLE_BLUE_BOLD,
|
|
99
|
+
box=SIMPLE_HEAD,
|
|
100
|
+
expand=False,
|
|
101
|
+
padding=(0, 1),
|
|
102
|
+
)
|
|
103
|
+
table.add_column("Original", style="", width=max_width, no_wrap=False)
|
|
104
|
+
table.add_column("Modified", style="", width=max_width, no_wrap=False)
|
|
105
|
+
|
|
106
|
+
original_lines = original.split("\n")
|
|
107
|
+
modified_lines = modified.split("\n")
|
|
108
|
+
|
|
109
|
+
matcher = difflib.SequenceMatcher(None, original_lines, modified_lines)
|
|
110
|
+
|
|
111
|
+
for tag, i1, i2, j1, j2 in matcher.get_opcodes():
|
|
112
|
+
if tag == "equal":
|
|
113
|
+
for i, j in zip(range(i1, i2), range(j1, j2)):
|
|
114
|
+
orig_text = Text(original_lines[i], style="dim")
|
|
115
|
+
mod_text = Text(modified_lines[j], style="dim")
|
|
116
|
+
table.add_row(orig_text, mod_text)
|
|
117
|
+
|
|
118
|
+
elif tag == "delete":
|
|
119
|
+
for i in range(i1, i2):
|
|
120
|
+
orig_text = Text(original_lines[i], style="strike red")
|
|
121
|
+
mod_text = Text("", style="dim")
|
|
122
|
+
table.add_row(orig_text, mod_text)
|
|
123
|
+
|
|
124
|
+
elif tag == "insert":
|
|
125
|
+
for j in range(j1, j2):
|
|
126
|
+
orig_text = Text("", style="dim")
|
|
127
|
+
mod_text = Text(modified_lines[j], style="bold green")
|
|
128
|
+
table.add_row(orig_text, mod_text)
|
|
129
|
+
|
|
130
|
+
elif tag == "replace":
|
|
131
|
+
max_lines = max(i2 - i1, j2 - j1)
|
|
132
|
+
|
|
133
|
+
for offset in range(max_lines):
|
|
134
|
+
orig_idx = i1 + offset
|
|
135
|
+
mod_idx = j1 + offset
|
|
136
|
+
|
|
137
|
+
if orig_idx < i2 and mod_idx < j2:
|
|
138
|
+
orig_line = original_lines[orig_idx]
|
|
139
|
+
mod_line = modified_lines[mod_idx]
|
|
140
|
+
|
|
141
|
+
char_matcher = difflib.SequenceMatcher(
|
|
142
|
+
None, orig_line, mod_line
|
|
143
|
+
)
|
|
144
|
+
if char_matcher.ratio() > 0.5:
|
|
145
|
+
orig_text = DiffDisplay._highlight_char_diff(
|
|
146
|
+
orig_line, mod_line, is_original=True
|
|
147
|
+
)
|
|
148
|
+
mod_text = DiffDisplay._highlight_char_diff(
|
|
149
|
+
orig_line, mod_line, is_original=False
|
|
150
|
+
)
|
|
151
|
+
else:
|
|
152
|
+
orig_text = Text(orig_line, style="strike red")
|
|
153
|
+
mod_text = Text(mod_line, style="bold green")
|
|
154
|
+
|
|
155
|
+
table.add_row(orig_text, mod_text)
|
|
156
|
+
|
|
157
|
+
elif orig_idx < i2:
|
|
158
|
+
orig_text = Text(original_lines[orig_idx], style="strike red")
|
|
159
|
+
mod_text = Text("", style="dim")
|
|
160
|
+
table.add_row(orig_text, mod_text)
|
|
161
|
+
|
|
162
|
+
elif mod_idx < j2:
|
|
163
|
+
orig_text = Text("", style="dim")
|
|
164
|
+
mod_text = Text(modified_lines[mod_idx], style="bold green")
|
|
165
|
+
table.add_row(orig_text, mod_text)
|
|
166
|
+
|
|
167
|
+
return table
|
|
168
|
+
|
|
169
|
+
@staticmethod
|
|
170
|
+
def _highlight_char_diff(orig_line: str, mod_line: str, is_original: bool) -> Text:
|
|
171
|
+
"""
|
|
172
|
+
Highlight character-level differences within a line.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
orig_line: Original line text
|
|
176
|
+
mod_line: Modified line text
|
|
177
|
+
is_original: True to highlight original, False for modified
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Rich Text with character-level highlighting
|
|
181
|
+
"""
|
|
182
|
+
result = Text()
|
|
183
|
+
matcher = difflib.SequenceMatcher(None, orig_line, mod_line)
|
|
184
|
+
|
|
185
|
+
for tag, i1, i2, j1, j2 in matcher.get_opcodes():
|
|
186
|
+
if is_original:
|
|
187
|
+
segment = orig_line[i1:i2]
|
|
188
|
+
if tag == "equal":
|
|
189
|
+
result.append(segment, style="dim")
|
|
190
|
+
elif tag == "delete":
|
|
191
|
+
result.append(segment, style="bold red on #3d0000")
|
|
192
|
+
elif tag == "replace":
|
|
193
|
+
result.append(segment, style="bold red on #3d0000")
|
|
194
|
+
else:
|
|
195
|
+
segment = mod_line[j1:j2]
|
|
196
|
+
if tag == "equal":
|
|
197
|
+
result.append(segment, style="dim")
|
|
198
|
+
elif tag == "insert":
|
|
199
|
+
result.append(segment, style="bold green on #003d00")
|
|
200
|
+
elif tag == "replace":
|
|
201
|
+
result.append(segment, style="bold green on #003d00")
|
|
202
|
+
|
|
203
|
+
return result
|
|
@@ -217,6 +217,9 @@ class DisplayHandlers:
|
|
|
217
217
|
transfer_text.append(f"{msg.get('agent', 'unknown')} agent")
|
|
218
218
|
self.display_message(transfer_text)
|
|
219
219
|
continue
|
|
220
|
+
|
|
221
|
+
elif content.startswith("Content of "):
|
|
222
|
+
continue
|
|
220
223
|
self.display_user_message(content)
|
|
221
224
|
elif role == "assistant":
|
|
222
225
|
agent_name = msg.get("agent") or default_agent_name
|
|
@@ -11,6 +11,7 @@ from rich.panel import Panel
|
|
|
11
11
|
from rich.box import HORIZONTALS
|
|
12
12
|
from rich.text import Text
|
|
13
13
|
|
|
14
|
+
from .diff_display import DiffDisplay
|
|
14
15
|
from .constants import (
|
|
15
16
|
RICH_STYLE_GRAY,
|
|
16
17
|
RICH_STYLE_YELLOW,
|
|
@@ -59,7 +60,19 @@ class ToolDisplayHandlers:
|
|
|
59
60
|
header.append(tool_use["name"], style=RICH_STYLE_GRAY)
|
|
60
61
|
|
|
61
62
|
tool_parameters = tool_use.get("input") or tool_use.get("arguments")
|
|
62
|
-
|
|
63
|
+
|
|
64
|
+
if tool_use["name"] == "write_or_edit_file" and isinstance(
|
|
65
|
+
tool_parameters, dict
|
|
66
|
+
):
|
|
67
|
+
file_path = tool_parameters.get("file_path", "")
|
|
68
|
+
text_or_blocks = tool_parameters.get("text_or_search_replace_blocks", "")
|
|
69
|
+
|
|
70
|
+
if DiffDisplay.has_search_replace_blocks(text_or_blocks):
|
|
71
|
+
self._display_write_or_edit_file_use(
|
|
72
|
+
tool_use, file_path, text_or_blocks
|
|
73
|
+
)
|
|
74
|
+
return
|
|
75
|
+
|
|
63
76
|
if isinstance(tool_parameters, dict):
|
|
64
77
|
tool_texts_group.append(Text("Parameters:", style=RICH_STYLE_YELLOW))
|
|
65
78
|
for key, value in tool_parameters.items():
|
|
@@ -96,6 +109,27 @@ class ToolDisplayHandlers:
|
|
|
96
109
|
)
|
|
97
110
|
)
|
|
98
111
|
|
|
112
|
+
def _display_write_or_edit_file_use(
|
|
113
|
+
self, tool_use: Dict, file_path: str, blocks_text: str
|
|
114
|
+
):
|
|
115
|
+
"""Display write_or_edit_file tool with split diff view."""
|
|
116
|
+
tool_icon = self.get_tool_icon(tool_use["name"])
|
|
117
|
+
|
|
118
|
+
header = Text(f"{tool_icon} Tool: ", style=RICH_STYLE_GRAY)
|
|
119
|
+
header.append("write_or_edit_file", style=RICH_STYLE_GRAY)
|
|
120
|
+
header.append(f" → {file_path}", style=RICH_STYLE_BLUE)
|
|
121
|
+
|
|
122
|
+
self.console.print(Panel(header, box=HORIZONTALS, title_align="left"))
|
|
123
|
+
|
|
124
|
+
blocks = DiffDisplay.parse_search_replace_blocks(blocks_text)
|
|
125
|
+
|
|
126
|
+
if blocks:
|
|
127
|
+
for block in blocks:
|
|
128
|
+
diff_table = DiffDisplay.create_split_diff_table(
|
|
129
|
+
block["search"], block["replace"], max_width=self.console.width - 4
|
|
130
|
+
)
|
|
131
|
+
self.console.print(diff_table)
|
|
132
|
+
|
|
99
133
|
def display_tool_result(self, data: Dict):
|
|
100
134
|
"""Display the result of a tool execution."""
|
|
101
135
|
tool_use = data["tool_use"]
|
|
@@ -104,13 +138,10 @@ class ToolDisplayHandlers:
|
|
|
104
138
|
|
|
105
139
|
tool_texts_group = []
|
|
106
140
|
|
|
107
|
-
# Display tool result with better formatting
|
|
108
141
|
header = Text(f"{tool_icon} Tool Result: ", style=RICH_STYLE_GREEN)
|
|
109
142
|
header.append(tool_use["name"], style=RICH_STYLE_GREEN_BOLD)
|
|
110
143
|
|
|
111
|
-
# Format the result based on type
|
|
112
144
|
result_str = str(tool_result)
|
|
113
|
-
# If result is very long, try to format it
|
|
114
145
|
if len(result_str) > 500:
|
|
115
146
|
result_line = Text(result_str[:500] + "...", style=RICH_STYLE_GREEN)
|
|
116
147
|
tool_texts_group.append(result_line)
|
|
@@ -93,15 +93,23 @@ class UIEffects:
|
|
|
93
93
|
self._loading_thread.join(timeout=0.5)
|
|
94
94
|
self._loading_thread = None
|
|
95
95
|
|
|
96
|
-
def start_streaming_response(self, agent_name: str):
|
|
96
|
+
def start_streaming_response(self, agent_name: str, is_thinking=False):
|
|
97
97
|
"""Start streaming the assistant's response."""
|
|
98
98
|
from .constants import RICH_STYLE_GREEN_BOLD
|
|
99
99
|
from rich.text import Text
|
|
100
100
|
|
|
101
|
-
header = Text(
|
|
101
|
+
header = Text(
|
|
102
|
+
f"💭 {agent_name.upper()}'s thinking:"
|
|
103
|
+
if is_thinking
|
|
104
|
+
else f"🤖 {agent_name.upper()}:",
|
|
105
|
+
style=RICH_STYLE_GRAY if is_thinking else RICH_STYLE_GREEN_BOLD,
|
|
106
|
+
)
|
|
102
107
|
|
|
103
108
|
live_panel = Panel(
|
|
104
|
-
"",
|
|
109
|
+
"",
|
|
110
|
+
title=header,
|
|
111
|
+
box=HORIZONTALS,
|
|
112
|
+
border_style=RICH_STYLE_GRAY if is_thinking else RICH_STYLE_GREEN,
|
|
105
113
|
)
|
|
106
114
|
|
|
107
115
|
self.live = Live(
|
|
@@ -123,13 +131,13 @@ class UIEffects:
|
|
|
123
131
|
if self.live:
|
|
124
132
|
self.update_live_display(self.updated_text)
|
|
125
133
|
|
|
126
|
-
def update_live_display(self, chunk: str):
|
|
134
|
+
def update_live_display(self, chunk: str, is_thinking: bool = False):
|
|
127
135
|
"""Update the live display with a new chunk of the response."""
|
|
128
136
|
if not self.live:
|
|
129
|
-
self.start_streaming_response(self.message_handler.agent.name)
|
|
137
|
+
self.start_streaming_response(self.message_handler.agent.name, is_thinking)
|
|
130
138
|
|
|
131
139
|
if chunk != self.updated_text:
|
|
132
|
-
self.updated_text = chunk
|
|
140
|
+
self.updated_text = self.updated_text + chunk if is_thinking else chunk
|
|
133
141
|
|
|
134
142
|
# Only show the last part that fits in the console
|
|
135
143
|
lines = self.updated_text.split("\n")
|
|
@@ -151,9 +159,13 @@ class UIEffects:
|
|
|
151
159
|
from .constants import RICH_STYLE_GREEN_BOLD
|
|
152
160
|
from rich.text import Text
|
|
153
161
|
|
|
162
|
+
agent_name = self.message_handler.agent.name
|
|
163
|
+
|
|
154
164
|
header = Text(
|
|
155
|
-
f"
|
|
156
|
-
|
|
165
|
+
f"💭 {agent_name.upper()}'s thinking:"
|
|
166
|
+
if is_thinking
|
|
167
|
+
else f"🤖 {agent_name.upper()}:",
|
|
168
|
+
style=RICH_STYLE_GRAY if is_thinking else RICH_STYLE_GREEN_BOLD,
|
|
157
169
|
)
|
|
158
170
|
subtitle = Text(
|
|
159
171
|
f"{next(self.spinner)}(Use Ctrl+U/Ctrl+D to scroll)",
|
|
@@ -163,11 +175,11 @@ class UIEffects:
|
|
|
163
175
|
Markdown("\n".join(lines), code_theme=CODE_THEME),
|
|
164
176
|
title=header,
|
|
165
177
|
box=HORIZONTALS,
|
|
166
|
-
subtitle=subtitle,
|
|
178
|
+
subtitle=subtitle if not is_thinking else None,
|
|
167
179
|
title_align="left",
|
|
168
180
|
expand=False,
|
|
169
181
|
height=min(height_limit, len(lines)),
|
|
170
|
-
border_style=RICH_STYLE_GREEN,
|
|
182
|
+
border_style=RICH_STYLE_GRAY if is_thinking else RICH_STYLE_GREEN,
|
|
171
183
|
)
|
|
172
184
|
self.live.update(live_panel, refresh=True)
|
|
173
185
|
|
|
@@ -182,7 +194,7 @@ class UIEffects:
|
|
|
182
194
|
self.live.stop()
|
|
183
195
|
self.live = None
|
|
184
196
|
|
|
185
|
-
def finish_response(self, response: str):
|
|
197
|
+
def finish_response(self, response: str, is_thinking: bool = False):
|
|
186
198
|
"""Finalize and display the complete response."""
|
|
187
199
|
from .constants import RICH_STYLE_GREEN_BOLD
|
|
188
200
|
from rich.text import Text
|
|
@@ -202,16 +214,20 @@ class UIEffects:
|
|
|
202
214
|
if not markdown_formatted_response.strip():
|
|
203
215
|
return
|
|
204
216
|
|
|
217
|
+
agent_name = self.message_handler.agent.name
|
|
218
|
+
|
|
205
219
|
header = Text(
|
|
206
|
-
f"
|
|
207
|
-
|
|
220
|
+
f"💭 {agent_name.upper()}'s thinking:"
|
|
221
|
+
if is_thinking
|
|
222
|
+
else f"🤖 {agent_name.upper()}:",
|
|
223
|
+
style=RICH_STYLE_GRAY if is_thinking else RICH_STYLE_GREEN_BOLD,
|
|
208
224
|
)
|
|
209
225
|
assistant_panel = Panel(
|
|
210
226
|
Markdown(markdown_formatted_response, code_theme=CODE_THEME),
|
|
211
227
|
title=header,
|
|
212
228
|
box=HORIZONTALS,
|
|
213
229
|
title_align="left",
|
|
214
|
-
border_style=RICH_STYLE_GREEN,
|
|
230
|
+
border_style=RICH_STYLE_GRAY if is_thinking else RICH_STYLE_GREEN,
|
|
215
231
|
)
|
|
216
232
|
self.console.print(assistant_panel)
|
|
217
233
|
|
|
@@ -45,19 +45,32 @@ class DeepInfraService(CustomLLMService):
|
|
|
45
45
|
thinking_data
|
|
46
46
|
)
|
|
47
47
|
"""
|
|
48
|
-
chunk_text =
|
|
48
|
+
chunk_text = ""
|
|
49
49
|
input_tokens = 0
|
|
50
50
|
output_tokens = 0
|
|
51
51
|
thinking_content = None # OpenAI doesn't support thinking mode
|
|
52
52
|
|
|
53
|
+
if (not chunk.choices) or (len(chunk.choices) == 0):
|
|
54
|
+
return (
|
|
55
|
+
assistant_response or " ",
|
|
56
|
+
tool_uses,
|
|
57
|
+
input_tokens,
|
|
58
|
+
output_tokens,
|
|
59
|
+
"",
|
|
60
|
+
(thinking_content, None) if thinking_content else None,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
delta_chunk = chunk.choices[0].delta
|
|
53
64
|
# Handle regular content chunks
|
|
65
|
+
#
|
|
54
66
|
if (
|
|
55
|
-
|
|
56
|
-
and
|
|
57
|
-
and hasattr(chunk.choices[0].delta, "content")
|
|
58
|
-
and chunk.choices[0].delta.content is not None
|
|
67
|
+
hasattr(delta_chunk, "reasoning_content")
|
|
68
|
+
and delta_chunk.reasoning_content is not None
|
|
59
69
|
):
|
|
60
|
-
|
|
70
|
+
thinking_content = delta_chunk.reasoning_content
|
|
71
|
+
|
|
72
|
+
if hasattr(delta_chunk, "content") and delta_chunk.content is not None:
|
|
73
|
+
chunk_text = delta_chunk.content
|
|
61
74
|
if "<think>" in chunk_text:
|
|
62
75
|
self._is_thinking = True
|
|
63
76
|
|
|
@@ -87,11 +100,7 @@ class DeepInfraService(CustomLLMService):
|
|
|
87
100
|
output_tokens = chunk.usage.completion_tokens
|
|
88
101
|
|
|
89
102
|
# Handle tool call chunks
|
|
90
|
-
if (
|
|
91
|
-
chunk.choices
|
|
92
|
-
and len(chunk.choices) > 0
|
|
93
|
-
and hasattr(chunk.choices[0].delta, "tool_calls")
|
|
94
|
-
):
|
|
103
|
+
if hasattr(delta_chunk, "tool_calls"):
|
|
95
104
|
delta_tool_calls = chunk.choices[0].delta.tool_calls
|
|
96
105
|
if delta_tool_calls:
|
|
97
106
|
# Process each tool call in the delta
|
|
@@ -162,14 +171,6 @@ class DeepInfraService(CustomLLMService):
|
|
|
162
171
|
except json.JSONDecodeError:
|
|
163
172
|
# Arguments JSON is still incomplete, keep accumulating
|
|
164
173
|
pass
|
|
165
|
-
return (
|
|
166
|
-
assistant_response or " ",
|
|
167
|
-
tool_uses,
|
|
168
|
-
input_tokens,
|
|
169
|
-
output_tokens,
|
|
170
|
-
"",
|
|
171
|
-
(thinking_content, None) if thinking_content else None,
|
|
172
|
-
)
|
|
173
174
|
|
|
174
175
|
return (
|
|
175
176
|
assistant_response or " ",
|
|
@@ -3,7 +3,7 @@ from .service import CustomLLMService
|
|
|
3
3
|
import os
|
|
4
4
|
from dotenv import load_dotenv
|
|
5
5
|
from loguru import logger
|
|
6
|
-
from typing import Dict, Any, Optional, List
|
|
6
|
+
from typing import Dict, Any, Optional, List, Tuple
|
|
7
7
|
from datetime import datetime
|
|
8
8
|
import json
|
|
9
9
|
from uuid import uuid4
|
|
@@ -76,8 +76,26 @@ class GithubCopilotService(CustomLLMService):
|
|
|
76
76
|
return False
|
|
77
77
|
|
|
78
78
|
def _convert_internal_format(self, messages: List[Dict[str, Any]]):
|
|
79
|
-
|
|
79
|
+
thinking_block = None
|
|
80
|
+
for i, msg in enumerate(messages):
|
|
80
81
|
msg.pop("agent", None)
|
|
82
|
+
if msg.get("role") == "assistant":
|
|
83
|
+
if thinking_block:
|
|
84
|
+
msg["reasoning_text"] = thinking_block.get("thinking", "")
|
|
85
|
+
msg["reasoning_opaque"] = thinking_block.get("signature", "")
|
|
86
|
+
thinking_block = None
|
|
87
|
+
del messages[i - 1]
|
|
88
|
+
if isinstance(msg.get("content", ""), List):
|
|
89
|
+
thinking_block = next(
|
|
90
|
+
(
|
|
91
|
+
block
|
|
92
|
+
for block in msg.get("content", [])
|
|
93
|
+
if block.get("type", "text") == "thinking"
|
|
94
|
+
),
|
|
95
|
+
None,
|
|
96
|
+
)
|
|
97
|
+
msg["content"] = []
|
|
98
|
+
|
|
81
99
|
if "tool_calls" in msg and msg.get("tool_calls", []):
|
|
82
100
|
for tool_call in msg["tool_calls"]:
|
|
83
101
|
tool_call["function"] = {}
|
|
@@ -85,6 +103,7 @@ class GithubCopilotService(CustomLLMService):
|
|
|
85
103
|
tool_call["function"]["arguments"] = json.dumps(
|
|
86
104
|
tool_call.pop("arguments", {})
|
|
87
105
|
)
|
|
106
|
+
|
|
88
107
|
if msg.get("role") == "tool":
|
|
89
108
|
# Special treatment for GitHub Copilot GPT-4.1 model
|
|
90
109
|
# At the the time of writing, GitHub Copilot GPT-4.1 model cannot read tool results with array content
|
|
@@ -115,6 +134,8 @@ class GithubCopilotService(CustomLLMService):
|
|
|
115
134
|
msg["content"] = (
|
|
116
135
|
"\n".join(parsed_tool_result) if parsed_tool_result else ""
|
|
117
136
|
)
|
|
137
|
+
elif isinstance(msg.get("content", ""), str):
|
|
138
|
+
msg["content"] = [{"type": "text", "text": msg["content"]}]
|
|
118
139
|
|
|
119
140
|
return messages
|
|
120
141
|
|
|
@@ -127,6 +148,140 @@ class GithubCopilotService(CustomLLMService):
|
|
|
127
148
|
self.extra_headers["X-Request-Id"] = str(uuid4())
|
|
128
149
|
return await super().process_message(prompt, temperature)
|
|
129
150
|
|
|
151
|
+
def _process_stream_chunk(
|
|
152
|
+
self, chunk, assistant_response: str, tool_uses: List[Dict]
|
|
153
|
+
) -> Tuple[str, List[Dict], int, int, Optional[str], Optional[tuple]]:
|
|
154
|
+
"""
|
|
155
|
+
Process a single chunk from the streaming response.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
chunk: The chunk from the stream
|
|
159
|
+
assistant_response: Current accumulated assistant response
|
|
160
|
+
tool_uses: Current tool use information
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
tuple: (
|
|
164
|
+
updated_assistant_response,
|
|
165
|
+
updated_tool_uses,
|
|
166
|
+
input_tokens,
|
|
167
|
+
output_tokens,
|
|
168
|
+
chunk_text,
|
|
169
|
+
thinking_data
|
|
170
|
+
)
|
|
171
|
+
"""
|
|
172
|
+
chunk_text = ""
|
|
173
|
+
input_tokens = 0
|
|
174
|
+
output_tokens = 0
|
|
175
|
+
thinking_content = None # OpenAI doesn't support thinking mode
|
|
176
|
+
thinking_signature = None
|
|
177
|
+
|
|
178
|
+
if (not chunk.choices) or (len(chunk.choices) == 0):
|
|
179
|
+
return (
|
|
180
|
+
assistant_response or " ",
|
|
181
|
+
tool_uses,
|
|
182
|
+
input_tokens,
|
|
183
|
+
output_tokens,
|
|
184
|
+
"",
|
|
185
|
+
(thinking_content, None) if thinking_content else None,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
delta_chunk = chunk.choices[0].delta
|
|
189
|
+
|
|
190
|
+
# Handle thinking content
|
|
191
|
+
if (
|
|
192
|
+
hasattr(delta_chunk, "reasoning_text")
|
|
193
|
+
and delta_chunk.reasoning_text is not None
|
|
194
|
+
):
|
|
195
|
+
thinking_content = delta_chunk.reasoning_text
|
|
196
|
+
|
|
197
|
+
if (
|
|
198
|
+
hasattr(delta_chunk, "reasoning_opaque")
|
|
199
|
+
and delta_chunk.reasoning_opaque is not None
|
|
200
|
+
):
|
|
201
|
+
thinking_signature = delta_chunk.reasoning_opaque
|
|
202
|
+
# Handle regular content chunks
|
|
203
|
+
if hasattr(delta_chunk, "content") and delta_chunk.content is not None:
|
|
204
|
+
chunk_text = chunk.choices[0].delta.content
|
|
205
|
+
assistant_response += chunk_text
|
|
206
|
+
|
|
207
|
+
# Handle final chunk with usage information
|
|
208
|
+
if hasattr(chunk, "usage"):
|
|
209
|
+
if hasattr(chunk.usage, "prompt_tokens"):
|
|
210
|
+
input_tokens = chunk.usage.prompt_tokens
|
|
211
|
+
if hasattr(chunk.usage, "completion_tokens"):
|
|
212
|
+
output_tokens = chunk.usage.completion_tokens
|
|
213
|
+
|
|
214
|
+
# Handle tool call chunks
|
|
215
|
+
if hasattr(delta_chunk, "tool_calls"):
|
|
216
|
+
delta_tool_calls = chunk.choices[0].delta.tool_calls
|
|
217
|
+
if delta_tool_calls:
|
|
218
|
+
# Process each tool call in the delta
|
|
219
|
+
for tool_call_delta in delta_tool_calls:
|
|
220
|
+
# Check if this is a new tool call
|
|
221
|
+
if getattr(tool_call_delta, "id"):
|
|
222
|
+
# Create a new tool call entry
|
|
223
|
+
tool_uses.append(
|
|
224
|
+
{
|
|
225
|
+
"id": getattr(tool_call_delta, "id")
|
|
226
|
+
if hasattr(tool_call_delta, "id")
|
|
227
|
+
else f"toolu_{len(tool_uses)}",
|
|
228
|
+
"name": getattr(tool_call_delta.function, "name", "")
|
|
229
|
+
if hasattr(tool_call_delta, "function")
|
|
230
|
+
else "",
|
|
231
|
+
"input": {},
|
|
232
|
+
"type": "function",
|
|
233
|
+
"response": "",
|
|
234
|
+
}
|
|
235
|
+
)
|
|
236
|
+
tool_call_index = len(tool_uses) - 1
|
|
237
|
+
|
|
238
|
+
# # Update existing tool call with new data
|
|
239
|
+
# if hasattr(tool_call_delta, "id") and tool_call_delta.id:
|
|
240
|
+
# tool_uses[tool_call_index]["id"] = tool_call_delta.id
|
|
241
|
+
|
|
242
|
+
if hasattr(tool_call_delta, "function"):
|
|
243
|
+
if (
|
|
244
|
+
hasattr(tool_call_delta.function, "name")
|
|
245
|
+
and tool_call_delta.function.name
|
|
246
|
+
):
|
|
247
|
+
tool_uses[tool_call_index]["name"] = (
|
|
248
|
+
tool_call_delta.function.name
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
if (
|
|
252
|
+
hasattr(tool_call_delta.function, "arguments")
|
|
253
|
+
and tool_call_delta.function.arguments
|
|
254
|
+
):
|
|
255
|
+
# Accumulate arguments as they come in chunks
|
|
256
|
+
current_args = tool_uses[tool_call_index].get(
|
|
257
|
+
"args_json", ""
|
|
258
|
+
)
|
|
259
|
+
tool_uses[tool_call_index]["args_json"] = (
|
|
260
|
+
current_args + tool_call_delta.function.arguments
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Try to parse JSON if it seems complete
|
|
264
|
+
try:
|
|
265
|
+
args_json = tool_uses[tool_call_index]["args_json"]
|
|
266
|
+
tool_uses[tool_call_index]["input"] = json.loads(
|
|
267
|
+
args_json
|
|
268
|
+
)
|
|
269
|
+
# Keep args_json for accumulation but use input for execution
|
|
270
|
+
except json.JSONDecodeError:
|
|
271
|
+
# Arguments JSON is still incomplete, keep accumulating
|
|
272
|
+
pass
|
|
273
|
+
|
|
274
|
+
return (
|
|
275
|
+
assistant_response or " ",
|
|
276
|
+
tool_uses,
|
|
277
|
+
input_tokens,
|
|
278
|
+
output_tokens,
|
|
279
|
+
chunk_text,
|
|
280
|
+
(thinking_content, thinking_signature)
|
|
281
|
+
if thinking_content or thinking_signature
|
|
282
|
+
else None,
|
|
283
|
+
)
|
|
284
|
+
|
|
130
285
|
async def stream_assistant_response(self, messages):
|
|
131
286
|
"""Stream the assistant's response with tool support."""
|
|
132
287
|
|
|
@@ -358,7 +358,7 @@ class CustomLLMService(OpenAIService):
|
|
|
358
358
|
thinking_data
|
|
359
359
|
)
|
|
360
360
|
"""
|
|
361
|
-
chunk_text =
|
|
361
|
+
chunk_text = ""
|
|
362
362
|
input_tokens = 0
|
|
363
363
|
output_tokens = 0
|
|
364
364
|
thinking_content = None # OpenAI doesn't support thinking mode
|
|
@@ -462,14 +462,6 @@ class CustomLLMService(OpenAIService):
|
|
|
462
462
|
except json.JSONDecodeError:
|
|
463
463
|
# Arguments JSON is still incomplete, keep accumulating
|
|
464
464
|
pass
|
|
465
|
-
return (
|
|
466
|
-
assistant_response or " ",
|
|
467
|
-
tool_uses,
|
|
468
|
-
input_tokens,
|
|
469
|
-
output_tokens,
|
|
470
|
-
"",
|
|
471
|
-
(thinking_content, None) if thinking_content else None,
|
|
472
|
-
)
|
|
473
465
|
|
|
474
466
|
return (
|
|
475
467
|
assistant_response or " ",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentcrew-ai
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.5
|
|
4
4
|
Summary: Multi-Agents Interactive Chat Tool
|
|
5
5
|
Author-email: Quy Truong <quy.truong@saigontechnology.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -47,7 +47,7 @@ Requires-Dist: sounddevice>=0.5.2
|
|
|
47
47
|
Requires-Dist: soundfile>=0.13.1
|
|
48
48
|
Requires-Dist: jsonref>=1.1.0
|
|
49
49
|
Requires-Dist: pychromedevtools>=0.3.3
|
|
50
|
-
Requires-Dist: html-to-markdown
|
|
50
|
+
Requires-Dist: html-to-markdown>=2.9.1
|
|
51
51
|
Requires-Dist: pip-system-certs>=5.2
|
|
52
52
|
Requires-Dist: loguru>=0.7.3
|
|
53
53
|
Requires-Dist: jsonschema>=4.25.1
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
AgentCrew/__init__.py,sha256=
|
|
1
|
+
AgentCrew/__init__.py,sha256=K0kGrhh1kzVisZcoSkeuJdC06rTwxufV05Vy2hOVGoo,22
|
|
2
2
|
AgentCrew/app.py,sha256=dXjwoHLKf1tSOq1Vc-9wsF1dpjS5CaGqdiFSksKKwKA,36584
|
|
3
3
|
AgentCrew/main.py,sha256=tjyw4Z29YKKyzVKqDgF5icI1IM59DUiJrXZw6Sv_xN8,11187
|
|
4
4
|
AgentCrew/main_docker.py,sha256=1jpB-XOm-t3GWTKYidGHHkCSzJ-p39ua0E6-_Nj8_9Y,5472
|
|
@@ -10,7 +10,7 @@ AgentCrew/modules/a2a/agent_cards.py,sha256=SWmo3jVNSrPUqpL8Tv6T3h1a-0oXrOgqJrbP
|
|
|
10
10
|
AgentCrew/modules/a2a/errors.py,sha256=Yaw7Ew5LYDvrYgKZ34heBnEqlIXFF3_5GqqpNhNlveI,2353
|
|
11
11
|
AgentCrew/modules/a2a/registry.py,sha256=1PkasOOpRqulE7mMl1UNSt5GrqGT8AhP9UCWWSAF34I,2671
|
|
12
12
|
AgentCrew/modules/a2a/server.py,sha256=CT_0Te4BBWJEwgbX0wLvHcu6gJAMFsGswwS4IOC5L_k,12345
|
|
13
|
-
AgentCrew/modules/a2a/task_manager.py,sha256=
|
|
13
|
+
AgentCrew/modules/a2a/task_manager.py,sha256=u-3Wfk4gVqScGtqQd4E9A7lcACFFKOxsGmPWG_kXzIk,25624
|
|
14
14
|
AgentCrew/modules/a2a/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
AgentCrew/modules/a2a/common/client/__init__.py,sha256=PoBTKucH8G2onta-vHT4qUJcBYa3TIabGqCO5GVVyT0,118
|
|
16
16
|
AgentCrew/modules/a2a/common/client/card_resolver.py,sha256=imtwwMNb1yLxTPcrct1ToH9fA18mzgAFYKR5vYTHHRw,637
|
|
@@ -22,7 +22,7 @@ AgentCrew/modules/a2a/common/server/utils.py,sha256=pmIBG5cyzaVNXbyESLux2AcogwPa
|
|
|
22
22
|
AgentCrew/modules/agents/__init__.py,sha256=rMwchcGZzapLLZdUQUaJtCLowHoG-dOn8_VljKz8NrI,165
|
|
23
23
|
AgentCrew/modules/agents/base.py,sha256=i7w0X7mcTRWwgyQwqN-iOIniLce9cSM2wGG3njlZpHk,2641
|
|
24
24
|
AgentCrew/modules/agents/example.py,sha256=_-Nd7EKHprKXZLN9_ava0b9vR7wGyUzsXSEV7_js7Ho,6894
|
|
25
|
-
AgentCrew/modules/agents/local_agent.py,sha256=
|
|
25
|
+
AgentCrew/modules/agents/local_agent.py,sha256=LiylFxS7dgAC0-w3ZXJ92SE1ZUyWdrJR3j_zH9xnGuM,31880
|
|
26
26
|
AgentCrew/modules/agents/manager.py,sha256=JHwEp-GwlYMRTv2RItfJOnVJlePZOLK31xYi3taEuF0,24863
|
|
27
27
|
AgentCrew/modules/agents/remote_agent.py,sha256=bBgO8E3dXZuu9ofklXLfL5-8mxF_XZOLyqj6rrwTXR8,6432
|
|
28
28
|
AgentCrew/modules/agents/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -33,9 +33,9 @@ AgentCrew/modules/anthropic/__init__.py,sha256=OcpRL0PKADMAS7hxTOH6U7J-NeiQJBm6x
|
|
|
33
33
|
AgentCrew/modules/anthropic/service.py,sha256=olpiiptIVubc11zmbALOoAMFU-XVx_bIHcj82sUBTS4,18839
|
|
34
34
|
AgentCrew/modules/browser_automation/__init__.py,sha256=1Qq6Rqz4Mv4arPKWKSDgIOV-G72R5Jey2-qdkLMLVKA,86
|
|
35
35
|
AgentCrew/modules/browser_automation/chrome_manager.py,sha256=iBFcImVoeUF5qc8eRzSsihuBSiPhDF0DOSYfda7OHas,11901
|
|
36
|
-
AgentCrew/modules/browser_automation/element_extractor.py,sha256=
|
|
36
|
+
AgentCrew/modules/browser_automation/element_extractor.py,sha256=J1Gp6xDSWFDt7690JgBhX6ryXfNeVUCLwYBA3IGtCqo,15914
|
|
37
37
|
AgentCrew/modules/browser_automation/js_loader.py,sha256=vdm-cQA5I4sL5Y0s7iaMnxSwcu0IZEdtczCgoHXAkK4,18315
|
|
38
|
-
AgentCrew/modules/browser_automation/service.py,sha256=
|
|
38
|
+
AgentCrew/modules/browser_automation/service.py,sha256=VFgDOoCqamL_YTRrJ7G596KKsOz3UF6Kg2u4FRfYYwU,28247
|
|
39
39
|
AgentCrew/modules/browser_automation/tool.py,sha256=q62Cmo8bgGFs-8PrXKqOYJRNniNAUkCc54wUirOKG2s,23997
|
|
40
40
|
AgentCrew/modules/browser_automation/js/click_element.js,sha256=eNkbPX9wXyj9mPnfNILJjtbiPF0iKCT52bfTFQRhT1Y,1652
|
|
41
41
|
AgentCrew/modules/browser_automation/js/draw_element_boxes.js,sha256=4ToM8UYy2HUyXCC2l219Zghjj2lG3JZD1X2mjsLgdtU,5781
|
|
@@ -55,9 +55,9 @@ AgentCrew/modules/chat/history.py,sha256=I7q1_s16h975uAtq6fxC4UcS9iuk1aUpKkMMYCc
|
|
|
55
55
|
AgentCrew/modules/chat/message_handler.py,sha256=A8DfuIPjW0R7s3a0P8VzNa2PF6HgXcRt8CEm2nL8fEE,306
|
|
56
56
|
AgentCrew/modules/chat/message/__init__.py,sha256=SllV6nacLgBSTwkUhI7VbEW-7gwQxQh6cCjRtbU02O8,146
|
|
57
57
|
AgentCrew/modules/chat/message/base.py,sha256=Xnes4tQCfMfWShSHG8f3ZZ5y5Q0wTWy5g0EwnNmflK0,1016
|
|
58
|
-
AgentCrew/modules/chat/message/command_processor.py,sha256=
|
|
59
|
-
AgentCrew/modules/chat/message/conversation.py,sha256
|
|
60
|
-
AgentCrew/modules/chat/message/handler.py,sha256=
|
|
58
|
+
AgentCrew/modules/chat/message/command_processor.py,sha256=3yz5bCRybFDmmBl0lK-MMQZlQCiDvCr0JdXOVTUX8S8,33541
|
|
59
|
+
AgentCrew/modules/chat/message/conversation.py,sha256=olGWFPi3u1wbnqpbPllMhKo4qGepvCj0O_Bmdh-ovwY,10841
|
|
60
|
+
AgentCrew/modules/chat/message/handler.py,sha256=bUL-BfNxWs1-1IQ-CkN0Gq6QkhDSZqQDrCtlV0fFxIg,23508
|
|
61
61
|
AgentCrew/modules/chat/message/tool_manager.py,sha256=JZTmAQ08MMO42t9FeSKYzuSunkaBVJaragDfZBnIEkM,12852
|
|
62
62
|
AgentCrew/modules/clipboard/__init__.py,sha256=Xb0wwQ-9dwwLT3R3TYB87pwtDTNX5HAzhkr8iuR6mBc,77
|
|
63
63
|
AgentCrew/modules/clipboard/service.py,sha256=FCo3zSJH0zDdvW73u4D_ty9fTMsXmOeVsAIOfyJ54Ws,6246
|
|
@@ -74,22 +74,23 @@ AgentCrew/modules/command_execution/types.py,sha256=KVxSmqTF_DdUoGBMMVt2reRLItw_
|
|
|
74
74
|
AgentCrew/modules/config/__init__.py,sha256=ehO0aAkK98f9BmMjG9uR15Hc9Lwj9CnJZl24XUtUT_M,80
|
|
75
75
|
AgentCrew/modules/config/config_management.py,sha256=yxmfx1nQAZxTzJ4g3F3ybYlmTTFpMmKxfNETyzsQ9uA,33648
|
|
76
76
|
AgentCrew/modules/console/__init__.py,sha256=nO53lUaMEAshdIqDEmgNZ_r35jyg6CuMa7Tsj55Y09g,66
|
|
77
|
-
AgentCrew/modules/console/command_handlers.py,sha256=
|
|
77
|
+
AgentCrew/modules/console/command_handlers.py,sha256=qBl5GjZVnuaMJdilJKcPsYpU1E-XbI_Nv5veINU-cic,16232
|
|
78
78
|
AgentCrew/modules/console/completers.py,sha256=WeP5rJvCWq4Ept2_ajK9wjpDiTT4C5blcX5TeV3GSzU,17682
|
|
79
|
-
AgentCrew/modules/console/confirmation_handler.py,sha256=
|
|
80
|
-
AgentCrew/modules/console/console_ui.py,sha256=
|
|
79
|
+
AgentCrew/modules/console/confirmation_handler.py,sha256=sOhJVmrMgiqTlUI6G9xjzseeeWn54G-aOc6WevWcXL8,10221
|
|
80
|
+
AgentCrew/modules/console/console_ui.py,sha256=09XkGsIivVQK4z1-gOyuHtdXfiMMAyc4DvnWHlvIqho,29836
|
|
81
81
|
AgentCrew/modules/console/constants.py,sha256=fwLj52O96_t6m1qb0SOiaotM2dMLwXH83KAERm9ltLA,704
|
|
82
82
|
AgentCrew/modules/console/conversation_handler.py,sha256=vVtGxQJ4sEZJ77svBFJMIGiWiEfE47yDxvt7gZ9bRCA,3632
|
|
83
|
-
AgentCrew/modules/console/
|
|
84
|
-
AgentCrew/modules/console/
|
|
85
|
-
AgentCrew/modules/console/
|
|
86
|
-
AgentCrew/modules/console/
|
|
83
|
+
AgentCrew/modules/console/diff_display.py,sha256=rXkrCQBuQ9K6V22uS-PKd6qz6MtC8ZfdvDuIolCuGok,7063
|
|
84
|
+
AgentCrew/modules/console/display_handlers.py,sha256=ZSpBZxvX5X9VB2dvAB679KQwPTVhRPHRrmsgCMhANyA,17651
|
|
85
|
+
AgentCrew/modules/console/input_handler.py,sha256=f31LiMwOTZ3No0ktWLMxQAnm7Fij0GN9-ECyDWInYlI,18141
|
|
86
|
+
AgentCrew/modules/console/tool_display.py,sha256=aTjNWcfzmJXwpXU64PqMfNFRCiVrtMD-JFn_Wd2cNYY,7306
|
|
87
|
+
AgentCrew/modules/console/ui_effects.py,sha256=n5w863kI0VKU_afGmXjscYg8FmsaX4IJyNQRGCDoOLI,8218
|
|
87
88
|
AgentCrew/modules/console/utils.py,sha256=TFIyyYVFlWMB0FCAq4H09Yp6UCezUEHg3HNEZXuVmiA,273
|
|
88
89
|
AgentCrew/modules/custom_llm/__init__.py,sha256=NvgE3c6rYd52SmRITqGeHqaGOnHrK9vU8RBqD9_6Mdo,337
|
|
89
90
|
AgentCrew/modules/custom_llm/copilot_response_service.py,sha256=tySzxnLMzMt3WjwhvBegtDP7Qk43D2Wc5bxZAi0Z3sA,4605
|
|
90
|
-
AgentCrew/modules/custom_llm/deepinfra_service.py,sha256=
|
|
91
|
-
AgentCrew/modules/custom_llm/github_copilot_service.py,sha256=
|
|
92
|
-
AgentCrew/modules/custom_llm/service.py,sha256=
|
|
91
|
+
AgentCrew/modules/custom_llm/deepinfra_service.py,sha256=uY7f-lg-hnR7A1ZYwKSE10c4WlJ9C693eBiZxLV6jlw,7553
|
|
92
|
+
AgentCrew/modules/custom_llm/github_copilot_service.py,sha256=yCWpD5Sn6LiWMeMtR6sKMTq-xeEXxge5WFpHP84SvjU,14391
|
|
93
|
+
AgentCrew/modules/custom_llm/service.py,sha256=UkF8RtCw56eY1Tusc-SUSobz7IvnYBIgGvZNctuIWQk,19437
|
|
93
94
|
AgentCrew/modules/file_editing/__init__.py,sha256=Q6oDyP3bHslFpmxHdrCCLVx1M_awaHD2WqFTSEU3VIY,241
|
|
94
95
|
AgentCrew/modules/file_editing/safety_validator.py,sha256=lLNb_GpIIX2QtN86U2NKbM-b3VYW3b6jNkXL5hKPlok,5575
|
|
95
96
|
AgentCrew/modules/file_editing/search_replace_engine.py,sha256=i8e18VIvxOwCazjnfrmLp637laDCsJSpZs5YsW9Ic-Y,10751
|
|
@@ -185,9 +186,9 @@ AgentCrew/modules/voice/text_cleaner.py,sha256=NgAVBPkP2hFI330nJOyMK_oqP3R2AGZ22
|
|
|
185
186
|
AgentCrew/modules/web_search/__init__.py,sha256=sVf_z6nH2JghK46pG92PUtDghPIkceiX1F0Ul30euXU,111
|
|
186
187
|
AgentCrew/modules/web_search/service.py,sha256=DKcOdRSHB5AEvbK8pvTXdffSnk6rFRTzaM1FXf2B70E,4006
|
|
187
188
|
AgentCrew/modules/web_search/tool.py,sha256=GV4xleVFs0UwiPS9toSzPzZei3ehsDZWxTQCJCRaEs8,6655
|
|
188
|
-
agentcrew_ai-0.8.
|
|
189
|
-
agentcrew_ai-0.8.
|
|
190
|
-
agentcrew_ai-0.8.
|
|
191
|
-
agentcrew_ai-0.8.
|
|
192
|
-
agentcrew_ai-0.8.
|
|
193
|
-
agentcrew_ai-0.8.
|
|
189
|
+
agentcrew_ai-0.8.5.dist-info/licenses/LICENSE,sha256=O51CIaOUcxVLNf0_PE_a8ap5bf3iXe4SrWN_5NO1PSU,11348
|
|
190
|
+
agentcrew_ai-0.8.5.dist-info/METADATA,sha256=abjIS_DBdZu09Ird2G5jkFNgc4njvOQflV0ttMlZyYg,18056
|
|
191
|
+
agentcrew_ai-0.8.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
192
|
+
agentcrew_ai-0.8.5.dist-info/entry_points.txt,sha256=N9R5jslrBYA8dTaNMRJ_JdSosJ6jkpGEtnJFzlcZD6k,54
|
|
193
|
+
agentcrew_ai-0.8.5.dist-info/top_level.txt,sha256=bSsmhCOn6g-JytoVMpxB_QAnCgb7lV56fcnxUhbfrvg,10
|
|
194
|
+
agentcrew_ai-0.8.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|