agentscope-runtime 1.0.5.post1__py3-none-any.whl → 1.1.0b2__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.
- agentscope_runtime/__init__.py +3 -0
- agentscope_runtime/adapters/agentscope/message.py +36 -295
- agentscope_runtime/adapters/agentscope/stream.py +89 -2
- agentscope_runtime/adapters/agno/message.py +11 -2
- agentscope_runtime/adapters/agno/stream.py +1 -0
- agentscope_runtime/adapters/langgraph/__init__.py +1 -3
- agentscope_runtime/adapters/langgraph/message.py +11 -106
- agentscope_runtime/adapters/langgraph/stream.py +1 -0
- agentscope_runtime/adapters/ms_agent_framework/message.py +11 -1
- agentscope_runtime/adapters/ms_agent_framework/stream.py +1 -0
- agentscope_runtime/adapters/text/stream.py +1 -0
- agentscope_runtime/common/container_clients/agentrun_client.py +0 -3
- agentscope_runtime/common/container_clients/boxlite_client.py +26 -15
- agentscope_runtime/common/container_clients/fc_client.py +0 -11
- agentscope_runtime/common/utils/deprecation.py +14 -17
- agentscope_runtime/common/utils/logging.py +44 -0
- agentscope_runtime/engine/app/agent_app.py +5 -5
- agentscope_runtime/engine/app/celery_mixin.py +43 -4
- agentscope_runtime/engine/deployers/adapter/agui/__init__.py +8 -1
- agentscope_runtime/engine/deployers/adapter/agui/agui_adapter_utils.py +6 -1
- agentscope_runtime/engine/deployers/adapter/agui/agui_protocol_adapter.py +2 -2
- agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +13 -0
- agentscope_runtime/engine/runner.py +31 -6
- agentscope_runtime/engine/schemas/agent_schemas.py +28 -0
- agentscope_runtime/engine/services/sandbox/sandbox_service.py +41 -9
- agentscope_runtime/sandbox/box/base/base_sandbox.py +4 -0
- agentscope_runtime/sandbox/box/browser/browser_sandbox.py +4 -0
- agentscope_runtime/sandbox/box/dummy/dummy_sandbox.py +9 -2
- agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +4 -0
- agentscope_runtime/sandbox/box/gui/gui_sandbox.py +5 -1
- agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +4 -0
- agentscope_runtime/sandbox/box/sandbox.py +122 -13
- agentscope_runtime/sandbox/client/async_http_client.py +1 -0
- agentscope_runtime/sandbox/client/base.py +0 -1
- agentscope_runtime/sandbox/client/http_client.py +0 -2
- agentscope_runtime/sandbox/manager/heartbeat_mixin.py +486 -0
- agentscope_runtime/sandbox/manager/sandbox_manager.py +740 -153
- agentscope_runtime/sandbox/manager/server/app.py +18 -11
- agentscope_runtime/sandbox/manager/server/config.py +10 -2
- agentscope_runtime/sandbox/mcp_server.py +0 -1
- agentscope_runtime/sandbox/model/__init__.py +2 -1
- agentscope_runtime/sandbox/model/container.py +90 -3
- agentscope_runtime/sandbox/model/manager_config.py +45 -1
- agentscope_runtime/version.py +1 -1
- {agentscope_runtime-1.0.5.post1.dist-info → agentscope_runtime-1.1.0b2.dist-info}/METADATA +36 -54
- {agentscope_runtime-1.0.5.post1.dist-info → agentscope_runtime-1.1.0b2.dist-info}/RECORD +50 -69
- agentscope_runtime/adapters/agentscope/long_term_memory/__init__.py +0 -6
- agentscope_runtime/adapters/agentscope/long_term_memory/_long_term_memory_adapter.py +0 -258
- agentscope_runtime/adapters/agentscope/memory/__init__.py +0 -6
- agentscope_runtime/adapters/agentscope/memory/_memory_adapter.py +0 -152
- agentscope_runtime/engine/services/agent_state/__init__.py +0 -25
- agentscope_runtime/engine/services/agent_state/redis_state_service.py +0 -166
- agentscope_runtime/engine/services/agent_state/state_service.py +0 -179
- agentscope_runtime/engine/services/agent_state/state_service_factory.py +0 -52
- agentscope_runtime/engine/services/memory/__init__.py +0 -33
- agentscope_runtime/engine/services/memory/mem0_memory_service.py +0 -128
- agentscope_runtime/engine/services/memory/memory_service.py +0 -292
- agentscope_runtime/engine/services/memory/memory_service_factory.py +0 -126
- agentscope_runtime/engine/services/memory/redis_memory_service.py +0 -290
- agentscope_runtime/engine/services/memory/reme_personal_memory_service.py +0 -109
- agentscope_runtime/engine/services/memory/reme_task_memory_service.py +0 -11
- agentscope_runtime/engine/services/memory/tablestore_memory_service.py +0 -301
- agentscope_runtime/engine/services/session_history/__init__.py +0 -32
- agentscope_runtime/engine/services/session_history/redis_session_history_service.py +0 -283
- agentscope_runtime/engine/services/session_history/session_history_service.py +0 -267
- agentscope_runtime/engine/services/session_history/session_history_service_factory.py +0 -73
- agentscope_runtime/engine/services/session_history/tablestore_session_history_service.py +0 -288
- {agentscope_runtime-1.0.5.post1.dist-info → agentscope_runtime-1.1.0b2.dist-info}/WHEEL +0 -0
- {agentscope_runtime-1.0.5.post1.dist-info → agentscope_runtime-1.1.0b2.dist-info}/entry_points.txt +0 -0
- {agentscope_runtime-1.0.5.post1.dist-info → agentscope_runtime-1.1.0b2.dist-info}/licenses/LICENSE +0 -0
- {agentscope_runtime-1.0.5.post1.dist-info → agentscope_runtime-1.1.0b2.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(
|
|
@@ -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 =
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
read_only =
|
|
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
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
"
|
|
248
|
+
"Starting AgentApp with FastAPIAppFactory...",
|
|
249
249
|
)
|
|
250
250
|
fastapi_app = self.get_fastapi_app(**kwargs)
|
|
251
251
|
|
|
252
|
-
logger.info(f"
|
|
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"
|
|
260
|
+
logger.info(f"WebUI started at {webui_url}")
|
|
261
261
|
logger.info(
|
|
262
|
-
"
|
|
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
|
-
"
|
|
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
|
-
|
|
46
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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())
|