agentle 0.9.35__py3-none-any.whl → 0.9.36__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.
@@ -29,14 +29,17 @@ from agentle.generations.providers.openrouter._types import (
29
29
  OpenRouterMessage,
30
30
  OpenRouterSystemMessage,
31
31
  OpenRouterToolCall,
32
+ OpenRouterToolMessage,
32
33
  OpenRouterUserMessage,
33
34
  )
35
+ from agentle.generations.tools.tool_execution_result import ToolExecutionResult
36
+ import json
34
37
 
35
38
 
36
39
  class AgentleMessageToOpenRouterMessageAdapter(
37
40
  Adapter[
38
41
  AssistantMessage | DeveloperMessage | UserMessage,
39
- OpenRouterMessage,
42
+ OpenRouterMessage | list[OpenRouterMessage],
40
43
  ]
41
44
  ):
42
45
  """
@@ -44,15 +47,18 @@ class AgentleMessageToOpenRouterMessageAdapter(
44
47
 
45
48
  Handles conversion of:
46
49
  - DeveloperMessage -> OpenRouterSystemMessage
47
- - UserMessage -> OpenRouterUserMessage
50
+ - UserMessage -> OpenRouterUserMessage (or OpenRouterToolMessage if contains tool results)
48
51
  - AssistantMessage -> OpenRouterAssistantMessage (with tool calls)
52
+
53
+ Note: When a message contains ToolExecutionResult parts, they are extracted
54
+ and returned as separate OpenRouterToolMessage objects.
49
55
  """
50
56
 
51
57
  @override
52
58
  def adapt(
53
59
  self,
54
60
  _f: AssistantMessage | DeveloperMessage | UserMessage,
55
- ) -> OpenRouterMessage:
61
+ ) -> OpenRouterMessage | list[OpenRouterMessage]:
56
62
  """
57
63
  Convert an Agentle message to OpenRouter format.
58
64
 
@@ -60,7 +66,9 @@ class AgentleMessageToOpenRouterMessageAdapter(
60
66
  _f: The Agentle message to convert.
61
67
 
62
68
  Returns:
63
- The corresponding OpenRouter message.
69
+ The corresponding OpenRouter message(s). Returns a list when the message
70
+ contains ToolExecutionResult parts that need to be split into separate
71
+ tool messages.
64
72
  """
65
73
  message = _f
66
74
  part_adapter = AgentlePartToOpenRouterPartAdapter()
@@ -76,12 +84,28 @@ class AgentleMessageToOpenRouterMessageAdapter(
76
84
  )
77
85
 
78
86
  case UserMessage():
87
+ # Check if this message contains tool execution results
88
+ tool_results = [
89
+ p for p in message.parts if isinstance(p, ToolExecutionResult)
90
+ ]
91
+
92
+ if tool_results:
93
+ # Convert each tool result to a separate tool message
94
+ return [
95
+ OpenRouterToolMessage(
96
+ role="tool",
97
+ tool_call_id=result.suggestion.id,
98
+ content=self._serialize_tool_result(result.result),
99
+ )
100
+ for result in tool_results
101
+ ]
102
+
79
103
  # User messages can have multimodal content
80
- # Filter out non-content parts (like tool execution suggestions)
104
+ # Filter out non-content parts (like tool execution suggestions and results)
81
105
  content_parts = [
82
106
  p
83
107
  for p in message.parts
84
- if not isinstance(p, ToolExecutionSuggestion)
108
+ if not isinstance(p, (ToolExecutionSuggestion, ToolExecutionResult))
85
109
  ]
86
110
 
87
111
  # If only text parts, concatenate into a string
@@ -102,6 +126,69 @@ class AgentleMessageToOpenRouterMessageAdapter(
102
126
  )
103
127
 
104
128
  case AssistantMessage():
