aury-agent 0.0.10__py3-none-any.whl → 0.0.12__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.
aury/agents/core/base.py CHANGED
@@ -352,6 +352,13 @@ class BaseAgent(ABC):
352
352
  logger.error(f"{self.agent_type}Agent run error, error={type(e).__name__}, invocation_id={self._ctx.invocation_id}", exc_info=True)
353
353
  raise
354
354
  finally:
355
+ # Cancel exec_task if still running (e.g., when run() is cancelled externally)
356
+ if exec_task and not exec_task.done():
357
+ exec_task.cancel()
358
+ try:
359
+ await exec_task
360
+ except asyncio.CancelledError:
361
+ pass
355
362
  _reset_current_ctx(ctx_token)
356
363
  _emit_queue_var.reset(queue_token)
357
364
  self._run_config = {} # Clear runtime overrides
@@ -148,6 +148,7 @@ class ToolInvocation:
148
148
  args: dict[str, Any] = field(default_factory=dict)
149
149
  args_raw: str = "" # Raw JSON string for streaming
150
150
  result: str | None = None
151
+ truncated_result: str | None = None # Shortened result for context window
151
152
  is_error: bool = False
152
153
 
153
154
  # Timing
@@ -161,10 +162,22 @@ class ToolInvocation:
161
162
  self.state = ToolInvocationState.CALL
162
163
  self.time["start"] = datetime.now()
163
164
 
164
- def mark_result(self, result: str, is_error: bool = False) -> None:
165
- """Mark execution complete."""
165
+ def mark_result(
166
+ self,
167
+ result: str,
168
+ is_error: bool = False,
169
+ truncated_result: str | None = None,
170
+ ) -> None:
171
+ """Mark execution complete.
172
+
173
+ Args:
174
+ result: Complete result (raw)
175
+ is_error: Whether this is an error result
176
+ truncated_result: Shortened result for context window (defaults to result)
177
+ """
166
178
  self.state = ToolInvocationState.RESULT
167
179
  self.result = result
180
+ self.truncated_result = truncated_result if truncated_result is not None else result
168
181
  self.is_error = is_error
169
182
  self.time["end"] = datetime.now()
170
183
 
@@ -261,6 +261,9 @@ class ModelClientProvider:
261
261
  case Evt.thinking:
262
262
  return LLMEvent(type="thinking", delta=event.delta)
263
263
 
264
+ case Evt.thinking_completed:
265
+ return LLMEvent(type="thinking_completed")
266
+
264
267
  case Evt.tool_call_start:
265
268
  if event.tool_call:
