agentscope-runtime 1.0.5.post1__py3-none-any.whl → 1.1.0b3__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 (71) hide show
  1. agentscope_runtime/__init__.py +3 -0
  2. agentscope_runtime/adapters/agentscope/message.py +85 -295
  3. agentscope_runtime/adapters/agentscope/stream.py +133 -3
  4. agentscope_runtime/adapters/agno/message.py +11 -2
  5. agentscope_runtime/adapters/agno/stream.py +1 -0
  6. agentscope_runtime/adapters/langgraph/__init__.py +1 -3
  7. agentscope_runtime/adapters/langgraph/message.py +11 -106
  8. agentscope_runtime/adapters/langgraph/stream.py +1 -0
  9. agentscope_runtime/adapters/ms_agent_framework/message.py +11 -1
  10. agentscope_runtime/adapters/ms_agent_framework/stream.py +1 -0
  11. agentscope_runtime/adapters/text/stream.py +1 -0
  12. agentscope_runtime/common/container_clients/agentrun_client.py +0 -3
  13. agentscope_runtime/common/container_clients/boxlite_client.py +26 -15
  14. agentscope_runtime/common/container_clients/fc_client.py +0 -11
  15. agentscope_runtime/common/utils/deprecation.py +14 -17
  16. agentscope_runtime/common/utils/logging.py +44 -0
  17. agentscope_runtime/engine/app/agent_app.py +5 -5
  18. agentscope_runtime/engine/app/celery_mixin.py +43 -4
  19. agentscope_runtime/engine/deployers/adapter/agui/__init__.py +8 -1
  20. agentscope_runtime/engine/deployers/adapter/agui/agui_adapter_utils.py +6 -1
  21. agentscope_runtime/engine/deployers/adapter/agui/agui_protocol_adapter.py +2 -2
  22. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +13 -0
  23. agentscope_runtime/engine/runner.py +31 -6
  24. agentscope_runtime/engine/schemas/agent_schemas.py +28 -0
  25. agentscope_runtime/engine/services/sandbox/sandbox_service.py +41 -9
  26. agentscope_runtime/sandbox/box/base/base_sandbox.py +4 -0
  27. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +4 -0
  28. agentscope_runtime/sandbox/box/dummy/dummy_sandbox.py +9 -2
  29. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +4 -0
  30. agentscope_runtime/sandbox/box/gui/gui_sandbox.py +5 -1
  31. agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +4 -0
  32. agentscope_runtime/sandbox/box/sandbox.py +122 -13
  33. agentscope_runtime/sandbox/client/async_http_client.py +1 -0
  34. agentscope_runtime/sandbox/client/base.py +0 -1
  35. agentscope_runtime/sandbox/client/http_client.py +0 -2
  36. agentscope_runtime/sandbox/manager/heartbeat_mixin.py +486 -0
  37. agentscope_runtime/sandbox/manager/sandbox_manager.py +740 -153
  38. agentscope_runtime/sandbox/manager/server/app.py +18 -11
  39. agentscope_runtime/sandbox/manager/server/config.py +10 -2
  40. agentscope_runtime/sandbox/mcp_server.py +0 -1
  41. agentscope_runtime/sandbox/model/__init__.py +2 -1
  42. agentscope_runtime/sandbox/model/container.py +90 -3
  43. agentscope_runtime/sandbox/model/manager_config.py +45 -1
  44. agentscope_runtime/version.py +1 -1
  45. {agentscope_runtime-1.0.5.post1.dist-info → agentscope_runtime-1.1.0b3.dist-info}/METADATA +37 -54
  46. {agentscope_runtime-1.0.5.post1.dist-info → agentscope_runtime-1.1.0b3.dist-info}/RECORD +50 -69
  47. agentscope_runtime/adapters/agentscope/long_term_memory/__init__.py +0 -6
  48. agentscope_runtime/adapters/agentscope/long_term_memory/_long_term_memory_adapter.py +0 -258
  49. agentscope_runtime/adapters/agentscope/memory/__init__.py +0 -6
  50. agentscope_runtime/adapters/agentscope/memory/_memory_adapter.py +0 -152
  51. agentscope_runtime/engine/services/agent_state/__init__.py +0 -25
  52. agentscope_runtime/engine/services/agent_state/redis_state_service.py +0 -166
  53. agentscope_runtime/engine/services/agent_state/state_service.py +0 -179
  54. agentscope_runtime/engine/services/agent_state/state_service_factory.py +0 -52
  55. agentscope_runtime/engine/services/memory/__init__.py +0 -33
  56. agentscope_runtime/engine/services/memory/mem0_memory_service.py +0 -128
  57. agentscope_runtime/engine/services/memory/memory_service.py +0 -292
  58. agentscope_runtime/engine/services/memory/memory_service_factory.py +0 -126
  59. agentscope_runtime/engine/services/memory/redis_memory_service.py +0 -290
  60. agentscope_runtime/engine/services/memory/reme_personal_memory_service.py +0 -109
  61. agentscope_runtime/engine/services/memory/reme_task_memory_service.py +0 -11
  62. agentscope_runtime/engine/services/memory/tablestore_memory_service.py +0 -301
  63. agentscope_runtime/engine/services/session_history/__init__.py +0 -32
  64. agentscope_runtime/engine/services/session_history/redis_session_history_service.py +0 -283
  65. agentscope_runtime/engine/services/session_history/session_history_service.py +0 -267
  66. agentscope_runtime/engine/services/session_history/session_history_service_factory.py +0 -73
  67. agentscope_runtime/engine/services/session_history/tablestore_session_history_service.py +0 -288
  68. {agentscope_runtime-1.0.5.post1.dist-info → agentscope_runtime-1.1.0b3.dist-info}/WHEEL +0 -0
  69. {agentscope_runtime-1.0.5.post1.dist-info → agentscope_runtime-1.1.0b3.dist-info}/entry_points.txt +0 -0
  70. {agentscope_runtime-1.0.5.post1.dist-info → agentscope_runtime-1.1.0b3.dist-info}/licenses/LICENSE +0 -0
  71. {agentscope_runtime-1.0.5.post1.dist-info → agentscope_runtime-1.1.0b3.dist-info}/top_level.txt +0 -0
