agentkit-sdk-python 0.6.4__py3-none-any.whl → 0.7.0__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.
@@ -26,6 +26,7 @@ auth block), serialized as a layered JSON document::
26
26
  "tools": ["web_search"],
27
27
  "skills": [],
28
28
  "system_prompt": "You are a helpful assistant.",
29
+ "description": "A helpful assistant.",
29
30
  "runtime": "adk",
30
31
  "knowledgebase": {"type": "viking", "project": "...", "region": "..."},
31
32
  "long_term_memory": {"type": ""},
@@ -38,9 +39,11 @@ into the existing file, so configuration can be built up incrementally.
38
39
  """
39
40
 
40
41
  import json
42
+ import os
41
43
  import re
42
44
  from pathlib import Path
43
- from typing import Optional
45
+ from typing import Any, Optional
46
+ from urllib.parse import parse_qs, urlparse, urlunparse
44
47
 
45
48
  import typer
46
49
  from rich.console import Console
@@ -62,6 +65,29 @@ _SHORT_TERM_MEMORY_TYPES = ("local", "sqlite", "mysql", "postgresql")
62
65
 
63
66
  # Harness name charset (matches `init`'s directory-name rule).
64
67
  _NAME_RE = re.compile(r"^[a-zA-Z0-9_-]+$")
68
+ _REGISTRY_QUERY_KEYS = {
69
+ "space_id",
70
+ "space_name",
71
+ "top_k",
72
+ "endpoint",
73
+ "region",
74
+ }
75
+ _REGISTRY_INT_KEYS = {"top_k"}
76
+ _REGISTER_NETWORK_TYPES = {"public", "private"}
77
+ _REGISTER_DEFAULT_VERSION = "2025-10-30"
78
+ _DEFAULT_A2A_REGISTRY_URI = (
79
+ "agentkit://a2a-registry?"
80
+ "space_name=Default&region=cn-beijing&endpoint=https://open.volcengineapi.com/"
81
+ )
82
+
83
+
84
+ class _A2ARegisterError(Exception):
85
+ def __init__(
86
+ self, message: str, diagnostics: dict[str, Any] | None = None
87
+ ) -> None:
88
+ super().__init__(message)
89
+ self.message = message
90
+ self.diagnostics = diagnostics or {}
65
91
 
66
92
 
67
93
  def _split_csv(value: Optional[str]) -> Optional[list[str]]:
@@ -75,6 +101,17 @@ def _is_blank(value: object) -> bool:
75
101
  return value is None or value == "" or value == [] or value == {}
76
102
 
77
103
 
104
+ def _derive_agent_name(harness_name: str) -> str:
105
+ """ADK agent name derived from a harness name.
106
+
107
+ Must mirror veadk's ``harness_app/utils.py::agent_name_from_harness``.
108
+ """
109
+ name = re.sub(r"[^0-9A-Za-z_]", "_", harness_name or "")
110
+ if not name or name[0].isdigit():
111
+ name = f"_{name}"
112
+ return f"{name}_" if name == "user" else name
113
+
114
+
78
115
  def _prune(data: dict) -> None:
79
116
  """Drop unset fields so the file holds only what is configured.
80
117
 
