agentkit-sdk-python 0.6.4__py3-none-any.whl → 0.6.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.
- agentkit/toolkit/cli/cli_add.py +604 -6
- agentkit/toolkit/cli/cli_invoke.py +230 -6
- agentkit/toolkit/cli/sandbox/model_config.py +22 -21
- agentkit/version.py +1 -1
- {agentkit_sdk_python-0.6.4.dist-info → agentkit_sdk_python-0.6.5.dist-info}/METADATA +1 -1
- {agentkit_sdk_python-0.6.4.dist-info → agentkit_sdk_python-0.6.5.dist-info}/RECORD +10 -10
- {agentkit_sdk_python-0.6.4.dist-info → agentkit_sdk_python-0.6.5.dist-info}/WHEEL +0 -0
- {agentkit_sdk_python-0.6.4.dist-info → agentkit_sdk_python-0.6.5.dist-info}/entry_points.txt +0 -0
- {agentkit_sdk_python-0.6.4.dist-info → agentkit_sdk_python-0.6.5.dist-info}/licenses/LICENSE +0 -0
- {agentkit_sdk_python-0.6.4.dist-info → agentkit_sdk_python-0.6.5.dist-info}/top_level.txt +0 -0
agentkit/toolkit/cli/cli_add.py
CHANGED
|
@@ -38,9 +38,11 @@ into the existing file, so configuration can be built up incrementally.
|
|
|
38
38
|
"""
|
|
39
39
|
|
|
40
40
|
import json
|
|
41
|
+
import os
|
|
41
42
|
import re
|
|
42
43
|
from pathlib import Path
|
|
43
|
-
from typing import Optional
|
|
44
|
+
from typing import Any, Optional
|
|
45
|
+
from urllib.parse import parse_qs, urlparse
|
|
44
46
|
|
|
45
47
|
import typer
|
|
46
48
|
from rich.console import Console
|
|
@@ -62,6 +64,25 @@ _SHORT_TERM_MEMORY_TYPES = ("local", "sqlite", "mysql", "postgresql")
|
|
|
62
64
|
|
|
63
65
|
# Harness name charset (matches `init`'s directory-name rule).
|
|
64
66
|
_NAME_RE = re.compile(r"^[a-zA-Z0-9_-]+$")
|
|
67
|
+
_REGISTRY_QUERY_KEYS = {
|
|
68
|
+
"space_id",
|
|
69
|
+
"space_name",
|
|
70
|
+
"top_k",
|
|
71
|
+
"endpoint",
|
|
72
|
+
"region",
|
|
73
|
+
}
|
|
74
|
+
_REGISTRY_INT_KEYS = {"top_k"}
|
|
75
|
+
_REGISTER_NETWORK_TYPES = {"public", "private"}
|
|
76
|
+
_REGISTER_DEFAULT_VERSION = "2025-10-30"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class _A2ARegisterError(Exception):
|
|
80
|
+
def __init__(
|
|
81
|
+
self, message: str, diagnostics: dict[str, Any] | None = None
|
|
82
|
+
) -> None:
|
|
83
|
+
super().__init__(message)
|
|
84
|
+
self.message = message
|
|
85
|
+
self.diagnostics = diagnostics or {}
|
|
65
86
|
|
|
66
87
|
|
|
67
88
|
def _split_csv(value: Optional[str]) -> Optional[list[str]]:
|
|
@@ -112,6 +133,449 @@ def _validate_choice(
|
|
|
112
133
|
raise typer.Exit(1)
|
|
113
134
|
|
|
114
135
|
|
|
136
|
+
def _parse_registry_int(key: str, value: object) -> object:
|
|
137
|
+
if key not in _REGISTRY_INT_KEYS:
|
|
138
|
+
return value
|
|
139
|
+
try:
|
|
140
|
+
return int(str(value))
|
|
141
|
+
except ValueError as exc:
|
|
142
|
+
raise ValueError(f"Registry param `{key}` must be an integer, got {value!r}.") from exc
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _parse_registry_uri(value: str) -> dict:
|
|
146
|
+
"""Parse the supported AgentKit A2A registry URI into a spec section."""
|
|
147
|
+
raw = value.strip()
|
|
148
|
+
if raw.lower() == "disabled":
|
|
149
|
+
return {"type": ""}
|
|
150
|
+
|
|
151
|
+
parsed = urlparse(raw)
|
|
152
|
+
if (
|
|
153
|
+
parsed.scheme != "agentkit"
|
|
154
|
+
or parsed.netloc != "a2a-registry"
|
|
155
|
+
or parsed.path not in {"", "/"}
|
|
156
|
+
):
|
|
157
|
+
raise ValueError(
|
|
158
|
+
"Unsupported registry URI. Currently only "
|
|
159
|
+
"`agentkit://a2a-registry?space_id=xxx&top_k=3` or "
|
|
160
|
+
"`disabled` is supported."
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
query = {
|
|
164
|
+
key.replace("-", "_"): values[-1]
|
|
165
|
+
for key, values in parse_qs(parsed.query, keep_blank_values=False).items()
|
|
166
|
+
if values and values[-1] != ""
|
|
167
|
+
}
|
|
168
|
+
unknown = sorted(set(query) - _REGISTRY_QUERY_KEYS)
|
|
169
|
+
if unknown:
|
|
170
|
+
raise ValueError(
|
|
171
|
+
f"Unsupported registry query param(s): {', '.join(unknown)}. "
|
|
172
|
+
f"Known: {', '.join(sorted(_REGISTRY_QUERY_KEYS))}"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
section: dict = {"type": "agentkit_a2a"}
|
|
176
|
+
for key, raw_value in query.items():
|
|
177
|
+
section[key] = _parse_registry_int(key, raw_value)
|
|
178
|
+
return section
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _set_registry_value(section: dict, key: str, value: object | None) -> None:
|
|
182
|
+
if value is not None:
|
|
183
|
+
section[key] = _parse_registry_int(key, value)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _apply_registry_config(
|
|
187
|
+
data: dict,
|
|
188
|
+
registry: Optional[str],
|
|
189
|
+
registry_space_id: Optional[str],
|
|
190
|
+
registry_space_name: Optional[str],
|
|
191
|
+
registry_top_k: Optional[int],
|
|
192
|
+
registry_endpoint: Optional[str],
|
|
193
|
+
registry_region: Optional[str],
|
|
194
|
+
) -> None:
|
|
195
|
+
has_registry_update = any(
|
|
196
|
+
value is not None
|
|
197
|
+
for value in [
|
|
198
|
+
registry,
|
|
199
|
+
registry_space_id,
|
|
200
|
+
registry_space_name,
|
|
201
|
+
registry_top_k,
|
|
202
|
+
registry_endpoint,
|
|
203
|
+
registry_region,
|
|
204
|
+
]
|
|
205
|
+
)
|
|
206
|
+
if not has_registry_update:
|
|
207
|
+
return
|
|
208
|
+
|
|
209
|
+
section = data.get("registry")
|
|
210
|
+
if not isinstance(section, dict):
|
|
211
|
+
section = {}
|
|
212
|
+
|
|
213
|
+
if registry is not None:
|
|
214
|
+
section.update(_parse_registry_uri(registry))
|
|
215
|
+
|
|
216
|
+
_set_registry_value(section, "space_id", registry_space_id)
|
|
217
|
+
_set_registry_value(section, "space_name", registry_space_name)
|
|
218
|
+
_set_registry_value(section, "top_k", registry_top_k)
|
|
219
|
+
_set_registry_value(section, "endpoint", registry_endpoint)
|
|
220
|
+
_set_registry_value(section, "region", registry_region)
|
|
221
|
+
|
|
222
|
+
if section.get("type") != "":
|
|
223
|
+
section["type"] = "agentkit_a2a"
|
|
224
|
+
|
|
225
|
+
if section.get("type") == "agentkit_a2a" and not section.get("space_id"):
|
|
226
|
+
space_name = section.pop("space_name", None)
|
|
227
|
+
if space_name:
|
|
228
|
+
resolved_region = (
|
|
229
|
+
registry_region
|
|
230
|
+
or section.get("region")
|
|
231
|
+
or os.getenv("AGENTKIT_REGION")
|
|
232
|
+
or os.getenv("VOLCENGINE_REGION")
|
|
233
|
+
or "cn-beijing"
|
|
234
|
+
)
|
|
235
|
+
resolved_endpoint = (
|
|
236
|
+
registry_endpoint
|
|
237
|
+
or section.get("endpoint")
|
|
238
|
+
or _default_agentkit_endpoint(resolved_region)
|
|
239
|
+
)
|
|
240
|
+
section["space_id"] = _resolve_a2a_space_id_by_name(
|
|
241
|
+
str(space_name),
|
|
242
|
+
endpoint=str(resolved_endpoint),
|
|
243
|
+
region=str(resolved_region),
|
|
244
|
+
)
|
|
245
|
+
else:
|
|
246
|
+
section.pop("space_name", None)
|
|
247
|
+
|
|
248
|
+
if section.get("type") == "agentkit_a2a" and not section.get("space_id"):
|
|
249
|
+
raise ValueError(
|
|
250
|
+
"Registry space_id is required. Use "
|
|
251
|
+
'`--registry "agentkit://a2a-registry?space_id=xxx"` '
|
|
252
|
+
"or `--registry-space-id xxx` / `--registry-space-name name`."
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
data["registry"] = section
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _load_spec(path: Path) -> dict:
|
|
259
|
+
if not path.is_file():
|
|
260
|
+
return {}
|
|
261
|
+
return json.loads(path.read_text())
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _write_spec(path: Path, data: dict) -> None:
|
|
265
|
+
path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n")
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _load_json(path: Path) -> dict:
|
|
269
|
+
return json.loads(path.read_text()) if path.is_file() else {}
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _parse_register_tags(values: list[str]) -> list[dict[str, str]]:
|
|
273
|
+
tags = []
|
|
274
|
+
for value in values:
|
|
275
|
+
if "=" not in value:
|
|
276
|
+
console.print(
|
|
277
|
+
f"[red]Error: invalid --register-tag {value!r}. "
|
|
278
|
+
"Expected KEY=VALUE.[/red]"
|
|
279
|
+
)
|
|
280
|
+
raise typer.Exit(1)
|
|
281
|
+
key, tag_value = value.split("=", 1)
|
|
282
|
+
key = key.strip()
|
|
283
|
+
tag_value = tag_value.strip()
|
|
284
|
+
if not key:
|
|
285
|
+
console.print("[red]Error: --register-tag key must not be empty.[/red]")
|
|
286
|
+
raise typer.Exit(1)
|
|
287
|
+
tags.append({"Key": key, "Value": tag_value})
|
|
288
|
+
return tags
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _default_agentkit_endpoint(region: str) -> str:
|
|
292
|
+
if region == "cn-beijing":
|
|
293
|
+
return "https://agentkit.cn-beijing.volcengineapi.com/"
|
|
294
|
+
return f"https://agentkit.{region}.volcengineapi.com/"
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def _request_id(response: dict[str, Any]) -> str | None:
|
|
298
|
+
return (response.get("ResponseMetadata") or {}).get("RequestId")
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _resolve_agentkit_credentials():
|
|
302
|
+
from agentkit.platform import VolcConfiguration, resolve_credentials
|
|
303
|
+
|
|
304
|
+
return resolve_credentials("agentkit", platform_config=VolcConfiguration())
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def _agentkit_post(
|
|
308
|
+
*,
|
|
309
|
+
endpoint: str,
|
|
310
|
+
version: str,
|
|
311
|
+
region: str,
|
|
312
|
+
action: str,
|
|
313
|
+
body: dict[str, Any],
|
|
314
|
+
) -> tuple[dict[str, Any], int]:
|
|
315
|
+
import time
|
|
316
|
+
|
|
317
|
+
import requests
|
|
318
|
+
|
|
319
|
+
from agentkit.auth._sigv4 import sign_headers
|
|
320
|
+
|
|
321
|
+
credentials = _resolve_agentkit_credentials()
|
|
322
|
+
started = time.monotonic()
|
|
323
|
+
body_bytes = json.dumps(body, ensure_ascii=False).encode("utf-8")
|
|
324
|
+
parsed = urlparse(endpoint)
|
|
325
|
+
path = parsed.path or "/"
|
|
326
|
+
query = {"Action": action, "Version": version}
|
|
327
|
+
headers = sign_headers(
|
|
328
|
+
"POST",
|
|
329
|
+
parsed.netloc,
|
|
330
|
+
query,
|
|
331
|
+
body_bytes,
|
|
332
|
+
access_key=credentials.access_key,
|
|
333
|
+
secret_key=credentials.secret_key,
|
|
334
|
+
service="agentkit",
|
|
335
|
+
region=region,
|
|
336
|
+
session_token=credentials.session_token or None,
|
|
337
|
+
path=path,
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
response = None
|
|
341
|
+
try:
|
|
342
|
+
response = requests.post(
|
|
343
|
+
endpoint,
|
|
344
|
+
params=query,
|
|
345
|
+
headers=headers,
|
|
346
|
+
data=body_bytes,
|
|
347
|
+
timeout=60,
|
|
348
|
+
)
|
|
349
|
+
response.raise_for_status()
|
|
350
|
+
data = response.json()
|
|
351
|
+
except requests.RequestException as exc:
|
|
352
|
+
diagnostics = {}
|
|
353
|
+
if response is not None:
|
|
354
|
+
diagnostics["status_code"] = response.status_code
|
|
355
|
+
try:
|
|
356
|
+
diagnostics["response"] = response.json()
|
|
357
|
+
except ValueError:
|
|
358
|
+
pass
|
|
359
|
+
raise _A2ARegisterError(
|
|
360
|
+
f"AgentKit OpenAPI request failed: {exc}", diagnostics
|
|
361
|
+
) from exc
|
|
362
|
+
except ValueError as exc:
|
|
363
|
+
raise _A2ARegisterError(
|
|
364
|
+
"AgentKit OpenAPI returned non-JSON response"
|
|
365
|
+
) from exc
|
|
366
|
+
|
|
367
|
+
duration_ms = int((time.monotonic() - started) * 1000)
|
|
368
|
+
if data.get("Error"):
|
|
369
|
+
raise _A2ARegisterError(
|
|
370
|
+
"AgentKit OpenAPI returned an error", {"response": data.get("Error")}
|
|
371
|
+
)
|
|
372
|
+
if "Result" not in data:
|
|
373
|
+
raise _A2ARegisterError("AgentKit OpenAPI response missing Result")
|
|
374
|
+
return data, duration_ms
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _resolve_a2a_space_id_by_name(
|
|
378
|
+
space_name: str,
|
|
379
|
+
*,
|
|
380
|
+
endpoint: str,
|
|
381
|
+
region: str,
|
|
382
|
+
) -> str:
|
|
383
|
+
normalized_name = space_name.strip()
|
|
384
|
+
if not normalized_name:
|
|
385
|
+
raise ValueError("A2A space name must not be empty.")
|
|
386
|
+
|
|
387
|
+
matches: list[dict[str, Any]] = []
|
|
388
|
+
page_number = 1
|
|
389
|
+
page_size = 100
|
|
390
|
+
while True:
|
|
391
|
+
response, _ = _agentkit_post(
|
|
392
|
+
endpoint=endpoint,
|
|
393
|
+
version=_REGISTER_DEFAULT_VERSION,
|
|
394
|
+
region=region,
|
|
395
|
+
action="ListA2aSpaces",
|
|
396
|
+
body={"PageNumber": page_number, "PageSize": page_size},
|
|
397
|
+
)
|
|
398
|
+
result = response.get("Result") or {}
|
|
399
|
+
items = result.get("Items") or []
|
|
400
|
+
if not isinstance(items, list):
|
|
401
|
+
raise _A2ARegisterError("ListA2aSpaces response Items is not a list")
|
|
402
|
+
matches.extend(
|
|
403
|
+
item
|
|
404
|
+
for item in items
|
|
405
|
+
if isinstance(item, dict) and item.get("Name") == normalized_name
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
total_count = int(result.get("TotalCount") or 0)
|
|
409
|
+
if page_number * page_size >= total_count or not items:
|
|
410
|
+
break
|
|
411
|
+
page_number += 1
|
|
412
|
+
|
|
413
|
+
if not matches:
|
|
414
|
+
raise ValueError(f"A2A space name '{normalized_name}' was not found.")
|
|
415
|
+
if len(matches) > 1:
|
|
416
|
+
ids = ", ".join(str(item.get("Id", "")) for item in matches if item.get("Id"))
|
|
417
|
+
raise ValueError(
|
|
418
|
+
f"A2A space name '{normalized_name}' matched multiple spaces"
|
|
419
|
+
+ (f": {ids}" if ids else ".")
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
space_id = matches[0].get("Id")
|
|
423
|
+
if not space_id:
|
|
424
|
+
raise _A2ARegisterError(
|
|
425
|
+
f"ListA2aSpaces result for '{normalized_name}' is missing Id"
|
|
426
|
+
)
|
|
427
|
+
return str(space_id)
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def _create_a2a_agent(
|
|
431
|
+
*,
|
|
432
|
+
a2a_space_id: str,
|
|
433
|
+
runtime_id: str,
|
|
434
|
+
network_type: str,
|
|
435
|
+
tags: list[dict[str, str]] | None,
|
|
436
|
+
endpoint: str,
|
|
437
|
+
region: str,
|
|
438
|
+
) -> dict[str, Any]:
|
|
439
|
+
body: dict[str, Any] = {
|
|
440
|
+
"Source": "Runtime",
|
|
441
|
+
"A2aSpaceId": a2a_space_id,
|
|
442
|
+
"RuntimeConfig": {
|
|
443
|
+
"RuntimeId": runtime_id,
|
|
444
|
+
"NetworkType": network_type,
|
|
445
|
+
},
|
|
446
|
+
"SetDefaultVersion": True,
|
|
447
|
+
}
|
|
448
|
+
if tags:
|
|
449
|
+
body["Tags"] = tags
|
|
450
|
+
|
|
451
|
+
response, request_duration_ms = _agentkit_post(
|
|
452
|
+
endpoint=endpoint,
|
|
453
|
+
version=_REGISTER_DEFAULT_VERSION,
|
|
454
|
+
region=region,
|
|
455
|
+
action="CreateA2aAgent",
|
|
456
|
+
body=body,
|
|
457
|
+
)
|
|
458
|
+
result = response.get("Result") or {}
|
|
459
|
+
return {
|
|
460
|
+
"outcome": "success",
|
|
461
|
+
"agent_id": result.get("Id", ""),
|
|
462
|
+
"tags": result.get("Tags") or [],
|
|
463
|
+
"diagnostics": {
|
|
464
|
+
"request_id": _request_id(response),
|
|
465
|
+
"request_duration_ms": request_duration_ms,
|
|
466
|
+
},
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def _resolve_self_register_entry(directory: str, name: str) -> dict[str, Any]:
|
|
471
|
+
registry_path = Path(directory).resolve() / "harness.json"
|
|
472
|
+
registry = _load_json(registry_path)
|
|
473
|
+
entry = registry.get(name)
|
|
474
|
+
if not isinstance(entry, dict):
|
|
475
|
+
console.print(
|
|
476
|
+
f"[red]Error: cannot register harness '{name}'. "
|
|
477
|
+
f"{registry_path} does not contain an entry for '{name}'. "
|
|
478
|
+
"Deploy the harness first or check --directory.[/red]"
|
|
479
|
+
)
|
|
480
|
+
raise typer.Exit(1)
|
|
481
|
+
|
|
482
|
+
missing = [field for field in ("url", "runtime_id") if not entry.get(field)]
|
|
483
|
+
if missing:
|
|
484
|
+
console.print(
|
|
485
|
+
f"[red]Error: cannot register harness '{name}'. "
|
|
486
|
+
f"{registry_path} entry is missing required field(s): "
|
|
487
|
+
f"{', '.join(missing)}. Deploy the harness again so harness.json "
|
|
488
|
+
"records url and runtime_id.[/red]"
|
|
489
|
+
)
|
|
490
|
+
raise typer.Exit(1)
|
|
491
|
+
return entry
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def _resolve_register_space_id(
|
|
495
|
+
data: dict,
|
|
496
|
+
space_id: Optional[str],
|
|
497
|
+
space_name: Optional[str],
|
|
498
|
+
*,
|
|
499
|
+
endpoint: str,
|
|
500
|
+
region: str,
|
|
501
|
+
) -> str:
|
|
502
|
+
if space_id:
|
|
503
|
+
return space_id
|
|
504
|
+
if space_name:
|
|
505
|
+
return _resolve_a2a_space_id_by_name(
|
|
506
|
+
space_name,
|
|
507
|
+
endpoint=endpoint,
|
|
508
|
+
region=region,
|
|
509
|
+
)
|
|
510
|
+
registry = data.get("registry") if isinstance(data, dict) else None
|
|
511
|
+
if isinstance(registry, dict) and registry.get("space_id"):
|
|
512
|
+
return str(registry["space_id"])
|
|
513
|
+
if isinstance(registry, dict) and registry.get("space_name"):
|
|
514
|
+
return _resolve_a2a_space_id_by_name(
|
|
515
|
+
str(registry["space_name"]),
|
|
516
|
+
endpoint=endpoint,
|
|
517
|
+
region=region,
|
|
518
|
+
)
|
|
519
|
+
console.print(
|
|
520
|
+
"[red]Error: A2A space id is required for registration. Pass "
|
|
521
|
+
"--register-space-id / --register-space-name, --registry-space-id / "
|
|
522
|
+
"--registry-space-name, or set `registry.space_id` in the harness spec.[/red]"
|
|
523
|
+
)
|
|
524
|
+
raise typer.Exit(1)
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
def _register_a2a_runtime_agent(
|
|
528
|
+
*,
|
|
529
|
+
subject: str,
|
|
530
|
+
space_id: str,
|
|
531
|
+
runtime_id: str,
|
|
532
|
+
network_type: str,
|
|
533
|
+
tags: list[str],
|
|
534
|
+
endpoint: Optional[str],
|
|
535
|
+
region: Optional[str],
|
|
536
|
+
) -> None:
|
|
537
|
+
normalized_network_type = network_type.strip().lower()
|
|
538
|
+
if normalized_network_type not in _REGISTER_NETWORK_TYPES:
|
|
539
|
+
console.print(
|
|
540
|
+
"[red]Error: --register-network-type must be one of: public, private.[/red]"
|
|
541
|
+
)
|
|
542
|
+
raise typer.Exit(1)
|
|
543
|
+
|
|
544
|
+
resolved_region = (
|
|
545
|
+
region
|
|
546
|
+
or os.getenv("AGENTKIT_REGION")
|
|
547
|
+
or os.getenv("VOLCENGINE_REGION")
|
|
548
|
+
or "cn-beijing"
|
|
549
|
+
)
|
|
550
|
+
resolved_endpoint = endpoint or _default_agentkit_endpoint(resolved_region)
|
|
551
|
+
parsed_tags = _parse_register_tags(tags)
|
|
552
|
+
|
|
553
|
+
console.print(
|
|
554
|
+
f"[cyan]Registering {subject} runtime {runtime_id} "
|
|
555
|
+
f"to A2A space {space_id}...[/cyan]"
|
|
556
|
+
)
|
|
557
|
+
try:
|
|
558
|
+
result = _create_a2a_agent(
|
|
559
|
+
a2a_space_id=space_id,
|
|
560
|
+
runtime_id=runtime_id,
|
|
561
|
+
network_type=normalized_network_type,
|
|
562
|
+
tags=parsed_tags or None,
|
|
563
|
+
endpoint=resolved_endpoint,
|
|
564
|
+
region=resolved_region,
|
|
565
|
+
)
|
|
566
|
+
except _A2ARegisterError as exc:
|
|
567
|
+
console.print(f"[red]❌ A2A registration failed: {exc.message}[/red]")
|
|
568
|
+
if exc.diagnostics:
|
|
569
|
+
console.print(json.dumps(exc.diagnostics, ensure_ascii=False, indent=2))
|
|
570
|
+
raise typer.Exit(1) from exc
|
|
571
|
+
|
|
572
|
+
console.print("[green]✅ A2A agent registered[/green]")
|
|
573
|
+
console.print(f"[cyan]AgentId:[/cyan] {result.get('agent_id', '')}")
|
|
574
|
+
diagnostics = result.get("diagnostics") or {}
|
|
575
|
+
if diagnostics.get("request_id"):
|
|
576
|
+
console.print(f"[cyan]RequestId:[/cyan] {diagnostics['request_id']}")
|
|
577
|
+
|
|
578
|
+
|
|
115
579
|
@add_app.command("harness")
|
|
116
580
|
def harness_command(
|
|
117
581
|
name: str = typer.Option(
|
|
@@ -135,6 +599,76 @@ def harness_command(
|
|
|
135
599
|
runtime: Optional[str] = typer.Option(
|
|
136
600
|
None, "--runtime", help=f"Agent runtime backend ({' | '.join(_RUNTIMES)})."
|
|
137
601
|
),
|
|
602
|
+
structured_tool_calls: Optional[bool] = typer.Option(
|
|
603
|
+
None,
|
|
604
|
+
"--structured-tool-calls/--no-structured-tool-calls",
|
|
605
|
+
help="Ask the model to return executable structured tool calls.",
|
|
606
|
+
),
|
|
607
|
+
include_tools_every_turn: Optional[bool] = typer.Option(
|
|
608
|
+
None,
|
|
609
|
+
"--include-tools-every-turn/--reuse-tool-context",
|
|
610
|
+
help="Include tool definitions on every model turn.",
|
|
611
|
+
),
|
|
612
|
+
registry: Optional[str] = typer.Option(
|
|
613
|
+
None,
|
|
614
|
+
"--registry",
|
|
615
|
+
help='AgentKit A2A registry URI, e.g. "agentkit://a2a-registry?space_id=xxx&top_k=3".',
|
|
616
|
+
),
|
|
617
|
+
registry_space_id: Optional[str] = typer.Option(
|
|
618
|
+
None, "--registry-space-id", help="AgentKit A2A SpaceId."
|
|
619
|
+
),
|
|
620
|
+
registry_space_name: Optional[str] = typer.Option(
|
|
621
|
+
None,
|
|
622
|
+
"--registry-space-name",
|
|
623
|
+
help="AgentKit A2A space name. Resolved to space_id via ListA2aSpaces.",
|
|
624
|
+
),
|
|
625
|
+
registry_top_k: Optional[int] = typer.Option(
|
|
626
|
+
None,
|
|
627
|
+
"--registry-top-k",
|
|
628
|
+
help="Number of candidate AgentCards to retrieve from the registry.",
|
|
629
|
+
),
|
|
630
|
+
registry_endpoint: Optional[str] = typer.Option(
|
|
631
|
+
None, "--registry-endpoint", help="AgentKit OpenAPI endpoint for A2A registry."
|
|
632
|
+
),
|
|
633
|
+
registry_region: Optional[str] = typer.Option(
|
|
634
|
+
None, "--registry-region", help="AgentKit OpenAPI region."
|
|
635
|
+
),
|
|
636
|
+
# --- A2A registry registration action -----------------------------------
|
|
637
|
+
register_self: bool = typer.Option(
|
|
638
|
+
False,
|
|
639
|
+
"--register-self",
|
|
640
|
+
help="Register this deployed harness Runtime Agent from harness.json.",
|
|
641
|
+
),
|
|
642
|
+
register_space_id: Optional[str] = typer.Option(
|
|
643
|
+
None,
|
|
644
|
+
"--register-space-id",
|
|
645
|
+
help="A2A registry space id for registration. Defaults to registry.space_id.",
|
|
646
|
+
),
|
|
647
|
+
register_space_name: Optional[str] = typer.Option(
|
|
648
|
+
None,
|
|
649
|
+
"--register-space-name",
|
|
650
|
+
help="A2A registry space name for registration. Resolved via ListA2aSpaces.",
|
|
651
|
+
),
|
|
652
|
+
register_network_type: str = typer.Option(
|
|
653
|
+
"public",
|
|
654
|
+
"--register-network-type",
|
|
655
|
+
help="Runtime network address to register: public or private.",
|
|
656
|
+
),
|
|
657
|
+
register_tag: list[str] = typer.Option(
|
|
658
|
+
[],
|
|
659
|
+
"--register-tag",
|
|
660
|
+
help="A2A agent tag in KEY=VALUE form. Can be repeated.",
|
|
661
|
+
),
|
|
662
|
+
register_endpoint: Optional[str] = typer.Option(
|
|
663
|
+
None,
|
|
664
|
+
"--register-endpoint",
|
|
665
|
+
help="AgentKit OpenAPI endpoint for CreateA2aAgent.",
|
|
666
|
+
),
|
|
667
|
+
register_region: Optional[str] = typer.Option(
|
|
668
|
+
None,
|
|
669
|
+
"--register-region",
|
|
670
|
+
help="AgentKit OpenAPI region. Defaults to AGENTKIT_REGION/VOLCENGINE_REGION/cn-beijing.",
|
|
671
|
+
),
|
|
138
672
|
# --- component backend types --------------------------------------------
|
|
139
673
|
knowledgebase_type: Optional[str] = typer.Option(
|
|
140
674
|
None, "--knowledgebase-type", help="Knowledge base backend."
|
|
@@ -237,7 +771,9 @@ def harness_command(
|
|
|
237
771
|
None, "--allowed-id", help="Comma-separated allowed client IDs for OAuth2/JWT."
|
|
238
772
|
),
|
|
239
773
|
directory: str = typer.Option(
|
|
240
|
-
".",
|
|
774
|
+
".",
|
|
775
|
+
"--directory",
|
|
776
|
+
help="Directory to write <name>.harness.json into.",
|
|
241
777
|
),
|
|
242
778
|
):
|
|
243
779
|
"""Create or update a harness config file ``<name>.harness.json``.
|
|
@@ -269,9 +805,8 @@ def harness_command(
|
|
|
269
805
|
raise typer.Exit(1)
|
|
270
806
|
|
|
271
807
|
# Start from the existing file (merge) or a minimal default scaffold.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
else:
|
|
808
|
+
data = _load_spec(target)
|
|
809
|
+
if not data:
|
|
275
810
|
data = {
|
|
276
811
|
"harness_name": name,
|
|
277
812
|
"runtime": "adk",
|
|
@@ -287,6 +822,10 @@ def harness_command(
|
|
|
287
822
|
data["system_prompt"] = system_prompt
|
|
288
823
|
if runtime is not None:
|
|
289
824
|
data["runtime"] = runtime
|
|
825
|
+
if structured_tool_calls is not None:
|
|
826
|
+
data["structured_tool_calls"] = structured_tool_calls
|
|
827
|
+
if include_tools_every_turn is not None:
|
|
828
|
+
data["include_tools_every_turn"] = include_tools_every_turn
|
|
290
829
|
tools_list = _split_csv(tools)
|
|
291
830
|
if tools_list is not None:
|
|
292
831
|
data["tools"] = tools_list
|
|
@@ -354,10 +893,69 @@ def harness_command(
|
|
|
354
893
|
auth["allowed_ids"] = allowed_ids
|
|
355
894
|
data["auth"] = auth
|
|
356
895
|
|
|
896
|
+
try:
|
|
897
|
+
_apply_registry_config(
|
|
898
|
+
data,
|
|
899
|
+
registry,
|
|
900
|
+
registry_space_id,
|
|
901
|
+
registry_space_name,
|
|
902
|
+
registry_top_k,
|
|
903
|
+
registry_endpoint,
|
|
904
|
+
registry_region,
|
|
905
|
+
)
|
|
906
|
+
except _A2ARegisterError as exc:
|
|
907
|
+
console.print(f"[red]Error: failed to resolve A2A space name: {exc.message}[/red]")
|
|
908
|
+
if exc.diagnostics:
|
|
909
|
+
console.print(json.dumps(exc.diagnostics, ensure_ascii=False, indent=2))
|
|
910
|
+
raise typer.Exit(1) from exc
|
|
911
|
+
except ValueError as exc:
|
|
912
|
+
console.print(f"[red]Error: {exc}[/red]")
|
|
913
|
+
raise typer.Exit(1) from exc
|
|
914
|
+
|
|
357
915
|
_prune(data)
|
|
358
|
-
target
|
|
916
|
+
_write_spec(target, data)
|
|
359
917
|
console.print(f"[green]✓ Wrote harness config: {target}[/green]")
|
|
360
918
|
|
|
919
|
+
if register_self:
|
|
920
|
+
entry = _resolve_self_register_entry(directory, name)
|
|
921
|
+
register_resolved_region = (
|
|
922
|
+
register_region
|
|
923
|
+
or os.getenv("AGENTKIT_REGION")
|
|
924
|
+
or os.getenv("VOLCENGINE_REGION")
|
|
925
|
+
or "cn-beijing"
|
|
926
|
+
)
|
|
927
|
+
register_resolved_endpoint = register_endpoint or _default_agentkit_endpoint(
|
|
928
|
+
register_resolved_region
|
|
929
|
+
)
|
|
930
|
+
try:
|
|
931
|
+
resolved_space_id = _resolve_register_space_id(
|
|
932
|
+
data,
|
|
933
|
+
register_space_id,
|
|
934
|
+
register_space_name,
|
|
935
|
+
endpoint=register_resolved_endpoint,
|
|
936
|
+
region=register_resolved_region,
|
|
937
|
+
)
|
|
938
|
+
except _A2ARegisterError as exc:
|
|
939
|
+
console.print(
|
|
940
|
+
f"[red]Error: failed to resolve A2A space name: {exc.message}[/red]"
|
|
941
|
+
)
|
|
942
|
+
if exc.diagnostics:
|
|
943
|
+
console.print(json.dumps(exc.diagnostics, ensure_ascii=False, indent=2))
|
|
944
|
+
raise typer.Exit(1) from exc
|
|
945
|
+
except ValueError as exc:
|
|
946
|
+
console.print(f"[red]Error: {exc}[/red]")
|
|
947
|
+
raise typer.Exit(1) from exc
|
|
948
|
+
console.print(f"[cyan]Harness URL:[/cyan] {entry['url']}")
|
|
949
|
+
_register_a2a_runtime_agent(
|
|
950
|
+
subject=f"harness '{name}'",
|
|
951
|
+
space_id=resolved_space_id,
|
|
952
|
+
runtime_id=str(entry["runtime_id"]),
|
|
953
|
+
network_type=register_network_type,
|
|
954
|
+
tags=register_tag,
|
|
955
|
+
endpoint=register_resolved_endpoint,
|
|
956
|
+
region=register_resolved_region,
|
|
957
|
+
)
|
|
958
|
+
|
|
361
959
|
|
|
362
960
|
# Credential types accepted by ``agentkit add credential``. Only ``api-key`` is
|
|
363
961
|
# wired up today; the value maps to the API's ``AuthType`` field.
|
|
@@ -19,6 +19,7 @@ from typing import Optional, Any
|
|
|
19
19
|
import base64
|
|
20
20
|
import binascii
|
|
21
21
|
import json
|
|
22
|
+
import os
|
|
22
23
|
import typer
|
|
23
24
|
from typer.core import TyperGroup
|
|
24
25
|
from rich.console import Console
|
|
@@ -27,6 +28,7 @@ import random
|
|
|
27
28
|
import uuid
|
|
28
29
|
from agentkit.toolkit.config import get_config
|
|
29
30
|
import logging
|
|
31
|
+
from urllib.parse import parse_qs, urlparse
|
|
30
32
|
|
|
31
33
|
# Note: Avoid importing heavy packages at the top to keep CLI startup fast
|
|
32
34
|
logger = logging.getLogger(__name__)
|
|
@@ -607,13 +609,18 @@ def build_harness_overrides(
|
|
|
607
609
|
tools: Optional[str],
|
|
608
610
|
skills: Optional[str],
|
|
609
611
|
runtime: Optional[str],
|
|
612
|
+
registry_space_id: Optional[str] = None,
|
|
613
|
+
registry_top_k: Optional[int] = None,
|
|
614
|
+
registry_endpoint: Optional[str] = None,
|
|
615
|
+
registry_region: Optional[str] = None,
|
|
610
616
|
) -> dict:
|
|
611
617
|
"""Collect the non-null fields for the harness app's ``HarnessOverrides``.
|
|
612
618
|
|
|
613
|
-
Field names/shapes match
|
|
614
|
-
(string), ``tools`` / ``skills`` as comma-separated strings,
|
|
615
|
-
``runtime
|
|
616
|
-
(``model_fields_set``); unset
|
|
619
|
+
Field names/shapes match AgentKit's ``HarnessOverrides`` model:
|
|
620
|
+
``model_name`` (string), ``tools`` / ``skills`` as comma-separated strings,
|
|
621
|
+
``system_prompt``, ``runtime``, and optional registry overrides. Only the
|
|
622
|
+
keys present here are applied server-side (``model_fields_set``); unset
|
|
623
|
+
fields keep the deployed harness's values.
|
|
617
624
|
"""
|
|
618
625
|
overrides: dict[str, Any] = {}
|
|
619
626
|
if system_prompt is not None:
|
|
@@ -626,6 +633,156 @@ def build_harness_overrides(
|
|
|
626
633
|
overrides["skills"] = skills
|
|
627
634
|
if runtime is not None:
|
|
628
635
|
overrides["runtime"] = runtime
|
|
636
|
+
if registry_space_id is not None:
|
|
637
|
+
overrides["registry_space_id"] = registry_space_id
|
|
638
|
+
if registry_top_k is not None:
|
|
639
|
+
overrides["registry_top_k"] = registry_top_k
|
|
640
|
+
if registry_endpoint is not None:
|
|
641
|
+
overrides["registry_endpoint"] = registry_endpoint
|
|
642
|
+
if registry_region is not None:
|
|
643
|
+
overrides["registry_region"] = registry_region
|
|
644
|
+
return overrides
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
_INVOKE_REGISTRY_QUERY_ALIASES = {
|
|
648
|
+
"space_id": "registry_space_id",
|
|
649
|
+
"registry_space_id": "registry_space_id",
|
|
650
|
+
"space_name": "registry_space_name",
|
|
651
|
+
"top_k": "registry_top_k",
|
|
652
|
+
"registry_top_k": "registry_top_k",
|
|
653
|
+
"endpoint": "registry_endpoint",
|
|
654
|
+
"registry_endpoint": "registry_endpoint",
|
|
655
|
+
"region": "registry_region",
|
|
656
|
+
"registry_region": "registry_region",
|
|
657
|
+
}
|
|
658
|
+
_INVOKE_REGISTRY_INT_KEYS = {"registry_top_k"}
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
def _parse_harness_registry_override(value: Optional[str]) -> dict[str, Any]:
|
|
662
|
+
"""Parse ``--registry`` into one-time harness registry overrides.
|
|
663
|
+
|
|
664
|
+
Supported forms:
|
|
665
|
+
- agentkit://a2a-registry?space_id=xxx&top_k=3®ion=cn-beijing
|
|
666
|
+
- https://... (treated as registry_endpoint; recognized query params are
|
|
667
|
+
also extracted when present)
|
|
668
|
+
"""
|
|
669
|
+
if value is None:
|
|
670
|
+
return {}
|
|
671
|
+
|
|
672
|
+
raw = value.strip()
|
|
673
|
+
if not raw:
|
|
674
|
+
return {}
|
|
675
|
+
|
|
676
|
+
parsed = urlparse(raw)
|
|
677
|
+
overrides: dict[str, Any] = {}
|
|
678
|
+
|
|
679
|
+
query = {
|
|
680
|
+
key.replace("-", "_"): values[-1]
|
|
681
|
+
for key, values in parse_qs(parsed.query, keep_blank_values=False).items()
|
|
682
|
+
if values and values[-1] != ""
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
if parsed.scheme == "agentkit":
|
|
686
|
+
if parsed.netloc != "a2a-registry" or parsed.path not in {"", "/"}:
|
|
687
|
+
raise ValueError(
|
|
688
|
+
"Unsupported registry URI. Use "
|
|
689
|
+
'`agentkit://a2a-registry?space_id=xxx&top_k=3` or an http(s) URL.'
|
|
690
|
+
)
|
|
691
|
+
elif parsed.scheme in {"http", "https"}:
|
|
692
|
+
overrides["registry_endpoint"] = raw
|
|
693
|
+
else:
|
|
694
|
+
raise ValueError(
|
|
695
|
+
"Unsupported registry value. Use "
|
|
696
|
+
'`agentkit://a2a-registry?space_id=xxx&top_k=3` or an http(s) URL.'
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
unknown = sorted(set(query) - set(_INVOKE_REGISTRY_QUERY_ALIASES))
|
|
700
|
+
if unknown and parsed.scheme == "agentkit":
|
|
701
|
+
raise ValueError(
|
|
702
|
+
f"Unsupported registry query param(s): {', '.join(unknown)}. "
|
|
703
|
+
f"Known: {', '.join(sorted(_INVOKE_REGISTRY_QUERY_ALIASES))}"
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
for key, raw_value in query.items():
|
|
707
|
+
if key not in _INVOKE_REGISTRY_QUERY_ALIASES:
|
|
708
|
+
continue
|
|
709
|
+
target = _INVOKE_REGISTRY_QUERY_ALIASES[key]
|
|
710
|
+
if target in _INVOKE_REGISTRY_INT_KEYS:
|
|
711
|
+
try:
|
|
712
|
+
overrides[target] = int(str(raw_value))
|
|
713
|
+
except ValueError as exc:
|
|
714
|
+
raise ValueError(
|
|
715
|
+
f"Registry param `{key}` must be an integer, got {raw_value!r}."
|
|
716
|
+
) from exc
|
|
717
|
+
else:
|
|
718
|
+
overrides[target] = raw_value
|
|
719
|
+
|
|
720
|
+
return overrides
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
def _resolve_harness_registry_space_name(
|
|
724
|
+
overrides: dict[str, Any],
|
|
725
|
+
*,
|
|
726
|
+
registry_endpoint: Optional[str],
|
|
727
|
+
registry_region: Optional[str],
|
|
728
|
+
) -> None:
|
|
729
|
+
if overrides.get("registry_space_id"):
|
|
730
|
+
overrides.pop("registry_space_name", None)
|
|
731
|
+
return
|
|
732
|
+
|
|
733
|
+
space_name = overrides.pop("registry_space_name", None)
|
|
734
|
+
if not space_name:
|
|
735
|
+
return
|
|
736
|
+
|
|
737
|
+
from agentkit.toolkit.cli.cli_add import (
|
|
738
|
+
_default_agentkit_endpoint,
|
|
739
|
+
_resolve_a2a_space_id_by_name,
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
resolved_region = (
|
|
743
|
+
registry_region
|
|
744
|
+
or overrides.get("registry_region")
|
|
745
|
+
or os.getenv("AGENTKIT_REGION")
|
|
746
|
+
or os.getenv("VOLCENGINE_REGION")
|
|
747
|
+
or "cn-beijing"
|
|
748
|
+
)
|
|
749
|
+
resolved_endpoint = (
|
|
750
|
+
registry_endpoint
|
|
751
|
+
or overrides.get("registry_endpoint")
|
|
752
|
+
or _default_agentkit_endpoint(str(resolved_region))
|
|
753
|
+
)
|
|
754
|
+
overrides["registry_space_id"] = _resolve_a2a_space_id_by_name(
|
|
755
|
+
str(space_name),
|
|
756
|
+
endpoint=str(resolved_endpoint),
|
|
757
|
+
region=str(resolved_region),
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
def _merge_harness_registry_overrides(
|
|
762
|
+
*,
|
|
763
|
+
registry: Optional[str],
|
|
764
|
+
registry_space_id: Optional[str],
|
|
765
|
+
registry_space_name: Optional[str],
|
|
766
|
+
registry_top_k: Optional[int],
|
|
767
|
+
registry_endpoint: Optional[str],
|
|
768
|
+
registry_region: Optional[str],
|
|
769
|
+
) -> dict[str, Any]:
|
|
770
|
+
overrides = _parse_harness_registry_override(registry)
|
|
771
|
+
if registry_space_id is not None:
|
|
772
|
+
overrides["registry_space_id"] = registry_space_id
|
|
773
|
+
if registry_space_name is not None:
|
|
774
|
+
overrides["registry_space_name"] = registry_space_name
|
|
775
|
+
if registry_top_k is not None:
|
|
776
|
+
overrides["registry_top_k"] = registry_top_k
|
|
777
|
+
if registry_endpoint is not None:
|
|
778
|
+
overrides["registry_endpoint"] = registry_endpoint
|
|
779
|
+
if registry_region is not None:
|
|
780
|
+
overrides["registry_region"] = registry_region
|
|
781
|
+
_resolve_harness_registry_space_name(
|
|
782
|
+
overrides,
|
|
783
|
+
registry_endpoint=registry_endpoint,
|
|
784
|
+
registry_region=registry_region,
|
|
785
|
+
)
|
|
629
786
|
return overrides
|
|
630
787
|
|
|
631
788
|
|
|
@@ -826,6 +983,39 @@ def harness_command(
|
|
|
826
983
|
runtime: str = typer.Option(
|
|
827
984
|
None, "--runtime", help="Override the harness runtime backend for this call."
|
|
828
985
|
),
|
|
986
|
+
registry_space_id: str = typer.Option(
|
|
987
|
+
None,
|
|
988
|
+
"--registry-space-id",
|
|
989
|
+
help="Override the A2A registry space id for this invocation.",
|
|
990
|
+
),
|
|
991
|
+
registry_space_name: str = typer.Option(
|
|
992
|
+
None,
|
|
993
|
+
"--registry-space-name",
|
|
994
|
+
help="Override the A2A registry space name for this invocation.",
|
|
995
|
+
),
|
|
996
|
+
registry: str = typer.Option(
|
|
997
|
+
None,
|
|
998
|
+
"--registry",
|
|
999
|
+
help=(
|
|
1000
|
+
"Override A2A registry for this invocation. Accepts "
|
|
1001
|
+
"`agentkit://a2a-registry?space_id=xxx&top_k=3` or an http(s) URL."
|
|
1002
|
+
),
|
|
1003
|
+
),
|
|
1004
|
+
registry_top_k: int = typer.Option(
|
|
1005
|
+
None,
|
|
1006
|
+
"--registry-top-k",
|
|
1007
|
+
help="Override the number of A2A AgentCards to retrieve for this invocation.",
|
|
1008
|
+
),
|
|
1009
|
+
registry_endpoint: str = typer.Option(
|
|
1010
|
+
None,
|
|
1011
|
+
"--registry-endpoint",
|
|
1012
|
+
help="Override the A2A registry OpenAPI endpoint for this invocation.",
|
|
1013
|
+
),
|
|
1014
|
+
registry_region: str = typer.Option(
|
|
1015
|
+
None,
|
|
1016
|
+
"--registry-region",
|
|
1017
|
+
help="Override the A2A registry OpenAPI region for this invocation.",
|
|
1018
|
+
),
|
|
829
1019
|
apikey: str = typer.Option(
|
|
830
1020
|
None,
|
|
831
1021
|
"--apikey",
|
|
@@ -859,12 +1049,30 @@ def harness_command(
|
|
|
859
1049
|
# Per-call overrides
|
|
860
1050
|
agentkit invoke harness my-harness --system-prompt "Be terse." "What is 2+2?"
|
|
861
1051
|
agentkit invoke harness my-harness --max-llm-calls 10 "Plan a trip."
|
|
1052
|
+
agentkit invoke harness my-harness --registry-space-id as-xxx "Find an agent."
|
|
862
1053
|
"""
|
|
863
1054
|
import requests
|
|
1055
|
+
from agentkit.toolkit.cli.cli_add import _A2ARegisterError
|
|
864
1056
|
from agentkit.toolkit.harness import load_harness_registry
|
|
865
1057
|
|
|
866
1058
|
console.print("[cyan]Invoking harness...[/cyan]")
|
|
867
1059
|
|
|
1060
|
+
try:
|
|
1061
|
+
registry_overrides = _merge_harness_registry_overrides(
|
|
1062
|
+
registry=registry,
|
|
1063
|
+
registry_space_id=registry_space_id,
|
|
1064
|
+
registry_space_name=registry_space_name,
|
|
1065
|
+
registry_top_k=registry_top_k,
|
|
1066
|
+
registry_endpoint=registry_endpoint,
|
|
1067
|
+
registry_region=registry_region,
|
|
1068
|
+
)
|
|
1069
|
+
except _A2ARegisterError as e:
|
|
1070
|
+
console.print(f"[red]Error: failed to resolve A2A space name: {e}[/red]")
|
|
1071
|
+
raise typer.Exit(1)
|
|
1072
|
+
except ValueError as e:
|
|
1073
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
1074
|
+
raise typer.Exit(1)
|
|
1075
|
+
|
|
868
1076
|
registry = load_harness_registry(directory)
|
|
869
1077
|
entry = registry.get(name)
|
|
870
1078
|
if not isinstance(entry, dict) or not entry.get("url"):
|
|
@@ -932,7 +1140,15 @@ def harness_command(
|
|
|
932
1140
|
prompt=message,
|
|
933
1141
|
session_id=session_id,
|
|
934
1142
|
overrides=build_harness_overrides(
|
|
935
|
-
system_prompt,
|
|
1143
|
+
system_prompt,
|
|
1144
|
+
model_name,
|
|
1145
|
+
tools,
|
|
1146
|
+
skills,
|
|
1147
|
+
runtime,
|
|
1148
|
+
registry_space_id=registry_overrides.get("registry_space_id"),
|
|
1149
|
+
registry_top_k=registry_overrides.get("registry_top_k"),
|
|
1150
|
+
registry_endpoint=registry_overrides.get("registry_endpoint"),
|
|
1151
|
+
registry_region=registry_overrides.get("registry_region"),
|
|
936
1152
|
),
|
|
937
1153
|
raw=raw,
|
|
938
1154
|
)
|
|
@@ -955,7 +1171,15 @@ def harness_command(
|
|
|
955
1171
|
"run_agent_request": run_agent_request,
|
|
956
1172
|
}
|
|
957
1173
|
overrides = build_harness_overrides(
|
|
958
|
-
system_prompt,
|
|
1174
|
+
system_prompt,
|
|
1175
|
+
model_name,
|
|
1176
|
+
tools,
|
|
1177
|
+
skills,
|
|
1178
|
+
runtime,
|
|
1179
|
+
registry_overrides.get("registry_space_id"),
|
|
1180
|
+
registry_overrides.get("registry_top_k"),
|
|
1181
|
+
registry_overrides.get("registry_endpoint"),
|
|
1182
|
+
registry_overrides.get("registry_region"),
|
|
959
1183
|
)
|
|
960
1184
|
if overrides:
|
|
961
1185
|
body["harness"] = overrides
|
|
@@ -64,6 +64,7 @@ class ModelProviderConfig:
|
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
DEFAULT_MODEL_CONTEXT_WINDOW = 1000000
|
|
67
|
+
LIMITED_MODEL_CONTEXT_WINDOW = 200000
|
|
67
68
|
DEFAULT_MODEL_PROVIDER = ModelProviderType.MODEL_SQUARE.value
|
|
68
69
|
|
|
69
70
|
MODEL_PROVIDER_CONFIGS: dict[str, ModelProviderConfig] = {
|
|
@@ -74,7 +75,7 @@ MODEL_PROVIDER_CONFIGS: dict[str, ModelProviderConfig] = {
|
|
|
74
75
|
models={
|
|
75
76
|
"doubao-seed-2-0-pro-260215": ModelSpec(
|
|
76
77
|
supports_reasoning_summaries=True,
|
|
77
|
-
context_window=
|
|
78
|
+
context_window=LIMITED_MODEL_CONTEXT_WINDOW,
|
|
78
79
|
),
|
|
79
80
|
"deepseek-v4-flash-260425": ModelSpec(
|
|
80
81
|
supports_reasoning_summaries=True,
|
|
@@ -84,10 +85,6 @@ MODEL_PROVIDER_CONFIGS: dict[str, ModelProviderConfig] = {
|
|
|
84
85
|
supports_reasoning_summaries=True,
|
|
85
86
|
context_window=DEFAULT_MODEL_CONTEXT_WINDOW,
|
|
86
87
|
),
|
|
87
|
-
"glm-4-7-251222": ModelSpec(
|
|
88
|
-
supports_reasoning_summaries=False,
|
|
89
|
-
context_window=200000,
|
|
90
|
-
),
|
|
91
88
|
},
|
|
92
89
|
),
|
|
93
90
|
ModelProviderType.CODING_PLAN.value: ModelProviderConfig(
|
|
@@ -97,7 +94,7 @@ MODEL_PROVIDER_CONFIGS: dict[str, ModelProviderConfig] = {
|
|
|
97
94
|
models={
|
|
98
95
|
"doubao-seed-2.0-pro": ModelSpec(
|
|
99
96
|
supports_reasoning_summaries=True,
|
|
100
|
-
context_window=
|
|
97
|
+
context_window=LIMITED_MODEL_CONTEXT_WINDOW,
|
|
101
98
|
),
|
|
102
99
|
"deepseek-v4-flash": ModelSpec(
|
|
103
100
|
supports_reasoning_summaries=True,
|
|
@@ -107,10 +104,6 @@ MODEL_PROVIDER_CONFIGS: dict[str, ModelProviderConfig] = {
|
|
|
107
104
|
supports_reasoning_summaries=True,
|
|
108
105
|
context_window=DEFAULT_MODEL_CONTEXT_WINDOW,
|
|
109
106
|
),
|
|
110
|
-
"glm-5.2": ModelSpec(
|
|
111
|
-
supports_reasoning_summaries=False,
|
|
112
|
-
context_window=DEFAULT_MODEL_CONTEXT_WINDOW,
|
|
113
|
-
),
|
|
114
107
|
},
|
|
115
108
|
),
|
|
116
109
|
ModelProviderType.AGENT_PLAN.value: ModelProviderConfig(
|
|
@@ -120,7 +113,7 @@ MODEL_PROVIDER_CONFIGS: dict[str, ModelProviderConfig] = {
|
|
|
120
113
|
models={
|
|
121
114
|
"doubao-seed-2.0-pro": ModelSpec(
|
|
122
115
|
supports_reasoning_summaries=True,
|
|
123
|
-
context_window=
|
|
116
|
+
context_window=LIMITED_MODEL_CONTEXT_WINDOW,
|
|
124
117
|
),
|
|
125
118
|
"deepseek-v4-flash": ModelSpec(
|
|
126
119
|
supports_reasoning_summaries=True,
|
|
@@ -130,10 +123,6 @@ MODEL_PROVIDER_CONFIGS: dict[str, ModelProviderConfig] = {
|
|
|
130
123
|
supports_reasoning_summaries=True,
|
|
131
124
|
context_window=DEFAULT_MODEL_CONTEXT_WINDOW,
|
|
132
125
|
),
|
|
133
|
-
"glm-5.2": ModelSpec(
|
|
134
|
-
supports_reasoning_summaries=False,
|
|
135
|
-
context_window=DEFAULT_MODEL_CONTEXT_WINDOW,
|
|
136
|
-
),
|
|
137
126
|
},
|
|
138
127
|
),
|
|
139
128
|
}
|
|
@@ -291,6 +280,22 @@ def _build_model_catalog_item(model_name: str, spec: ModelSpec) -> dict:
|
|
|
291
280
|
}
|
|
292
281
|
|
|
293
282
|
|
|
283
|
+
def infer_model_spec(model_name: str) -> ModelSpec:
|
|
284
|
+
normalized_model_name = model_name.strip().lower()
|
|
285
|
+
if (
|
|
286
|
+
normalized_model_name == "glm-5.2"
|
|
287
|
+
or normalized_model_name.startswith("deepseek-v4")
|
|
288
|
+
):
|
|
289
|
+
context_window = DEFAULT_MODEL_CONTEXT_WINDOW
|
|
290
|
+
else:
|
|
291
|
+
context_window = LIMITED_MODEL_CONTEXT_WINDOW
|
|
292
|
+
|
|
293
|
+
return ModelSpec(
|
|
294
|
+
supports_reasoning_summaries=True,
|
|
295
|
+
context_window=context_window,
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
|
|
294
299
|
def model_catalog_context_window(
|
|
295
300
|
model_name: str,
|
|
296
301
|
model_provider: str | ModelProviderType | None = None,
|
|
@@ -299,7 +304,7 @@ def model_catalog_context_window(
|
|
|
299
304
|
spec = config.models.get(model_name)
|
|
300
305
|
if spec:
|
|
301
306
|
return spec.context_window
|
|
302
|
-
return
|
|
307
|
+
return infer_model_spec(model_name).context_window
|
|
303
308
|
|
|
304
309
|
|
|
305
310
|
def build_codex_model_catalog_json(
|
|
@@ -310,15 +315,11 @@ def build_codex_model_catalog_json(
|
|
|
310
315
|
config = MODEL_PROVIDER_CONFIGS[resolved_provider]
|
|
311
316
|
resolved_model_name = resolve_model_name(model_name, resolved_provider)
|
|
312
317
|
deduped_model_names = list(dict.fromkeys((resolved_model_name, *config.models)))
|
|
313
|
-
fallback_spec = ModelSpec(
|
|
314
|
-
supports_reasoning_summaries=True,
|
|
315
|
-
context_window=DEFAULT_MODEL_CONTEXT_WINDOW,
|
|
316
|
-
)
|
|
317
318
|
payload = {
|
|
318
319
|
"models": [
|
|
319
320
|
_build_model_catalog_item(
|
|
320
321
|
name,
|
|
321
|
-
config.models.get(name
|
|
322
|
+
config.models.get(name) or infer_model_spec(name),
|
|
322
323
|
)
|
|
323
324
|
for name in deduped_model_names
|
|
324
325
|
]
|
agentkit/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentkit-sdk-python
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
4
4
|
Summary: Python SDK for transforming any AI agent into a production-ready application. Framework-agnostic primitives for runtime, memory, authentication, and tools with volcengine-managed infrastructure.
|
|
5
5
|
Author-email: Xiangrui Cheng <innsdcc@gmail.com>, Yumeng Bao <baoyumeng.123@gmail.com>, Yaozheng Fang <fangyozheng@gmail.com>, Guodong Li <cu.eric.lee@gmail.com>
|
|
6
6
|
License: Apache License
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
agentkit/__init__.py,sha256=l27ZMDslc3VhmmnPZJyrqVvTDoZ0LqhCtM5hw0caHcU,1021
|
|
2
|
-
agentkit/version.py,sha256
|
|
2
|
+
agentkit/version.py,sha256=-8u8ma3ZjT78npvdx0yb_H-M501lF15MhCY0uJyY6CU,653
|
|
3
3
|
agentkit/apps/__init__.py,sha256=-oXTjxV3gejpJ8pffTUcUAXw0LfxKCYZJk-_hIJhtLM,1921
|
|
4
4
|
agentkit/apps/base_app.py,sha256=3hZZExL1wyTGWveJEZZoqXN086MzmkVS_WU1vyulIWg,754
|
|
5
5
|
agentkit/apps/utils.py,sha256=IzimIDmT6FS6PV9MLimPh46mq5zT-EjVmnYPxBdyEq0,1934
|
|
@@ -81,7 +81,7 @@ agentkit/toolkit/builders/ve_pipeline.py,sha256=n2ZbjqkSBKv5oOsqD-E0SpC5NeP2Impr
|
|
|
81
81
|
agentkit/toolkit/cli/__init__.py,sha256=pkSabKw7_ai4NOo56pXKL40EcaxIDh6HYxPXOY7qWbo,634
|
|
82
82
|
agentkit/toolkit/cli/__main__.py,sha256=arwJ1gHkaaoQAXb0ezo_FeyxiMqVy0FXujB2WOX_ohU,775
|
|
83
83
|
agentkit/toolkit/cli/cli.py,sha256=PFe6axB5f2Ox10azlf2Ditb1rXeVVMI5BbX46Cnu33k,4769
|
|
84
|
-
agentkit/toolkit/cli/cli_add.py,sha256=
|
|
84
|
+
agentkit/toolkit/cli/cli_add.py,sha256=1yP5zyErRJa4RfYKcI-OaPlyIaLHg62nHk87aEaKzPY,36505
|
|
85
85
|
agentkit/toolkit/cli/cli_auth.py,sha256=eislIHxafJovjDSQai8zfO4zcd502-U5oJYelnPFKgQ,26445
|
|
86
86
|
agentkit/toolkit/cli/cli_build.py,sha256=gtRBPJfm6Z4y37ISC_CiGwEkMhD4fD7IQi1H6Lzc2Yo,2984
|
|
87
87
|
agentkit/toolkit/cli/cli_config.py,sha256=cb-QGXNvm8kQiaii4vjfCwt6Y9oukiZFSgHp1Y8Ovuk,29096
|
|
@@ -89,7 +89,7 @@ agentkit/toolkit/cli/cli_delete.py,sha256=Q6utRf5ak1fHF_Bm73weWgiiQA3Oz3iWzl5ij5
|
|
|
89
89
|
agentkit/toolkit/cli/cli_deploy.py,sha256=B912gRSpgLFBCLF8w8lD1J_jP5-PvpT_Zjblx-0yees,7066
|
|
90
90
|
agentkit/toolkit/cli/cli_destroy.py,sha256=QpH7cctsaIFd2io6hhEeKnCjxK7DbE7AnFsecWL5BxY,1797
|
|
91
91
|
agentkit/toolkit/cli/cli_init.py,sha256=feuypfgggXj6_0ZP4TEQ3rIZOuYRVUxXwvpdq9nsOY0,17911
|
|
92
|
-
agentkit/toolkit/cli/cli_invoke.py,sha256=
|
|
92
|
+
agentkit/toolkit/cli/cli_invoke.py,sha256=6fvL0yprSHHfj04vD2uA6AS27ouvpD33QIjgREZz01k,45204
|
|
93
93
|
agentkit/toolkit/cli/cli_knowledge.py,sha256=J3pcJs01tWJAMp79nFnX5pU6AEZK7XVCZtyPRLKkfUI,25142
|
|
94
94
|
agentkit/toolkit/cli/cli_launch.py,sha256=vkhjTBcxteVGwxtkDXKQxDjqTsFyDPx0SZcKL-zkPMg,3774
|
|
95
95
|
agentkit/toolkit/cli/cli_list.py,sha256=KBqSRMaiO6Frej_ZvTV4_ODWgMVdHzJtsl63K43Y3hY,14256
|
|
@@ -116,7 +116,7 @@ agentkit/toolkit/cli/sandbox/cli_run.py,sha256=7TTRssT1CNGIC-8JRGtPazRJZDX_9WM-M
|
|
|
116
116
|
agentkit/toolkit/cli/sandbox/cli_shell.py,sha256=PniGXNmw4PoZRiB0P29jryra_aFtpuCwJgv5vlxfFeU,4642
|
|
117
117
|
agentkit/toolkit/cli/sandbox/cli_web.py,sha256=nvBEp99yqA3-sDNA_PBn7jOG_VgkHTw_8lDSvRNYSRA,2496
|
|
118
118
|
agentkit/toolkit/cli/sandbox/git_config.py,sha256=y0omhCCsH33XCMEpmbtAdHM8pevRFQdil1-JfOV8RTo,6097
|
|
119
|
-
agentkit/toolkit/cli/sandbox/model_config.py,sha256=
|
|
119
|
+
agentkit/toolkit/cli/sandbox/model_config.py,sha256=rAXSEwtkiv7sxiuwWl8Bh-I511GnsAg6IcSC1Hd4Asc,10821
|
|
120
120
|
agentkit/toolkit/cli/sandbox/sandbox_client.py,sha256=h7cuAgYLRjhpAvqZXAZHvBNkB3N2sda3HXZ_Acvt_1g,13837
|
|
121
121
|
agentkit/toolkit/cli/sandbox/session_create.py,sha256=o8UcfSyIZfXyP7F3tsmebn7xHnPlIqU7W3q-x8-pxhI,12655
|
|
122
122
|
agentkit/toolkit/cli/sandbox/session_sync.py,sha256=pRX8nHTlAALdBIi4utupEvjfoL1BVTW2YcOSGC66iyM,3133
|
|
@@ -228,9 +228,9 @@ agentkit/utils/misc.py,sha256=FXMlcupj3KzxfJvMYFp0uTQk3FRMx8FiEXkYiy-UQ8w,3437
|
|
|
228
228
|
agentkit/utils/request.py,sha256=IVoat3EavR9rQ_fXi0eA2rZPCJ9lVZAXXn7uIIX6bY4,1614
|
|
229
229
|
agentkit/utils/template_utils.py,sha256=Qjg9V6dEpjAd_yYezIIPwuDsDeeMmp6XTMn5ZECifNc,6336
|
|
230
230
|
agentkit/utils/ve_sign.py,sha256=nrYiS2bYncvWKQVpVMzwYwPDfkA0OJS7LO9l_wfW0xc,9310
|
|
231
|
-
agentkit_sdk_python-0.6.
|
|
232
|
-
agentkit_sdk_python-0.6.
|
|
233
|
-
agentkit_sdk_python-0.6.
|
|
234
|
-
agentkit_sdk_python-0.6.
|
|
235
|
-
agentkit_sdk_python-0.6.
|
|
236
|
-
agentkit_sdk_python-0.6.
|
|
231
|
+
agentkit_sdk_python-0.6.5.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
232
|
+
agentkit_sdk_python-0.6.5.dist-info/METADATA,sha256=-cvLyYEu2KfEUXw6UkEhVrbRr7_Ud-IiQYTurDxz3q4,19462
|
|
233
|
+
agentkit_sdk_python-0.6.5.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
234
|
+
agentkit_sdk_python-0.6.5.dist-info/entry_points.txt,sha256=fhzZUsvsLXeB4mPaa0SBQiTBqFT404uDAKPaePAOUAE,58
|
|
235
|
+
agentkit_sdk_python-0.6.5.dist-info/top_level.txt,sha256=ipy8JF-QQ-V0C1oRFLxsyaW8zwrasfJ-zjAh9vgOc7U,9
|
|
236
|
+
agentkit_sdk_python-0.6.5.dist-info/RECORD,,
|
|
File without changes
|
{agentkit_sdk_python-0.6.4.dist-info → agentkit_sdk_python-0.6.5.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{agentkit_sdk_python-0.6.4.dist-info → agentkit_sdk_python-0.6.5.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|