266
269
  return LLMEvent(
@@ -134,11 +134,16 @@ class LLMMessage:
134
134
  - user: User message (can include images)
135
135
  - assistant: Assistant response (can include tool_calls)
136
136
  - tool: Tool result (requires tool_call_id and name)
137
+
138
+ Supports dual content for context management:
139
+ - content: Complete content (raw), for storage and recall
140
+ - truncated_content: Shortened content for context window (defaults to content)
137
141
  """
138
142
  role: Literal["system", "user", "assistant", "tool"]
139
143
  content: str | list[dict[str, Any]]
140
144
  tool_call_id: str | None = None # Required for tool role
141
145
  name: str | None = None # Tool name, required for Gemini compatibility
146
+ truncated_content: str | list[dict[str, Any]] | None = None # Shortened content (defaults to content)
142
147
 
143
148
  def to_dict(self) -> dict[str, Any]:
144
149
  d = {"role": self.role, "content": self.content}
@@ -146,6 +151,8 @@ class LLMMessage:
146
151
  d["tool_call_id"] = self.tool_call_id
147
152
  if self.name:
148
153
  d["name"] = self.name
154
+ if self.truncated_content is not None:
155
+ d["truncated_content"] = self.truncated_content
149
156
  return d
150
157
 
151
158
  def get(self, key: str, default: Any = None) -> Any:
@@ -174,15 +181,28 @@ class LLMMessage:
174
181
  return cls(role="assistant", content=content)
175
182
 
176
183
  @classmethod
177
- def tool(cls, content: str, tool_call_id: str, name: str | None = None) -> "LLMMessage":
184
+ def tool(
185
+ cls,
186
+ content: str,
187
+ tool_call_id: str,
188
+ name: str | None = None,
189
+ truncated_content: str | None = None,
190
+ ) -> "LLMMessage":
178
191
  """Create tool result message.
179
192
 
180
193
  Args:
181
- content: Tool result content
194
+ content: Tool result content (complete/raw)
182
195
  tool_call_id: ID of the tool call this result is for
183
196
  name: Tool name (required for Gemini compatibility)
197
+ truncated_content: Shortened content for context window (defaults to content)
184
198
  """
185
- return cls(role="tool", content=content, tool_call_id=tool_call_id, name=name)
199
+ return cls(
200
+ role="tool",
201
+ content=content,
202
+ tool_call_id=tool_call_id,
203
+ name=name,
204
+ truncated_content=truncated_content,
205
+ )
186
206
 
187
207
 
188
208
  @runtime_checkable
@@ -157,7 +157,7 @@ async def save_tool_messages(agent: "ReactAgent") -> None:
157
157
  """
158
158
  for inv in agent._tool_invocations:
159
159
  if inv.result is not None:
160
- # Build tool result message
160
+ # Build tool result message (raw content)
161
161
  content: list[dict] = [{
162
162
  "type": "tool_result",
163
163
  "tool_use_id": inv.tool_call_id,
@@ -165,11 +165,21 @@ async def save_tool_messages(agent: "ReactAgent") -> None:
165
165
  "is_error": inv.is_error,
166
166
  }]
167
167
 
168
- message = {
168
+ message: dict = {
169
169
  "role": "tool",
170
170
  "content": content,
171
171
  "tool_call_id": inv.tool_call_id,
172
172
  "invocation_id": agent._current_invocation.id if agent._current_invocation else "",
173
173
  }
174
174
 
175
+ # Add truncated_content if different from raw
176
+ if inv.truncated_result is not None and inv.truncated_result != inv.result:
177
+ truncated_content: list[dict] = [{
178
+ "type": "tool_result",
179
+ "tool_use_id": inv.tool_call_id,
180
+ "content": inv.truncated_result,
181
+ "is_error": inv.is_error,
182
+ }]
183
+ message["truncated_content"] = truncated_content
184
+
175
185
  await trigger_message_save(agent, message)
aury/agents/react/step.py CHANGED
@@ -196,6 +196,9 @@ async def execute_step(agent: "ReactAgent") -> str | None:
196
196
  agent._current_text_block_id = None
197
197
  agent._current_thinking_block_id = None
198
198
 
199
+ # Track if thinking_completed has been emitted (to avoid duplicate)
200
+ thinking_completed_emitted = False
201
+
199
202
  # Reset tool call tracking
200
203
  agent._call_id_to_tool = {}
201
204
  agent._tool_call_blocks = {}
@@ -355,6 +358,17 @@ async def execute_step(agent: "ReactAgent") -> str | None:
355
358
  data={"content": delta},
356
359
  ))
357
360
 
361
+ elif event.type == "thinking_completed":
362
+ # Thinking completed - emit block completed status
363
+ if agent._current_thinking_block_id and not thinking_completed_emitted:
364
+ await agent.ctx.emit(BlockEvent(
365
+ block_id=agent._current_thinking_block_id,
366
+ kind=BlockKind.THINKING,
367
+ op=BlockOp.PATCH,
368
+ data={"status": "completed"},
369
+ ))
370
+ thinking_completed_emitted = True
371
+
358
372
  elif event.type == "tool_call_start":
359
373
  # Tool call started (name known, arguments pending)
360
374
  if event.tool_call:
@@ -569,15 +583,6 @@ async def execute_step(agent: "ReactAgent") -> str | None:
569
583
  extra={"invocation_id": agent._current_invocation.id},
570
584
  )
571
585
 
572
- # Emit text block completed status
573
- if agent._current_text_block_id:
574
- await agent.ctx.emit(BlockEvent(
575
- block_id=agent._current_text_block_id,
576
- kind=BlockKind.TEXT,
577
- op=BlockOp.PATCH,
578
- data={"status": "completed"},
579
- ))
580
-
581
586
  # === Middleware: on_thinking_stream_end (flush buffered thinking) ===
582
587
  if agent.middleware:
583
588
  thinking_chunks = await agent.middleware.process_thinking_stream_end()