@@ -112,6 +149,514 @@ def _validate_choice(
112
149
  raise typer.Exit(1)
113
150
 
114
151
 
152
+ def _parse_registry_int(key: str, value: object) -> object:
153
+ if key not in _REGISTRY_INT_KEYS:
154
+ return value
155
+ try:
156
+ return int(str(value))
157
+ except ValueError as exc:
158
+ raise ValueError(f"Registry param `{key}` must be an integer, got {value!r}.") from exc
159
+
160
+
161
+ def _expand_default_registry_uri(value: str) -> str:
162
+ raw = value.strip()
163
+ if raw.lower() == "default":
164
+ return _DEFAULT_A2A_REGISTRY_URI
165
+ return raw
166
+
167
+
168
+ def _parse_registry_uri(value: str) -> dict:
169
+ """Parse the supported AgentKit A2A registry URI into a spec section."""
170
+ raw = _expand_default_registry_uri(value)
171
+ if raw.lower() == "disabled":
172
+ return {"type": ""}
173
+
174
+ parsed = urlparse(raw)
175
+ if (
176
+ parsed.scheme != "agentkit"
177
+ or parsed.netloc != "a2a-registry"
178
+ or parsed.path not in {"", "/"}
179
+ ):
180
+ raise ValueError(
181
+ "Unsupported registry URI. Currently only "
182
+ "`agentkit://a2a-registry?space_id=xxx&top_k=3` or "
183
+ "`default` / `disabled` is supported."
184
+ )
185
+
186
+ query = {
187
+ key.replace("-", "_"): values[-1]
188
+ for key, values in parse_qs(parsed.query, keep_blank_values=False).items()
189
+ if values and values[-1] != ""
190
+ }
191
+ unknown = sorted(set(query) - _REGISTRY_QUERY_KEYS)
192
+ if unknown:
193
+ raise ValueError(
194
+ f"Unsupported registry query param(s): {', '.join(unknown)}. "
195
+ f"Known: {', '.join(sorted(_REGISTRY_QUERY_KEYS))}"
196
+ )
197
+
198
+ section: dict = {"type": "agentkit_a2a"}
199
+ for key, raw_value in query.items():
200
+ section[key] = _parse_registry_int(key, raw_value)
201
+ return section
202
+
203
+
204
+ def _set_registry_value(section: dict, key: str, value: object | None) -> None:
205
+ if value is not None:
206
+ section[key] = _parse_registry_int(key, value)
207
+
208
+
209
+ def _apply_registry_config(
210
+ data: dict,
211
+ registry: Optional[str],
212
+ registry_space_id: Optional[str],
213
+ registry_space_name: Optional[str],
214
+ registry_top_k: Optional[int],
215
+ registry_endpoint: Optional[str],
216
+ registry_region: Optional[str],
217
+ ) -> None:
218
+ has_registry_update = any(
219
+ value is not None
220
+ for value in [
221
+ registry,
222
+ registry_space_id,
223
+ registry_space_name,
224
+ registry_top_k,
225
+ registry_endpoint,
226
+ registry_region,
227
+ ]
228
+ )
229
+ if not has_registry_update:
230
+ return
231
+
232
+ section = data.get("registry")
233
+ if not isinstance(section, dict):
234
+ section = {}
235
+
236
+ if registry is not None:
237
+ parsed_registry = _parse_registry_uri(registry)
238
+ if parsed_registry.get("space_name") and "space_id" not in parsed_registry:
239
+ section.pop("space_id", None)
240
+ section.update(parsed_registry)
241
+
242
+ if registry_space_name is not None:
243
+ section.pop("space_id", None)
244
+ _set_registry_value(section, "space_id", registry_space_id)
245
+ _set_registry_value(section, "space_name", registry_space_name)
246
+ _set_registry_value(section, "top_k", registry_top_k)
247
+ _set_registry_value(section, "endpoint", registry_endpoint)
248
+ _set_registry_value(section, "region", registry_region)
249
+
250
+ if section.get("type") != "":
251
+ section["type"] = "agentkit_a2a"
252
+
253
+ if section.get("type") == "agentkit_a2a" and not section.get("space_id"):
254
+ space_name = section.pop("space_name", None)
255
+ if space_name:
256
+ resolved_region = (
257
+ registry_region
258
+ or section.get("region")
259
+ or os.getenv("AGENTKIT_REGION")
260
+ or os.getenv("VOLCENGINE_REGION")
261
+ or "cn-beijing"
262
+ )
263
+ resolved_endpoint = (
264
+ registry_endpoint
265
+ or section.get("endpoint")
266
+ or _default_agentkit_endpoint(resolved_region)
267
+ )
268
+ section["space_id"] = _resolve_a2a_space_id_by_name(
269
+ str(space_name),
270
+ endpoint=str(resolved_endpoint),
271
+ region=str(resolved_region),
272
+ )
273
+ else:
274
+ section.pop("space_name", None)
275
+
276
+ if section.get("type") == "agentkit_a2a" and not section.get("space_id"):
277
+ raise ValueError(
278
+ "Registry space_id is required. Use "
279
+ '`--registry "agentkit://a2a-registry?space_id=xxx"` '
280
+ "or `--registry-space-id xxx` / `--registry-space-name name`."
281
+ )
282
+
283
+ data["registry"] = section
284
+ if section.get("type") == "agentkit_a2a":
285
+ resolved_endpoint, resolved_region = _resolve_agentkit_openapi_target(
286
+ endpoint=str(section["endpoint"]) if section.get("endpoint") else None,
287
+ region=str(section["region"]) if section.get("region") else None,
288
+ )
289
+ _enable_a2a_space_intent(
290
+ str(section["space_id"]),
291
+ endpoint=resolved_endpoint,
292
+ region=resolved_region,
293
+ )
294
+
295
+
296
+ def _load_spec(path: Path) -> dict:
297
+ if not path.is_file():
298
+ return {}
299
+ return json.loads(path.read_text())
300
+
301
+
302
+ def _write_spec(path: Path, data: dict) -> None:
303
+ path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n")
304
+
305
+
306
+ def _load_json(path: Path) -> dict:
307
+ return json.loads(path.read_text()) if path.is_file() else {}
308
+
309
+
310
+ def _parse_register_tags(values: list[str]) -> list[dict[str, str]]:
311
+ tags = []
312
+ for value in values:
313
+ if "=" not in value:
314
+ console.print(
315
+ f"[red]Error: invalid --register-tag {value!r}. "
316
+ "Expected KEY=VALUE.[/red]"
317
+ )
318
+ raise typer.Exit(1)
319
+ key, tag_value = value.split("=", 1)
320
+ key = key.strip()
321
+ tag_value = tag_value.strip()
322
+ if not key:
323
+ console.print("[red]Error: --register-tag key must not be empty.[/red]")
324
+ raise typer.Exit(1)
325
+ tags.append({"Key": key, "Value": tag_value})
326
+ return tags
327
+
328
+
329
+ def _default_agentkit_endpoint(region: str) -> str:
330
+ if region == "cn-beijing":
331
+ return "https://agentkit.cn-beijing.volcengineapi.com/"
332
+ return f"https://agentkit.{region}.volcengineapi.com/"
333
+
334
+
335
+ def _resolve_agentkit_openapi_target(
336
+ *,
337
+ endpoint: Optional[str],
338
+ region: Optional[str],
339
+ ) -> tuple[str, str]:
340
+ resolved_region = (
341
+ region
342
+ or os.getenv("AGENTKIT_REGION")
343
+ or os.getenv("VOLCENGINE_REGION")
344
+ or "cn-beijing"
345
+ )
346
+ resolved_endpoint = endpoint or _default_agentkit_endpoint(str(resolved_region))
347
+ parsed = urlparse(resolved_endpoint)
348
+ if (
349
+ parsed.scheme
350
+ and parsed.netloc
351
+ and (parsed.query or parsed.params or parsed.fragment)
352
+ ):
353
+ resolved_endpoint = urlunparse(
354
+ (parsed.scheme, parsed.netloc, parsed.path or "/", "", "", "")
355
+ )
356
+ return resolved_endpoint, str(resolved_region)
357
+
358
+
359
+ def _request_id(response: dict[str, Any]) -> str | None:
360
+ return (response.get("ResponseMetadata") or {}).get("RequestId")
361
+
362
+
363
+ def _resolve_agentkit_credentials():
364
+ from agentkit.platform import VolcConfiguration, resolve_credentials
365
+
366
+ return resolve_credentials("agentkit", platform_config=VolcConfiguration())
367
+
368
+
369
+ def _agentkit_post(
370
+ *,
371
+ endpoint: str,
372
+ version: str,
373
+ region: str,
374
+ action: str,
375
+ body: dict[str, Any],
376
+ ) -> tuple[dict[str, Any], int]:
377
+ import time
378
+
379
+ import requests
380
+
381
+ from agentkit.auth._sigv4 import sign_headers
382
+
383
+ credentials = _resolve_agentkit_credentials()
384
+ started = time.monotonic()
385
+ body_bytes = json.dumps(body, ensure_ascii=False).encode("utf-8")
386
+ parsed = urlparse(endpoint)
387
+ path = parsed.path or "/"
388
+ query = {"Action": action, "Version": version}
389
+ headers = sign_headers(
390
+ "POST",
391
+ parsed.netloc,
392
+ query,
393
+ body_bytes,
394
+ access_key=credentials.access_key,
395
+ secret_key=credentials.secret_key,
396
+ service="agentkit",
397
+ region=region,
398
+ session_token=credentials.session_token or None,
399
+ path=path,
400
+ )
401
+
402
+ response = None
403
+ try:
404
+ response = requests.post(
405
+ endpoint,
406
+ params=query,
407
+ headers=headers,
408
+ data=body_bytes,
409
+ timeout=60,
410
+ )
411
+ response.raise_for_status()
412
+ data = response.json()
413
+ except requests.RequestException as exc:
414
+ diagnostics = {}
415
+ if response is not None:
416
+ diagnostics["status_code"] = response.status_code
417
+ try:
418
+ diagnostics["response"] = response.json()
419
+ except ValueError:
420
+ pass
421
+ raise _A2ARegisterError(
422
+ f"AgentKit OpenAPI request failed: {exc}", diagnostics
423
+ ) from exc
424
+ except ValueError as exc:
425
+ raise _A2ARegisterError(
426
+ "AgentKit OpenAPI returned non-JSON response"
427
+ ) from exc
428
+
429
+ duration_ms = int((time.monotonic() - started) * 1000)
430
+ if data.get("Error"):
431
+ raise _A2ARegisterError(
432
+ "AgentKit OpenAPI returned an error", {"response": data.get("Error")}
433
+ )
434
+ if "Result" not in data:
435
+ raise _A2ARegisterError("AgentKit OpenAPI response missing Result")
436
+ return data, duration_ms
437
+
438
+
439
+ def _enable_a2a_space_intent(
440
+ space_id: str,
441
+ *,
442
+ endpoint: str,
443
+ region: str,
444
+ ) -> dict[str, Any]:
445
+ response, request_duration_ms = _agentkit_post(
446
+ endpoint=endpoint,
447
+ version=_REGISTER_DEFAULT_VERSION,
448
+ region=region,
449
+ action="UpdateA2aSpace",
450
+ body={"Id": space_id, "IntentEnabled": True},
451
+ )
452
+ return {
453
+ "request_id": _request_id(response),
454
+ "request_duration_ms": request_duration_ms,
455
+ }
456
+
457
+
458
+ def _resolve_a2a_space_id_by_name(
459
+ space_name: str,
460
+ *,
461
+ endpoint: str,
462
+ region: str,
463
+ ) -> str:
464
+ normalized_name = space_name.strip()
465
+ if not normalized_name:
466
+ raise ValueError("A2A space name must not be empty.")
467
+
468
+ matches: list[dict[str, Any]] = []
469
+ page_number = 1
470
+ page_size = 100
471
+ while True:
472
+ response, _ = _agentkit_post(
473
+ endpoint=endpoint,
474
+ version=_REGISTER_DEFAULT_VERSION,
475
+ region=region,
476
+ action="ListA2aSpaces",
477
+ body={"PageNumber": page_number, "PageSize": page_size},
478
+ )
479
+ result = response.get("Result") or {}
480
+ items = result.get("Items") or []
481
+ if not isinstance(items, list):
482
+ raise _A2ARegisterError("ListA2aSpaces response Items is not a list")
483
+ matches.extend(
484
+ item
485
+ for item in items
486
+ if isinstance(item, dict) and item.get("Name") == normalized_name
487
+ )
488
+
489
+ total_count = int(result.get("TotalCount") or 0)
490
+ if page_number * page_size >= total_count or not items:
491
+ break
492
+ page_number += 1
493
+
494
+ if not matches:
495
+ raise ValueError(f"A2A space name '{normalized_name}' was not found.")
496
+ if len(matches) > 1:
497
+ ids = ", ".join(str(item.get("Id", "")) for item in matches if item.get("Id"))
498
+ raise ValueError(
499
+ f"A2A space name '{normalized_name}' matched multiple spaces"
500
+ + (f": {ids}" if ids else ".")
501
+ )
502
+
503
+ space_id = matches[0].get("Id")
504
+ if not space_id:
505
+ raise _A2ARegisterError(
506
+ f"ListA2aSpaces result for '{normalized_name}' is missing Id"
507
+ )
508
+ return str(space_id)
509
+
510
+
511
+ def _create_a2a_agent(
512
+ *,
513
+ a2a_space_id: str,
514
+ runtime_id: str,
515
+ network_type: str,
516
+ tags: list[dict[str, str]] | None,
517
+ endpoint: str,
518
+ region: str,
519
+ ) -> dict[str, Any]:
520
+ body: dict[str, Any] = {
521
+ "Source": "Runtime",
522
+ "A2aSpaceId": a2a_space_id,
523
+ "RuntimeConfig": {
524
+ "RuntimeId": runtime_id,
525
+ "NetworkType": network_type,
526
+ },
527
+ "SetDefaultVersion": True,
528
+ }
529
+ if tags:
530
+ body["Tags"] = tags
531
+
532
+ response, request_duration_ms = _agentkit_post(
533
+ endpoint=endpoint,
534
+ version=_REGISTER_DEFAULT_VERSION,
535
+ region=region,
536
+ action="CreateA2aAgent",
537
+ body=body,
538
+ )
539
+ result = response.get("Result") or {}
540
+ return {
541
+ "outcome": "success",
542
+ "agent_id": result.get("Id", ""),
543
+ "tags": result.get("Tags") or [],
544
+ "diagnostics": {
545
+ "request_id": _request_id(response),
546
+ "request_duration_ms": request_duration_ms,
547
+ },
548
+ }
549
+
550
+
551
+ def _resolve_self_register_entry(directory: str, name: str) -> dict[str, Any]:
552
+ registry_path = Path(directory).resolve() / "harness.json"
553
+ registry = _load_json(registry_path)
554
+ entry = registry.get(name)
555
+ if not isinstance(entry, dict):
556
+ console.print(
557
+ f"[red]Error: cannot register harness '{name}'. "
558
+ f"{registry_path} does not contain an entry for '{name}'. "
559
+ "Deploy the harness first or check --directory.[/red]"
560
+ )
561
+ raise typer.Exit(1)
562
+
563
+ missing = [field for field in ("url", "runtime_id") if not entry.get(field)]
564
+ if missing:
565
+ console.print(
566
+ f"[red]Error: cannot register harness '{name}'. "
567
+ f"{registry_path} entry is missing required field(s): "
568
+ f"{', '.join(missing)}. Deploy the harness again so harness.json "
569
+ "records url and runtime_id.[/red]"
570
+ )
571
+ raise typer.Exit(1)
572
+ return entry
573
+
574
+
575
+ def _resolve_register_space_id(
576
+ data: dict,
577
+ space_id: Optional[str],
578
+ space_name: Optional[str],
579
+ *,
580
+ endpoint: str,
581
+ region: str,
582
+ ) -> str:
583
+ if space_id:
584
+ return space_id
585
+ if space_name:
586
+ return _resolve_a2a_space_id_by_name(
587
+ space_name,
588
+ endpoint=endpoint,
589
+ region=region,
590
+ )
591
+ registry = data.get("registry") if isinstance(data, dict) else None
592
+ if isinstance(registry, dict) and registry.get("space_id"):
593
+ return str(registry["space_id"])
594
+ if isinstance(registry, dict) and registry.get("space_name"):
595
+ return _resolve_a2a_space_id_by_name(
596
+ str(registry["space_name"]),
597
+ endpoint=endpoint,
598
+ region=region,
599
+ )
600
+ console.print(
601
+ "[red]Error: A2A space id is required for registration. Pass "
602
+ "--register-space-id / --register-space-name, --registry-space-id / "
603
+ "--registry-space-name, or set `registry.space_id` in the harness spec.[/red]"
604
+ )
605
+ raise typer.Exit(1)
606
+
607
+
608
+ def _register_a2a_runtime_agent(
609
+ *,
610
+ subject: str,
611
+ space_id: str,
612
+ runtime_id: str,
613
+ network_type: str,
614
+ tags: list[str],
615
+ endpoint: Optional[str],
616
+ region: Optional[str],
617
+ ) -> None:
618
+ normalized_network_type = network_type.strip().lower()
619
+ if normalized_network_type not in _REGISTER_NETWORK_TYPES:
620
+ console.print(
621
+ "[red]Error: --register-network-type must be one of: public, private.[/red]"
622
+ )
623
+ raise typer.Exit(1)
624
+
625
+ resolved_region = (
626
+ region
627
+ or os.getenv("AGENTKIT_REGION")
628
+ or os.getenv("VOLCENGINE_REGION")
629
+ or "cn-beijing"
630
+ )
631
+ resolved_endpoint = endpoint or _default_agentkit_endpoint(resolved_region)
632
+ parsed_tags = _parse_register_tags(tags)
633
+
634
+ console.print(
635
+ f"[cyan]Registering {subject} runtime {runtime_id} "
636
+ f"to A2A space {space_id}...[/cyan]"
637
+ )
638
+ try:
639
+ result = _create_a2a_agent(
640
+ a2a_space_id=space_id,
641
+ runtime_id=runtime_id,
642
+ network_type=normalized_network_type,
643
+ tags=parsed_tags or None,
644
+ endpoint=resolved_endpoint,
645
+ region=resolved_region,
646
+ )
647
+ except _A2ARegisterError as exc:
648
+ console.print(f"[red]❌ A2A registration failed: {exc.message}[/red]")
649
+ if exc.diagnostics:
650
+ console.print(json.dumps(exc.diagnostics, ensure_ascii=False, indent=2))
651
+ raise typer.Exit(1) from exc
652
+
653
+ console.print("[green]✅ A2A agent registered[/green]")
654
+ console.print(f"[cyan]AgentId:[/cyan] {result.get('agent_id', '')}")
655
+ diagnostics = result.get("diagnostics") or {}
656
+ if diagnostics.get("request_id"):
657
+ console.print(f"[cyan]RequestId:[/cyan] {diagnostics['request_id']}")
658
+
659
+
115
660
  @add_app.command("harness")
116
661
  def harness_command(
117
662
  name: str = typer.Option(
@@ -123,6 +668,12 @@ def harness_command(
123
668
  system_prompt: Optional[str] = typer.Option(
124
669
  None, "--system-prompt", help="Agent system prompt / instruction."
125
670
  ),
671
+ description: Optional[str] = typer.Option(
672
+ None,
673
+ "--description",
674
+ "--desc",
675
+ help="Agent description (used at agent init, e.g. for A2A discovery).",
676
+ ),
126
677
  model_name: Optional[str] = typer.Option(
127
678
  None, "--model-name", help="Reasoning model name."
128
679
  ),
@@ -135,6 +686,79 @@ def harness_command(
135
686
  runtime: Optional[str] = typer.Option(
136
687
  None, "--runtime", help=f"Agent runtime backend ({' | '.join(_RUNTIMES)})."
137
688
  ),
689
+ structured_tool_calls: Optional[bool] = typer.Option(
690
+ None,
691
+ "--structured-tool-calls/--no-structured-tool-calls",
692
+ help="Ask the model to return executable structured tool calls.",
693
+ ),
694
+ include_tools_every_turn: Optional[bool] = typer.Option(
695
+ None,
696
+ "--include-tools-every-turn/--reuse-tool-context",
697
+ help="Include tool definitions on every model turn.",
698
+ ),
699
+ registry: Optional[str] = typer.Option(
700
+ None,
701
+ "--registry",
702
+ help=(
703
+ 'AgentKit A2A registry URI, "default", or "disabled", e.g. '
704
+ '"agentkit://a2a-registry?space_id=xxx&top_k=3".'
705
+ ),
706
+ ),
707
+ registry_space_id: Optional[str] = typer.Option(
708
+ None, "--registry-space-id", help="AgentKit A2A SpaceId."
709
+ ),
710
+ registry_space_name: Optional[str] = typer.Option(
711
+ None,
712
+ "--registry-space-name",
713
+ help="AgentKit A2A space name. Resolved to space_id via ListA2aSpaces.",
714
+ ),
715
+ registry_top_k: Optional[int] = typer.Option(
716
+ None,
717
+ "--registry-top-k",
718
+ help="Number of candidate AgentCards to retrieve from the registry.",
719
+ ),
720
+ registry_endpoint: Optional[str] = typer.Option(
721
+ None, "--registry-endpoint", help="AgentKit OpenAPI endpoint for A2A registry."
722
+ ),
723
+ registry_region: Optional[str] = typer.Option(
724
+ None, "--registry-region", help="AgentKit OpenAPI region."
725
+ ),
726
+ # --- A2A registry registration action -----------------------------------
727
+ register_self: bool = typer.Option(
728
+ False,
729
+ "--register-self",
730
+ help="Register this deployed harness Runtime Agent from harness.json.",
731
+ ),
732
+ register_space_id: Optional[str] = typer.Option(
733
+ None,
734
+ "--register-space-id",
735
+ help="A2A registry space id for registration. Defaults to registry.space_id.",
736
+ ),
737
+ register_space_name: Optional[str] = typer.Option(
738
+ None,
739
+ "--register-space-name",
740
+ help="A2A registry space name for registration. Resolved via ListA2aSpaces.",
741
+ ),
742
+ register_network_type: str = typer.Option(
743
+ "public",
744
+ "--register-network-type",
745
+ help="Runtime network address to register: public or private.",
746
+ ),
747
+ register_tag: list[str] = typer.Option(
748
+ [],
749
+ "--register-tag",
750
+ help="A2A agent tag in KEY=VALUE form. Can be repeated.",
751
+ ),
752
+ register_endpoint: Optional[str] = typer.Option(
753
+ None,
754
+ "--register-endpoint",
755
+ help="AgentKit OpenAPI endpoint for CreateA2aAgent.",
756
+ ),
757
+ register_region: Optional[str] = typer.Option(
758
+ None,
759
+ "--register-region",
760
+ help="AgentKit OpenAPI region. Defaults to AGENTKIT_REGION/VOLCENGINE_REGION/cn-beijing.",
761
+ ),
138
762
  # --- component backend types --------------------------------------------
139
763
  knowledgebase_type: Optional[str] = typer.Option(
140
764
  None, "--knowledgebase-type", help="Knowledge base backend."
@@ -237,7 +861,9 @@ def harness_command(
237
861
  None, "--allowed-id", help="Comma-separated allowed client IDs for OAuth2/JWT."
238
862
  ),
239
863
  directory: str = typer.Option(
240
- ".", "--directory", help="Directory to write <name>.harness.json into."
864
+ ".",
865
+ "--directory",
866
+ help="Directory to write <name>.harness.json into.",
241
867
  ),
242
868
  ):
243
869
  """Create or update a harness config file ``<name>.harness.json``.
@@ -254,6 +880,13 @@ def harness_command(
254
880
  )
255
881
  raise typer.Exit(1)
256
882
 
883
+ agent_name = _derive_agent_name(name)
884
+ if agent_name != name:
885
+ console.print(
886
+ f"[yellow]ℹ Once deployed, the agent will be named "
887
+ f"'{agent_name}' instead of '{name}'.[/yellow]"
888
+ )
889
+
257
890
  _validate_choice("--runtime", runtime, _RUNTIMES)
258
891
  _validate_choice("--knowledgebase-type", knowledgebase_type, _KNOWLEDGEBASE_TYPES)
259
892
  _validate_choice(
@@ -269,9 +902,8 @@ def harness_command(
269
902
  raise typer.Exit(1)
270
903
 
271
904
  # Start from the existing file (merge) or a minimal default scaffold.
272
- if target.is_file():
273
- data = json.loads(target.read_text())
274
- else:
905
+ data = _load_spec(target)
906
+ if not data:
275
907
  data = {
276
908
  "harness_name": name,
277
909
  "runtime": "adk",
@@ -285,8 +917,14 @@ def harness_command(
285
917
  data["model"]["name"] = model_name
286
918
  if system_prompt is not None:
287
919
  data["system_prompt"] = system_prompt
920
+ if description is not None:
921
+ data["description"] = description
288
922
  if runtime is not None:
289
923
  data["runtime"] = runtime
924
+ if structured_tool_calls is not None:
925
+ data["structured_tool_calls"] = structured_tool_calls
926
+ if include_tools_every_turn is not None:
927
+ data["include_tools_every_turn"] = include_tools_every_turn
290
928
  tools_list = _split_csv(tools)
291
929
  if tools_list is not None:
292
930
  data["tools"] = tools_list
@@ -354,10 +992,71 @@ def harness_command(
354
992
  auth["allowed_ids"] = allowed_ids
355
993
  data["auth"] = auth
356
994
 
995
+ try:
996
+ _apply_registry_config(
997
+ data,
998
+ registry,
999
+ registry_space_id,
1000
+ registry_space_name,
1001
+ registry_top_k,
1002
+ registry_endpoint,
1003
+ registry_region,
1004
+ )
1005
+ except _A2ARegisterError as exc:
1006
+ console.print(
1007
+ f"[red]Error: failed to configure A2A registry: {exc.message}[/red]"
1008
+ )
1009
+ if exc.diagnostics:
1010
+ console.print(json.dumps(exc.diagnostics, ensure_ascii=False, indent=2))
1011
+ raise typer.Exit(1) from exc
1012
+ except ValueError as exc:
1013
+ console.print(f"[red]Error: {exc}[/red]")
1014
+ raise typer.Exit(1) from exc
1015
+
357
1016
  _prune(data)
358
- target.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n")
1017
+ _write_spec(target, data)
359
1018
  console.print(f"[green]✓ Wrote harness config: {target}[/green]")
360
1019
 
1020
+ if register_self:
1021
+ entry = _resolve_self_register_entry(directory, name)
1022
+ register_resolved_region = (
1023
+ register_region
1024
+ or os.getenv("AGENTKIT_REGION")
1025
+ or os.getenv("VOLCENGINE_REGION")
1026
+ or "cn-beijing"
1027
+ )
1028
+ register_resolved_endpoint = register_endpoint or _default_agentkit_endpoint(
1029
+ register_resolved_region
1030
+ )
1031
+ try:
1032
+ resolved_space_id = _resolve_register_space_id(
1033
+ data,
1034
+ register_space_id,
1035
+ register_space_name,
1036
+ endpoint=register_resolved_endpoint,
1037
+ region=register_resolved_region,
1038
+ )
1039
+ except _A2ARegisterError as exc:
1040
+ console.print(
1041
+ f"[red]Error: failed to resolve A2A space name: {exc.message}[/red]"
1042
+ )
1043
+ if exc.diagnostics:
1044
+ console.print(json.dumps(exc.diagnostics, ensure_ascii=False, indent=2))
1045
+ raise typer.Exit(1) from exc
1046
+ except ValueError as exc:
1047
+ console.print(f"[red]Error: {exc}[/red]")
1048
+ raise typer.Exit(1) from exc
1049
+ console.print(f"[cyan]Harness URL:[/cyan] {entry['url']}")
1050
+ _register_a2a_runtime_agent(
1051
+ subject=f"harness '{name}'",
1052
+ space_id=resolved_space_id,
1053
+ runtime_id=str(entry["runtime_id"]),
1054
+ network_type=register_network_type,
1055
+ tags=register_tag,
1056
+ endpoint=register_resolved_endpoint,
1057
+ region=register_resolved_region,
1058
+ )
1059
+
361
1060
 
362
1061
  # Credential types accepted by ``agentkit add credential``. Only ``api-key`` is
363
1062
  # 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 veadk's ``HarnessOverrides`` model: ``model_name``
614
- (string), ``tools`` / ``skills`` as comma-separated strings, ``system_prompt``,
615
- ``runtime``. Only the keys present here are applied server-side
616
- (``model_fields_set``); unset fields keep the deployed harness's values.
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,9 +633,182 @@ 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
629
644
  return overrides
630
645
 
631
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
+ - default (Default space in cn-beijing via https://open.volcengineapi.com/)
666
+ - agentkit://a2a-registry?space_id=xxx&top_k=3&region=cn-beijing
667
+ - https://... (treated as registry_endpoint; recognized query params are
668
+ also extracted when present)
669
+ """
670
+ if value is None:
671
+ return {}
672
+
673
+ raw = value.strip()
674
+ if not raw:
675
+ return {}
676
+
677
+ from agentkit.toolkit.cli.cli_add import _expand_default_registry_uri
678
+
679
+ raw = _expand_default_registry_uri(raw)
680
+ parsed = urlparse(raw)
681
+ overrides: dict[str, Any] = {}
682
+
683
+ query = {
684
+ key.replace("-", "_"): values[-1]
685
+ for key, values in parse_qs(parsed.query, keep_blank_values=False).items()
686
+ if values and values[-1] != ""
687
+ }
688
+
689
+ if parsed.scheme == "agentkit":
690
+ if parsed.netloc != "a2a-registry" or parsed.path not in {"", "/"}:
691
+ raise ValueError(
692
+ "Unsupported registry URI. Use "
693
+ '`default`, `agentkit://a2a-registry?space_id=xxx&top_k=3`, '
694
+ "or an http(s) URL."
695
+ )
696
+ elif parsed.scheme in {"http", "https"}:
697
+ overrides["registry_endpoint"] = raw
698
+ else:
699
+ raise ValueError(
700
+ "Unsupported registry value. Use "
701
+ '`default`, `agentkit://a2a-registry?space_id=xxx&top_k=3`, '
702
+ "or an http(s) URL."
703
+ )
704
+
705
+ unknown = sorted(set(query) - set(_INVOKE_REGISTRY_QUERY_ALIASES))
706
+ if unknown and parsed.scheme == "agentkit":
707
+ raise ValueError(
708
+ f"Unsupported registry query param(s): {', '.join(unknown)}. "
709
+ f"Known: {', '.join(sorted(_INVOKE_REGISTRY_QUERY_ALIASES))}"
710
+ )
711
+
712
+ for key, raw_value in query.items():
713
+ if key not in _INVOKE_REGISTRY_QUERY_ALIASES:
714
+ continue
715
+ target = _INVOKE_REGISTRY_QUERY_ALIASES[key]
716
+ if target in _INVOKE_REGISTRY_INT_KEYS:
717
+ try:
718
+ overrides[target] = int(str(raw_value))
719
+ except ValueError as exc:
720
+ raise ValueError(
721
+ f"Registry param `{key}` must be an integer, got {raw_value!r}."
722
+ ) from exc
723
+ else:
724
+ overrides[target] = raw_value
725
+
726
+ return overrides
727
+
728
+
729
+ def _resolve_harness_registry_space_name(
730
+ overrides: dict[str, Any],
731
+ *,
732
+ registry_endpoint: Optional[str],
733
+ registry_region: Optional[str],
734
+ ) -> None:
735
+ if overrides.get("registry_space_id"):
736
+ overrides.pop("registry_space_name", None)
737
+ return
738
+
739
+ space_name = overrides.pop("registry_space_name", None)
740
+ if not space_name:
741
+ return
742
+
743
+ from agentkit.toolkit.cli.cli_add import (
744
+ _default_agentkit_endpoint,
745
+ _resolve_a2a_space_id_by_name,
746
+ )
747
+
748
+ resolved_region = (
749
+ registry_region
750
+ or overrides.get("registry_region")
751
+ or os.getenv("AGENTKIT_REGION")
752
+ or os.getenv("VOLCENGINE_REGION")
753
+ or "cn-beijing"
754
+ )
755
+ resolved_endpoint = (
756
+ registry_endpoint
757
+ or overrides.get("registry_endpoint")
758
+ or _default_agentkit_endpoint(str(resolved_region))
759
+ )
760
+ overrides["registry_space_id"] = _resolve_a2a_space_id_by_name(
761
+ str(space_name),
762
+ endpoint=str(resolved_endpoint),
763
+ region=str(resolved_region),
764
+ )
765
+
766
+
767
+ def _merge_harness_registry_overrides(
768
+ *,
769
+ registry: Optional[str],
770
+ registry_space_id: Optional[str],
771
+ registry_space_name: Optional[str],
772
+ registry_top_k: Optional[int],
773
+ registry_endpoint: Optional[str],
774
+ registry_region: Optional[str],
775
+ ) -> dict[str, Any]:
776
+ overrides = _parse_harness_registry_override(registry)
777
+ if registry_space_id is not None:
778
+ overrides["registry_space_id"] = registry_space_id
779
+ if registry_space_name is not None:
780
+ overrides["registry_space_name"] = registry_space_name
781
+ if registry_top_k is not None:
782
+ overrides["registry_top_k"] = registry_top_k
783
+ if registry_endpoint is not None:
784
+ overrides["registry_endpoint"] = registry_endpoint
785
+ if registry_region is not None:
786
+ overrides["registry_region"] = registry_region
787
+ _resolve_harness_registry_space_name(
788
+ overrides,
789
+ registry_endpoint=registry_endpoint,
790
+ registry_region=registry_region,
791
+ )
792
+ return overrides
793
+
794
+
795
+ def _enable_harness_registry_intent(overrides: dict[str, Any]) -> None:
796
+ space_id = overrides.get("registry_space_id")
797
+ if not space_id:
798
+ return
799
+
800
+ from agentkit.toolkit.cli.cli_add import (
801
+ _enable_a2a_space_intent,
802
+ _resolve_agentkit_openapi_target,
803
+ )
804
+
805
+ endpoint, region = _resolve_agentkit_openapi_target(
806
+ endpoint=overrides.get("registry_endpoint"),
807
+ region=overrides.get("registry_region"),
808
+ )
809
+ _enable_a2a_space_intent(str(space_id), endpoint=endpoint, region=region)
810
+
811
+
632
812
  # Fixed ADK app name for the run_sse path. The harness loader serves its single
633
813
  # agent under any app name, so a stable constant keeps the CLI decoupled from the
634
814
  # deployed HARNESS_NAME.
@@ -826,6 +1006,40 @@ def harness_command(
826
1006
  runtime: str = typer.Option(
827
1007
  None, "--runtime", help="Override the harness runtime backend for this call."
828
1008
  ),
1009
+ registry_space_id: str = typer.Option(
1010
+ None,
1011
+ "--registry-space-id",
1012
+ help="Override the A2A registry space id for this invocation.",
1013
+ ),
1014
+ registry_space_name: str = typer.Option(
1015
+ None,
1016
+ "--registry-space-name",
1017
+ help="Override the A2A registry space name for this invocation.",
1018
+ ),
1019
+ registry: str = typer.Option(
1020
+ None,
1021
+ "--registry",
1022
+ help=(
1023
+ "Override A2A registry for this invocation. Accepts "
1024
+ "`default`, `agentkit://a2a-registry?space_id=xxx&top_k=3`, "
1025
+ "or an http(s) URL."
1026
+ ),
1027
+ ),
1028
+ registry_top_k: int = typer.Option(
1029
+ None,
1030
+ "--registry-top-k",
1031
+ help="Override the number of A2A AgentCards to retrieve for this invocation.",
1032
+ ),
1033
+ registry_endpoint: str = typer.Option(
1034
+ None,
1035
+ "--registry-endpoint",
1036
+ help="Override the A2A registry OpenAPI endpoint for this invocation.",
1037
+ ),
1038
+ registry_region: str = typer.Option(
1039
+ None,
1040
+ "--registry-region",
1041
+ help="Override the A2A registry OpenAPI region for this invocation.",
1042
+ ),
829
1043
  apikey: str = typer.Option(
830
1044
  None,
831
1045
  "--apikey",
@@ -859,12 +1073,31 @@ def harness_command(
859
1073
  # Per-call overrides
860
1074
  agentkit invoke harness my-harness --system-prompt "Be terse." "What is 2+2?"
861
1075
  agentkit invoke harness my-harness --max-llm-calls 10 "Plan a trip."
1076
+ agentkit invoke harness my-harness --registry-space-id as-xxx "Find an agent."
862
1077
  """
863
1078
  import requests
1079
+ from agentkit.toolkit.cli.cli_add import _A2ARegisterError
864
1080
  from agentkit.toolkit.harness import load_harness_registry
865
1081
 
866
1082
  console.print("[cyan]Invoking harness...[/cyan]")
867
1083
 
1084
+ try:
1085
+ registry_overrides = _merge_harness_registry_overrides(
1086
+ registry=registry,
1087
+ registry_space_id=registry_space_id,
1088
+ registry_space_name=registry_space_name,
1089
+ registry_top_k=registry_top_k,
1090
+ registry_endpoint=registry_endpoint,
1091
+ registry_region=registry_region,
1092
+ )
1093
+ _enable_harness_registry_intent(registry_overrides)
1094
+ except _A2ARegisterError as e:
1095
+ console.print(f"[red]Error: failed to configure A2A registry: {e}[/red]")
1096
+ raise typer.Exit(1)
1097
+ except ValueError as e:
1098
+ console.print(f"[red]Error: {e}[/red]")
1099
+ raise typer.Exit(1)
1100
+
868
1101
  registry = load_harness_registry(directory)
869
1102
  entry = registry.get(name)
870
1103
  if not isinstance(entry, dict) or not entry.get("url"):
@@ -932,7 +1165,15 @@ def harness_command(
932
1165
  prompt=message,
933
1166
  session_id=session_id,
934
1167
  overrides=build_harness_overrides(
935
- system_prompt, model_name, tools, skills, runtime
1168
+ system_prompt,
1169
+ model_name,
1170
+ tools,
1171
+ skills,
1172
+ runtime,
1173
+ registry_space_id=registry_overrides.get("registry_space_id"),
1174
+ registry_top_k=registry_overrides.get("registry_top_k"),
1175
+ registry_endpoint=registry_overrides.get("registry_endpoint"),
1176
+ registry_region=registry_overrides.get("registry_region"),
936
1177
  ),
937
1178
  raw=raw,
938
1179
  )
@@ -955,7 +1196,15 @@ def harness_command(
955
1196
  "run_agent_request": run_agent_request,
956
1197
  }
957
1198
  overrides = build_harness_overrides(
958
- system_prompt, model_name, tools, skills, runtime
1199
+ system_prompt,
1200
+ model_name,
1201
+ tools,
1202
+ skills,
1203
+ runtime,
1204
+ registry_overrides.get("registry_space_id"),
1205
+ registry_overrides.get("registry_top_k"),
1206
+ registry_overrides.get("registry_endpoint"),
1207
+ registry_overrides.get("registry_region"),
959
1208
  )
960
1209
  if overrides:
961
1210
  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=256000,
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=256000,
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=256000,
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 DEFAULT_MODEL_CONTEXT_WINDOW
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, fallback_spec),
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
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- VERSION = "0.6.4"
15
+ VERSION = "0.7.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentkit-sdk-python
3
- Version: 0.6.4
3
+ Version: 0.7.0
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=Gqi1HbRwc0kvmHCsWkr5xNkdwCCb5yUMM7RR000G9Bs,653
2
+ agentkit/version.py,sha256=BOemjyuETOvcsDflmRuS8m6Al0Y-3yY5iAvVltXGQYQ,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=bXNYGikOms-nWFSjMTgFsY-ZVdd1KM2U9nb7Eiq4amI,16981
84
+ agentkit/toolkit/cli/cli_add.py,sha256=aLyvN9Vcjis_KLhy92uuPi7e8ltFdHjwFOb-nME7CV0,39730
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=n86EvG2IKnsf6JvlUiyRfGVB_F-iz1odsoeMvPhfZsg,37487
92
+ agentkit/toolkit/cli/cli_invoke.py,sha256=sLaCXXoOeTPzECZFtNCV8EIlIQPQZsaih2s5rocz2h8,46082
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=jMGucgNK6cJYg4crzX2vOSMa6dg2ZUqHGs4xpP3sCTU,10839
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.4.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
232
- agentkit_sdk_python-0.6.4.dist-info/METADATA,sha256=YGZUey_M5jNhkkemTq7EQOA8XvN-Rjj6kyL-k2IKJUg,19462
233
- agentkit_sdk_python-0.6.4.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
234
- agentkit_sdk_python-0.6.4.dist-info/entry_points.txt,sha256=fhzZUsvsLXeB4mPaa0SBQiTBqFT404uDAKPaePAOUAE,58
235
- agentkit_sdk_python-0.6.4.dist-info/top_level.txt,sha256=ipy8JF-QQ-V0C1oRFLxsyaW8zwrasfJ-zjAh9vgOc7U,9
236
- agentkit_sdk_python-0.6.4.dist-info/RECORD,,
231
+ agentkit_sdk_python-0.7.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
232
+ agentkit_sdk_python-0.7.0.dist-info/METADATA,sha256=DzhyZDfEWulnjIa5mSvkyAB0oeCwAXFoW1vCE8Qix5U,19462
233
+ agentkit_sdk_python-0.7.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
234
+ agentkit_sdk_python-0.7.0.dist-info/entry_points.txt,sha256=fhzZUsvsLXeB4mPaa0SBQiTBqFT404uDAKPaePAOUAE,58
235
+ agentkit_sdk_python-0.7.0.dist-info/top_level.txt,sha256=ipy8JF-QQ-V0C1oRFLxsyaW8zwrasfJ-zjAh9vgOc7U,9
236
+ agentkit_sdk_python-0.7.0.dist-info/RECORD,,