janito 1.8.1__py3-none-any.whl → 1.10.0__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.
- janito/__init__.py +1 -1
- janito/agent/api_exceptions.py +4 -0
- janito/agent/config.py +1 -1
- janito/agent/config_defaults.py +2 -3
- janito/agent/config_utils.py +0 -9
- janito/agent/conversation.py +177 -114
- janito/agent/conversation_api.py +179 -159
- janito/agent/conversation_tool_calls.py +11 -8
- janito/agent/llm_conversation_history.py +70 -0
- janito/agent/openai_client.py +44 -21
- janito/agent/openai_schema_generator.py +164 -128
- janito/agent/platform_discovery.py +134 -77
- janito/agent/profile_manager.py +5 -5
- janito/agent/rich_message_handler.py +80 -31
- janito/agent/templates/profiles/system_prompt_template_base.txt.j2 +9 -8
- janito/agent/test_openai_schema_generator.py +93 -0
- janito/agent/tool_base.py +7 -2
- janito/agent/tool_executor.py +63 -50
- janito/agent/tool_registry.py +5 -2
- janito/agent/tool_use_tracker.py +42 -5
- janito/agent/tools/__init__.py +13 -12
- janito/agent/tools/create_directory.py +9 -6
- janito/agent/tools/create_file.py +35 -54
- janito/agent/tools/delete_text_in_file.py +97 -0
- janito/agent/tools/fetch_url.py +50 -5
- janito/agent/tools/find_files.py +40 -26
- janito/agent/tools/get_file_outline/__init__.py +1 -0
- janito/agent/tools/{outline_file/__init__.py → get_file_outline/core.py} +14 -18
- janito/agent/tools/get_file_outline/python_outline.py +134 -0
- janito/agent/tools/{search_outline.py → get_file_outline/search_outline.py} +11 -0
- janito/agent/tools/get_lines.py +21 -12
- janito/agent/tools/move_file.py +13 -12
- janito/agent/tools/present_choices.py +3 -1
- janito/agent/tools/python_command_runner.py +150 -0
- janito/agent/tools/python_file_runner.py +148 -0
- janito/agent/tools/python_stdin_runner.py +154 -0
- janito/agent/tools/remove_directory.py +4 -2
- janito/agent/tools/remove_file.py +15 -13
- janito/agent/tools/replace_file.py +72 -0
- janito/agent/tools/replace_text_in_file.py +7 -5
- janito/agent/tools/run_bash_command.py +29 -72
- janito/agent/tools/run_powershell_command.py +142 -102
- janito/agent/tools/search_text.py +177 -131
- janito/agent/tools/validate_file_syntax/__init__.py +1 -0
- janito/agent/tools/validate_file_syntax/core.py +94 -0
- janito/agent/tools/validate_file_syntax/css_validator.py +35 -0
- janito/agent/tools/validate_file_syntax/html_validator.py +77 -0
- janito/agent/tools/validate_file_syntax/js_validator.py +27 -0
- janito/agent/tools/validate_file_syntax/json_validator.py +6 -0
- janito/agent/tools/validate_file_syntax/markdown_validator.py +66 -0
- janito/agent/tools/validate_file_syntax/ps1_validator.py +32 -0
- janito/agent/tools/validate_file_syntax/python_validator.py +5 -0
- janito/agent/tools/validate_file_syntax/xml_validator.py +11 -0
- janito/agent/tools/validate_file_syntax/yaml_validator.py +6 -0
- janito/agent/tools_utils/__init__.py +1 -0
- janito/agent/tools_utils/action_type.py +7 -0
- janito/agent/tools_utils/dir_walk_utils.py +24 -0
- janito/agent/tools_utils/formatting.py +49 -0
- janito/agent/tools_utils/gitignore_utils.py +69 -0
- janito/agent/tools_utils/test_gitignore_utils.py +46 -0
- janito/agent/tools_utils/utils.py +30 -0
- janito/cli/_livereload_log_utils.py +13 -0
- janito/cli/_print_config.py +63 -61
- janito/cli/arg_parser.py +57 -14
- janito/cli/cli_main.py +270 -0
- janito/cli/livereload_starter.py +60 -0
- janito/cli/main.py +166 -99
- janito/cli/one_shot.py +80 -0
- janito/cli/termweb_starter.py +2 -2
- janito/i18n/__init__.py +1 -1
- janito/livereload/app.py +25 -0
- janito/rich_utils.py +41 -25
- janito/{cli_chat_shell → shell}/commands/__init__.py +19 -14
- janito/{cli_chat_shell → shell}/commands/config.py +4 -4
- janito/shell/commands/conversation_restart.py +74 -0
- janito/shell/commands/edit.py +24 -0
- janito/shell/commands/history_view.py +18 -0
- janito/{cli_chat_shell → shell}/commands/lang.py +3 -0
- janito/shell/commands/livelogs.py +42 -0
- janito/{cli_chat_shell → shell}/commands/prompt.py +16 -6
- janito/shell/commands/session.py +35 -0
- janito/{cli_chat_shell → shell}/commands/session_control.py +3 -5
- janito/{cli_chat_shell → shell}/commands/termweb_log.py +18 -10
- janito/shell/commands/tools.py +26 -0
- janito/shell/commands/track.py +36 -0
- janito/shell/commands/utility.py +28 -0
- janito/{cli_chat_shell → shell}/commands/verbose.py +4 -5
- janito/shell/commands.py +40 -0
- janito/shell/input_history.py +62 -0
- janito/shell/main.py +257 -0
- janito/{cli_chat_shell/shell_command_completer.py → shell/prompt/completer.py} +1 -1
- janito/{cli_chat_shell/chat_ui.py → shell/prompt/session_setup.py} +19 -5
- janito/shell/session/manager.py +101 -0
- janito/{cli_chat_shell/ui.py → shell/ui/interactive.py} +23 -17
- janito/termweb/app.py +3 -3
- janito/termweb/static/editor.css +142 -0
- janito/termweb/static/editor.css.bak +27 -0
- janito/termweb/static/editor.html +15 -213
- janito/termweb/static/editor.html.bak +16 -215
- janito/termweb/static/editor.js +209 -0
- janito/termweb/static/editor.js.bak +227 -0
- janito/termweb/static/index.html +2 -3
- janito/termweb/static/index.html.bak +2 -3
- janito/termweb/static/termweb.css.bak +33 -84
- janito/termweb/static/termweb.js +15 -34
- janito/termweb/static/termweb.js.bak +18 -36
- janito/tests/test_rich_utils.py +44 -0
- janito/web/app.py +0 -75
- {janito-1.8.1.dist-info → janito-1.10.0.dist-info}/METADATA +62 -42
- janito-1.10.0.dist-info/RECORD +158 -0
- {janito-1.8.1.dist-info → janito-1.10.0.dist-info}/WHEEL +1 -1
- janito/agent/tools/dir_walk_utils.py +0 -16
- janito/agent/tools/gitignore_utils.py +0 -46
- janito/agent/tools/memory.py +0 -48
- janito/agent/tools/outline_file/formatting.py +0 -20
- janito/agent/tools/outline_file/python_outline.py +0 -71
- janito/agent/tools/present_choices_test.py +0 -18
- janito/agent/tools/rich_live.py +0 -44
- janito/agent/tools/run_python_command.py +0 -163
- janito/agent/tools/tools_utils.py +0 -56
- janito/agent/tools/utils.py +0 -33
- janito/agent/tools/validate_file_syntax.py +0 -163
- janito/cli/runner/cli_main.py +0 -180
- janito/cli_chat_shell/chat_loop.py +0 -163
- janito/cli_chat_shell/chat_state.py +0 -38
- janito/cli_chat_shell/commands/history_start.py +0 -37
- janito/cli_chat_shell/commands/session.py +0 -48
- janito/cli_chat_shell/commands/sum.py +0 -49
- janito/cli_chat_shell/commands/utility.py +0 -32
- janito/cli_chat_shell/session_manager.py +0 -72
- janito-1.8.1.dist-info/RECORD +0 -127
- /janito/agent/tools/{outline_file → get_file_outline}/markdown_outline.py +0 -0
- /janito/cli/{runner/_termweb_log_utils.py → _termweb_log_utils.py} +0 -0
- /janito/cli/{runner/config.py → config_runner.py} +0 -0
- /janito/cli/{runner/formatting.py → formatting_runner.py} +0 -0
- /janito/{cli/runner → shell}/__init__.py +0 -0
- /janito/{cli_chat_shell → shell/prompt}/load_prompt.py +0 -0
- /janito/{cli_chat_shell/config_shell.py → shell/session/config.py} +0 -0
- /janito/{cli_chat_shell/__init__.py → shell/session/history.py} +0 -0
- {janito-1.8.1.dist-info → janito-1.10.0.dist-info}/entry_points.txt +0 -0
- {janito-1.8.1.dist-info → janito-1.10.0.dist-info}/licenses/LICENSE +0 -0
- {janito-1.8.1.dist-info → janito-1.10.0.dist-info}/top_level.txt +0 -0
janito/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "1.
|
1
|
+
__version__ = "1.10.0-dev"
|
janito/agent/config.py
CHANGED
@@ -55,7 +55,7 @@ class FileConfig(BaseConfig):
|
|
55
55
|
CONFIG_OPTIONS = {
|
56
56
|
"api_key": "API key for OpenAI-compatible service (required)",
|
57
57
|
"trust": "Trust mode: suppress all console output (bool, default: False)",
|
58
|
-
"model": "Model name to use (e.g., '
|
58
|
+
"model": "Model name to use (e.g., 'gpt-4.1')",
|
59
59
|
"base_url": "API base URL (OpenAI-compatible endpoint)",
|
60
60
|
"role": "Role description for the Agent Profile (e.g., 'software engineer')",
|
61
61
|
"system_prompt_template": "Override the entire Agent Profile prompt text",
|
janito/agent/config_defaults.py
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
# Centralized config defaults for Janito
|
2
2
|
CONFIG_DEFAULTS = {
|
3
3
|
"api_key": None, # Must be set by user
|
4
|
-
"model": "
|
5
|
-
"base_url": "https://openrouter.ai/api/v1",
|
4
|
+
"model": "gpt-4.1", # Default model
|
6
5
|
"role": "software developer", # Part of the Agent Profile
|
7
6
|
"system_prompt_template": None, # None means auto-generate from Agent Profile role
|
8
7
|
"temperature": 0.2,
|
9
|
-
"max_tokens":
|
8
|
+
"max_tokens": 32000,
|
10
9
|
"use_azure_openai": False,
|
11
10
|
"azure_openai_api_version": "2023-05-15",
|
12
11
|
"profile": "base",
|
janito/agent/config_utils.py
CHANGED
janito/agent/conversation.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
from janito.agent.conversation_api import (
|
2
2
|
get_openai_response,
|
3
|
-
get_openai_stream_response,
|
4
3
|
retry_api_call,
|
5
4
|
)
|
6
5
|
from janito.agent.conversation_tool_calls import handle_tool_calls
|
@@ -11,7 +10,32 @@ from janito.agent.conversation_exceptions import (
|
|
11
10
|
NoToolSupportError,
|
12
11
|
)
|
13
12
|
from janito.agent.runtime_config import unified_config, runtime_config
|
13
|
+
from janito.agent.api_exceptions import ApiError
|
14
14
|
import pprint
|
15
|
+
from janito.agent.llm_conversation_history import LLMConversationHistory
|
16
|
+
|
17
|
+
|
18
|
+
def get_openai_response_with_content_check(client, model, messages, max_tokens):
|
19
|
+
response = get_openai_response(client, model, messages, max_tokens)
|
20
|
+
# Check for empty assistant message content, but allow tool/function calls
|
21
|
+
if not hasattr(response, "choices") or not response.choices:
|
22
|
+
return response # Let normal error handling occur
|
23
|
+
choice = response.choices[0]
|
24
|
+
content = getattr(choice.message, "content", None)
|
25
|
+
# Check for function_call (legacy OpenAI) or tool_calls (OpenAI v2 and others)
|
26
|
+
has_function_call = (
|
27
|
+
hasattr(choice.message, "function_call") and choice.message.function_call
|
28
|
+
)
|
29
|
+
has_tool_calls = hasattr(choice.message, "tool_calls") and choice.message.tool_calls
|
30
|
+
if (content is None or str(content).strip() == "") and not (
|
31
|
+
has_function_call or has_tool_calls
|
32
|
+
):
|
33
|
+
print(
|
34
|
+
"[DEBUG] Empty assistant message detected with no tool/function call. Will retry. Raw response:"
|
35
|
+
)
|
36
|
+
print(repr(response))
|
37
|
+
raise EmptyResponseError("Empty assistant message content.")
|
38
|
+
return response
|
15
39
|
|
16
40
|
|
17
41
|
class ConversationHandler:
|
@@ -27,24 +51,10 @@ class ConversationHandler:
|
|
27
51
|
"""
|
28
52
|
return [msg for msg in messages if msg.get("role") != "system"]
|
29
53
|
|
30
|
-
def
|
31
|
-
self,
|
32
|
-
messages,
|
33
|
-
max_rounds=50,
|
34
|
-
message_handler=None,
|
35
|
-
verbose_response=False,
|
36
|
-
spinner=False,
|
37
|
-
max_tokens=None,
|
38
|
-
verbose_events=False,
|
39
|
-
stream=False,
|
40
|
-
verbose_stream=False,
|
41
|
-
):
|
42
|
-
if not messages:
|
43
|
-
raise ValueError("No prompt provided in messages")
|
44
|
-
|
54
|
+
def _resolve_max_tokens(self, max_tokens):
|
45
55
|
resolved_max_tokens = max_tokens
|
46
56
|
if resolved_max_tokens is None:
|
47
|
-
resolved_max_tokens = unified_config.get("max_tokens",
|
57
|
+
resolved_max_tokens = unified_config.get("max_tokens", 32000)
|
48
58
|
try:
|
49
59
|
resolved_max_tokens = int(resolved_max_tokens)
|
50
60
|
except (TypeError, ValueError):
|
@@ -53,123 +63,176 @@ class ConversationHandler:
|
|
53
63
|
resolved_max_tokens=resolved_max_tokens
|
54
64
|
)
|
55
65
|
)
|
56
|
-
|
57
|
-
# If vanilla mode is set and max_tokens was not provided, default to 8000
|
58
66
|
if runtime_config.get("vanilla_mode", False) and max_tokens is None:
|
59
67
|
resolved_max_tokens = 8000
|
68
|
+
return resolved_max_tokens
|
69
|
+
|
70
|
+
def _call_openai_api(self, history, resolved_max_tokens, spinner):
|
71
|
+
def api_call():
|
72
|
+
return get_openai_response_with_content_check(
|
73
|
+
self.client,
|
74
|
+
self.model,
|
75
|
+
history.get_messages(),
|
76
|
+
resolved_max_tokens,
|
77
|
+
)
|
78
|
+
|
79
|
+
user_message_on_empty = "Received an empty message from you. Please try again."
|
80
|
+
if spinner:
|
81
|
+
response = show_spinner(
|
82
|
+
"Waiting for AI response...",
|
83
|
+
retry_api_call,
|
84
|
+
api_call,
|
85
|
+
history=history,
|
86
|
+
user_message_on_empty=user_message_on_empty,
|
87
|
+
)
|
88
|
+
else:
|
89
|
+
response = retry_api_call(
|
90
|
+
api_call, history=history, user_message_on_empty=user_message_on_empty
|
91
|
+
)
|
92
|
+
return response
|
93
|
+
|
94
|
+
def _handle_no_tool_support(self, messages, max_tokens, spinner):
|
95
|
+
print(
|
96
|
+
"\u26a0\ufe0f Endpoint does not support tool use. Proceeding in vanilla mode (tools disabled)."
|
97
|
+
)
|
98
|
+
runtime_config.set("vanilla_mode", True)
|
99
|
+
resolved_max_tokens = 8000
|
100
|
+
if max_tokens is None:
|
101
|
+
runtime_config.set("max_tokens", 8000)
|
102
|
+
|
103
|
+
def api_call_vanilla():
|
104
|
+
return get_openai_response_with_content_check(
|
105
|
+
self.client, self.model, messages, resolved_max_tokens
|
106
|
+
)
|
107
|
+
|
108
|
+
user_message_on_empty = "Received an empty message from you. Please try again."
|
109
|
+
if spinner:
|
110
|
+
response = show_spinner(
|
111
|
+
"Waiting for AI response (tools disabled)...",
|
112
|
+
retry_api_call,
|
113
|
+
api_call_vanilla,
|
114
|
+
history=None,
|
115
|
+
user_message_on_empty=user_message_on_empty,
|
116
|
+
)
|
117
|
+
else:
|
118
|
+
response = retry_api_call(
|
119
|
+
api_call_vanilla,
|
120
|
+
history=None,
|
121
|
+
user_message_on_empty=user_message_on_empty,
|
122
|
+
)
|
123
|
+
print(
|
124
|
+
"[DEBUG] OpenAI API raw response (tools disabled):",
|
125
|
+
repr(response),
|
126
|
+
)
|
127
|
+
return response, resolved_max_tokens
|
128
|
+
|
129
|
+
def _process_response(self, response):
|
130
|
+
if runtime_config.get("verbose_response", False):
|
131
|
+
pprint.pprint(response)
|
132
|
+
if response is None or not getattr(response, "choices", None):
|
133
|
+
error = getattr(response, "error", None)
|
134
|
+
if error:
|
135
|
+
print(f"ApiError: {error.get('message', error)}")
|
136
|
+
raise ApiError(error.get("message", str(error)))
|
137
|
+
raise EmptyResponseError(
|
138
|
+
f"No choices in response; possible API or LLM error. Raw response: {response!r}"
|
139
|
+
)
|
140
|
+
choice = response.choices[0]
|
141
|
+
usage = getattr(response, "usage", None)
|
142
|
+
usage_info = (
|
143
|
+
{
|
144
|
+
"_debug_raw_usage": getattr(response, "usage", None),
|
145
|
+
"prompt_tokens": getattr(usage, "prompt_tokens", None),
|
146
|
+
"completion_tokens": getattr(usage, "completion_tokens", None),
|
147
|
+
"total_tokens": getattr(usage, "total_tokens", None),
|
148
|
+
}
|
149
|
+
if usage
|
150
|
+
else None
|
151
|
+
)
|
152
|
+
return choice, usage_info
|
153
|
+
|
154
|
+
def _handle_tool_calls(
|
155
|
+
self, choice, history, message_handler, usage_info, tool_user=False
|
156
|
+
):
|
157
|
+
tool_responses = handle_tool_calls(
|
158
|
+
choice.message.tool_calls, message_handler=message_handler
|
159
|
+
)
|
160
|
+
agent_idx = len([m for m in history.get_messages() if m.get("role") == "agent"])
|
161
|
+
self.usage_history.append({"agent_index": agent_idx, "usage": usage_info})
|
162
|
+
history.add_message(
|
163
|
+
{
|
164
|
+
"role": "assistant",
|
165
|
+
"content": choice.message.content,
|
166
|
+
"tool_calls": [tc.to_dict() for tc in choice.message.tool_calls],
|
167
|
+
}
|
168
|
+
)
|
169
|
+
for tool_response in tool_responses:
|
170
|
+
history.add_message(
|
171
|
+
{
|
172
|
+
"role": "user" if tool_user else "tool",
|
173
|
+
"tool_call_id": tool_response["tool_call_id"],
|
174
|
+
"content": tool_response["content"],
|
175
|
+
}
|
176
|
+
)
|
177
|
+
|
178
|
+
def handle_conversation(
|
179
|
+
self,
|
180
|
+
messages,
|
181
|
+
max_rounds=100,
|
182
|
+
message_handler=None,
|
183
|
+
verbose_response=False,
|
184
|
+
spinner=False,
|
185
|
+
max_tokens=None,
|
186
|
+
verbose_events=False,
|
187
|
+
tool_user=False,
|
188
|
+
):
|
189
|
+
|
190
|
+
if isinstance(messages, LLMConversationHistory):
|
191
|
+
history = messages
|
192
|
+
else:
|
193
|
+
history = LLMConversationHistory(messages)
|
194
|
+
|
195
|
+
if len(history) == 0:
|
196
|
+
raise ValueError("No prompt provided in messages")
|
197
|
+
|
198
|
+
resolved_max_tokens = self._resolve_max_tokens(max_tokens)
|
60
199
|
|
61
200
|
for _ in range(max_rounds):
|
62
201
|
try:
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
self.model,
|
69
|
-
messages,
|
70
|
-
resolved_max_tokens,
|
71
|
-
verbose_stream=runtime_config.get("verbose_stream", False),
|
72
|
-
message_handler=message_handler,
|
73
|
-
)
|
74
|
-
|
75
|
-
retry_api_call(get_stream)
|
76
|
-
return None
|
77
|
-
else:
|
78
|
-
# Non-streaming mode
|
79
|
-
def api_call():
|
80
|
-
return get_openai_response(
|
81
|
-
self.client, self.model, messages, resolved_max_tokens
|
82
|
-
)
|
83
|
-
|
84
|
-
if spinner:
|
85
|
-
response = show_spinner(
|
86
|
-
"Waiting for AI response...", retry_api_call, api_call
|
87
|
-
)
|
88
|
-
else:
|
89
|
-
response = retry_api_call(api_call)
|
90
|
-
print("[DEBUG] OpenAI API raw response:", repr(response))
|
202
|
+
response = self._call_openai_api(history, resolved_max_tokens, spinner)
|
203
|
+
error = getattr(response, "error", None)
|
204
|
+
if error:
|
205
|
+
print(f"ApiError: {error.get('message', error)}")
|
206
|
+
raise ApiError(error.get("message", str(error)))
|
91
207
|
except NoToolSupportError:
|
92
|
-
|
93
|
-
|
208
|
+
response, resolved_max_tokens = self._handle_no_tool_support(
|
209
|
+
messages, max_tokens, spinner
|
94
210
|
)
|
95
|
-
|
96
|
-
if max_tokens is None:
|
97
|
-
runtime_config.set("max_tokens", 8000)
|
98
|
-
resolved_max_tokens = 8000
|
99
|
-
|
100
|
-
# Remove system prompt for vanilla mode if needed (call this externally when appropriate)
|
101
|
-
# messages = ConversationHandler.remove_system_prompt(messages)
|
102
|
-
# Retry once with tools disabled
|
103
|
-
def api_call_vanilla():
|
104
|
-
return get_openai_response(
|
105
|
-
self.client, self.model, messages, resolved_max_tokens
|
106
|
-
)
|
107
|
-
|
108
|
-
if spinner:
|
109
|
-
response = show_spinner(
|
110
|
-
"Waiting for AI response (tools disabled)...",
|
111
|
-
retry_api_call,
|
112
|
-
api_call_vanilla,
|
113
|
-
)
|
114
|
-
else:
|
115
|
-
response = retry_api_call(api_call_vanilla)
|
116
|
-
print(
|
117
|
-
"[DEBUG] OpenAI API raw response (tools disabled):",
|
118
|
-
repr(response),
|
119
|
-
)
|
120
|
-
if runtime_config.get("verbose_response", False):
|
121
|
-
pprint.pprint(response)
|
122
|
-
if response is None or not getattr(response, "choices", None):
|
123
|
-
raise EmptyResponseError(
|
124
|
-
f"No choices in response; possible API or LLM error. Raw response: {response!r}"
|
125
|
-
)
|
126
|
-
choice = response.choices[0]
|
127
|
-
usage = getattr(response, "usage", None)
|
128
|
-
usage_info = (
|
129
|
-
{
|
130
|
-
# DEBUG: Show usage extraction
|
131
|
-
"_debug_raw_usage": getattr(response, "usage", None),
|
132
|
-
"prompt_tokens": getattr(usage, "prompt_tokens", None),
|
133
|
-
"completion_tokens": getattr(usage, "completion_tokens", None),
|
134
|
-
"total_tokens": getattr(usage, "total_tokens", None),
|
135
|
-
}
|
136
|
-
if usage
|
137
|
-
else None
|
138
|
-
)
|
211
|
+
choice, usage_info = self._process_response(response)
|
139
212
|
event = {"type": "content", "message": choice.message.content}
|
140
213
|
if runtime_config.get("verbose_events", False):
|
141
214
|
print_verbose_event(event)
|
142
215
|
if message_handler is not None and choice.message.content:
|
143
216
|
message_handler.handle_message(event)
|
144
217
|
if not choice.message.tool_calls:
|
145
|
-
agent_idx = len(
|
218
|
+
agent_idx = len(
|
219
|
+
[m for m in history.get_messages() if m.get("role") == "agent"]
|
220
|
+
)
|
146
221
|
self.usage_history.append(
|
147
222
|
{"agent_index": agent_idx, "usage": usage_info}
|
148
223
|
)
|
224
|
+
history.add_message(
|
225
|
+
{
|
226
|
+
"role": "assistant",
|
227
|
+
"content": choice.message.content,
|
228
|
+
}
|
229
|
+
)
|
149
230
|
return {
|
150
231
|
"content": choice.message.content,
|
151
232
|
"usage": usage_info,
|
152
233
|
"usage_history": self.usage_history,
|
153
234
|
}
|
154
|
-
|
155
|
-
|
156
|
-
choice.message.tool_calls, message_handler=message_handler
|
235
|
+
self._handle_tool_calls(
|
236
|
+
choice, history, message_handler, usage_info, tool_user=tool_user
|
157
237
|
)
|
158
|
-
agent_idx = len([m for m in messages if m.get("role") == "agent"])
|
159
|
-
self.usage_history.append({"agent_index": agent_idx, "usage": usage_info})
|
160
|
-
messages.append(
|
161
|
-
{
|
162
|
-
"role": "assistant",
|
163
|
-
"content": choice.message.content,
|
164
|
-
"tool_calls": [tc.to_dict() for tc in choice.message.tool_calls],
|
165
|
-
}
|
166
|
-
)
|
167
|
-
for tool_response in tool_responses:
|
168
|
-
messages.append(
|
169
|
-
{
|
170
|
-
"role": "tool",
|
171
|
-
"tool_call_id": tool_response["tool_call_id"],
|
172
|
-
"content": tool_response["content"],
|
173
|
-
}
|
174
|
-
)
|
175
238
|
raise MaxRoundsExceededError(f"Max conversation rounds exceeded ({max_rounds})")
|