agentscope-runtime 0.1.1__py3-none-any.whl → 0.1.3__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.
Files changed (33) hide show
  1. agentscope_runtime/engine/agents/agentscope_agent/agent.py +105 -50
  2. agentscope_runtime/engine/agents/agentscope_agent/hooks.py +16 -3
  3. agentscope_runtime/engine/helpers/helper.py +33 -0
  4. agentscope_runtime/engine/runner.py +33 -1
  5. agentscope_runtime/engine/schemas/agent_schemas.py +208 -13
  6. agentscope_runtime/engine/services/context_manager.py +34 -1
  7. agentscope_runtime/engine/services/rag_service.py +195 -0
  8. agentscope_runtime/engine/services/reme_personal_memory_service.py +106 -0
  9. agentscope_runtime/engine/services/reme_task_memory_service.py +11 -0
  10. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +25 -0
  11. agentscope_runtime/sandbox/box/sandbox.py +60 -7
  12. agentscope_runtime/sandbox/box/shared/routers/mcp_utils.py +20 -2
  13. agentscope_runtime/sandbox/box/training_box/env_service.py +1 -1
  14. agentscope_runtime/sandbox/box/training_box/environments/bfcl/bfcl_dataprocess.py +216 -0
  15. agentscope_runtime/sandbox/box/training_box/environments/bfcl/bfcl_env.py +380 -0
  16. agentscope_runtime/sandbox/box/training_box/environments/bfcl/env_handler.py +934 -0
  17. agentscope_runtime/sandbox/box/training_box/training_box.py +139 -9
  18. agentscope_runtime/sandbox/client/http_client.py +1 -1
  19. agentscope_runtime/sandbox/enums.py +2 -0
  20. agentscope_runtime/sandbox/manager/container_clients/docker_client.py +19 -9
  21. agentscope_runtime/sandbox/manager/container_clients/kubernetes_client.py +61 -6
  22. agentscope_runtime/sandbox/manager/sandbox_manager.py +95 -35
  23. agentscope_runtime/sandbox/manager/server/app.py +128 -17
  24. agentscope_runtime/sandbox/model/__init__.py +1 -5
  25. agentscope_runtime/sandbox/model/manager_config.py +2 -13
  26. agentscope_runtime/sandbox/tools/mcp_tool.py +1 -1
  27. agentscope_runtime/version.py +1 -1
  28. {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.3.dist-info}/METADATA +59 -3
  29. {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.3.dist-info}/RECORD +33 -27
  30. {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.3.dist-info}/WHEEL +0 -0
  31. {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.3.dist-info}/entry_points.txt +0 -0
  32. {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.3.dist-info}/licenses/LICENSE +0 -0
  33. {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.3.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,14 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  # pylint:disable=too-many-nested-blocks, too-many-branches, too-many-statements
3
+ # pylint:disable=line-too-long, protected-access
4
+
3
5
  import json
4
6
  import threading
5
7
  import uuid
6
8
  from functools import partial
7
9
  from typing import Optional, Type
8
10
 
11
+ from agentscope import setup_logger
9
12
  from agentscope.agent import ReActAgent
10
13
  from agentscope.formatter import (
11
14
  FormatterBase,
@@ -15,6 +18,8 @@ from agentscope.formatter import (
15
18
  OllamaChatFormatter,
16
19
  GeminiChatFormatter,
17
20
  )
21
+ from agentscope.memory import InMemoryMemory
22
+ from agentscope.message import Msg, ToolUseBlock, ToolResultBlock
18
23
  from agentscope.model import (
19
24
  ChatModelBase,
20
25
  DashScopeChatModel,
@@ -23,9 +28,6 @@ from agentscope.model import (
23
28
  OllamaChatModel,
24
29
  GeminiChatModel,
25
30
  )
26
- from agentscope.memory import InMemoryMemory
27
- from agentscope.message import Msg
28
- from agentscope import setup_logger
29
31
  from agentscope.tool import (
30
32
  Toolkit,
31
33
  ToolResponse,
@@ -46,7 +48,6 @@ from ...schemas.agent_schemas import (
46
48
  FunctionCall,
47
49
  FunctionCallOutput,
48
50
  MessageType,
49
- RunStatus,
50
51
  )
51
52
  from ...schemas.context import Context
52
53
 
@@ -90,12 +91,33 @@ class AgentScopeContextAdapter:
90
91
  role_label = "user"
91
92
  else:
92
93
  role_label = message.role
93
-
94
- return {
94
+ result = {
95
95
  "name": message.role,
96
96
  "role": role_label,
97
- "content": message.content[0].text if message.content else "",
98
97
  }
98
+ if message.type == MessageType.PLUGIN_CALL:
99
+ result["content"] = [
100
+ ToolUseBlock(
101
+ type="tool_use",
102
+ id=message.content[0].data["call_id"],
103
+ name=message.role,
104
+ input=json.loads(message.content[0].data["arguments"]),
105
+ ),
106
+ ]
107
+ elif message.type == MessageType.PLUGIN_CALL_OUTPUT:
108
+ result["content"] = [
109
+ ToolResultBlock(
110
+ type="tool_result",
111
+ id=message.content[0].data["call_id"],
112
+ name=message.role,
113
+ output=message.content[0].data["output"],
114
+ ),
115
+ ]
116
+ else:
117
+ result["content"] = (
118
+ message.content[0].text if message.content else ""
119
+ )
120
+ return result
99
121
 
100
122
  async def adapt_new_message(self):
101
123
  last_message = self.context.session.messages[-1]
@@ -213,6 +235,7 @@ class AgentScopeAgent(Agent):
213
235
  memory=as_context.memory,
214
236
  toolkit=as_context.toolkit,
215
237
  )
238
+ self._agent._disable_console_output = True
216
239
 
217
240
  self._agent.register_instance_hook(
218
241
  "pre_print",
@@ -225,6 +248,7 @@ class AgentScopeAgent(Agent):
225
248
  async def run(self, context):
226
249
  as_context = AgentScopeContextAdapter(context=context, attr=self._attr)
227
250
  await as_context.initialize()
251
+ local_truncate_memory = ""
228
252
 
229
253
  # We should always build a new agent since the state is manage outside
230
254
  # the agent
@@ -245,89 +269,120 @@ class AgentScopeAgent(Agent):
245
269
  # Yield new Msg instances as they are logged
246
270
  last_content = ""
247
271
 
272
+ message = Message(type=MessageType.MESSAGE, role="assistant")
273
+ yield message.in_progress()
274
+ index = None
275
+
248
276
  for msg, msg_len in get_msg_instances(thread_id=thread_id):
249
277
  if msg:
250
278
  content = msg.content
251
-
252
279
  if isinstance(content, str):
253
280
  last_content = content
254
281
  else:
255
282
  for element in content:
256
- if isinstance(element, str):
257
- content = TextContent(text=element)
258
- message = Message(
259
- type=MessageType.MESSAGE,
260
- role="assistant",
261
- content=[content],
262
- status=RunStatus.Completed,
283
+ if isinstance(element, str) and element:
284
+ text_delta_content = TextContent(
285
+ delta=True,
286
+ index=index,
287
+ text=element,
288
+ )
289
+ text_delta_content = message.add_delta_content(
290
+ new_content=text_delta_content,
263
291
  )
264
- yield message
292
+ index = text_delta_content.index
293
+ yield text_delta_content
265
294
  elif isinstance(element, dict):
266
295
  if element.get("type") == "text":
267
- content = TextContent(
268
- text=element.get("text"),
269
- )
270
- message = Message(
271
- type=MessageType.MESSAGE,
272
- role="assistant",
273
- status=RunStatus.Completed,
274
- content=[content],
296
+ text = element.get(
297
+ "text",
298
+ "",
275
299
  )
276
- yield message
300
+ if text:
301
+ text_delta_content = TextContent(
302
+ delta=True,
303
+ index=index,
304
+ text=text.removeprefix(
305
+ local_truncate_memory,
306
+ ),
307
+ )
308
+ local_truncate_memory = element.get(
309
+ "text",
310
+ "",
311
+ )
312
+ text_delta_content = (
313
+ message.add_delta_content(
314
+ new_content=text_delta_content,
315
+ )
316
+ )
317
+ index = text_delta_content.index
318
+ yield text_delta_content
319
+ if hasattr(msg, "is_last"):
320
+ yield message.completed()
321
+ message = Message(
322
+ type=MessageType.MESSAGE,
323
+ role="assistant",
324
+ )
325
+ index = None
326
+
277
327
  elif element.get("type") == "tool_use":
278
328
  json_str = json.dumps(element.get("input"))
279
- data = DataContent(
329
+ data_delta_content = DataContent(
330
+ index=index,
280
331
  data=FunctionCall(
281
332
  call_id=element.get("id"),
282
333
  name=element.get("name"),
283
334
  arguments=json_str,
284
335
  ).model_dump(),
285
336
  )
286
- message = Message(
337
+ plugin_call_message = Message(
287
338
  type=MessageType.PLUGIN_CALL,
288
339
  role="assistant",
289
- status=RunStatus.Completed,
290
- content=[data],
340
+ content=[data_delta_content],
291
341
  )
292
- yield message
342
+ yield plugin_call_message.completed()
293
343
  elif element.get("type") == "tool_result":
294
- data = DataContent(
344
+ data_delta_content = DataContent(
345
+ index=index,
295
346
  data=FunctionCallOutput(
296
347
  call_id=element.get("id"),
297
348
  output=str(element.get("output")),
298
349
  ).model_dump(),
299
350
  )
300
- message = Message(
351
+ plugin_output_message = Message(
301
352
  type=MessageType.PLUGIN_CALL_OUTPUT,
302
353
  role="assistant",
303
- status=RunStatus.Completed,
304
- content=[data],
354
+ content=[data_delta_content],
305
355
  )
306
- yield message
356
+ yield plugin_output_message.completed()
307
357
  else:
308
- message = Message(
309
- type=MessageType.MESSAGE,
310
- role="assistant",
311
- status=RunStatus.Completed,
312
- content=[
313
- TextContent(text=f"{element}"),
314
- ],
358
+ text_delta_content = TextContent(
359
+ delta=True,
360
+ index=index,
361
+ text=f"{element}",
315
362
  )
316
- yield message
363
+ text_delta_content = (
364
+ message.add_delta_content(
365
+ new_content=text_delta_content,
366
+ )
367
+ )
368
+ index = text_delta_content.index
369
+ yield text_delta_content
317
370
 
318
371
  # Break if the thread is dead and no more messages are expected
319
372
  if not thread.is_alive() and msg_len == 0:
320
373
  break
321
374
 
322
375
  if last_content:
323
- content = TextContent(text=last_content)
324
- message = Message(
325
- type=MessageType.MESSAGE,
326
- role="assistant",
327
- content=[content],
328
- status=RunStatus.Completed,
376
+ text_delta_content = TextContent(
377
+ delta=True,
378
+ index=index,
379
+ text=last_content,
380
+ )
381
+ text_delta_content = message.add_delta_content(
382
+ new_content=text_delta_content,
329
383
  )
330
- yield message
384
+ yield text_delta_content
385
+ yield message.completed()
331
386
 
332
387
  # Wait for the function to finish
333
388
  thread.join()
@@ -2,19 +2,20 @@
2
2
  """ Hooks for stream output """
3
3
  # pylint: disable=unused-argument,too-many-nested-blocks
4
4
  import asyncio
5
+ import os
5
6
  import time
6
7
  import threading
7
8
  import logging
8
9
 
9
10
  from collections import defaultdict
10
- from typing import Union, Optional, Generator, Any
11
+ from typing import Union, Optional, Generator, Any, List
11
12
 
12
13
  from agentscope.agent import AgentBase
13
14
  from agentscope.message import Msg
14
15
 
15
16
  _MSG_INSTANCE = defaultdict(list)
16
17
  _LOCKS = defaultdict(threading.Lock)
17
- TIMEOUT = 30
18
+ TIMEOUT = int(os.getenv("AGENTSCOPE_AGENT_TIMEOUT", "30"))
18
19
 
19
20
 
20
21
  def run_async_in_thread(coro):
@@ -83,7 +84,19 @@ def pre_speak_msg_buffer_hook(
83
84
  thread_id = threading.current_thread().name
84
85
  if thread_id.startswith("pipeline"):
85
86
  with _LOCKS[thread_id]:
86
- _MSG_INSTANCE[thread_id].append(msg)
87
+ if kwargs.get("last", True):
88
+ msg.is_last = True
89
+ _MSG_INSTANCE[thread_id].append(msg)
90
+ else:
91
+ new_blocks = []
92
+ if isinstance(msg.content, List):
93
+ for block in msg.content:
94
+ if block.get("type", "") != "tool_use":
95
+ new_blocks.append(block)
96
+ msg.content = new_blocks
97
+ if msg.content:
98
+ _MSG_INSTANCE[thread_id].append(msg)
99
+
87
100
  return kwargs
88
101
 
89
102
 
@@ -93,6 +93,39 @@ async def simple_call_agent_tool(agent, query):
93
93
  return all_result
94
94
 
95
95
 
96
+ async def simple_call_agent_tool_auto_lifecycle(agent, query):
97
+ all_result = ""
98
+ async with Runner(
99
+ agent=agent,
100
+ context_manager=create_context_manager(),
101
+ environment_manager=create_environment_manager(),
102
+ ) as runner:
103
+ request = AgentRequest(
104
+ input=[
105
+ {
106
+ "role": "user",
107
+ "content": [
108
+ {
109
+ "type": "text",
110
+ "text": query,
111
+ },
112
+ ],
113
+ },
114
+ ],
115
+ )
116
+
117
+ async for message in runner.stream_query(
118
+ request=request,
119
+ ):
120
+ if (
121
+ message.object == "message"
122
+ and MessageType.MESSAGE == message.type
123
+ and RunStatus.Completed == message.status
124
+ ):
125
+ all_result = message.content[0].text
126
+ return all_result
127
+
128
+
96
129
  async def simple_call_agent_tool_wo_env(agent, query):
97
130
  all_result = ""
98
131
  async with create_context_manager() as context_manager:
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import uuid
3
3
  from typing import Optional, List, AsyncGenerator, Any
4
+ from contextlib import AsyncExitStack
4
5
 
5
6
  from openai.types.chat import ChatCompletion
6
7
 
@@ -41,8 +42,35 @@ class Runner:
41
42
  self._environment_manager = environment_manager
42
43
  self._context_manager = context_manager
43
44
  self._deploy_managers = {}
45
+ self._exit_stack = AsyncExitStack()
46
+
47
+ async def __aenter__(self) -> "Runner":
48
+ """
49
+ Initializes the runner and ensures context/environment managers
50
+ are fully entered so that attributes like compose_session are
51
+ available.
52
+ """
53
+ if self._environment_manager:
54
+ # enter_async_context returns the "real" object
55
+ self._environment_manager = (
56
+ await self._exit_stack.enter_async_context(
57
+ self._environment_manager,
58
+ )
59
+ )
60
+
61
+ if self._context_manager:
62
+ self._context_manager = await self._exit_stack.enter_async_context(
63
+ self._context_manager,
64
+ )
65
+
66
+ return self
67
+
68
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
69
+ try:
70
+ await self._exit_stack.aclose()
71
+ except Exception:
72
+ pass
44
73
 
45
- # TODO: should be sync method?
46
74
  async def deploy(
47
75
  self,
48
76
  deploy_manager: DeployManager = LocalDeployManager(),
@@ -139,18 +167,22 @@ class Runner:
139
167
  request_input=request_input,
140
168
  )
141
169
 
170
+ sequence_number = 0
142
171
  async for event in context.agent.run_async(context):
143
172
  if (
144
173
  event.status == RunStatus.Completed
145
174
  and event.object == "message"
146
175
  ):
147
176
  response.add_new_message(event)
177
+ event.sequence_number = sequence_number
148
178
  yield event
179
+ sequence_number += 1
149
180
 
150
181
  await context.context_manager.append(
151
182
  session=context.session,
152
183
  event_output=response.output,
153
184
  )
185
+ response.sequence_number = sequence_number
154
186
  yield response.completed()
155
187
 
156
188
  @trace(TraceType.AGENT_STEP)