agentscope-runtime 1.0.1__py3-none-any.whl → 1.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentscope_runtime/adapters/agentscope/message.py +32 -7
- agentscope_runtime/adapters/agentscope/stream.py +121 -91
- agentscope_runtime/adapters/agno/__init__.py +0 -0
- agentscope_runtime/adapters/agno/message.py +30 -0
- agentscope_runtime/adapters/agno/stream.py +122 -0
- agentscope_runtime/adapters/langgraph/__init__.py +12 -0
- agentscope_runtime/adapters/langgraph/message.py +257 -0
- agentscope_runtime/adapters/langgraph/stream.py +205 -0
- agentscope_runtime/cli/__init__.py +7 -0
- agentscope_runtime/cli/cli.py +63 -0
- agentscope_runtime/cli/commands/__init__.py +2 -0
- agentscope_runtime/cli/commands/chat.py +815 -0
- agentscope_runtime/cli/commands/deploy.py +1074 -0
- agentscope_runtime/cli/commands/invoke.py +58 -0
- agentscope_runtime/cli/commands/list_cmd.py +103 -0
- agentscope_runtime/cli/commands/run.py +176 -0
- agentscope_runtime/cli/commands/sandbox.py +128 -0
- agentscope_runtime/cli/commands/status.py +60 -0
- agentscope_runtime/cli/commands/stop.py +185 -0
- agentscope_runtime/cli/commands/web.py +166 -0
- agentscope_runtime/cli/loaders/__init__.py +6 -0
- agentscope_runtime/cli/loaders/agent_loader.py +295 -0
- agentscope_runtime/cli/state/__init__.py +10 -0
- agentscope_runtime/cli/utils/__init__.py +18 -0
- agentscope_runtime/cli/utils/console.py +378 -0
- agentscope_runtime/cli/utils/validators.py +118 -0
- agentscope_runtime/common/collections/redis_mapping.py +4 -1
- agentscope_runtime/engine/app/agent_app.py +55 -9
- agentscope_runtime/engine/deployers/__init__.py +1 -0
- agentscope_runtime/engine/deployers/adapter/a2a/__init__.py +56 -1
- agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +449 -41
- agentscope_runtime/engine/deployers/adapter/a2a/a2a_registry.py +273 -0
- agentscope_runtime/engine/deployers/adapter/a2a/nacos_a2a_registry.py +640 -0
- agentscope_runtime/engine/deployers/agentrun_deployer.py +152 -22
- agentscope_runtime/engine/deployers/base.py +27 -2
- agentscope_runtime/engine/deployers/kubernetes_deployer.py +161 -31
- agentscope_runtime/engine/deployers/local_deployer.py +188 -25
- agentscope_runtime/engine/deployers/modelstudio_deployer.py +109 -18
- agentscope_runtime/engine/deployers/state/__init__.py +9 -0
- agentscope_runtime/engine/deployers/state/manager.py +388 -0
- agentscope_runtime/engine/deployers/state/schema.py +96 -0
- agentscope_runtime/engine/deployers/utils/build_cache.py +736 -0
- agentscope_runtime/engine/deployers/utils/detached_app.py +105 -30
- agentscope_runtime/engine/deployers/utils/docker_image_utils/docker_image_builder.py +31 -10
- agentscope_runtime/engine/deployers/utils/docker_image_utils/dockerfile_generator.py +23 -10
- agentscope_runtime/engine/deployers/utils/docker_image_utils/image_factory.py +35 -2
- agentscope_runtime/engine/deployers/utils/k8s_utils.py +241 -0
- agentscope_runtime/engine/deployers/utils/net_utils.py +65 -0
- agentscope_runtime/engine/deployers/utils/package.py +56 -6
- agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +16 -2
- agentscope_runtime/engine/deployers/utils/service_utils/process_manager.py +155 -5
- agentscope_runtime/engine/deployers/utils/wheel_packager.py +107 -123
- agentscope_runtime/engine/runner.py +30 -9
- agentscope_runtime/engine/schemas/exception.py +604 -0
- agentscope_runtime/engine/services/agent_state/redis_state_service.py +61 -8
- agentscope_runtime/engine/services/agent_state/state_service_factory.py +2 -5
- agentscope_runtime/engine/services/memory/redis_memory_service.py +129 -25
- agentscope_runtime/engine/services/session_history/redis_session_history_service.py +160 -34
- agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +113 -39
- agentscope_runtime/sandbox/box/shared/routers/mcp_utils.py +20 -4
- agentscope_runtime/sandbox/build.py +50 -57
- agentscope_runtime/sandbox/utils.py +2 -0
- agentscope_runtime/version.py +1 -1
- {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.3.dist-info}/METADATA +31 -8
- {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.3.dist-info}/RECORD +69 -36
- {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.3.dist-info}/entry_points.txt +1 -0
- {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.3.dist-info}/WHEEL +0 -0
- {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.3.dist-info}/licenses/LICENSE +0 -0
- {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Console output utilities for CLI using Rich library."""
|
|
3
|
+
|
|
4
|
+
import io
|
|
5
|
+
import json
|
|
6
|
+
from typing import Optional, Any
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
from rich.json import JSON
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Centralized style configuration
|
|
15
|
+
CONSOLE_CONFIG = {
|
|
16
|
+
"success": {
|
|
17
|
+
"emoji": "✓",
|
|
18
|
+
"style": "bold green",
|
|
19
|
+
},
|
|
20
|
+
"error": {
|
|
21
|
+
"emoji": "✗",
|
|
22
|
+
"style": "bold red",
|
|
23
|
+
},
|
|
24
|
+
"warning": {
|
|
25
|
+
"emoji": "⚠",
|
|
26
|
+
"style": "bold yellow",
|
|
27
|
+
},
|
|
28
|
+
"info": {
|
|
29
|
+
"emoji": "ℹ",
|
|
30
|
+
"style": "bold blue",
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Module-level console instances (lazy initialization)
|
|
35
|
+
_console = None
|
|
36
|
+
_err_console = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _get_console() -> Console:
|
|
40
|
+
"""Get or create the shared console instance for stdout."""
|
|
41
|
+
global _console
|
|
42
|
+
if _console is None:
|
|
43
|
+
_console = Console()
|
|
44
|
+
return _console
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _get_err_console() -> Console:
|
|
48
|
+
"""Get or create the shared console instance for stderr."""
|
|
49
|
+
global _err_console
|
|
50
|
+
if _err_console is None:
|
|
51
|
+
_err_console = Console(stderr=True)
|
|
52
|
+
return _err_console
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _process_kwargs(kwargs: dict) -> tuple[dict, str]:
|
|
56
|
+
"""
|
|
57
|
+
Process kwargs to handle Click-specific parameters.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Tuple of (rich_kwargs, end_char)
|
|
61
|
+
"""
|
|
62
|
+
# Handle Click's nl parameter
|
|
63
|
+
nl = kwargs.pop("nl", True)
|
|
64
|
+
end = "\n" if nl else ""
|
|
65
|
+
|
|
66
|
+
# Handle Click's err parameter (should be handled by console selection)
|
|
67
|
+
kwargs.pop("err", None)
|
|
68
|
+
|
|
69
|
+
# Click's color parameters - ignore, use theme instead
|
|
70
|
+
kwargs.pop("fg", None)
|
|
71
|
+
kwargs.pop("bg", None)
|
|
72
|
+
kwargs.pop("bold", None)
|
|
73
|
+
kwargs.pop("dim", None)
|
|
74
|
+
|
|
75
|
+
return kwargs, end
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def echo_success(message: str, **kwargs) -> None:
|
|
79
|
+
"""
|
|
80
|
+
Print success message in green with checkmark.
|
|
81
|
+
|
|
82
|
+
Uses Rich library for enhanced terminal output with automatic
|
|
83
|
+
cross-platform support (including Windows).
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
message: Message to display
|
|
87
|
+
**kwargs: Additional Rich Console.print() parameters
|
|
88
|
+
(e.g., highlight=False, markup=False)
|
|
89
|
+
|
|
90
|
+
Note:
|
|
91
|
+
Legacy Click kwargs (nl, err, fg, bold) are supported for
|
|
92
|
+
backward compatibility but are deprecated.
|
|
93
|
+
|
|
94
|
+
Example:
|
|
95
|
+
>>> echo_success("Deployment completed successfully")
|
|
96
|
+
✓ Deployment completed successfully
|
|
97
|
+
"""
|
|
98
|
+
console = _get_console()
|
|
99
|
+
config = CONSOLE_CONFIG["success"]
|
|
100
|
+
kwargs, end = _process_kwargs(kwargs)
|
|
101
|
+
|
|
102
|
+
formatted_message = f"{config['emoji']} {message}"
|
|
103
|
+
console.print(formatted_message, style=config["style"], end=end, **kwargs)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def echo_error(message: str, **kwargs) -> None:
|
|
107
|
+
"""
|
|
108
|
+
Print error message in red with X mark to stderr.
|
|
109
|
+
|
|
110
|
+
Uses Rich library for enhanced terminal output with automatic
|
|
111
|
+
cross-platform support (including Windows).
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
message: Message to display
|
|
115
|
+
**kwargs: Additional Rich Console.print() parameters
|
|
116
|
+
|
|
117
|
+
Note:
|
|
118
|
+
Legacy Click kwargs (nl, err, fg, bold) are supported for
|
|
119
|
+
backward compatibility but are deprecated.
|
|
120
|
+
|
|
121
|
+
Example:
|
|
122
|
+
>>> echo_error("Deployment failed")
|
|
123
|
+
✗ Deployment failed
|
|
124
|
+
"""
|
|
125
|
+
console = _get_err_console()
|
|
126
|
+
config = CONSOLE_CONFIG["error"]
|
|
127
|
+
kwargs, end = _process_kwargs(kwargs)
|
|
128
|
+
|
|
129
|
+
formatted_message = f"{config['emoji']} {message}"
|
|
130
|
+
console.print(formatted_message, style=config["style"], end=end, **kwargs)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def echo_warning(message: str, **kwargs) -> None:
|
|
134
|
+
"""
|
|
135
|
+
Print warning message in yellow with warning symbol.
|
|
136
|
+
|
|
137
|
+
Uses Rich library for enhanced terminal output with automatic
|
|
138
|
+
cross-platform support (including Windows).
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
message: Message to display
|
|
142
|
+
**kwargs: Additional Rich Console.print() parameters
|
|
143
|
+
|
|
144
|
+
Note:
|
|
145
|
+
Legacy Click kwargs (nl, err, fg, bold) are supported for
|
|
146
|
+
backward compatibility but are deprecated.
|
|
147
|
+
|
|
148
|
+
Example:
|
|
149
|
+
>>> echo_warning("Configuration file not found, using defaults")
|
|
150
|
+
⚠ Configuration file not found, using defaults
|
|
151
|
+
"""
|
|
152
|
+
console = _get_console()
|
|
153
|
+
config = CONSOLE_CONFIG["warning"]
|
|
154
|
+
kwargs, end = _process_kwargs(kwargs)
|
|
155
|
+
|
|
156
|
+
formatted_message = f"{config['emoji']} {message}"
|
|
157
|
+
console.print(formatted_message, style=config["style"], end=end, **kwargs)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def echo_info(message: str, **kwargs) -> None:
|
|
161
|
+
"""
|
|
162
|
+
Print info message in blue with info symbol.
|
|
163
|
+
|
|
164
|
+
Uses Rich library for enhanced terminal output with automatic
|
|
165
|
+
cross-platform support (including Windows).
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
message: Message to display
|
|
169
|
+
**kwargs: Additional Rich Console.print() parameters
|
|
170
|
+
|
|
171
|
+
Note:
|
|
172
|
+
Legacy Click kwargs (nl, err, fg, bold) are supported for
|
|
173
|
+
backward compatibility but are deprecated.
|
|
174
|
+
|
|
175
|
+
Example:
|
|
176
|
+
>>> echo_info("Loading configuration...")
|
|
177
|
+
ℹ Loading configuration...
|
|
178
|
+
"""
|
|
179
|
+
console = _get_console()
|
|
180
|
+
config = CONSOLE_CONFIG["info"]
|
|
181
|
+
kwargs, end = _process_kwargs(kwargs)
|
|
182
|
+
|
|
183
|
+
formatted_message = f"{config['emoji']} {message}"
|
|
184
|
+
console.print(formatted_message, style=config["style"], end=end, **kwargs)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def echo_header(message: str, **kwargs) -> None:
|
|
188
|
+
"""
|
|
189
|
+
Print header message in bold.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
message: Message to display
|
|
193
|
+
**kwargs: Additional Rich Console.print() parameters
|
|
194
|
+
|
|
195
|
+
Example:
|
|
196
|
+
>>> echo_header("Deployment Summary")
|
|
197
|
+
Deployment Summary
|
|
198
|
+
"""
|
|
199
|
+
console = _get_console()
|
|
200
|
+
kwargs, end = _process_kwargs(kwargs)
|
|
201
|
+
console.print(message, style="bold", end=end, **kwargs)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def echo_dim(message: str, **kwargs) -> None:
|
|
205
|
+
"""
|
|
206
|
+
Print dimmed message.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
message: Message to display
|
|
210
|
+
**kwargs: Additional Rich Console.print() parameters
|
|
211
|
+
|
|
212
|
+
Example:
|
|
213
|
+
>>> echo_dim("Additional details...")
|
|
214
|
+
Additional details...
|
|
215
|
+
"""
|
|
216
|
+
console = _get_console()
|
|
217
|
+
kwargs, end = _process_kwargs(kwargs)
|
|
218
|
+
console.print(message, style="dim", end=end, **kwargs)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def format_table(
|
|
222
|
+
headers: list[str],
|
|
223
|
+
rows: list[list[Any]],
|
|
224
|
+
max_width: Optional[int] = None,
|
|
225
|
+
) -> str:
|
|
226
|
+
"""
|
|
227
|
+
Format data as a Rich Table (returns rendered string).
|
|
228
|
+
|
|
229
|
+
Uses Rich library for enhanced table rendering with better alignment
|
|
230
|
+
and visual appeal compared to ASCII tables.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
headers: List of column headers
|
|
234
|
+
rows: List of rows, where each row is a list of cell values
|
|
235
|
+
max_width: Optional maximum width for columns
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
Rendered table as a string for backward compatibility
|
|
239
|
+
|
|
240
|
+
Example:
|
|
241
|
+
>>> headers = ["ID", "Status"]
|
|
242
|
+
>>> rows = [["deploy_1", "running"], ["deploy_2", "stopped"]]
|
|
243
|
+
>>> print(format_table(headers, rows))
|
|
244
|
+
"""
|
|
245
|
+
if not rows:
|
|
246
|
+
return "No data to display."
|
|
247
|
+
|
|
248
|
+
# Create Rich table
|
|
249
|
+
table = Table(show_header=True, header_style="bold cyan")
|
|
250
|
+
|
|
251
|
+
# Add columns
|
|
252
|
+
for header in headers:
|
|
253
|
+
table.add_column(header, max_width=max_width)
|
|
254
|
+
|
|
255
|
+
# Add rows
|
|
256
|
+
for row in rows:
|
|
257
|
+
table.add_row(*[str(cell) for cell in row])
|
|
258
|
+
|
|
259
|
+
# Render table to string for backward compatibility
|
|
260
|
+
string_io = io.StringIO()
|
|
261
|
+
temp_console = Console(file=string_io, force_terminal=True)
|
|
262
|
+
temp_console.print(table)
|
|
263
|
+
return string_io.getvalue()
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def format_json(data: Any, indent: int = 2) -> str:
|
|
267
|
+
"""
|
|
268
|
+
Format data as JSON with syntax highlighting.
|
|
269
|
+
|
|
270
|
+
Uses Rich library for syntax-highlighted JSON output.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
data: Data to format as JSON
|
|
274
|
+
indent: Indentation level (default: 2)
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
Rendered JSON as a string with syntax highlighting
|
|
278
|
+
|
|
279
|
+
Example:
|
|
280
|
+
>>> data = {"key": "value", "number": 123}
|
|
281
|
+
>>> print(format_json(data))
|
|
282
|
+
"""
|
|
283
|
+
# Rich's JSON requires a string, not an object
|
|
284
|
+
json_string = json.dumps(data, indent=indent, default=str)
|
|
285
|
+
|
|
286
|
+
# Render to string for backward compatibility
|
|
287
|
+
string_io = io.StringIO()
|
|
288
|
+
temp_console = Console(file=string_io, force_terminal=True)
|
|
289
|
+
temp_console.print(JSON(json_string, indent=indent))
|
|
290
|
+
return string_io.getvalue()
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def format_deployment_info(deployment: dict) -> str:
|
|
294
|
+
"""
|
|
295
|
+
Format deployment information using Rich Table.
|
|
296
|
+
|
|
297
|
+
Uses Rich library for structured key-value display.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
deployment: Dictionary containing deployment information
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
Rendered deployment info as a string
|
|
304
|
+
|
|
305
|
+
Example:
|
|
306
|
+
>>> deployment = {
|
|
307
|
+
... "id": "local_123",
|
|
308
|
+
... "platform": "local",
|
|
309
|
+
... "status": "running",
|
|
310
|
+
... "url": "http://localhost:8000",
|
|
311
|
+
... "created_at": "2025-01-01 12:00:00",
|
|
312
|
+
... "agent_source": "agent.py"
|
|
313
|
+
... }
|
|
314
|
+
>>> print(format_deployment_info(deployment))
|
|
315
|
+
"""
|
|
316
|
+
# Create table with no header for key-value pairs
|
|
317
|
+
table = Table(show_header=False, box=None, padding=(0, 2))
|
|
318
|
+
table.add_column("Key", style="bold cyan")
|
|
319
|
+
table.add_column("Value", style="white")
|
|
320
|
+
|
|
321
|
+
# Add deployment information
|
|
322
|
+
table.add_row("Deployment ID", deployment["id"])
|
|
323
|
+
table.add_row("Platform", deployment["platform"])
|
|
324
|
+
table.add_row("Status", deployment["status"])
|
|
325
|
+
table.add_row("URL", deployment["url"])
|
|
326
|
+
table.add_row("Created", deployment["created_at"])
|
|
327
|
+
table.add_row("Agent Source", deployment["agent_source"])
|
|
328
|
+
|
|
329
|
+
# Add token if present
|
|
330
|
+
if deployment.get("token"):
|
|
331
|
+
token = deployment["token"]
|
|
332
|
+
display_token = f"{token[:20]}..." if len(token) > 20 else token
|
|
333
|
+
table.add_row("Token", display_token)
|
|
334
|
+
|
|
335
|
+
# Render to string for backward compatibility
|
|
336
|
+
string_io = io.StringIO()
|
|
337
|
+
temp_console = Console(file=string_io, force_terminal=True)
|
|
338
|
+
temp_console.print(table)
|
|
339
|
+
return string_io.getvalue()
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def confirm(message: str, default: bool = False) -> bool:
|
|
343
|
+
"""
|
|
344
|
+
Prompt user for confirmation.
|
|
345
|
+
|
|
346
|
+
Uses Click's confirm function (unchanged from original implementation).
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
message: Confirmation message
|
|
350
|
+
default: Default value if user just presses Enter
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
True if user confirms, False otherwise
|
|
354
|
+
|
|
355
|
+
Example:
|
|
356
|
+
>>> if confirm("Delete deployment?"):
|
|
357
|
+
... # proceed with deletion
|
|
358
|
+
"""
|
|
359
|
+
return click.confirm(message, default=default)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def prompt(message: str, default: Optional[str] = None) -> str:
|
|
363
|
+
"""
|
|
364
|
+
Prompt user for input.
|
|
365
|
+
|
|
366
|
+
Uses Click's prompt function (unchanged from original implementation).
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
message: Prompt message
|
|
370
|
+
default: Default value if user just presses Enter
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
User input as string
|
|
374
|
+
|
|
375
|
+
Example:
|
|
376
|
+
>>> name = prompt("Enter deployment name")
|
|
377
|
+
"""
|
|
378
|
+
return click.prompt(message, default=default)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Input validation utilities for CLI."""
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from agentscope_runtime.engine.deployers.state import DeploymentStateManager
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ValidationError(Exception):
|
|
10
|
+
"""Raised when validation fails."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def validate_agent_source(source: str) -> tuple[str, str]:
|
|
14
|
+
"""
|
|
15
|
+
Validate and determine the type of agent source.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
source: Path to file/directory or deployment ID
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Tuple of (source_type, normalized_path/id)
|
|
22
|
+
source_type can be: 'file', 'directory', 'deployment_id'
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
ValidationError: If source is invalid
|
|
26
|
+
"""
|
|
27
|
+
if not source:
|
|
28
|
+
raise ValidationError("Agent source cannot be empty")
|
|
29
|
+
|
|
30
|
+
# Check if it's a file
|
|
31
|
+
if os.path.isfile(source):
|
|
32
|
+
if not source.endswith(".py"):
|
|
33
|
+
raise ValidationError(
|
|
34
|
+
f"Agent source file must be a Python file: {source}",
|
|
35
|
+
)
|
|
36
|
+
return ("file", os.path.abspath(source))
|
|
37
|
+
|
|
38
|
+
# Check if it's a directory
|
|
39
|
+
if os.path.isdir(source):
|
|
40
|
+
return ("directory", os.path.abspath(source))
|
|
41
|
+
|
|
42
|
+
# Check if it's a deployment ID by querying state manager
|
|
43
|
+
# This ensures we only accept deployment IDs that actually exist
|
|
44
|
+
# Support both formats: platform_timestamp_id (with underscore) and UUID
|
|
45
|
+
# format
|
|
46
|
+
state_manager = DeploymentStateManager()
|
|
47
|
+
if state_manager.exists(source):
|
|
48
|
+
return ("deployment_id", source)
|
|
49
|
+
|
|
50
|
+
raise ValidationError(
|
|
51
|
+
f"Invalid agent source: {source}\n"
|
|
52
|
+
"Must be a Python file (.py), directory, or deployment ID",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def validate_port(port: int) -> int:
|
|
57
|
+
"""Validate port number."""
|
|
58
|
+
if not isinstance(port, int):
|
|
59
|
+
try:
|
|
60
|
+
port = int(port)
|
|
61
|
+
except (ValueError, TypeError) as e:
|
|
62
|
+
raise ValidationError(f"Port must be an integer: {port}") from e
|
|
63
|
+
|
|
64
|
+
if port < 1 or port > 65535:
|
|
65
|
+
raise ValidationError(f"Port must be between 1 and 65535: {port}")
|
|
66
|
+
|
|
67
|
+
return port
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def validate_platform(platform: str, supported_platforms: list[str]) -> str:
|
|
71
|
+
"""Validate platform name."""
|
|
72
|
+
if platform not in supported_platforms:
|
|
73
|
+
raise ValidationError(
|
|
74
|
+
f"Unsupported platform: {platform}\n"
|
|
75
|
+
f"Supported platforms: {', '.join(supported_platforms)}",
|
|
76
|
+
)
|
|
77
|
+
return platform
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def validate_file_exists(file_path: str) -> str:
|
|
81
|
+
"""Validate that file exists."""
|
|
82
|
+
if not os.path.isfile(file_path):
|
|
83
|
+
raise ValidationError(f"File not found: {file_path}")
|
|
84
|
+
return os.path.abspath(file_path)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def validate_directory_exists(dir_path: str) -> str:
|
|
88
|
+
"""Validate that directory exists."""
|
|
89
|
+
if not os.path.isdir(dir_path):
|
|
90
|
+
raise ValidationError(f"Directory not found: {dir_path}")
|
|
91
|
+
return os.path.abspath(dir_path)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def validate_url(url: str) -> str:
|
|
95
|
+
"""Basic URL validation."""
|
|
96
|
+
if not url:
|
|
97
|
+
raise ValidationError("URL cannot be empty")
|
|
98
|
+
|
|
99
|
+
if not (url.startswith("http://") or url.startswith("https://")):
|
|
100
|
+
raise ValidationError(
|
|
101
|
+
f"URL must start with http:// or https://: {url}",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return url
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def validate_deployment_id(deploy_id: str) -> str:
|
|
108
|
+
"""Validate deployment ID format."""
|
|
109
|
+
if not deploy_id:
|
|
110
|
+
raise ValidationError("Deployment ID cannot be empty")
|
|
111
|
+
|
|
112
|
+
if "_" not in deploy_id:
|
|
113
|
+
raise ValidationError(
|
|
114
|
+
f"Invalid deployment ID format: {deploy_id}\n"
|
|
115
|
+
"Expected format: platform_timestamp_id",
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
return deploy_id
|
|
@@ -38,7 +38,10 @@ class RedisMapping(Mapping):
|
|
|
38
38
|
match=search_pattern,
|
|
39
39
|
)
|
|
40
40
|
for key in keys:
|
|
41
|
-
|
|
41
|
+
if isinstance(key, bytes):
|
|
42
|
+
decoded_key = key.decode("utf-8")
|
|
43
|
+
else:
|
|
44
|
+
decoded_key = str(key)
|
|
42
45
|
yield self._strip_prefix(decoded_key)
|
|
43
46
|
|
|
44
47
|
if cursor == 0:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
import logging
|
|
3
|
+
import os
|
|
3
4
|
import types
|
|
4
5
|
import platform
|
|
5
6
|
import subprocess
|
|
@@ -11,7 +12,11 @@ from pydantic import BaseModel
|
|
|
11
12
|
|
|
12
13
|
from .base_app import BaseApp
|
|
13
14
|
from ..deployers import DeployManager
|
|
14
|
-
from ..deployers.adapter.a2a import
|
|
15
|
+
from ..deployers.adapter.a2a import (
|
|
16
|
+
A2AFastAPIDefaultAdapter,
|
|
17
|
+
AgentCardWithRuntimeConfig,
|
|
18
|
+
extract_a2a_config,
|
|
19
|
+
)
|
|
15
20
|
from ..deployers.adapter.responses.response_api_protocol_adapter import (
|
|
16
21
|
ResponseAPIDefaultAdapter,
|
|
17
22
|
)
|
|
@@ -22,6 +27,8 @@ from ..schemas.agent_schemas import AgentRequest
|
|
|
22
27
|
from ...version import __version__
|
|
23
28
|
|
|
24
29
|
logger = logging.getLogger(__name__)
|
|
30
|
+
HOST = os.getenv("HOST", "0.0.0.0")
|
|
31
|
+
PORT = int(os.getenv("PORT", "8080"))
|
|
25
32
|
|
|
26
33
|
|
|
27
34
|
class AgentApp(BaseApp):
|
|
@@ -44,14 +51,49 @@ class AgentApp(BaseApp):
|
|
|
44
51
|
backend_url: Optional[str] = None,
|
|
45
52
|
runner: Optional[Runner] = None,
|
|
46
53
|
enable_embedded_worker: bool = False,
|
|
54
|
+
a2a_config: Optional["AgentCardWithRuntimeConfig"] = None,
|
|
47
55
|
**kwargs,
|
|
48
56
|
):
|
|
49
57
|
"""
|
|
50
58
|
Initialize the AgentApp.
|
|
51
59
|
|
|
52
60
|
Args:
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
app_name: Name of the agent application
|
|
62
|
+
app_description: Description of the agent application
|
|
63
|
+
endpoint_path: API endpoint path for processing requests
|
|
64
|
+
response_type: Type of response (default: "sse")
|
|
65
|
+
stream: Whether to enable streaming responses
|
|
66
|
+
request_model: Request model class
|
|
67
|
+
before_start: Callback function to execute before starting
|
|
68
|
+
after_finish: Callback function to execute after finishing
|
|
69
|
+
broker_url: URL for message broker
|
|
70
|
+
backend_url: URL for backend service
|
|
71
|
+
runner: Optional runner instance
|
|
72
|
+
enable_embedded_worker: Whether to enable embedded worker
|
|
73
|
+
a2a_config: Optional A2A runtime configuration.
|
|
74
|
+
Must be an ``AgentCardWithRuntimeConfig`` instance, which
|
|
75
|
+
contains ``agent_card`` (AgentCard object or dict) and runtime
|
|
76
|
+
settings (host, port, registry, task_timeout, etc.).
|
|
77
|
+
Example:
|
|
78
|
+
from a2a.types import AgentCard, AgentCapabilities
|
|
79
|
+
from agentscope_runtime.engine.deployers.adapter.a2a import ( # noqa: E501
|
|
80
|
+
AgentCardWithRuntimeConfig,
|
|
81
|
+
)
|
|
82
|
+
config = AgentCardWithRuntimeConfig(
|
|
83
|
+
agent_card={
|
|
84
|
+
"name": "MyAgent",
|
|
85
|
+
"version": "1.0.0",
|
|
86
|
+
"description": "My agent",
|
|
87
|
+
"url": "http://localhost:8080",
|
|
88
|
+
"capabilities": AgentCapabilities(),
|
|
89
|
+
"default_input_modes": ["text"],
|
|
90
|
+
"default_output_modes": ["text"],
|
|
91
|
+
"skills": [],
|
|
92
|
+
},
|
|
93
|
+
registry=[nacos_registry],
|
|
94
|
+
task_timeout=120,
|
|
95
|
+
)
|
|
96
|
+
**kwargs: Additional keyword arguments passed to FastAPI app
|
|
55
97
|
"""
|
|
56
98
|
|
|
57
99
|
self.endpoint_path = endpoint_path
|
|
@@ -73,9 +115,13 @@ class AgentApp(BaseApp):
|
|
|
73
115
|
self._shutdown_handler: Optional[Callable] = None
|
|
74
116
|
self._framework_type: Optional[str] = None
|
|
75
117
|
|
|
118
|
+
# Prepare A2A protocol adapter configuration
|
|
119
|
+
a2a_config = extract_a2a_config(a2a_config=a2a_config)
|
|
120
|
+
|
|
76
121
|
a2a_protocol = A2AFastAPIDefaultAdapter(
|
|
77
122
|
agent_name=app_name,
|
|
78
123
|
agent_description=app_description,
|
|
124
|
+
a2a_config=a2a_config,
|
|
79
125
|
)
|
|
80
126
|
|
|
81
127
|
response_protocol = ResponseAPIDefaultAdapter()
|
|
@@ -93,8 +139,8 @@ class AgentApp(BaseApp):
|
|
|
93
139
|
backend_url=backend_url,
|
|
94
140
|
)
|
|
95
141
|
|
|
96
|
-
# Store custom endpoints
|
|
97
|
-
#
|
|
142
|
+
# Store custom endpoints for deployment
|
|
143
|
+
# FastAPIAppFactory will handle adding them to FastAPI
|
|
98
144
|
|
|
99
145
|
def init(self, func):
|
|
100
146
|
"""Register init hook (support async and sync functions)."""
|
|
@@ -150,8 +196,8 @@ class AgentApp(BaseApp):
|
|
|
150
196
|
|
|
151
197
|
def run(
|
|
152
198
|
self,
|
|
153
|
-
host=
|
|
154
|
-
port=
|
|
199
|
+
host=HOST,
|
|
200
|
+
port=PORT,
|
|
155
201
|
web_ui=False,
|
|
156
202
|
**kwargs,
|
|
157
203
|
):
|
|
@@ -171,7 +217,7 @@ class AgentApp(BaseApp):
|
|
|
171
217
|
|
|
172
218
|
Args:
|
|
173
219
|
host (str): Host address to bind to. Default "0.0.0.0".
|
|
174
|
-
port (int): Port number to serve the application on. Default
|
|
220
|
+
port (int): Port number to serve the application on. Default 8080.
|
|
175
221
|
web_ui (bool): If True, launches the Agentscope Web UI in a
|
|
176
222
|
separate process, pointing it to the API endpoint. This
|
|
177
223
|
allows interactive use via browser. Default False.
|
|
@@ -180,7 +226,7 @@ class AgentApp(BaseApp):
|
|
|
180
226
|
|
|
181
227
|
Example:
|
|
182
228
|
>>> app = AgentApp(app_name="MyAgent")
|
|
183
|
-
>>> app.
|
|
229
|
+
>>> app.chat(host="127.0.0.1", port=8080, web_ui=True)
|
|
184
230
|
"""
|
|
185
231
|
# Build runner
|
|
186
232
|
self._build_runner()
|