agentscope-runtime 1.0.1__py3-none-any.whl → 1.0.2__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 (58) hide show
  1. agentscope_runtime/adapters/agentscope/message.py +32 -7
  2. agentscope_runtime/adapters/agentscope/stream.py +121 -91
  3. agentscope_runtime/adapters/agno/__init__.py +0 -0
  4. agentscope_runtime/adapters/agno/message.py +30 -0
  5. agentscope_runtime/adapters/agno/stream.py +122 -0
  6. agentscope_runtime/adapters/langgraph/__init__.py +12 -0
  7. agentscope_runtime/adapters/langgraph/message.py +257 -0
  8. agentscope_runtime/adapters/langgraph/stream.py +205 -0
  9. agentscope_runtime/cli/__init__.py +7 -0
  10. agentscope_runtime/cli/cli.py +63 -0
  11. agentscope_runtime/cli/commands/__init__.py +2 -0
  12. agentscope_runtime/cli/commands/chat.py +815 -0
  13. agentscope_runtime/cli/commands/deploy.py +1062 -0
  14. agentscope_runtime/cli/commands/invoke.py +58 -0
  15. agentscope_runtime/cli/commands/list_cmd.py +103 -0
  16. agentscope_runtime/cli/commands/run.py +176 -0
  17. agentscope_runtime/cli/commands/sandbox.py +128 -0
  18. agentscope_runtime/cli/commands/status.py +60 -0
  19. agentscope_runtime/cli/commands/stop.py +185 -0
  20. agentscope_runtime/cli/commands/web.py +166 -0
  21. agentscope_runtime/cli/loaders/__init__.py +6 -0
  22. agentscope_runtime/cli/loaders/agent_loader.py +295 -0
  23. agentscope_runtime/cli/state/__init__.py +10 -0
  24. agentscope_runtime/cli/utils/__init__.py +18 -0
  25. agentscope_runtime/cli/utils/console.py +378 -0
  26. agentscope_runtime/cli/utils/validators.py +118 -0
  27. agentscope_runtime/engine/app/agent_app.py +7 -4
  28. agentscope_runtime/engine/deployers/__init__.py +1 -0
  29. agentscope_runtime/engine/deployers/agentrun_deployer.py +152 -22
  30. agentscope_runtime/engine/deployers/base.py +27 -2
  31. agentscope_runtime/engine/deployers/kubernetes_deployer.py +158 -31
  32. agentscope_runtime/engine/deployers/local_deployer.py +188 -25
  33. agentscope_runtime/engine/deployers/modelstudio_deployer.py +109 -18
  34. agentscope_runtime/engine/deployers/state/__init__.py +9 -0
  35. agentscope_runtime/engine/deployers/state/manager.py +388 -0
  36. agentscope_runtime/engine/deployers/state/schema.py +96 -0
  37. agentscope_runtime/engine/deployers/utils/build_cache.py +736 -0
  38. agentscope_runtime/engine/deployers/utils/detached_app.py +105 -30
  39. agentscope_runtime/engine/deployers/utils/docker_image_utils/docker_image_builder.py +31 -10
  40. agentscope_runtime/engine/deployers/utils/docker_image_utils/dockerfile_generator.py +15 -8
  41. agentscope_runtime/engine/deployers/utils/docker_image_utils/image_factory.py +30 -2
  42. agentscope_runtime/engine/deployers/utils/k8s_utils.py +241 -0
  43. agentscope_runtime/engine/deployers/utils/package.py +56 -6
  44. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +16 -2
  45. agentscope_runtime/engine/deployers/utils/service_utils/process_manager.py +155 -5
  46. agentscope_runtime/engine/deployers/utils/wheel_packager.py +107 -123
  47. agentscope_runtime/engine/runner.py +25 -6
  48. agentscope_runtime/engine/schemas/exception.py +580 -0
  49. agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +113 -39
  50. agentscope_runtime/sandbox/box/shared/routers/mcp_utils.py +20 -4
  51. agentscope_runtime/sandbox/utils.py +2 -0
  52. agentscope_runtime/version.py +1 -1
  53. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.2.dist-info}/METADATA +24 -7
  54. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.2.dist-info}/RECORD +58 -28
  55. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.2.dist-info}/entry_points.txt +1 -0
  56. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.2.dist-info}/WHEEL +0 -0
  57. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.2.dist-info}/licenses/LICENSE +0 -0
  58. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.2.dist-info}/top_level.txt +0 -0