129
+ # Check if this message contains tool execution results
130
+ tool_results = [
131
+ p for p in message.parts if isinstance(p, ToolExecutionResult)
132
+ ]
133
+
134
+ if tool_results:
135
+ # If assistant message has tool results, we need to split it
136
+ # First, create the assistant message with tool calls (if any)
137
+ messages: list[OpenRouterMessage] = []
138
+
139
+ # Separate text content from tool calls
140
+ text_parts = [p for p in message.parts if isinstance(p, TextPart)]
141
+ tool_suggestions = [
142
+ p
143
+ for p in message.parts
144
+ if isinstance(p, ToolExecutionSuggestion)
145
+ ]
146
+
147
+ # Only create assistant message if there's content or tool calls
148
+ if text_parts or tool_suggestions:
149
+ content = (
150
+ "".join(str(p) for p in text_parts) if text_parts else None
151
+ )
152
+
153
+ tool_calls: list[OpenRouterToolCall] = [
154
+ OpenRouterToolCall(
155
+ id=suggestion.id,
156
+ type="function",
157
+ function={
158
+ "name": suggestion.tool_name,
159
+ "arguments": self._serialize_tool_arguments(
160
+ suggestion.args
161
+ ),
162
+ },
163
+ )
164
+ for suggestion in tool_suggestions
165
+ ]
166
+
167
+ assistant_msg = OpenRouterAssistantMessage(
168
+ role="assistant",
169
+ content=content,
170
+ )
171
+
172
+ if tool_calls:
173
+ assistant_msg["tool_calls"] = tool_calls
174
+
175
+ if hasattr(message, "reasoning") and message.reasoning:
176
+ assistant_msg["reasoning"] = message.reasoning
177
+
178
+ messages.append(assistant_msg)
179
+
180
+ # Add tool result messages
181
+ for result in tool_results:
182
+ messages.append(
183
+ OpenRouterToolMessage(
184
+ role="tool",
185
+ tool_call_id=result.suggestion.id,
186
+ content=self._serialize_tool_result(result.result),
187
+ )
188
+ )
189
+
190
+ return messages
191
+
105
192
  # Separate text content from tool calls
106
193
  text_parts = [p for p in message.parts if isinstance(p, TextPart)]