@@ -600,11 +605,11 @@ async def execute_step(agent: "ReactAgent") -> str | None:
600
605
  extra={"invocation_id": agent._current_invocation.id},
601
606
  )
602
607
 
603
- # Emit thinking block completed status
604
- if agent._current_thinking_block_id:
608
+ # Emit text block completed status
609
+ if agent._current_text_block_id:
605
610
  await agent.ctx.emit(BlockEvent(
606
- block_id=agent._current_thinking_block_id,
607
- kind=BlockKind.THINKING,
611
+ block_id=agent._current_text_block_id,
612
+ kind=BlockKind.TEXT,
608
613
  op=BlockOp.PATCH,
609
614
  data={"status": "completed"},
610
615
  ))
@@ -276,19 +276,23 @@ async def process_tool_results(agent: "ReactAgent") -> None:
276
276
  # HITL tool - record placeholder result
277
277
  invocation.mark_result(f"[等待用户输入: {suspend_signal.request_type}]", is_error=False)
278
278
  else:
279
- # Normal tool result
279
+ # Normal tool result - pass truncated_output
280
+ output = result.output if hasattr(result, 'output') else str(result)
281
+ truncated = getattr(result, 'truncated_output', None)
280
282
  invocation.mark_result(
281
- result.output if hasattr(result, 'output') else str(result),
282
- is_error=getattr(result, 'is_error', False)
283
+ output,
284
+ is_error=getattr(result, 'is_error', False),
285
+ truncated_result=truncated,
283
286
  )
284
287
 
285
- # Add to in-memory history
288
+ # Add to in-memory history (use truncated for context window)
286
289
  agent._message_history.append(
287
290
  LLMMessage(
288
291
  role="tool",
289
292
  content=invocation.result,
290
293
  tool_call_id=invocation.tool_call_id,
291
294
  name=invocation.tool_name, # Required for Gemini
295
+ truncated_content=invocation.truncated_result,
292
296
  )
293
297
  )
294
298
 
@@ -310,6 +314,13 @@ async def process_tool_results(agent: "ReactAgent") -> None:
310
314
  error_msg = f"Tool execution error: {str(result)}"
311
315
  invocation.mark_result(error_msg, is_error=True)
312
316
  result = ToolResult.error(error_msg)
317
+ else:
318
+ # Mark result with truncated_output
319
+ invocation.mark_result(
320
+ result.output,
321
+ is_error=result.is_error,
322
+ truncated_result=result.truncated_output,
323
+ )
313
324
 
314
325
  # Get parent block_id from tool_call mapping
315
326
  parent_block_id = agent._tool_call_blocks.get(invocation.tool_call_id)
@@ -342,6 +353,7 @@ async def process_tool_results(agent: "ReactAgent") -> None:
342
353
  "tool_use_id": invocation.tool_call_id,
343
354
  "tool_name": invocation.tool_name,
344
355
  "content": result.output,
356
+ "truncated_content": result.truncated_output,
345
357
  "is_error": invocation.is_error,
346
358
  }
347
359
  )
@@ -355,5 +367,6 @@ async def process_tool_results(agent: "ReactAgent") -> None:
355
367
  content=tr["content"],
356
368
  tool_call_id=tr["tool_use_id"],
357
369
  name=tr["tool_name"], # Required for Gemini
370
+ truncated_content=tr.get("truncated_content"),
358
371
  )
359
372
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aury-agent
3
- Version: 0.0.10
3
+ Version: 0.0.12
4
4
  Summary: Aury Agent Framework - React Agent and Workflow orchestration
5
5
  Author: Aury Team
6
6
  License: MIT
@@ -55,7 +55,7 @@ aury/agents/context_providers/skill.py,sha256=w_R1txvu45ruMjuOtckuD9oG_hqeFGqAa_
55
55
  aury/agents/context_providers/subagent.py,sha256=Y-6bYjaq6nbLS9d3siyy5eAt6SzFfk6N3Y1WU209lhI,1364
56
56
  aury/agents/context_providers/tool.py,sha256=wQGmcZi_XuB9ukweg5PUVO93GLzley0xftje-iVSfq8,2266
57
57
  aury/agents/core/__init__.py,sha256=-euIGAwoe2gU_2xEqJ8YmOMnDOaOlgWXwXxzIQnDG8E,1716