@@ -374,9 +374,10 @@ def message_to_agentscope_msg(
374
374
 
375
375
  if message.type in (
376
376
  MessageType.PLUGIN_CALL,
377
+ MessageType.MCP_TOOL_CALL,
377
378
  MessageType.FUNCTION_CALL,
378
379
  ):
379
- # convert PLUGIN_CALL, FUNCTION_CALL to ToolUseBlock
380
+ # convert CALL to ToolUseBlock
380
381
  tool_args = None
381
382
  for cnt in reversed(message.content):
382
383
  if hasattr(cnt, "data"):
@@ -398,10 +399,10 @@ def message_to_agentscope_msg(
398
399
  ]
399
400
  elif message.type in (
400
401
  MessageType.PLUGIN_CALL_OUTPUT,
402
+ MessageType.MCP_TOOL_CALL_OUTPUT,
401
403
  MessageType.FUNCTION_CALL_OUTPUT,
402
404
  ):
403
- # convert PLUGIN_CALL_OUTPUT, FUNCTION_CALL_OUTPUT to
404
- # ToolResultBlock
405
+ # convert CALL_OUTPUT to ToolResultBlock
405
406
  out = None
406
407
  raw_output = ""
407
408
  for cnt in reversed(message.content):
@@ -425,6 +426,22 @@ def message_to_agentscope_msg(
425
426
 
426
427
  if isinstance(blk, list):
427
428
  if not all(is_valid_block(item) for item in blk):
429
+ try:
430
+ # Try to convert MCP content list to blocks
431
+ call_tool_result = {
432
+ "content": blk,
433
+ "structuredContent": None,
434
+ "isError": False,
435
+ }
436
+ blk = MCPClientBase._convert_mcp_content_to_as_blocks(
437
+ CallToolResult.model_validate(
438
+ call_tool_result,
439
+ ).content,
440
+ )
441
+ except Exception:
442
+ blk = raw_output
443
+ elif isinstance(blk, dict):
444
+ if not is_valid_block(blk):
428
445
  try:
429
446
  # Try to convert to MCP CallToolResult then to blocks
430
447
  blk = CallToolResult.model_validate(blk)
@@ -433,9 +450,6 @@ def message_to_agentscope_msg(
433
450
  )
434
451
  except Exception:
435
452
  blk = raw_output
436
- elif isinstance(blk, dict):
437
- if not is_valid_block(blk):
438
- blk = raw_output
439
453
  else:
440
454
  blk = raw_output
441
455
 
@@ -459,6 +473,7 @@ def message_to_agentscope_msg(
459
473
  "text": (TextBlock, "text"),
460
474
  "image": (ImageBlock, "image_url"),
461
475
  "audio": (AudioBlock, "data"),
476
+ "data": (TextBlock, "data"),
462
477
  # "video": (VideoBlock, "video_url", True),
463
478
  # TODO: support video
464
479
  }
@@ -533,7 +548,17 @@ def message_to_agentscope_msg(
533
548
  block_cls(type=cnt_type, source=base64_source),
534
549
  )
535
550
  else:
536
- msg_content.append(block_cls(type=cnt_type, text=value))
551
+ # text & data
552
+ if isinstance(value, str):
553
+ msg_content.append(
554
+ TextBlock(type="text", text=value),
555
+ )
556
+ else:
557
+ try:
558
+ json_str = json.dumps(value, ensure_ascii=False)
559
+ except Exception:
560
+ json_str = str(value)
561
+ msg_content.append(TextBlock(text=json_str))
537
562
 
538
563
  result["content"] = msg_content
539
564
  _msg = Msg(**result)
@@ -3,13 +3,15 @@
3
3
  import copy
4
4
  import json
5
5
 
6
- from typing import AsyncIterator, Tuple, List
6
+ from typing import AsyncIterator, Tuple, List, Union
7
7
  from urllib.parse import urlparse
8
8
 
9
+ from agentscope import setup_logger
9
10
  from agentscope.message import Msg
10
11
 
11
12
  from ...engine.schemas.agent_schemas import (
12
13
  Message,
14
+ Content,
13
15
  TextContent,
14
16
  ImageContent,
15
17
  AudioContent,
@@ -21,6 +23,8 @@ from ...engine.schemas.agent_schemas import (
21
23
  MessageType,
22
24
  )
23
25
 
26
+ setup_logger("ERROR")
27
+
24
28
 
25
29
  def _update_obj_attrs(obj, **attrs):
26
30
  for key, value in attrs.items():
@@ -31,7 +35,7 @@ def _update_obj_attrs(obj, **attrs):
31
35
 
32
36
  async def adapt_agentscope_message_stream(
33
37
  source_stream: AsyncIterator[Tuple[Msg, bool]],
34
- ) -> AsyncIterator[Message]:
38
+ ) -> AsyncIterator[Union[Message, Content]]:
35
39
  # Initialize variables to avoid uncaught errors
36
40
  msg_id = None
37
41
  last_content = ""
@@ -48,6 +52,7 @@ async def adapt_agentscope_message_stream(
48
52
  should_start_message = True
49
53
  should_start_reasoning_message = True
50
54
  tool_use_messages_dict = {}
55
+ tool_result_messages_dict = {}
51
56
  index = None
52
57
 
53
58
  # Run agent
@@ -262,95 +267,69 @@ async def adapt_agentscope_message_stream(
262
267
  fc_cls = FunctionCall
263
268
  fc_kwargs = {}
264
269
 
265
- if last:
266
- plugin_call_message = tool_use_messages_dict.get(
267
- call_id,
268
- )
269
-
270
- if plugin_call_message is None:
271
- # Only one tool use message yields, we fake
272
- # Build a new tool call message
273
- plugin_call_message = Message(
274
- type=msg_type,
275
- role="assistant",
276
- )
277
-
278
- data_delta_content = DataContent(
279
- index=0,
280
- data=fc_cls(
281
- call_id=element.get("id"),
282
- name=element.get("name"),
283
- arguments="",
284
- **fc_kwargs,
285
- ).model_dump(),
286
- delta=False,
287
- )
288
-
289
- plugin_call_message = _update_obj_attrs(
290
- plugin_call_message,
291
- metadata=metadata,
292
- usage=usage,
293
- )
294
- yield plugin_call_message.in_progress()
295
- yield data_delta_content.in_progress()
270
+ plugin_call_message = tool_use_messages_dict.get(
271
+ call_id,
272
+ )
296
273
 
297
- json_str = json.dumps(
298
- element.get("input"),
299
- ensure_ascii=False,
274
+ if plugin_call_message is None:
275
+ # Create a new message
276
+ plugin_call_message = Message(
277
+ type=msg_type,
278
+ role="assistant",
300
279
  )
301
280
  data_delta_content = DataContent(
302
- index=None, # Will be set by `add_content`
281
+ index=0,
303
282
  data=fc_cls(
304
- call_id=element.get("id"),
283
+ call_id=element["id"],
305
284
  name=element.get("name"),
306
- arguments=json_str,
285
+ arguments="",
307
286
  **fc_kwargs,
308
287
  ).model_dump(),
309
288
  delta=False,
310
289
  )
311
- plugin_call_message.add_content(
312
- new_content=data_delta_content,
313
- )
314
- yield data_delta_content.completed()
315
290
  plugin_call_message = _update_obj_attrs(
316
291
  plugin_call_message,
317
292
  metadata=metadata,
318
293
  usage=usage,
319
294
  )
320
- yield plugin_call_message.completed()
321
- index = None
322
- else:
323
- if call_id in tool_use_messages_dict:
324
- pass
325
- else:
326
- # Build a new tool call message
327
- plugin_call_message = Message(
328
- type=msg_type,
329
- role="assistant",
330
- )
331
-
332
- data_delta_content = DataContent(
333
- index=0,
334
- data=fc_cls(
335
- call_id=element.get("id"),
336
- name=element.get("name"),
337
- arguments="",
338
- **fc_kwargs,
339
- ).model_dump(),
340
- delta=False,
341
- )
295
+ yield plugin_call_message.in_progress()
296
+ data_delta_content.msg_id = plugin_call_message.id
297
+ yield data_delta_content.in_progress()
298
+ tool_use_messages_dict[
299
+ call_id
300
+ ] = plugin_call_message
301
+
302
+ # Update arguments
303
+ json_str = json.dumps(
304
+ element.get("input"),
305
+ ensure_ascii=False,
306
+ )
307
+ data_delta_content = DataContent(
308
+ index=None if last else 0,
309
+ data=fc_cls(
310
+ call_id=element["id"],
311
+ name=element.get("name"),
312
+ arguments=json_str,
313
+ **fc_kwargs,
314
+ ).model_dump(),
315
+ delta=False,
316
+ )
342
317
 
343
- plugin_call_message = _update_obj_attrs(
344
- plugin_call_message,
345
- metadata=metadata,
346
- usage=usage,
347
- )
348
- yield plugin_call_message.in_progress()
349
- yield data_delta_content.in_progress()
318
+ plugin_call_message = _update_obj_attrs(
319
+ plugin_call_message,
320
+ metadata=metadata,
321
+ usage=usage,
322
+ )
350
323
 
351
- tool_use_messages_dict[
352
- call_id
353
- ] = plugin_call_message
324
+ if last:
325
+ plugin_call_message.add_content(
326
+ new_content=data_delta_content,
327
+ )
328
+ yield data_delta_content.completed()
329
+ yield plugin_call_message.completed()
330
+ else:
331
+ data_delta_content.msg_id = plugin_call_message.id
332
+ yield data_delta_content.in_progress()
354
333
 
355
334
  elif element.get("type") == "tool_result": # Tool result
356
335
  call_id = element.get("id")
@@ -372,35 +351,86 @@ async def adapt_agentscope_message_stream(
372
351
  msg_type = MessageType.MCP_TOOL_CALL_OUTPUT
373
352
  fc_cls = McpCallOutput
374
353
 
375
- json_str = json.dumps(
376
- element.get("output"),
377
- ensure_ascii=False,
354
+ plugin_output_message = tool_result_messages_dict.get(
355
+ call_id,
378
356
  )
357
+
358
+ if plugin_output_message is None:
359
+ # Create a new message
360
+ plugin_output_message = Message(
361
+ type=msg_type,
362
+ role="tool",
363
+ )
364
+ data_delta_content = DataContent(
365
+ index=0,
366
+ data=fc_cls(
367
+ call_id=element.get("id"),
368
+ name=element.get("name"),
369
+ output="",
370
+ ).model_dump(),
371
+ delta=False,
372
+ )
373
+ plugin_output_message = _update_obj_attrs(
374
+ plugin_output_message,
375
+ metadata=metadata,
376
+ usage=usage,
377
+ )
378
+ yield plugin_output_message.in_progress()
379
+ data_delta_content.msg_id = (
380
+ plugin_output_message.id
381
+ )
382
+ yield data_delta_content.in_progress()
383
+ tool_result_messages_dict[
384
+ call_id
385
+ ] = plugin_output_message
386
+
387
+ # Update output
388
+ try:
389
+ json_str = json.dumps(
390
+ element.get("output"),
391
+ ensure_ascii=False,
392
+ )
393
+ except Exception:
394
+ # For non-JSON outputs, we just use the string
395
+ # representation
396
+ json_str = str(element.get("output"))
397
+
379
398
  data_delta_content = DataContent(
380
- index=index,
399
+ index=None if last else 0,
381
400
  data=fc_cls(
382
401
  call_id=element.get("id"),
383
402
  name=element.get("name"),
384
403
  output=json_str,
385
404
  ).model_dump(),
405
+ delta=False,
386
406
  )
387
- plugin_output_message = Message(
388
- type=msg_type,
389
- role="tool",
390
- content=[data_delta_content],
391
- )
407
+
392
408
  plugin_output_message = _update_obj_attrs(
393
409
  plugin_output_message,
394
410
  metadata=metadata,
395
411
  usage=usage,
396
412
  )
397
- yield plugin_output_message.completed()
398
- message = Message(
399
- type=MessageType.MESSAGE,
400
- role="assistant",
401
- )
402
- should_start_message = True
403
- index = None
413
+
414
+ if last:
415
+ plugin_output_message.add_content(
416
+ new_content=data_delta_content,
417
+ )
418
+ yield data_delta_content.completed()
419
+ yield plugin_output_message.completed()
420
+
421
+ message = Message(
422
+ type=MessageType.MESSAGE,
423
+ role="assistant",
424
+ )
425
+ should_start_message = True
426
+ index = None
427
+
428
+ else:
429
+ data_delta_content.msg_id = (
430
+ plugin_output_message.id
431
+ )
432
+ yield data_delta_content.in_progress()
433
+
404
434
  else:
405
435
  # TODO: handle image/audio/video block
406
436
  if should_start_message:
File without changes
@@ -0,0 +1,30 @@
1
+ # -*- coding: utf-8 -*-
2
+ from typing import Union, List
3
+
4
+ from agentscope.formatter import OpenAIChatFormatter
5
+
6
+ from ...engine.schemas.agent_schemas import Message
7
+ from ..agentscope.message import message_to_agentscope_msg
8
+
9
+
10
+ async def message_to_agno_message(
11
+ messages: Union[Message, List[Message]],
12
+ ) -> Union[dict, List[dict]]:
13
+ """
14
+ Convert AgentScope runtime Message(s) to Agno Message(s).
15
+
16
+ Args:
17
+ messages: A single AgentScope runtime Message or list of Messages.
18
+
19
+ Returns:
20
+ A single AgnoMessage object or a list of AgnoMessage objects.
21
+ """
22
+
23
+ as_msgs = message_to_agentscope_msg(messages)
24
+ raw_list = isinstance(as_msgs, list)
25
+ as_msgs = as_msgs if raw_list else [as_msgs]
26
+
27
+ formatter = OpenAIChatFormatter()
28
+ agno_message = await formatter.format(as_msgs)
29
+
30
+ return agno_message if raw_list else agno_message[0]
@@ -0,0 +1,122 @@
1
+ # -*- coding: utf-8 -*-
2
+ # pylint:disable=too-many-branches,too-many-statements
3
+ import json
4
+
5
+ from typing import AsyncIterator, Union
6
+
7
+ from agno.run.agent import (
8
+ BaseAgentRunEvent,
9
+ RunContentEvent,
10
+ RunCompletedEvent,
11
+ RunContentCompletedEvent,
12
+ RunStartedEvent,
13
+ # ReasoningStartedEvent, # Not support now
14
+ # ReasoningStepEvent,
15
+ # ReasoningCompletedEvent,
16
+ ToolCallStartedEvent,
17
+ ToolCallCompletedEvent,
18
+ )
19
+
20
+
21
+ from ...engine.schemas.agent_schemas import (
22
+ Message,
23
+ Content,
24
+ DataContent,
25
+ FunctionCall,
26
+ FunctionCallOutput,
27
+ MessageType,
28
+ )
29
+ from ...engine.helpers.agent_api_builder import ResponseBuilder
30
+
31
+
32
+ async def adapt_agno_message_stream(
33
+ source_stream: AsyncIterator[BaseAgentRunEvent],
34
+ ) -> AsyncIterator[Union[Message, Content]]:
35
+ rb = ResponseBuilder()
36
+ mb = None
37
+ cb = None
38
+ mb_type = None
39
+
40
+ should_start_new_message = True
41
+
42
+ async for event in source_stream:
43
+ if isinstance(event, RunStartedEvent):
44
+ should_start_new_message = True
45
+ elif isinstance(event, RunCompletedEvent):
46
+ # Placeholder
47
+ return
48
+ elif isinstance(event, RunContentEvent):
49
+ if event.reasoning_content:
50
+ message_type = MessageType.REASONING
51
+ content = event.reasoning_content
52
+ else:
53
+ message_type = MessageType.MESSAGE
54
+ content = event.content
55
+
56
+ if message_type != mb_type:
57
+ # Complete previous message
58
+ should_start_new_message = True
59
+ mb_type = message_type
60
+ if cb is not None:
61
+ yield cb.complete()
62
+ if mb is not None:
63
+ yield mb.complete()
64
+
65
+ if should_start_new_message:
66
+ should_start_new_message = False
67
+ mb = rb.create_message_builder(
68
+ message_type=message_type,
69
+ role="assistant",
70
+ )
71
+ yield mb.get_message_data()
72
+
73
+ cb = mb.create_content_builder(
74
+ content_type="text",
75
+ )
76
+ yield cb.add_text_delta(content)
77
+ elif isinstance(event, RunContentCompletedEvent):
78
+ yield cb.complete()
79
+ yield mb.complete()
80
+ mb = None
81
+ cb = None
82
+ should_start_new_message = True
83
+ elif isinstance(event, ToolCallStartedEvent):
84
+ json_str = json.dumps(event.tool.tool_args, ensure_ascii=False)
85
+ data = DataContent(
86
+ data=FunctionCall(
87
+ call_id=event.tool.tool_call_id,
88
+ name=event.tool.tool_name,
89
+ arguments=json_str,
90
+ ).model_dump(),
91
+ )
92
+ # Not support streaming tool call
93
+ message = Message(
94
+ type=MessageType.PLUGIN_CALL,
95
+ role="assistant",
96
+ content=[data],
97
+ )
98
+ # No stream tool call
99
+ yield message.completed()
100
+
101
+ should_start_new_message = True
102
+ elif isinstance(event, ToolCallCompletedEvent):
103
+ try:
104
+ json_str = json.dumps(event.tool.result, ensure_ascii=False)
105
+ except Exception:
106
+ json_str = str(event.tool.result)
107
+
108
+ data = DataContent(
109
+ data=FunctionCallOutput(
110
+ name=event.tool.tool_name,
111
+ call_id=event.tool.tool_call_id,
112
+ output=json_str,
113
+ ).model_dump(),
114
+ )
115
+ message = Message(
116
+ type=MessageType.PLUGIN_CALL_OUTPUT,
117
+ role="tool",
118
+ content=[data],
119
+ )
120
+ yield message.completed()
121
+
122
+ should_start_new_message = True
@@ -0,0 +1,12 @@
1
+ # -*- coding: utf-8 -*-
2
+ """LangGraph adapter for AgentScope runtime."""
3
+
4
+ # todo Message(reasoning) Adapter
5
+ # todo Memory Adapter
6
+ # todo Sandbox Tools Adapter
7
+ from .message import langgraph_msg_to_message, message_to_langgraph_msg
8
+
9
+ __all__ = [
10
+ "langgraph_msg_to_message",
11
+ "message_to_langgraph_msg",
12
+ ]