@@ -4,7 +4,7 @@
4
4
  import json
5
5
 
6
6
  from collections import OrderedDict
7
- from typing import Union, List
7
+ from typing import Union, List, Callable, Optional, Dict
8
8
 
9
9
  from langchain_core.messages import (
10
10
  AIMessage,
@@ -16,129 +16,34 @@ from langchain_core.messages import (
16
16
 
17
17
  from ...engine.schemas.agent_schemas import (
18
18
  Message,
19
- FunctionCall,
20
19
  MessageType,
21
20
  )
22
- from ...engine.helpers.agent_api_builder import ResponseBuilder
23
-
24
-
25
- def langgraph_msg_to_message(
26
- messages: Union[BaseMessage, List[BaseMessage]],
27
- ) -> List[Message]:
28
- """
29
- Convert LangGraph BaseMessage(s) into one or more runtime Message objects
30
-
31
- Args:
32
- messages: LangGraph message(s) from streaming.
33
-
34
- Returns:
35
- List[Message]: One or more constructed runtime Message objects.
36
- """
37
- if isinstance(messages, BaseMessage):
38
- msgs = [messages]
39
- elif isinstance(messages, list):
40
- msgs = messages
41
- else:
42
- raise TypeError(
43
- f"Expected BaseMessage or list[BaseMessage], got {type(messages)}",
44
- )
45
-
46
- results: List[Message] = []
47
-
48
- for msg in msgs:
49
- # Map LangGraph roles to runtime roles
50
- if isinstance(msg, HumanMessage):
51
- role = "user"
52
- elif isinstance(msg, AIMessage):
53
- role = "assistant"
54
- elif isinstance(msg, SystemMessage):
55
- role = "system"
56
- elif isinstance(msg, ToolMessage):
57
- role = "tool"
58
- else:
59
- role = "assistant" # default fallback
60
-
61
- # Handle tool calls in AIMessage
62
- if isinstance(msg, AIMessage) and msg.tool_calls:
63
- # Convert each tool call to a PLUGIN_CALL message
64
- for tool_call in msg.tool_calls:
65
- rb = ResponseBuilder()
66
- mb = rb.create_message_builder(
67
- role=role,
68
- message_type=MessageType.PLUGIN_CALL,
69
- )
70
- # Add metadata
71
- mb.message.metadata = {
72
- "original_id": getattr(msg, "id", None),
73
- "name": getattr(msg, "name", None),
74
- "metadata": getattr(msg, "additional_kwargs", {}),
75
- }
76
- cb = mb.create_content_builder(content_type="data")
77
-
78
- call_data = FunctionCall(
79
- call_id=tool_call.get("id", ""),
80
- name=tool_call.get("name", ""),
81
- arguments=json.dumps(tool_call.get("args", {})),
82
- ).model_dump()
83
- cb.set_data(call_data)
84
- cb.complete()
85
- mb.complete()
86
- results.append(mb.get_message_data())
87
-
88
- # If there's content in addition to tool calls,
89
- # create a separate message
90
- if msg.content:
91
- rb = ResponseBuilder()
92
- mb = rb.create_message_builder(
93
- role=role,
94
- message_type=MessageType.MESSAGE,
95
- )
96
- mb.message.metadata = {
97
- "original_id": getattr(msg, "id", None),
98
- "name": getattr(msg, "name", None),
99
- "metadata": getattr(msg, "additional_kwargs", {}),
100
- }
101
- cb = mb.create_content_builder(content_type="text")
102
- cb.set_text(str(msg.content))
103
- cb.complete()
104
- mb.complete()
105
- results.append(mb.get_message_data())
106
- else:
107
- # Regular message conversion
108
- rb = ResponseBuilder()
109
- mb = rb.create_message_builder(
110
- role=role,
111
- message_type=MessageType.MESSAGE,
112
- )
113
- # Add metadata
114
- mb.message.metadata = {
115
- "original_id": getattr(msg, "id", None),
116
- "name": getattr(msg, "name", None),
117
- "metadata": getattr(msg, "additional_kwargs", {}),
118
- }
119
- cb = mb.create_content_builder(content_type="text")
120
- cb.set_text(str(msg.content) if msg.content else "")
121
- cb.complete()
122
- mb.complete()
123
- results.append(mb.get_message_data())
124
-
125
- return results
126
21
 
127
22
 
128
23
  def message_to_langgraph_msg(
129
24
  messages: Union[Message, List[Message]],
25
+ type_converters: Optional[Dict[str, Callable]] = None,
130
26
  ) -> Union[BaseMessage, List[BaseMessage]]:
131
27
  """
132
28
  Convert AgentScope runtime Message(s) to LangGraph BaseMessage(s).
133
29
 
134
30
  Args:
135
31
  messages: A single AgentScope runtime Message or list of Messages.
32
+ type_converters: Optional mapping from ``message.type`` to a callable
33
+ ``converter(message)``. When provided and the current
34
+ ``message.type`` exists in the mapping, the corresponding converter
35
+ will be used and the built-in conversion logic will be skipped for
36
+ that message.
136
37
 
137
38
  Returns:
138
39
  A single BaseMessage object or a list of BaseMessage objects.
139
40
  """
140
41
 
141
42
  def _convert_one(message: Message) -> BaseMessage:
43
+ # Used for custom conversion
44
+ if type_converters and message.type in type_converters:
45
+ return type_converters[message.type](message)
46
+
142
47
  # Map runtime roles to LangGraph roles
143
48
  role_map = {
144
49
  "user": HumanMessage,
@@ -27,6 +27,7 @@ from ...engine.schemas.agent_schemas import (
27
27
 
28
28
  async def adapt_langgraph_message_stream(
29
29
  source_stream: AsyncIterator[Tuple[BaseMessage, bool]],
30
+ **kwargs, # pylint:disable=unused-argument
30
31
  ) -> AsyncIterator[Message]:
31
32
  """
32
33
  Optimized version of the stream adapter for LangGraph messages.
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  # pylint: disable=too-many-branches,too-many-statements
3
3
  import json
4
- from typing import Union, List
4
+ from typing import Union, List, Callable, Optional, Dict
5
5
  from collections import OrderedDict
6
6
 
7
7
  from agent_framework import (
@@ -22,6 +22,7 @@ from ...engine.schemas.agent_schemas import (
22
22
 
23
23
  def message_to_ms_agent_framework_message(
24
24
  messages: Union[Message, List[Message]],
25
+ type_converters: Optional[Dict[str, Callable]] = None,
25
26
  ) -> Union[ChatMessage, List[ChatMessage]]:
26
27
  """
27
28
  Convert AgentScope runtime Message(s) to Microsoft agent framework
@@ -33,6 +34,11 @@ def message_to_ms_agent_framework_message(
33
34
 
34
35
  Args:
35
36
  messages: A single AgentScope runtime Message or list of Messages.
37
+ type_converters: Optional mapping from ``message.type`` to a callable
38
+ ``converter(message)``. When provided and the current
39
+ ``message.type`` exists in the mapping, the corresponding converter
40
+ will be used and the built-in conversion logic will be skipped for
41
+ that message.
36
42
 
37
43
  Returns:
38
44
  A single Microsoft agent framework Message object or a list of
@@ -50,6 +56,10 @@ def message_to_ms_agent_framework_message(
50
56
  return default
51
57
 
52
58
  def _convert_one(message: Message) -> ChatMessage:
59
+ # Used for custom conversion
60
+ if type_converters and message.type in type_converters:
61
+ return type_converters[message.type](message)
62
+
53
63
  result = {
54
64
  "author_name": getattr(message, "name", message.role),
55
65
  "role": message.role or "assistant",
@@ -35,6 +35,7 @@ from ...engine.schemas.agent_schemas import (
35
35
 
36
36
  async def adapt_ms_agent_framework_message_stream(
37
37
  source_stream: AsyncIterator[AgentRunResponseUpdate],
38
+ **kwargs, # pylint:disable=unused-argument
38
39
  ) -> AsyncIterator[Union[Message, Content]]:
39
40
  # Initialize variables to avoid uncaught errors
40
41
  msg_id = None
@@ -11,6 +11,7 @@ from ...engine.helpers.agent_api_builder import ResponseBuilder
11
11
 
12
12
  async def adapt_text_stream(
13
13
  source_stream: AsyncIterator[str],
14
+ **kwargs, # pylint:disable=unused-argument
14
15
  ) -> AsyncIterator[Message]:
15
16
  rb = ResponseBuilder()
16
17
  mb = rb.create_message_builder(
@@ -1095,6 +1095,3 @@ class AgentRunClient(BaseClient):
1095
1095
  image_name = image
1096
1096
 
1097
1097
  return replacement_map.get(image_name.strip(), image)
1098
-
1099
- def _is_browser_image(self, image: str):
1100
- return image.startswith("agentscope/runtime-sandbox-browser")
@@ -159,27 +159,38 @@ class BoxliteClient(BaseClient):
159
159
  # Convert environment dict to list of tuples
160
160
  env_list = []
161
161
  if environment:
162
- env_list = list(environment.items())
162
+ env_list = [
163
+ (str(k), "" if v is None else str(v))
164
+ for k, v in environment.items()
165
+ ]
163
166
 
164
167
  # Convert volumes to BoxLite format
165
168
  volume_list = []
166
169
  if volumes:
167
- for vol in volumes:
168
- if isinstance(vol, (list, tuple)) and len(vol) >= 2:
169
- host_path = vol[0]
170
- guest_path = vol[1]
171
- read_only = len(vol) > 2 and vol[2] in (
172
- "ro",
173
- "readonly",
174
- True,
175
- )
170
+ if isinstance(volumes, dict):
171
+ for host_path, spec in volumes.items():
172
+ guest_path = spec.get("bind")
173
+ mode = (spec.get("mode") or "rw").lower()
174
+ read_only = mode in ("ro", "readonly")
176
175
  volume_list.append(
177
- (
178
- host_path,
179
- guest_path,
180
- "ro" if read_only else "rw",
181
- ),
176
+ (host_path, guest_path, read_only),
182
177
  )
178
+ elif isinstance(volumes, (list, tuple)):
179
+ for vol in volumes:
180
+ if isinstance(vol, (list, tuple)) and len(vol) >= 2:
181
+ host_path, guest_path = vol[0], vol[1]
182
+ third = vol[2] if len(vol) > 2 else "rw"
183
+ read_only = third is True or str(
184
+ third,
185
+ ).lower() in (
186
+ "ro",
187
+ "readonly",
188
+ "true",
189
+ "1",
190
+ )
191
+ volume_list.append(
192
+ (host_path, guest_path, read_only),
193
+ )
183
194
 
184
195
  # Convert ports to BoxLite format
185
196
  port_list = []
@@ -844,14 +844,3 @@ class FCClient(BaseClient):
844
844
  image_name = image
845
845
 
846
846
  return replacement_map.get(image_name.strip(), image)
847
-
848
- def _is_browser_image(self, image: str) -> bool:
849
- """Check if the image is a browser image.
850
-
851
- Args:
852
- image (str): The image name to check.
853
-
854
- Returns:
855
- bool: True if the image is a browser image, False otherwise.
856
- """
857
- return image.startswith("agentscope/runtime-sandbox-browser")
@@ -1,11 +1,14 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  from __future__ import annotations
3
3
 
4
+ import logging
4
5
  import functools
5
- import warnings
6
+ import threading
6
7
  from dataclasses import dataclass
7
8
  from typing import Callable, TypeVar
8
9
 
10
+ logger = logging.getLogger(__name__)
11
+
9
12
  T = TypeVar("T", bound=object)
10
13
 
11
14
 
@@ -38,27 +41,26 @@ def format_deprecation_message(subject: str, info: DeprecationInfo) -> str:
38
41
  return " ".join(parts)
39
42
 
40
43
 
44
+ _LOGGED_ONCE_MESSAGES: set[str] = set()
45
+ _LOGGED_ONCE_LOCK = threading.Lock()
46
+
47
+
41
48
  def warn_deprecated(
42
49
  subject: str,
43
50
  info: DeprecationInfo,
44
51
  *,
45
- category=DeprecationWarning,
46
52
  stacklevel: int = 2,
47
53
  once: bool = False,
48
54
  ) -> None:
49
55
  message = format_deprecation_message(subject, info)
50
56
 
51
57
  if once:
52
- top = _toplevel_pkg()
53
- with warnings.catch_warnings():
54
- warnings.filterwarnings(
55
- "once",
56
- category=category,
57
- module=rf"^{top}(\.|$)",
58
- )
59
- warnings.warn(message, category=category, stacklevel=stacklevel)
60
- else:
61
- warnings.warn(message, category=category, stacklevel=stacklevel)
58
+ with _LOGGED_ONCE_LOCK:
59
+ if message in _LOGGED_ONCE_MESSAGES:
60
+ return
61
+ _LOGGED_ONCE_MESSAGES.add(message)
62
+
63
+ logger.warning(message, stacklevel=stacklevel)
62
64
 
63
65
 
64
66
  def deprecated(
@@ -68,7 +70,6 @@ def deprecated(
68
70
  removed_in: str | None = None,
69
71
  alternative: str | None = None,
70
72
  issue: str | int | None = None,
71
- category=DeprecationWarning,
72
73
  stacklevel: int = 2,
73
74
  once: bool = False,
74
75
  ) -> Callable[[T], T]:
@@ -104,7 +105,6 @@ def deprecated(
104
105
  warn_deprecated(
105
106
  subject,
106
107
  info,
107
- category=category,
108
108
  stacklevel=stacklevel,
109
109
  once=once,
110
110
  )
@@ -118,7 +118,6 @@ def deprecated(
118
118
  warn_deprecated(
119
119
  subject,
120
120
  info,
121
- category=category,
122
121
  stacklevel=stacklevel,
123
122
  once=once,
124
123
  )
@@ -137,7 +136,6 @@ def deprecated_module(
137
136
  removed_in: str | None = None,
138
137
  alternative: str | None = None,
139
138
  issue: str | int | None = None,
140
- category=DeprecationWarning,
141
139
  stacklevel: int = 2,
142
140
  once: bool = True,
143
141
  ) -> None:
@@ -158,7 +156,6 @@ def deprecated_module(
158
156
  warn_deprecated(
159
157
  f"Module `{module_name}`",
160
158
  info,
161
- category=category,
162
159
  stacklevel=stacklevel,
163
160
  once=once,
164
161
  )
@@ -0,0 +1,44 @@
1
+ # -*- coding: utf-8 -*-
2
+ import logging
3
+ import os
4
+
5
+
6
+ class ColorFormatter(logging.Formatter):
7
+ COLORS = {
8
+ logging.DEBUG: "\033[34m",
9
+ logging.INFO: "\033[32m",
10
+ logging.WARNING: "\033[33m",
11
+ logging.ERROR: "\033[31m",
12
+ logging.CRITICAL: "\033[41m\033[97m",
13
+ }
14
+ RESET = "\033[0m"
15
+
16
+ def format(self, record):
17
+ color = self.COLORS.get(record.levelno, "")
18
+ level = f"{color}{record.levelname}{self.RESET}"
19
+
20
+ full_path = record.pathname
21
+ cwd = os.getcwd()
22
+ if full_path.startswith(cwd):
23
+ full_path = full_path[len(cwd) + 1 :]
24
+
25
+ prefix = f"{level} {full_path}:{record.lineno}"
26
+ original_msg = super().format(record)
27
+
28
+ return f"{prefix} | {original_msg}"
29
+
30
+
31
+ def setup_logger(level=logging.INFO):
32
+ log_format = "%(asctime)s | %(message)s"
33
+ datefmt = "%Y-%m-%d %H:%M:%S"
34
+
35
+ formatter = ColorFormatter(log_format, datefmt)
36
+ logger = logging.getLogger()
37
+ logger.setLevel(level)
38
+
39
+ if not logger.handlers:
40
+ handler = logging.StreamHandler()
41
+ handler.setFormatter(formatter)
42
+ logger.addHandler(handler)
43
+
44
+ return logger
@@ -245,11 +245,11 @@ class AgentApp(BaseApp):
245
245
 
246
246
  try:
247
247
  logger.info(
248
- "[AgentApp] Starting AgentApp with FastAPIAppFactory...",
248
+ "Starting AgentApp with FastAPIAppFactory...",
249
249
  )
250
250
  fastapi_app = self.get_fastapi_app(**kwargs)
251
251
 
252
- logger.info(f"[AgentApp] Starting server on {host}:{port}")
252
+ logger.info(f"Starting server on {host}:{port}")
253
253
 
254
254
  if web_ui:
255
255
  webui_url = f"http://{host}:{port}{self.endpoint_path}"
@@ -257,9 +257,9 @@ class AgentApp(BaseApp):
257
257
  f"npx @agentscope-ai/chat agentscope-runtime-webui "
258
258
  f"--url {webui_url}"
259
259
  )
260
- logger.info(f"[AgentApp] WebUI started at {webui_url}")
260
+ logger.info(f"WebUI started at {webui_url}")
261
261
  logger.info(
262
- "[AgentApp] Note: First WebUI launch may take extra time "
262
+ "Note: First WebUI launch may take extra time "
263
263
  "as dependencies are installed.",
264
264
  )
265
265
 
@@ -287,7 +287,7 @@ class AgentApp(BaseApp):
287
287
 
288
288
  except KeyboardInterrupt:
289
289
  logger.info(
290
- "[AgentApp] KeyboardInterrupt received, shutting down...",
290
+ "KeyboardInterrupt received, shutting down...",
291
291
  )
292
292
 
293
293
  def get_fastapi_app(self, **kwargs):
@@ -40,12 +40,51 @@ class CeleryMixin:
40
40
 
41
41
  self._registered_queues.add(queue)
42
42
 
43
+ def _coerce_result(x):
44
+ # Normalize Pydantic models first
45
+ if hasattr(x, "model_dump"): # pydantic v2
46
+ x = x.model_dump()
47
+ elif hasattr(x, "dict"): # pydantic v1
48
+ x = x.dict()
49
+ # Preserve simple primitives as-is
50
+ if isinstance(x, (str, int, float, bool)) or x is None:
51
+ return x
52
+ # Recursively coerce dictionaries
53
+ if isinstance(x, dict):
54
+ return {k: _coerce_result(v) for k, v in x.items()}
55
+ # Recursively coerce lists
56
+ if isinstance(x, list):
57
+ return [_coerce_result(item) for item in x]
58
+ # Fallback: string representation for anything else
59
+ return str(x)
60
+
61
+ async def _collect_async_gen(agen):
62
+ items = []
63
+ async for x in agen:
64
+ items.append(_coerce_result(x))
65
+ return items
66
+
67
+ def _collect_gen(gen):
68
+ return [_coerce_result(x) for x in gen]
69
+
43
70
  @self.celery_app.task(queue=queue)
44
71
  def wrapper(*args, **kwargs):
45
- if inspect.iscoroutinefunction(func):
46
- return asyncio.run(func(*args, **kwargs))
72
+ # 1) async generator function
73
+ if inspect.isasyncgenfunction(func):
74
+ result = func(*args, **kwargs)
75
+ # 2) async function
76
+ elif inspect.iscoroutinefunction(func):
77
+ result = asyncio.run(func(*args, **kwargs))
47
78
  else:
48
- return func(*args, **kwargs)
79
+ result = func(*args, **kwargs)
80
+ # 3) async generator
81
+ if inspect.isasyncgen(result):
82
+ return asyncio.run(_collect_async_gen(result))
83
+ # 4) sync generator
84
+ if inspect.isgenerator(result):
85
+ return _collect_gen(result)
86
+ # 5) normal return
87
+ return _coerce_result(result)
49
88
 
50
89
  return wrapper
51
90
 
@@ -63,7 +102,7 @@ class CeleryMixin:
63
102
  if concurrency:
64
103
  cmd.append(f"--concurrency={concurrency}")
65
104
  if queues:
66
- cmd.append(f"-Q {','.join(queues)}")
105
+ cmd += ["-Q", ",".join(queues)]
67
106
 
68
107
  self.celery_app.worker_main(cmd)
69
108
 
@@ -5,4 +5,11 @@ from .agui_protocol_adapter import (
5
5
  AGUIAdaptorConfig,
6
6
  )
7
7
 
8
- __all__ = ["AGUIDefaultAdapter", "FlexibleRunAgentInput", "AGUIAdaptorConfig"]
8
+ from .agui_adapter_utils import AGUIAdapterUtils
9
+
10
+ __all__ = [
11
+ "AGUIDefaultAdapter",
12
+ "FlexibleRunAgentInput",
13
+ "AGUIAdaptorConfig",
14
+ "AGUIAdapterUtils",
15
+ ]
@@ -216,7 +216,7 @@ class AGUI_MESSAGE_STATUS(Enum):
216
216
  COMPLETED = "COMPLETED"
217
217
 
218
218
 
219
- class AGUIAdapter:
219
+ class AGUIAdapterUtils:
220
220
  """
221
221
  Utility adapter that converts between Agent API events and AG-UI events.
222
222
  """
@@ -285,6 +285,11 @@ class AGUIAdapter:
285
285
  "session_id": self.thread_id,
286
286
  "user_id": user_id,
287
287
  "tools": tools,
288
+ # extra fields from agui_request
289
+ "state": agui_request.state,
290
+ "forwarded_props": agui_request.forwarded_props,
291
+ "parent_run_id": agui_request.parent_run_id,
292
+ "context": agui_request.context,
288
293
  },
289
294
  )
290
295
  return agent_request
@@ -14,7 +14,7 @@ from pydantic import BaseModel, Field
14
14
  from agentscope_runtime.engine.schemas.agent_schemas import AgentRequest, Event
15
15
 
16
16
  from .agui_adapter_utils import (
17
- AGUIAdapter,
17
+ AGUIAdapterUtils,
18
18
  AGUIEvent,
19
19
  AGUIEventType,
20
20
  RunErrorEvent,
@@ -157,7 +157,7 @@ class AGUIDefaultAdapter(ProtocolAdapter):
157
157
  request: FlexibleRunAgentInput,
158
158
  ):
159
159
  assert self._execution_func is not None
160
- adapter = AGUIAdapter(
160
+ adapter = AGUIAdapterUtils(
161
161
  thread_id=request.thread_id,
162
162
  run_id=request.run_id,
163
163
  )
@@ -292,6 +292,19 @@ class FastAPIAppFactory:
292
292
  def start_celery_worker():
293
293
  try:
294
294
  celery_mixin = app.state.celery_mixin
295
+
296
+ for ep in app.state.custom_endpoints:
297
+ if ep.get("task_type"):
298
+ func = ep["handler"]
299
+ queue = ep.get("queue", "celery")
300
+ if not hasattr(func, "celery_task"):
301
+ func.celery_task = (
302
+ celery_mixin.register_celery_task(
303
+ func,
304
+ queue,
305
+ )
306
+ )
307
+
295
308
  # Get registered queues or use default
296
309
  queues = (
297
310
  list(celery_mixin.get_registered_queues())