58
- aury/agents/core/base.py,sha256=nqsQ7fG0HZ08bqJh7KdpBgIxtJzDXZ6qLMf5hZ5vBuc,21279
58
+ aury/agents/core/base.py,sha256=1QgPe50DGajBgRhd8HFrzLaRAqkYioGpvU_ARwtV2DQ,21585
59
59
  aury/agents/core/context.py,sha256=co_v6Mz7fYR0jaTlUHlnSBWaOgr_1_84lVO0FPo_tKo,28932
60
60
  aury/agents/core/context_builder.py,sha256=HF0hfnKBVCmlcFDmdyj53org90318ixmZl1LYQBl0fM,9075
61
61
  aury/agents/core/factory.py,sha256=8rx_2jD9Sj2tK-8_We09fHT1czjbN-yF1ws0hmUX2w4,6829
@@ -79,7 +79,7 @@ aury/agents/core/types/message.py,sha256=W0d8elddcbvW98bejDkHIzKiTigr6O5KxVuVUne
79
79
  aury/agents/core/types/recall.py,sha256=BZSDgv8Q0eK7Sshc-jA6ycpFpMHzndZ-lS86MPto9-c,4898
80
80
  aury/agents/core/types/session.py,sha256=oRn9jKFe8Tg-12ZFQcGJ0VLaJIWBfk8YQDzXUuXSNEk,9406
81
81
  aury/agents/core/types/subagent.py,sha256=vIxDkfekAaHj_V2UdSIaygDKb5RjiLqi8twTnhIUF_o,4651
82
- aury/agents/core/types/tool.py,sha256=sP0mA_YufOjlPnEKcQSAbp719GkJulNuvQ76yYmahFU,7213
82
+ aury/agents/core/types/tool.py,sha256=Zadqjp2QjNsX617whpWnXJ1XUvPXDl47f8nuCQ1AbaY,7676
83
83
  aury/agents/eval/__init__.py,sha256=uRGZN-RBpNlwOmrn66EwZBNKufDVKgMc3tODuMM6WB4,8850
84
84
  aury/agents/hitl/__init__.py,sha256=6H8cBU-dSR0eSOGWte_Iy7OvTKGDl4ZMb0E56KrfFkw,1020
85
85
  aury/agents/hitl/ask_user.py,sha256=3MAJOo0rI0Wl7ZpZdDSQVBkO0GJFYH6bT7hnrcED4qI,9071
@@ -88,9 +88,9 @@ aury/agents/hitl/exceptions.py,sha256=O1r9OCLwQqo7Ebr5o3kX0p0bvWYKbHapftf1vfnMh0
88
88
  aury/agents/hitl/permission.py,sha256=bbwp0heb8YZ_palRCU55QtXm1rfMi9hJ8g3DWKtTc3Q,19117
89
89
  aury/agents/hitl/revert.py,sha256=i3F8Zcibuo3fjhJTK-rQEYSPMKxmRQeua5cjoDzAR14,6684
90
90
  aury/agents/llm/__init__.py,sha256=teantqedfyQ6IxLD6LayLr2cbgFYbMT2I4TUSfkOrQ4,625
91
- aury/agents/llm/adapter.py,sha256=icX_RVk738PqxueCFj58WTPVhFXQpgNkjn__u2dZDEM,14916
91
+ aury/agents/llm/adapter.py,sha256=_8FKf_HqJip-oRTgKhai0OBZObSG_wbgigU0oK0bb9k,15029
92
92
  aury/agents/llm/openai.py,sha256=v4Qlc5EIJDNcqkGHA2aIEaTTKaPVGMPf1227aKcef58,11192
93
- aury/agents/llm/provider.py,sha256=oE7sBF64pq8h1-bowSwApmAl3SVlHV_I4aCMePD29Mk,16106
93
+ aury/agents/llm/provider.py,sha256=YRlDY0mSEMb4oTmKusLlj_vh5vhDdOf4yrY3c5vBpfM,16827
94
94
  aury/agents/mcp/__init__.py,sha256=7zsY-5LhQBLpiQI-XWe2uUHdTwJrDHrr0pDd_SXfO3o,4355