107
194
  tool_suggestions = [
@@ -118,7 +205,9 @@ class AgentleMessageToOpenRouterMessageAdapter(
118
205
  type="function",
119
206
  function={
120
207
  "name": suggestion.tool_name,
121
- "arguments": str(suggestion.args), # Should be JSON string
208
+ "arguments": self._serialize_tool_arguments(
209
+ suggestion.args
210
+ ),
122
211
  },
123
212
  )
124
213
  for suggestion in tool_suggestions
@@ -137,3 +226,34 @@ class AgentleMessageToOpenRouterMessageAdapter(
137
226
  result["reasoning"] = message.reasoning
138
227
 
139
228
  return result
229
+
230
+ def _serialize_tool_arguments(self, args: object) -> str:
231
+ """
232
+ Serialize tool arguments to JSON string.
233
+
234
+ Args:
235
+ args: The arguments to serialize.
236
+
237
+ Returns:
238
+ JSON string representation of the arguments.
239
+ """
240
+ if isinstance(args, str):
241
+ return args
242
+ return json.dumps(args)
243
+
244
+ def _serialize_tool_result(self, result: object) -> str:
245
+ """
246
+ Serialize tool execution result to string.
247
+
248
+ Args:
249
+ result: The result to serialize.
250
+
251
+ returns:
252
+ String representation of the result.
253
+ """
254
+ if isinstance(result, str):
255
+ return result
256
+ try:
257
+ return json.dumps(result)
258
+ except (TypeError, ValueError):
259
+ return str(result)
@@ -71,6 +71,7 @@ from agentle.generations.providers.openrouter._types import (
71
71
  OpenRouterFileParserPlugin,
72
72
  OpenRouterModelsResponse,
73
73
  OpenRouterModel,
74
+ OpenRouterMessage,
74
75
  )
75
76
  from agentle.generations.providers.openrouter.error_handler import (
76
77
  parse_and_raise_openrouter_error,
@@ -1247,10 +1248,14 @@ class OpenRouterGenerationProvider(GenerationProvider):
1247
1248
  ),
1248
1249
  )
1249
1250
 
1250
- # Convert messages
1251
- openrouter_messages = [
1252
- self.message_adapter.adapt(message) for message in messages_list
1253
- ]
1251
+ # Convert messages - adapter may return single message or list of messages
1252
+ openrouter_messages: list[OpenRouterMessage] = []
1253
+ for message in messages_list:
1254
+ adapted = self.message_adapter.adapt(message)
1255
+ if isinstance(adapted, list):
1256
+ openrouter_messages.extend(adapted)
1257
+ else:
1258
+ openrouter_messages.append(adapted)
1254
1259
 
1255
1260
  # Convert tools if provided
1256
1261
  openrouter_tools = (
@@ -1404,10 +1409,14 @@ class OpenRouterGenerationProvider(GenerationProvider):
1404
1409
  """
1405
1410
  _generation_config = self._normalize_generation_config(generation_config)
1406
1411
 
1407
- # Convert messages
1408
- openrouter_messages = [
1409
- self.message_adapter.adapt(message) for message in messages
1410
- ]
1412
+ # Convert messages - adapter may return single message or list of messages
1413
+ openrouter_messages: list[OpenRouterMessage] = []
1414
+ for message in messages:
1415
+ adapted = self.message_adapter.adapt(message)
1416
+ if isinstance(adapted, list):
1417
+ openrouter_messages.extend(adapted)
1418
+ else:
1419
+ openrouter_messages.append(adapted)
1411
1420
 
1412
1421
  # Convert tools if provided
1413
1422
  openrouter_tools = (
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentle
3
- Version: 0.9.35
3
+ Version: 0.9.36
4
4
  Summary: ...
5
5
  Author-email: Arthur Brenno <64020210+arthurbrenno@users.noreply.github.com>
6
6
  License-File: LICENSE
@@ -355,9 +355,9 @@ agentle/generations/providers/openrouter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5
355
355
  agentle/generations/providers/openrouter/_types.py,sha256=VSAf1auxopTqhYwLHPWl_Q0-okWUfLKJa0JmWlAFuGg,10557
356
356
  agentle/generations/providers/openrouter/error_handler.py,sha256=4qm8v_cjdrB-59UXyCJLnIkOxzIfmsltZY8Q137-8Qg,8075
357
357
  agentle/generations/providers/openrouter/exceptions.py,sha256=o3_-tuyhewc0v5L2cAXH0f9ixHyyDgZbHq0KX5cUyPE,23179
358
- agentle/generations/providers/openrouter/openrouter_generation_provider.py,sha256=c2fQMdm-xFzIR2Z3ClAXiu6BzKfrnQQTpzX2jJQSNlI,66596
358
+ agentle/generations/providers/openrouter/openrouter_generation_provider.py,sha256=XhXU_Ix10jmWl2SzRUHwsVSPRIWq6zHnia7IMaY6Yy4,67129
359
359
  agentle/generations/providers/openrouter/_adapters/__init__.py,sha256=orgZeEBqH4X_cpyOMiClvfZHY5cLwLNqhAYLqNjGIH4,1826
360
- agentle/generations/providers/openrouter/_adapters/agentle_message_to_openrouter_message_adapter.py,sha256=xqLumYTs__1UHD1_WwvHsHhzpEUKOAwMAj4pvo4BWMU,4957
360
+ agentle/generations/providers/openrouter/_adapters/agentle_message_to_openrouter_message_adapter.py,sha256=IvaovVP0ChYEreysiDyqF8SXdXxnD_Y9MjA2raEoncw,9790
361
361
  agentle/generations/providers/openrouter/_adapters/agentle_part_to_openrouter_part_adapter.py,sha256=eIfwQQ9BokHy3FQ90GJ4i_J3L46XCiSBd1RWnzu-gAo,5967
362
362
  agentle/generations/providers/openrouter/_adapters/agentle_tool_to_openrouter_tool_adapter.py,sha256=41i3B6awaTNZsdQ46Oi1e10cuNhQqWL-KBJ5V_sHkiI,9547
363
363
  agentle/generations/providers/openrouter/_adapters/openrouter_message_to_generated_assistant_message_adapter.py,sha256=uB4TYc0fuwsoYdRnEnLhbwQISyaZ2Z2RWkPFGQXUc80,5295
@@ -1018,7 +1018,7 @@ agentle/web/actions/scroll.py,sha256=WqVVAORNDK3BL1oASZBPmXJYeSVkPgAOmWA8ibYO82I
1018
1018
  agentle/web/actions/viewport.py,sha256=KCwm88Pri19Qc6GLHC69HsRxmdJz1gEEAODfggC_fHo,287
1019
1019
  agentle/web/actions/wait.py,sha256=IKEywjf-KC4ni9Gkkv4wgc7bY-hk7HwD4F-OFWlyf2w,571
1020
1020
  agentle/web/actions/write_text.py,sha256=9mxfHcpKs_L7BsDnJvOYHQwG8M0GWe61SRJAsKk3xQ8,748
1021
- agentle-0.9.35.dist-info/METADATA,sha256=x56kvGN-I-GmLRyrLOcTFTdrVFgpI4KV6tKbFRr_2Gc,86849
1022
- agentle-0.9.35.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1023
- agentle-0.9.35.dist-info/licenses/LICENSE,sha256=T90S9vqRS6qP-voULxAcvwEs558wRRo6dHuZrjgcOUI,1085
1024
- agentle-0.9.35.dist-info/RECORD,,
1021
+ agentle-0.9.36.dist-info/METADATA,sha256=hSUSyQ9vk4kdTs91_EW7Yb1tEi41flD2JGVlqugY4YA,86849
1022
+ agentle-0.9.36.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1023
+ agentle-0.9.36.dist-info/licenses/LICENSE,sha256=T90S9vqRS6qP-voULxAcvwEs558wRRo6dHuZrjgcOUI,1085
1024
+ agentle-0.9.36.dist-info/RECORD,,