agentscope-runtime 1.0.4a1__py3-none-any.whl → 1.0.5__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/adapters/agentscope/stream.py +2 -8
- agentscope_runtime/adapters/langgraph/stream.py +120 -70
- agentscope_runtime/adapters/ms_agent_framework/__init__.py +0 -0
- agentscope_runtime/adapters/ms_agent_framework/message.py +205 -0
- agentscope_runtime/adapters/ms_agent_framework/stream.py +418 -0
- agentscope_runtime/adapters/utils.py +6 -0
- agentscope_runtime/cli/commands/deploy.py +836 -1
- agentscope_runtime/cli/commands/stop.py +16 -0
- agentscope_runtime/common/container_clients/__init__.py +52 -0
- agentscope_runtime/common/container_clients/agentrun_client.py +6 -4
- agentscope_runtime/common/container_clients/boxlite_client.py +442 -0
- agentscope_runtime/common/container_clients/docker_client.py +0 -20
- agentscope_runtime/common/container_clients/fc_client.py +6 -4
- agentscope_runtime/common/container_clients/gvisor_client.py +38 -0
- agentscope_runtime/common/container_clients/knative_client.py +467 -0
- agentscope_runtime/common/utils/deprecation.py +164 -0
- agentscope_runtime/engine/__init__.py +4 -0
- agentscope_runtime/engine/app/agent_app.py +16 -4
- agentscope_runtime/engine/constant.py +1 -0
- agentscope_runtime/engine/deployers/__init__.py +34 -11
- agentscope_runtime/engine/deployers/adapter/__init__.py +8 -0
- agentscope_runtime/engine/deployers/adapter/a2a/__init__.py +26 -51
- agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +23 -13
- agentscope_runtime/engine/deployers/adapter/a2a/a2a_registry.py +4 -201
- agentscope_runtime/engine/deployers/adapter/a2a/nacos_a2a_registry.py +152 -25
- agentscope_runtime/engine/deployers/adapter/agui/__init__.py +8 -0
- agentscope_runtime/engine/deployers/adapter/agui/agui_adapter_utils.py +652 -0
- agentscope_runtime/engine/deployers/adapter/agui/agui_protocol_adapter.py +225 -0
- agentscope_runtime/engine/deployers/agentrun_deployer.py +2 -2
- agentscope_runtime/engine/deployers/fc_deployer.py +1506 -0
- agentscope_runtime/engine/deployers/knative_deployer.py +290 -0
- agentscope_runtime/engine/deployers/pai_deployer.py +2335 -0
- agentscope_runtime/engine/deployers/utils/net_utils.py +37 -0
- agentscope_runtime/engine/deployers/utils/oss_utils.py +38 -0
- agentscope_runtime/engine/deployers/utils/package.py +46 -42
- agentscope_runtime/engine/helpers/agent_api_client.py +372 -0
- agentscope_runtime/engine/runner.py +13 -0
- agentscope_runtime/engine/schemas/agent_schemas.py +9 -3
- agentscope_runtime/engine/services/agent_state/__init__.py +7 -0
- agentscope_runtime/engine/services/memory/__init__.py +7 -0
- agentscope_runtime/engine/services/memory/redis_memory_service.py +15 -16
- agentscope_runtime/engine/services/session_history/__init__.py +7 -0
- agentscope_runtime/engine/tracing/local_logging_handler.py +2 -3
- agentscope_runtime/engine/tracing/wrapper.py +18 -4
- agentscope_runtime/sandbox/__init__.py +14 -6
- agentscope_runtime/sandbox/box/base/__init__.py +2 -2
- agentscope_runtime/sandbox/box/base/base_sandbox.py +51 -1
- agentscope_runtime/sandbox/box/browser/__init__.py +2 -2
- agentscope_runtime/sandbox/box/browser/browser_sandbox.py +198 -2
- agentscope_runtime/sandbox/box/filesystem/__init__.py +2 -2
- agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +99 -2
- agentscope_runtime/sandbox/box/gui/__init__.py +2 -2
- agentscope_runtime/sandbox/box/gui/gui_sandbox.py +117 -1
- agentscope_runtime/sandbox/box/mobile/__init__.py +2 -2
- agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +247 -100
- agentscope_runtime/sandbox/box/sandbox.py +102 -65
- agentscope_runtime/sandbox/box/shared/routers/generic.py +36 -29
- agentscope_runtime/sandbox/client/__init__.py +6 -1
- agentscope_runtime/sandbox/client/async_http_client.py +339 -0
- agentscope_runtime/sandbox/client/base.py +74 -0
- agentscope_runtime/sandbox/client/http_client.py +108 -329
- agentscope_runtime/sandbox/enums.py +7 -0
- agentscope_runtime/sandbox/manager/sandbox_manager.py +275 -29
- agentscope_runtime/sandbox/manager/server/app.py +7 -1
- agentscope_runtime/sandbox/manager/server/config.py +3 -1
- agentscope_runtime/sandbox/model/manager_config.py +11 -9
- agentscope_runtime/tools/modelstudio_memory/__init__.py +106 -0
- agentscope_runtime/tools/modelstudio_memory/base.py +220 -0
- agentscope_runtime/tools/modelstudio_memory/config.py +86 -0
- agentscope_runtime/tools/modelstudio_memory/core.py +594 -0
- agentscope_runtime/tools/modelstudio_memory/exceptions.py +60 -0
- agentscope_runtime/tools/modelstudio_memory/schemas.py +253 -0
- agentscope_runtime/version.py +1 -1
- {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.dist-info}/METADATA +186 -73
- {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.dist-info}/RECORD +79 -55
- {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.dist-info}/WHEEL +0 -0
- {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.dist-info}/entry_points.txt +0 -0
- {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.dist-info}/licenses/LICENSE +0 -0
- {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
+
from functools import lru_cache
|
|
2
3
|
import ipaddress
|
|
3
4
|
import os
|
|
4
5
|
import socket
|
|
5
6
|
from typing import Optional
|
|
7
|
+
from urllib import parse
|
|
6
8
|
|
|
7
9
|
import psutil
|
|
8
10
|
|
|
@@ -63,3 +65,38 @@ def get_first_non_loopback_ip() -> Optional[str]:
|
|
|
63
65
|
pass
|
|
64
66
|
|
|
65
67
|
return None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@lru_cache()
|
|
71
|
+
def is_tcp_reachable(
|
|
72
|
+
endpoint: str,
|
|
73
|
+
port: int = None,
|
|
74
|
+
timeout: int = 1,
|
|
75
|
+
) -> bool:
|
|
76
|
+
"""Check if a domain is connectable, intelligently determining the port."""
|
|
77
|
+
|
|
78
|
+
parsed_url = parse.urlparse(endpoint)
|
|
79
|
+
|
|
80
|
+
scheme = parsed_url.scheme or "http"
|
|
81
|
+
hostname = parsed_url.hostname or endpoint
|
|
82
|
+
|
|
83
|
+
if not hostname:
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
if port is not None:
|
|
87
|
+
port_to_use = port
|
|
88
|
+
elif scheme == "https":
|
|
89
|
+
port_to_use = 443
|
|
90
|
+
else:
|
|
91
|
+
port_to_use = 80
|
|
92
|
+
|
|
93
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
94
|
+
sock.settimeout(timeout)
|
|
95
|
+
try:
|
|
96
|
+
ip = socket.gethostbyname(hostname)
|
|
97
|
+
sock.connect((ip, port_to_use))
|
|
98
|
+
return True
|
|
99
|
+
except (socket.timeout, socket.gaierror, socket.error):
|
|
100
|
+
return False
|
|
101
|
+
finally:
|
|
102
|
+
sock.close()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Optional, Tuple
|
|
4
|
+
from urllib import parse
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def parse_oss_uri(oss_uri: str) -> Tuple[str, Optional[str], str]:
|
|
11
|
+
"""
|
|
12
|
+
Parse the oss uri to the format of
|
|
13
|
+
("<bucket_name>", <endpoint>, <object_key>)
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
oss://my-bucket.oss-cn-hangzhou.aliyuncs.com/my-object-key
|
|
17
|
+
-> ("my-bucket", "oss-cn-hangzhou.aliyuncs.com", "my-object-key")
|
|
18
|
+
|
|
19
|
+
oss://my-bucket/my-object-key
|
|
20
|
+
-> ("my-bucket", None, "my-object-key")
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
oss_uri: The OSS URI to parse
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
A tuple of (bucket_name, endpoint, object_key)
|
|
27
|
+
"""
|
|
28
|
+
parsed_result = parse.urlparse(oss_uri)
|
|
29
|
+
if parsed_result.scheme != "oss":
|
|
30
|
+
raise ValueError(f"require oss uri but given '{oss_uri}'")
|
|
31
|
+
hostname = parsed_result.hostname
|
|
32
|
+
if hostname and "." in hostname:
|
|
33
|
+
bucket_name, endpoint = hostname.split(".", 1)
|
|
34
|
+
else:
|
|
35
|
+
bucket_name = hostname
|
|
36
|
+
endpoint = None
|
|
37
|
+
object_key = parsed_result.path
|
|
38
|
+
return bucket_name, endpoint, object_key.lstrip("/")
|
|
@@ -593,60 +593,64 @@ def package(
|
|
|
593
593
|
Package an AgentApp or Runner for deployment.
|
|
594
594
|
|
|
595
595
|
This function supports two deployment patterns:
|
|
596
|
+
|
|
596
597
|
1. Object-style: package(app=my_app) or package(runner=my_runner)
|
|
597
|
-
2. Entrypoint-style: package(entrypoint="app.py") or
|
|
598
|
-
|
|
598
|
+
2. Entrypoint-style: package(entrypoint="app.py") or
|
|
599
|
+
package(entrypoint="project_dir/")
|
|
599
600
|
|
|
600
601
|
For object-style deployment, this function will:
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
602
|
+
|
|
603
|
+
1. Extract the project directory containing the app/runner.
|
|
604
|
+
2. Generate a new main.py that imports and runs the app/runner.
|
|
605
|
+
3. Package the project with the generated main.py as entrypoint.
|
|
604
606
|
|
|
605
607
|
Build directory naming:
|
|
608
|
+
|
|
606
609
|
- When output_dir=None (default), creates workspace directory with
|
|
607
|
-
|
|
608
|
-
- Directory format: cwd/.agentscope_runtime/builds
|
|
609
|
-
|
|
610
|
-
- Explicit output_dir uses the provided path
|
|
610
|
+
platform-aware naming.
|
|
611
|
+
- Directory format: cwd/.agentscope_runtime/builds/
|
|
612
|
+
<platform>_<timestamp>_<code>/
|
|
613
|
+
- Explicit output_dir uses the provided path.
|
|
611
614
|
|
|
612
615
|
Args:
|
|
613
|
-
app: AgentApp instance (for object-style deployment)
|
|
614
|
-
runner: Runner instance (for object-style deployment)
|
|
615
|
-
entrypoint: Entrypoint specification (for CLI-style deployment)
|
|
616
|
-
output_dir: Output directory (creates temp dir if None)
|
|
617
|
-
host: Default host for the service (default: "0.0.0.0")
|
|
618
|
-
port: Default port for the service (default: 8090)
|
|
619
|
-
extra_parameters: Additional runtime parameters to expose via CLI
|
|
620
|
-
requirements: Additional pip requirements
|
|
621
|
-
platform: Deployment platform (k8s, modelstudio, agentrun, local)
|
|
622
|
-
**kwargs: Additional keyword arguments (ignored)
|
|
616
|
+
app: AgentApp instance (for object-style deployment).
|
|
617
|
+
runner: Runner instance (for object-style deployment).
|
|
618
|
+
entrypoint: Entrypoint specification (for CLI-style deployment).
|
|
619
|
+
output_dir: Output directory (creates temp dir if None).
|
|
620
|
+
host: Default host for the service (default: "0.0.0.0").
|
|
621
|
+
port: Default port for the service (default: 8090).
|
|
622
|
+
extra_parameters: Additional runtime parameters to expose via CLI.
|
|
623
|
+
requirements: Additional pip requirements.
|
|
624
|
+
platform: Deployment platform (k8s, modelstudio, agentrun, local).
|
|
625
|
+
**kwargs: Additional keyword arguments (ignored).
|
|
623
626
|
|
|
624
627
|
Returns:
|
|
625
|
-
Tuple of (package_path, project_info)
|
|
626
|
-
- package_path: Path to the deployment package directory
|
|
627
|
-
- project_info: ProjectInfo with project metadata
|
|
628
|
+
Tuple of (package_path, project_info).
|
|
628
629
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
RuntimeError: If packaging fails
|
|
630
|
+
- package_path: Path to the deployment package directory.
|
|
631
|
+
- project_info: ProjectInfo with project metadata.
|
|
632
632
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
633
|
+
Raises:
|
|
634
|
+
ValueError: If neither app/runner nor entrypoint is provided.
|
|
635
|
+
RuntimeError: If packaging fails.
|
|
636
|
+
|
|
637
|
+
Example::
|
|
638
|
+
|
|
639
|
+
extra_params = [
|
|
640
|
+
RuntimeParameter(
|
|
641
|
+
name="log_level",
|
|
642
|
+
type="str",
|
|
643
|
+
default="info",
|
|
644
|
+
help="Logging level",
|
|
645
|
+
),
|
|
646
|
+
RuntimeParameter(
|
|
647
|
+
name="workers",
|
|
648
|
+
type="int",
|
|
649
|
+
default=4,
|
|
650
|
+
help="Number of worker threads",
|
|
651
|
+
),
|
|
652
|
+
]
|
|
653
|
+
package(app=my_app, extra_parameters=extra_params, platform="k8s")
|
|
650
654
|
"""
|
|
651
655
|
# Determine project info and target object
|
|
652
656
|
target_obj = None
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Agent API Protocol Client Library.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from typing import AsyncIterator, Dict, Iterator, Optional
|
|
10
|
+
|
|
11
|
+
import httpx
|
|
12
|
+
|
|
13
|
+
from agentscope_runtime.engine.schemas.agent_schemas import (
|
|
14
|
+
AgentRequest,
|
|
15
|
+
AgentResponse,
|
|
16
|
+
Content,
|
|
17
|
+
DataContent,
|
|
18
|
+
Event,
|
|
19
|
+
ImageContent,
|
|
20
|
+
Message,
|
|
21
|
+
TextContent,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AgentAPIClientBase(ABC):
|
|
28
|
+
"""
|
|
29
|
+
Abstract base class for Agent API Protocol clients.
|
|
30
|
+
|
|
31
|
+
All Agent API clients must implement this interface, which defines
|
|
32
|
+
the core method for streaming requests and responses according to
|
|
33
|
+
the Agent API Protocol.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def stream(self, request: AgentRequest) -> Iterator[Event]:
|
|
38
|
+
"""
|
|
39
|
+
Send a request and stream the response events (synchronous).
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
request: AgentRequest object
|
|
43
|
+
|
|
44
|
+
Yields:
|
|
45
|
+
Event objects (Message, Content, AgentResponse, etc.)
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
Exception: If the request fails
|
|
49
|
+
"""
|
|
50
|
+
raise NotImplementedError
|
|
51
|
+
|
|
52
|
+
@abstractmethod
|
|
53
|
+
async def astream(self, request: AgentRequest) -> AsyncIterator[Event]:
|
|
54
|
+
"""
|
|
55
|
+
Send a request and stream the response events (asynchronous).
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
request: AgentRequest object
|
|
59
|
+
|
|
60
|
+
Yields:
|
|
61
|
+
Event objects (Message, Content, AgentResponse, etc.)
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
Exception: If the request fails
|
|
65
|
+
"""
|
|
66
|
+
raise NotImplementedError
|
|
67
|
+
# Make this an async generator for proper type checking
|
|
68
|
+
yield # pylint: disable=unreachable
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# ============================================================================
|
|
72
|
+
# HTTP Implementation
|
|
73
|
+
# ============================================================================
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def parse_sse_line_bytes(line: bytes) -> tuple[Optional[str], Optional[str]]:
|
|
77
|
+
"""
|
|
78
|
+
Parse a single SSE (Server-Sent Events) line from bytes.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
line: SSE line as bytes
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Tuple of (field, value) where field can be 'data', 'event',
|
|
85
|
+
'id', or 'retry'
|
|
86
|
+
"""
|
|
87
|
+
line_str = line.decode("utf-8").strip()
|
|
88
|
+
return parse_sse_line(line_str)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def parse_sse_line(line: str) -> tuple[Optional[str], Optional[str]]:
|
|
92
|
+
"""
|
|
93
|
+
Parse a single SSE (Server-Sent Events) line.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
line: SSE line string (already decoded from bytes)
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Tuple of (field, value) where field can be 'data', 'event',
|
|
100
|
+
'id', or 'retry'
|
|
101
|
+
"""
|
|
102
|
+
line_str = line.strip()
|
|
103
|
+
if line_str.startswith("data: "):
|
|
104
|
+
return "data", line_str[6:]
|
|
105
|
+
elif line_str.startswith("event:"):
|
|
106
|
+
return "event", line_str[7:].strip()
|
|
107
|
+
elif line_str.startswith("id: "):
|
|
108
|
+
return "id", line_str[4:].strip()
|
|
109
|
+
elif line_str.startswith("retry:"):
|
|
110
|
+
return "retry", line_str[7:].strip()
|
|
111
|
+
return None, None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def parse_event_from_json(data: Dict) -> Optional[Event]:
|
|
115
|
+
"""
|
|
116
|
+
Parse an Event object from JSON data according to Agent API Protocol.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
data: Parsed JSON response data
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Event object (Message, Content, or AgentResponse) if valid,
|
|
123
|
+
None otherwise
|
|
124
|
+
"""
|
|
125
|
+
try:
|
|
126
|
+
obj_type = data.get("object")
|
|
127
|
+
|
|
128
|
+
if obj_type == "response":
|
|
129
|
+
return AgentResponse(**data)
|
|
130
|
+
if obj_type == "message":
|
|
131
|
+
return Message(**data)
|
|
132
|
+
if obj_type == "content":
|
|
133
|
+
content_type = data.get("type", "")
|
|
134
|
+
content_class_map = {
|
|
135
|
+
"text": TextContent,
|
|
136
|
+
"image": ImageContent,
|
|
137
|
+
"data": DataContent,
|
|
138
|
+
}
|
|
139
|
+
content_class = content_class_map.get(content_type, Content)
|
|
140
|
+
return content_class(**data)
|
|
141
|
+
# Unknown object type, return as generic event if it has
|
|
142
|
+
# required fields
|
|
143
|
+
if "object" in data:
|
|
144
|
+
return Event(**data)
|
|
145
|
+
return None
|
|
146
|
+
except Exception as e:
|
|
147
|
+
logger.warning(
|
|
148
|
+
"Failed to parse event from JSON: %s, error: %s",
|
|
149
|
+
data,
|
|
150
|
+
e,
|
|
151
|
+
)
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class HTTPAgentAPIClient(AgentAPIClientBase):
|
|
156
|
+
"""
|
|
157
|
+
HTTP/SSE implementation of Agent API Protocol client.
|
|
158
|
+
|
|
159
|
+
This client uses HTTP POST with Server-Sent Events (SSE) for streaming
|
|
160
|
+
responses from Agent API Protocol endpoints.
|
|
161
|
+
|
|
162
|
+
Attributes:
|
|
163
|
+
endpoint: API endpoint URL
|
|
164
|
+
token: Optional authorization token
|
|
165
|
+
timeout: Request timeout in seconds
|
|
166
|
+
headers: Additional custom headers
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
def __init__(
|
|
170
|
+
self,
|
|
171
|
+
endpoint: str,
|
|
172
|
+
token: Optional[str] = None,
|
|
173
|
+
timeout: float = 300.0,
|
|
174
|
+
headers: Optional[Dict[str, str]] = None,
|
|
175
|
+
):
|
|
176
|
+
"""
|
|
177
|
+
Initialize HTTP Agent API client.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
endpoint: API endpoint URL
|
|
181
|
+
(e.g., "https://api.example.com/process")
|
|
182
|
+
token: Optional authorization token (Bearer token)
|
|
183
|
+
timeout: Request timeout in seconds (default: 300)
|
|
184
|
+
headers: Optional additional custom headers
|
|
185
|
+
"""
|
|
186
|
+
self.endpoint = endpoint
|
|
187
|
+
self.token = token
|
|
188
|
+
self.timeout = timeout
|
|
189
|
+
self.headers = headers or {}
|
|
190
|
+
|
|
191
|
+
def _prepare_headers(self) -> Dict[str, str]:
|
|
192
|
+
"""Prepare HTTP headers for the request."""
|
|
193
|
+
headers = {
|
|
194
|
+
"Content-Type": "application/json",
|
|
195
|
+
"Accept": "text/event-stream",
|
|
196
|
+
"Cache-Control": "no-cache",
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
# Add custom headers
|
|
200
|
+
headers.update(self.headers)
|
|
201
|
+
|
|
202
|
+
# Add authorization if token is provided
|
|
203
|
+
if self.token:
|
|
204
|
+
headers["Authorization"] = f"Bearer {self.token}"
|
|
205
|
+
|
|
206
|
+
return headers
|
|
207
|
+
|
|
208
|
+
def stream(self, request: AgentRequest) -> Iterator[Event]:
|
|
209
|
+
"""
|
|
210
|
+
Send a request and stream the response events (synchronous).
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
request: AgentRequest object
|
|
214
|
+
|
|
215
|
+
Yields:
|
|
216
|
+
Event objects (Message, Content, AgentResponse, etc.)
|
|
217
|
+
|
|
218
|
+
Raises:
|
|
219
|
+
requests.exceptions.RequestException: If the HTTP request fails
|
|
220
|
+
"""
|
|
221
|
+
import requests
|
|
222
|
+
|
|
223
|
+
headers = self._prepare_headers()
|
|
224
|
+
payload = request.model_dump(exclude_none=True)
|
|
225
|
+
|
|
226
|
+
try:
|
|
227
|
+
response = requests.post(
|
|
228
|
+
self.endpoint,
|
|
229
|
+
json=payload,
|
|
230
|
+
headers=headers,
|
|
231
|
+
stream=True,
|
|
232
|
+
timeout=self.timeout,
|
|
233
|
+
)
|
|
234
|
+
response.raise_for_status()
|
|
235
|
+
|
|
236
|
+
# Parse SSE stream
|
|
237
|
+
for line in response.iter_lines():
|
|
238
|
+
if not line:
|
|
239
|
+
continue
|
|
240
|
+
field, value = parse_sse_line_bytes(line)
|
|
241
|
+
if field != "data" or not value:
|
|
242
|
+
continue
|
|
243
|
+
try:
|
|
244
|
+
data = json.loads(value)
|
|
245
|
+
event = parse_event_from_json(data)
|
|
246
|
+
if event:
|
|
247
|
+
yield event
|
|
248
|
+
except json.JSONDecodeError:
|
|
249
|
+
logger.debug("Failed to parse JSON: %s", value)
|
|
250
|
+
|
|
251
|
+
except requests.exceptions.RequestException as e:
|
|
252
|
+
logger.error("HTTP request failed: %s", e)
|
|
253
|
+
raise
|
|
254
|
+
|
|
255
|
+
async def astream(self, request: AgentRequest) -> AsyncIterator[Event]:
|
|
256
|
+
"""
|
|
257
|
+
Send a request and stream the response events (asynchronous).
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
request: AgentRequest object
|
|
261
|
+
|
|
262
|
+
Yields:
|
|
263
|
+
Event objects (Message, Content, AgentResponse, etc.)
|
|
264
|
+
|
|
265
|
+
Raises:
|
|
266
|
+
httpx.HTTPError: If the HTTP request fails
|
|
267
|
+
"""
|
|
268
|
+
headers = self._prepare_headers()
|
|
269
|
+
payload = request.model_dump(exclude_none=True)
|
|
270
|
+
|
|
271
|
+
try:
|
|
272
|
+
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
|
273
|
+
async with client.stream(
|
|
274
|
+
"POST",
|
|
275
|
+
self.endpoint,
|
|
276
|
+
json=payload,
|
|
277
|
+
headers=headers,
|
|
278
|
+
) as response:
|
|
279
|
+
# chunks = ""
|
|
280
|
+
|
|
281
|
+
# async for c in response.aiter_bytes():
|
|
282
|
+
# if c:
|
|
283
|
+
# chunks += c.decode("utf-8")
|
|
284
|
+
# print(chunks)
|
|
285
|
+
|
|
286
|
+
response.raise_for_status()
|
|
287
|
+
|
|
288
|
+
# Parse SSE stream
|
|
289
|
+
async for line in response.aiter_lines():
|
|
290
|
+
if not line:
|
|
291
|
+
continue
|
|
292
|
+
field, value = parse_sse_line(line)
|
|
293
|
+
if field != "data" or not value:
|
|
294
|
+
continue
|
|
295
|
+
try:
|
|
296
|
+
data = json.loads(value)
|
|
297
|
+
event = parse_event_from_json(data)
|
|
298
|
+
if event:
|
|
299
|
+
yield event
|
|
300
|
+
except json.JSONDecodeError:
|
|
301
|
+
logger.debug(
|
|
302
|
+
"Failed to parse JSON: %s",
|
|
303
|
+
value,
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
except httpx.HTTPError as e:
|
|
307
|
+
logger.error("HTTP request failed: %s", e)
|
|
308
|
+
raise
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
# ============================================================================
|
|
312
|
+
# Convenience Utilities
|
|
313
|
+
# ============================================================================
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def extract_text_from_event(event: Event) -> Optional[str]:
|
|
317
|
+
"""
|
|
318
|
+
Extract text content from an Event.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
event: Event object
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
Text string if the event contains text content, None otherwise
|
|
325
|
+
"""
|
|
326
|
+
if isinstance(event, TextContent):
|
|
327
|
+
return event.text
|
|
328
|
+
elif isinstance(event, Message):
|
|
329
|
+
# Extract text from completed messages
|
|
330
|
+
if event.status == "completed" and event.content:
|
|
331
|
+
texts = []
|
|
332
|
+
for content_item in event.content:
|
|
333
|
+
if isinstance(content_item, TextContent) and content_item.text:
|
|
334
|
+
texts.append(content_item.text)
|
|
335
|
+
return "".join(texts) if texts else None
|
|
336
|
+
return None
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def create_simple_text_request(
|
|
340
|
+
query: str,
|
|
341
|
+
session_id: Optional[str] = None,
|
|
342
|
+
**kwargs,
|
|
343
|
+
) -> AgentRequest:
|
|
344
|
+
"""
|
|
345
|
+
Create a simple AgentRequest with a text query.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
query: User query text
|
|
349
|
+
session_id: Optional session ID for conversation continuity
|
|
350
|
+
**kwargs: Additional parameters for AgentRequest
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
AgentRequest object
|
|
354
|
+
"""
|
|
355
|
+
message = Message(
|
|
356
|
+
role="user",
|
|
357
|
+
type="message",
|
|
358
|
+
content=[TextContent(type="text", text=query)],
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
request_params = {
|
|
362
|
+
"input": [message],
|
|
363
|
+
"stream": True,
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if session_id:
|
|
367
|
+
request_params["session_id"] = session_id
|
|
368
|
+
|
|
369
|
+
# Merge with any additional parameters
|
|
370
|
+
request_params.update(kwargs)
|
|
371
|
+
|
|
372
|
+
return AgentRequest(**request_params)
|
|
@@ -236,6 +236,7 @@ class Runner:
|
|
|
236
236
|
|
|
237
237
|
query_kwargs = {
|
|
238
238
|
"request": request,
|
|
239
|
+
"response": response,
|
|
239
240
|
}
|
|
240
241
|
|
|
241
242
|
if self.framework_type == "text":
|
|
@@ -272,6 +273,18 @@ class Runner:
|
|
|
272
273
|
kwargs.update(
|
|
273
274
|
{"msgs": await message_to_agno_message(request.input)},
|
|
274
275
|
)
|
|
276
|
+
elif self.framework_type == "ms_agent_framework":
|
|
277
|
+
from ..adapters.ms_agent_framework.stream import (
|
|
278
|
+
adapt_ms_agent_framework_message_stream,
|
|
279
|
+
)
|
|
280
|
+
from ..adapters.ms_agent_framework.message import (
|
|
281
|
+
message_to_ms_agent_framework_message,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
stream_adapter = adapt_ms_agent_framework_message_stream
|
|
285
|
+
kwargs.update(
|
|
286
|
+
{"msgs": message_to_ms_agent_framework_message(request.input)},
|
|
287
|
+
)
|
|
275
288
|
# TODO: support other frameworks
|
|
276
289
|
else:
|
|
277
290
|
|
|
@@ -622,15 +622,17 @@ class Message(Event):
|
|
|
622
622
|
|
|
623
623
|
# new content
|
|
624
624
|
if new_content.index is None:
|
|
625
|
+
content_index = len(self.content)
|
|
625
626
|
copy = deepcopy(new_content)
|
|
626
627
|
copy.delta = None
|
|
627
|
-
copy.index =
|
|
628
|
-
copy.msg_id =
|
|
628
|
+
copy.index = content_index
|
|
629
|
+
copy.msg_id = self.id
|
|
629
630
|
self.content.append(copy)
|
|
630
631
|
|
|
631
|
-
new_content.index =
|
|
632
|
+
new_content.index = content_index
|
|
632
633
|
new_content.msg_id = self.id
|
|
633
634
|
new_content.in_progress()
|
|
635
|
+
|
|
634
636
|
return new_content
|
|
635
637
|
|
|
636
638
|
# delta content
|
|
@@ -869,6 +871,10 @@ class BaseResponse(Event):
|
|
|
869
871
|
self.output = self.output or []
|
|
870
872
|
self.output.append(message)
|
|
871
873
|
|
|
874
|
+
def completed(self) -> Self:
|
|
875
|
+
self.completed_at = int(datetime.now().timestamp())
|
|
876
|
+
return super().completed()
|
|
877
|
+
|
|
872
878
|
|
|
873
879
|
class AgentResponse(BaseResponse):
|
|
874
880
|
"""agent response"""
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
from typing import TYPE_CHECKING
|
|
3
3
|
from ....common.utils.lazy_loader import install_lazy_loader
|
|
4
|
+
from ....common.utils.deprecation import deprecated_module
|
|
5
|
+
|
|
6
|
+
deprecated_module(
|
|
7
|
+
module_name=__name__,
|
|
8
|
+
removed_in="v1.1",
|
|
9
|
+
alternative="agentscope.session",
|
|
10
|
+
)
|
|
4
11
|
|
|
5
12
|
if TYPE_CHECKING:
|
|
6
13
|
from .state_service import StateService, InMemoryStateService
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
from typing import TYPE_CHECKING
|
|
3
3
|
from ....common.utils.lazy_loader import install_lazy_loader
|
|
4
|
+
from ....common.utils.deprecation import deprecated_module
|
|
5
|
+
|
|
6
|
+
deprecated_module(
|
|
7
|
+
module_name=__name__,
|
|
8
|
+
removed_in="v1.1",
|
|
9
|
+
alternative="agentscope.memory",
|
|
10
|
+
)
|
|
4
11
|
|
|
5
12
|
if TYPE_CHECKING:
|
|
6
13
|
from .memory_service import MemoryService, InMemoryMemoryService
|