95
95
  aury/agents/memory/__init__.py,sha256=PMGMQ1VE-j3M0n4dIeb9DYO3qrCyv5AMSfMWTyhPARE,816
96
96
  aury/agents/memory/compaction.py,sha256=3inOVsbwSgTghcsSLgvUdX8IG2J6_0Qbr36vAqanV0M,12757
@@ -114,9 +114,9 @@ aury/agents/react/agent.py,sha256=DYWxuspMs3srOuxW4Sxis_Hkg4R6YkM0aM-rLRjD9Oo,23
114
114
  aury/agents/react/context.py,sha256=Q7pY5j6b-OvKAmI1UsuawC3BmVxFkKN1oXxeBTVFejQ,11067
115
115
  aury/agents/react/factory.py,sha256=7aWb2YqSdlIq161Ke0JZwnjpaA9U7Q5xejAnkrmjMUw,10776
116
116
  aury/agents/react/pause.py,sha256=bcrhbuoJQUz7WxZSsKr5qjxQlDdpwai54ns_qio0du4,7306
117
- aury/agents/react/persistence.py,sha256=bwRk5Dq1qIoIbfOsVe6BhGCuXJM5TUX-zyHt9zEVPzw,5845
118
- aury/agents/react/step.py,sha256=oUvmVUlzGX6aa_YcbhHAmA3UHgKJcMvK3IrMFzEX-IM,28994
119
- aury/agents/react/tools.py,sha256=VxXec1K8C0h85yQZBVOQt0dhsmhSKCVV7iV_LXlasAs,13368
117
+ aury/agents/react/persistence.py,sha256=4BYmx3Dxu1grEpt4Qw3fUG26LgYyAhRnXVQC03OJDtE,6354
118
+ aury/agents/react/step.py,sha256=LQ9O3tqIITnEfHGfbZ1Sm9wWdkpVKAWPBczjeEi2PUg,29316
119
+ aury/agents/react/tools.py,sha256=RmXImt7jbr8fxc3wmFDAY8XreeHOJGg84GSEK1xtfhI,14013
120
120
  aury/agents/sandbox/__init__.py,sha256=adH29NhzpZUIFVCxhRUZxyANKpFauUjsPVd8iktUOaI,635
121
121
  aury/agents/sandbox/local.py,sha256=Dl-zJZ5FUz7WONtuze7MdpCDBjm9YZbjLIkGa9lYbwY,8328
122
122
  aury/agents/sandbox/remote.py,sha256=6NcYIs_thjyxa5LgIqwoPNsHYpT70DG6xGbjDYV6-X4,6444
@@ -147,7 +147,7 @@ aury/agents/workflow/expression.py,sha256=Hsx2jBigtt5zbJb9uK9pveipqMbBBUUAvlgYjB
147
147
  aury/agents/workflow/parser.py,sha256=mfiFZ_TtFq3IxAqPlTGcNkluiZ8ww16y3NYwbmbsrwE,6149
148
148
  aury/agents/workflow/state.py,sha256=AxTSo7P9b1DbWjGdQdzFY4_7m6CJGNFo7xkGK28SPZw,4234
149
149
  aury/agents/workflow/types.py,sha256=PVvjTQRgTObx5Mq_lXFogyLjGChOi5pUgcJwF5ZzVtg,2354
150
- aury_agent-0.0.10.dist-info/METADATA,sha256=O36iw-FfvggVYFrIhm5tnHP33J3jS9vsrQCZdMCgCJA,2115
151
- aury_agent-0.0.10.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
152
- aury_agent-0.0.10.dist-info/entry_points.txt,sha256=7EC5SMAKdC9HZLG8RfPhGno4OKOeSjaGsOWi1gOL-K8,56
153
- aury_agent-0.0.10.dist-info/RECORD,,
150
+ aury_agent-0.0.12.dist-info/METADATA,sha256=FNI9HTb1CwWuXKdPxWGcHD2PCu8V3QIogdr126INtvU,2115
151
+ aury_agent-0.0.12.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
152
+ aury_agent-0.0.12.dist-info/entry_points.txt,sha256=7EC5SMAKdC9HZLG8RfPhGno4OKOeSjaGsOWi1gOL-K8,56
153
+ aury_agent-0.0.12.dist-info/RECORD,,