agentkit-sdk-python 0.7.0__py3-none-any.whl → 0.7.2__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.
Files changed (39) hide show
  1. agentkit/apps/simple_app/simple_app.py +4 -3
  2. agentkit/auth/_redact.py +5 -35
  3. agentkit/auth/errors.py +3 -1
  4. agentkit/auth/model_login.py +360 -0
  5. agentkit/auth/session.py +4 -2
  6. agentkit/client/base_agentkit_client.py +6 -1
  7. agentkit/client/base_service_client.py +89 -10
  8. agentkit/errors.py +32 -0
  9. agentkit/platform/configuration.py +2 -2
  10. agentkit/sdk/account/client.py +0 -2
  11. agentkit/sdk/identity/auth.py +3 -2
  12. agentkit/toolkit/cli/sandbox/cli.py +7 -1
  13. agentkit/toolkit/cli/sandbox/cli_create.py +269 -46
  14. agentkit/toolkit/cli/sandbox/cli_exec.py +145 -27
  15. agentkit/toolkit/cli/sandbox/cli_file.py +8 -2
  16. agentkit/toolkit/cli/sandbox/cli_model_login.py +238 -0
  17. agentkit/toolkit/cli/sandbox/cli_mount.py +2 -1
  18. agentkit/toolkit/cli/sandbox/cli_run.py +24 -10
  19. agentkit/toolkit/cli/sandbox/cli_shell.py +3 -5
  20. agentkit/toolkit/cli/sandbox/model_config.py +184 -39
  21. agentkit/toolkit/cli/sandbox/session_create.py +64 -29
  22. agentkit/toolkit/cli/sandbox/tool_resolve.py +129 -6
  23. agentkit/toolkit/cli/sandbox/tos_config.py +2 -1
  24. agentkit/toolkit/errors.py +16 -1
  25. agentkit/toolkit/runners/ve_agentkit.py +5 -2
  26. agentkit/toolkit/volcengine/cr.py +3 -3
  27. agentkit/toolkit/volcengine/iam.py +1 -1
  28. agentkit/utils/http_defaults.py +54 -0
  29. agentkit/utils/logging_config.py +22 -0
  30. agentkit/utils/redact.py +55 -0
  31. agentkit/utils/request.py +4 -4
  32. agentkit/utils/ve_sign.py +99 -10
  33. agentkit/version.py +1 -1
  34. {agentkit_sdk_python-0.7.0.dist-info → agentkit_sdk_python-0.7.2.dist-info}/METADATA +6 -1
  35. {agentkit_sdk_python-0.7.0.dist-info → agentkit_sdk_python-0.7.2.dist-info}/RECORD +39 -34
  36. {agentkit_sdk_python-0.7.0.dist-info → agentkit_sdk_python-0.7.2.dist-info}/WHEEL +0 -0
  37. {agentkit_sdk_python-0.7.0.dist-info → agentkit_sdk_python-0.7.2.dist-info}/entry_points.txt +0 -0
  38. {agentkit_sdk_python-0.7.0.dist-info → agentkit_sdk_python-0.7.2.dist-info}/licenses/LICENSE +0 -0
  39. {agentkit_sdk_python-0.7.0.dist-info → agentkit_sdk_python-0.7.2.dist-info}/top_level.txt +0 -0
agentkit/errors.py ADDED
@@ -0,0 +1,32 @@
1
+ # Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Top-level exception root for AgentKit.
16
+
17
+ This module is a stdlib-only leaf: it has no intra-package imports so that any
18
+ subpackage (``agentkit.auth``, ``agentkit.toolkit``, ...) can subclass the
19
+ single root without creating dependency cycles.
20
+ """
21
+
22
+
23
+ class AgentKitError(Exception):
24
+ """The single root of the AgentKit exception hierarchy.
25
+
26
+ Every domain-specific AgentKit exception (auth, toolkit, API, ...) ultimately
27
+ inherits from this class so callers can ``except AgentKitError`` to catch any
28
+ AgentKit-originated failure.
29
+ """
30
+
31
+
32
+ __all__ = ["AgentKitError"]
@@ -15,7 +15,6 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  import json
18
- import logging
19
18
  import os
20
19
  import threading
21
20
  import time
@@ -42,8 +41,9 @@ from agentkit.utils.global_config_io import (
42
41
  get_global_config_value,
43
42
  read_global_config_dict,
44
43
  )
44
+ from agentkit.utils.logging_config import get_logger
45
45
 
46
- logger = logging.getLogger(__name__)
46
+ logger = get_logger(__name__)
47
47
 
48
48
  VEFAAS_IAM_CREDENTIAL_PATH = "/var/run/secrets/iam/credential"
49
49
  VEFAAS_IAM_CREDENTIAL_FALLBACK_TTL_SECONDS = 60
@@ -66,9 +66,7 @@ class AgentkitAccountClient(BaseAgentkitClient):
66
66
  Returns:
67
67
  The status string ('Enabled' or 'Disabled'), or None if service not found.
68
68
  """
69
- print(1)
70
69
  response = self.list_account_linked_services(ListAccountLinkedServicesRequest())
71
- print(2)
72
70
  if not response.service_statuses:
73
71
  return None
74
72
  for svc in response.service_statuses:
@@ -21,6 +21,7 @@ from agentkit.platform import (
21
21
  resolve_credentials,
22
22
  resolve_endpoint,
23
23
  )
24
+ from agentkit.toolkit.errors import ApiError
24
25
 
25
26
 
26
27
  def requires_api_key(*, provider_name: str, into: str = "api_key"):
@@ -59,8 +60,8 @@ def requires_api_key(*, provider_name: str, into: str = "api_key"):
59
60
 
60
61
  try:
61
62
  return response["Result"]["ApiKey"]
62
- except Exception as _:
63
- raise RuntimeError(f"Get api key failed: {response}")
63
+ except Exception as e:
64
+ raise ApiError("GetResourceApiKey did not return an ApiKey") from e
64
65
 
65
66
  @wraps(func)
66
67
  async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
@@ -22,6 +22,7 @@ from agentkit.toolkit.cli.sandbox.cli_create import create_command
22
22
  from agentkit.toolkit.cli.sandbox.cli_exec import exec_command
23
23
  from agentkit.toolkit.cli.sandbox.cli_file import file_command
24
24
  from agentkit.toolkit.cli.sandbox.cli_get import get_command
25
+ from agentkit.toolkit.cli.sandbox.cli_model_login import codex_login_command
25
26
  from agentkit.toolkit.cli.sandbox.cli_mount import mount_command
26
27
  from agentkit.toolkit.cli.sandbox.cli_run import run_command
27
28
  from agentkit.toolkit.cli.sandbox.cli_shell import shell_command
@@ -33,7 +34,10 @@ sandbox_app = typer.Typer(
33
34
  no_args_is_help=True,
34
35
  )
35
36
 
36
- sandbox_app.command(name="create")(create_command)
37
+ sandbox_app.command(
38
+ name="create",
39
+ context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
40
+ )(create_command)
37
41
  sandbox_app.command(name="get")(get_command)
38
42
  sandbox_app.command(name="mount")(mount_command)
39
43
  sandbox_app.command(
@@ -46,4 +50,6 @@ sandbox_app.command(
46
50
  context_settings={"allow_extra_args": True},
47
51
  )(shell_command)
48
52
  sandbox_app.command(name="web")(web_command)
53
+ sandbox_app.command(name="codex-login")(codex_login_command)
54
+ sandbox_app.command(name="model-login")(codex_login_command) # provider-agnostic alias
49
55
  sandbox_app.add_typer(file_command, name="file")
@@ -32,16 +32,20 @@ from agentkit.toolkit.cli.sandbox.model_config import (
32
32
  CODE_ENV_HOME,
33
33
  CODEX_CONFIG_TOML_ENV,
34
34
  CODEX_MODEL_CATALOG_JSON_ENV,
35
- DEFAULT_MODEL_PROVIDER,
36
35
  ModelProviderType,
37
36
  MODEL_API_KEY_ENV,
38
37
  MODEL_API_KEY_ENV_KEYS,
39
38
  MODEL_BASE_URL_ENV_KEYS,
40
39
  MODEL_NAME_ENV_KEYS,
41
40
  MODEL_PROVIDER_ENV,
42
- get_model_provider_config,
41
+ infer_model_provider_from_base_url,
42
+ normalize_model_base_url,
43
43
  normalize_model_provider,
44
+ resolve_model_base_urls,
44
45
  resolve_model_name,
46
+ should_emit_codex_model_catalog,
47
+ should_emit_codex_model_config,
48
+ validate_model_provider_base_url,
45
49
  build_codex_config_toml as _shared_build_codex_config_toml,
46
50
  build_codex_model_catalog_json as _shared_build_codex_model_catalog_json,
47
51
  )
@@ -73,6 +77,8 @@ DEFAULT_BROWSER_EXTRA_ARGS = (
73
77
  "--enable-unsafe-swiftshader --use-gl=angle "
74
78
  "--use-angle=swiftshader-webgl --ignore-gpu-blocklist"
75
79
  )
80
+ WEB_SEARCH_API_KEY_ENV = "WEB_SEARCH_API_KEY"
81
+ SKILL_ROLE_NAME_OPTION = "--skill-role-name"
76
82
  TOOL_READY_STATUS = "Ready"
77
83
  TOOL_FAILED_STATUSES = {"Error", "Failed", "CreateFailed", "Deleting", "Deleted"}
78
84
  TOOL_WAIT_INTERVAL_SECONDS = 5
@@ -115,16 +121,16 @@ def _append_tool_envs(
115
121
  return
116
122
 
117
123
  envs.extend(
118
- tools_types.EnvsItemForCreateTool(Key=key, Value=resolved)
119
- for key in keys
124
+ tools_types.EnvsItemForCreateTool(Key=key, Value=resolved) for key in keys
120
125
  )
121
126
 
122
127
 
123
128
  def _build_codex_config_toml(
124
129
  model_name: str,
125
130
  model_provider: str | ModelProviderType | None = None,
131
+ model_base_url: Optional[str] = None,
126
132
  ) -> str:
127
- return _shared_build_codex_config_toml(model_name, model_provider)
133
+ return _shared_build_codex_config_toml(model_name, model_provider, model_base_url)
128
134
 
129
135
 
130
136
  def _build_codex_model_catalog_json(
@@ -138,31 +144,43 @@ def _append_code_env_tool_envs(
138
144
  envs: list[tools_types.EnvsItemForCreateTool],
139
145
  model_name: str,
140
146
  model_provider: str | ModelProviderType | None,
147
+ model_base_url: Optional[str],
148
+ *,
149
+ include_codex_model_config: bool = True,
141
150
  ) -> None:
142
- envs.extend(
143
- [
144
- tools_types.EnvsItemForCreateTool(
145
- Key="OPENCODE_DISABLE_AUTOUPDATE",
146
- Value="1",
147
- ),
148
- tools_types.EnvsItemForCreateTool(
149
- Key="HOME",
150
- Value=CODE_ENV_HOME,
151
- ),
152
- tools_types.EnvsItemForCreateTool(
153
- Key="CODEX_HOME",
154
- Value=CODE_ENV_CODEX_HOME,
155
- ),
151
+ code_envs = [
152
+ tools_types.EnvsItemForCreateTool(
153
+ Key="OPENCODE_DISABLE_AUTOUPDATE",
154
+ Value="1",
155
+ ),
156
+ tools_types.EnvsItemForCreateTool(
157
+ Key="HOME",
158
+ Value=CODE_ENV_HOME,
159
+ ),
160
+ tools_types.EnvsItemForCreateTool(
161
+ Key="CODEX_HOME",
162
+ Value=CODE_ENV_CODEX_HOME,
163
+ ),
164
+ ]
165
+ if include_codex_model_config:
166
+ code_envs.append(
156
167
  tools_types.EnvsItemForCreateTool(
157
168
  Key=CODEX_CONFIG_TOML_ENV,
158
- Value=_build_codex_config_toml(model_name, model_provider),
159
- ),
160
- tools_types.EnvsItemForCreateTool(
161
- Key=CODEX_MODEL_CATALOG_JSON_ENV,
162
- Value=_build_codex_model_catalog_json(model_name, model_provider),
163
- ),
164
- ]
165
- )
169
+ Value=_build_codex_config_toml(
170
+ model_name,
171
+ model_provider,
172
+ model_base_url,
173
+ ),
174
+ )
175
+ )
176
+ if should_emit_codex_model_catalog(model_provider):
177
+ code_envs.append(
178
+ tools_types.EnvsItemForCreateTool(
179
+ Key=CODEX_MODEL_CATALOG_JSON_ENV,
180
+ Value=_build_codex_model_catalog_json(model_name, model_provider),
181
+ )
182
+ )
183
+ envs.extend(code_envs)
166
184
 
167
185
 
168
186
  def _build_tool_model_envs(
@@ -170,12 +188,29 @@ def _build_tool_model_envs(
170
188
  tool_type: str,
171
189
  model_name: Optional[str] = None,
172
190
  model_api_key: Optional[str] = None,
173
- model_provider: str | ModelProviderType | None = DEFAULT_MODEL_PROVIDER,
191
+ model_provider: str | ModelProviderType | None = None,
192
+ model_base_url: Optional[str] = None,
193
+ model_provider_was_provided: Optional[bool] = None,
194
+ model_base_url_was_provided: Optional[bool] = None,
195
+ websearch_apikey: Optional[str] = None,
174
196
  ) -> list[tools_types.EnvsItemForCreateTool] | None:
175
197
  envs: list[tools_types.EnvsItemForCreateTool] = []
176
- provider_config = get_model_provider_config(model_provider)
177
- resolved_model_provider = normalize_model_provider(model_provider)
178
- resolved_model_name = resolve_model_name(model_name, model_provider)
198
+ validate_model_provider_base_url(
199
+ model_provider=model_provider,
200
+ model_base_url=model_base_url,
201
+ model_provider_was_provided=model_provider_was_provided,
202
+ model_base_url_was_provided=model_base_url_was_provided,
203
+ )
204
+ resolved_model_base_url = normalize_model_base_url(model_base_url)
205
+ effective_model_provider = model_provider or infer_model_provider_from_base_url(
206
+ resolved_model_base_url
207
+ )
208
+ resolved_model_provider = normalize_model_provider(effective_model_provider)
209
+ resolved_model_name = resolve_model_name(model_name, resolved_model_provider)
210
+ resolved_base_url, resolved_anthropic_base_url = resolve_model_base_urls(
211
+ model_provider=resolved_model_provider,
212
+ model_base_url=resolved_model_base_url,
213
+ )
179
214
  resolved_model_api_key = model_api_key or os.getenv(MODEL_API_KEY_ENV)
180
215
  _append_tool_envs(envs, (MODEL_PROVIDER_ENV,), resolved_model_provider)
181
216
  _append_tool_envs(envs, MODEL_NAME_ENV_KEYS, resolved_model_name)
@@ -183,17 +218,30 @@ def _build_tool_model_envs(
183
218
  _append_tool_envs(
184
219
  envs,
185
220
  MODEL_BASE_URL_ENV_KEYS,
186
- provider_config.model_base_url,
221
+ resolved_base_url,
187
222
  )
188
223
  _append_tool_envs(
189
224
  envs,
190
225
  ANTHROPIC_BASE_URL_ENV_KEYS,
191
- provider_config.anthropic_base_url,
226
+ resolved_anthropic_base_url,
192
227
  )
193
228
  _append_tool_envs(envs, DISABLED_SERVICE_ENV_KEYS, "true")
194
229
  _append_tool_envs(envs, (BROWSER_EXTRA_ARGS_ENV,), DEFAULT_BROWSER_EXTRA_ARGS)
230
+ _append_tool_envs(envs, (WEB_SEARCH_API_KEY_ENV,), websearch_apikey)
195
231
  if tool_type.strip() == DEFAULT_CREATE_TOOL_TYPE:
196
- _append_code_env_tool_envs(envs, resolved_model_name, model_provider)
232
+ _append_code_env_tool_envs(
233
+ envs,
234
+ resolved_model_name,
235
+ resolved_model_provider,
236
+ resolved_model_base_url,
237
+ include_codex_model_config=(
238
+ bool(resolved_model_name)
239
+ and should_emit_codex_model_config(
240
+ model_provider=resolved_model_provider,
241
+ model_base_url=resolved_model_base_url,
242
+ )
243
+ ),
244
+ )
197
245
  return envs or None
198
246
 
199
247
 
@@ -207,7 +255,12 @@ def _build_create_tool_request(
207
255
  cpu: int = DEFAULT_CPU,
208
256
  model_name: Optional[str] = None,
209
257
  model_api_key: Optional[str] = None,
210
- model_provider: str | ModelProviderType | None = DEFAULT_MODEL_PROVIDER,
258
+ model_provider: str | ModelProviderType | None = None,
259
+ model_base_url: Optional[str] = None,
260
+ model_provider_was_provided: Optional[bool] = None,
261
+ model_base_url_was_provided: Optional[bool] = None,
262
+ role_name: Optional[str] = None,
263
+ websearch_apikey: Optional[str] = None,
211
264
  ) -> tools_types.CreateToolRequest:
212
265
  resolved_tool_type = tool_type.strip() or DEFAULT_CREATE_TOOL_TYPE
213
266
  resolved_name = (name or "").strip() or _generate_tool_name(resolved_tool_type)
@@ -225,6 +278,7 @@ def _build_create_tool_request(
225
278
  ToolType=resolved_tool_type,
226
279
  CpuMilli=cpu_milli,
227
280
  MemoryMb=memory_mb,
281
+ RoleName=role_name,
228
282
  AuthorizerConfiguration=tools_types.AuthorizerForCreateTool(
229
283
  KeyAuth=tools_types.AuthorizerKeyAuthForCreateTool(
230
284
  ApiKeyName=generate_apikey_name(),
@@ -241,6 +295,10 @@ def _build_create_tool_request(
241
295
  model_name=model_name,
242
296
  model_api_key=model_api_key,
243
297
  model_provider=model_provider,
298
+ model_base_url=model_base_url,
299
+ model_provider_was_provided=model_provider_was_provided,
300
+ model_base_url_was_provided=model_base_url_was_provided,
301
+ websearch_apikey=websearch_apikey,
244
302
  ),
245
303
  )
246
304
 
@@ -301,6 +359,106 @@ def _wait_for_tool_ready(
301
359
  time.sleep(interval_seconds)
302
360
 
303
361
 
362
+ def _ensure_sandbox_role(
363
+ role_name: str,
364
+ region: str,
365
+ ) -> str:
366
+ import json as _json
367
+ from agentkit.toolkit.volcengine.iam import VeIAM
368
+
369
+ iam = VeIAM(region=region)
370
+ existing = iam.get_role(role_name)
371
+ if existing is not None:
372
+ return role_name
373
+
374
+ agentkit_service_code = (
375
+ (
376
+ os.getenv("VOLCENGINE_AGENTKIT_SERVICE")
377
+ or os.getenv("VOLC_AGENTKIT_SERVICE")
378
+ or os.getenv("BYTEPLUS_AGENTKIT_SERVICE")
379
+ or ""
380
+ )
381
+ .strip()
382
+ .lower()
383
+ )
384
+ service = "vefaas"
385
+ if "stg" in agentkit_service_code:
386
+ service = "vefaas_dev"
387
+ trust_policy = _json.dumps(
388
+ {
389
+ "Statement": [
390
+ {
391
+ "Effect": "Allow",
392
+ "Action": ["sts:AssumeRole"],
393
+ "Principal": {"Service": [service]},
394
+ }
395
+ ]
396
+ }
397
+ )
398
+ iam.create_role(role_name, trust_policy)
399
+ iam.attach_role_policy(
400
+ role_name,
401
+ policy_name="AgentKitSkillsSandboxAccess",
402
+ policy_type="System",
403
+ )
404
+ return role_name
405
+
406
+
407
+ def _generate_default_role_name() -> str:
408
+ return f"agentkit-sandbox-{generate_random_id(8)}"
409
+
410
+
411
+ def _resolve_skill_role(
412
+ skill_role_name: Optional[str],
413
+ skill_role_name_provided: bool,
414
+ region: str,
415
+ ) -> Optional[str]:
416
+ if not skill_role_name_provided:
417
+ return None
418
+ resolved_name = (skill_role_name or "").strip()
419
+ if not resolved_name:
420
+ resolved_name = _generate_default_role_name()
421
+ _ensure_sandbox_role(resolved_name, region)
422
+ return resolved_name
423
+
424
+
425
+ def _resolve_create_extra_args(
426
+ ctx: typer.Context,
427
+ ) -> tuple[Optional[str], bool]:
428
+ raw_args = list(ctx.args)
429
+ skill_role_name: Optional[str] = None
430
+ skill_role_name_provided = False
431
+ remaining_args: list[str] = []
432
+ index = 0
433
+ while index < len(raw_args):
434
+ current = raw_args[index]
435
+ if current == SKILL_ROLE_NAME_OPTION:
436
+ if skill_role_name_provided:
437
+ error(f"{SKILL_ROLE_NAME_OPTION} cannot be provided multiple times")
438
+ skill_role_name_provided = True
439
+ if index + 1 < len(raw_args) and not raw_args[index + 1].startswith("-"):
440
+ skill_role_name = raw_args[index + 1]
441
+ index += 2
442
+ continue
443
+ index += 1
444
+ continue
445
+ if current.startswith(f"{SKILL_ROLE_NAME_OPTION}="):
446
+ if skill_role_name_provided:
447
+ error(f"{SKILL_ROLE_NAME_OPTION} cannot be provided multiple times")
448
+ skill_role_name_provided = True
449
+ skill_role_name = current.split("=", 1)[1]
450
+ index += 1
451
+ continue
452
+ remaining_args.append(current)
453
+ index += 1
454
+
455
+ if remaining_args:
456
+ unknown = " ".join(remaining_args)
457
+ error(f"Unknown arguments: {unknown}")
458
+
459
+ return skill_role_name, skill_role_name_provided
460
+
461
+
304
462
  def create_tool(
305
463
  *,
306
464
  tool_type: str = DEFAULT_CREATE_TOOL_TYPE,
@@ -310,11 +468,35 @@ def create_tool(
310
468
  cpu: int = DEFAULT_CPU,
311
469
  model_name: Optional[str] = None,
312
470
  model_api_key: Optional[str] = None,
313
- model_provider: str | ModelProviderType | None = DEFAULT_MODEL_PROVIDER,
471
+ model_provider: str | ModelProviderType | None = None,
472
+ model_base_url: Optional[str] = None,
473
+ skill_role_name: Optional[str] = None,
474
+ skill_role_name_provided: bool = False,
475
+ websearch_apikey: Optional[str] = None,
314
476
  ) -> dict[str, object]:
315
- resolved_model_provider = normalize_model_provider(model_provider)
477
+ resolved_model_base_url = normalize_model_base_url(model_base_url)
478
+ raw_model_provider = (
479
+ model_provider.value
480
+ if isinstance(model_provider, ModelProviderType)
481
+ else model_provider
482
+ )
483
+ effective_model_provider = raw_model_provider or infer_model_provider_from_base_url(
484
+ resolved_model_base_url
485
+ )
486
+ resolved_model_provider = normalize_model_provider(effective_model_provider)
316
487
  region = _resolve_region(SANDBOX_REGION_ENV, "agentkit")
317
488
  tos_region = _resolve_region(SANDBOX_TOS_REGION_ENV, "tos")
489
+
490
+ if skill_role_name_provided and websearch_apikey:
491
+ error("--skill-role-name and --websearch-apikey are mutually exclusive")
492
+
493
+ resolved_role_name = _resolve_skill_role(
494
+ skill_role_name,
495
+ skill_role_name_provided,
496
+ region,
497
+ )
498
+ resolved_websearch_apikey = (websearch_apikey or "").strip() or None
499
+
318
500
  request = _build_create_tool_request(
319
501
  tool_type=tool_type,
320
502
  name=tool_name,
@@ -324,7 +506,12 @@ def create_tool(
324
506
  cpu=cpu,
325
507
  model_name=model_name,
326
508
  model_api_key=model_api_key,
327
- model_provider=resolved_model_provider,
509
+ model_provider=effective_model_provider,
510
+ model_base_url=resolved_model_base_url,
511
+ model_provider_was_provided=bool((raw_model_provider or "").strip()),
512
+ model_base_url_was_provided=bool(resolved_model_base_url),
513
+ role_name=resolved_role_name,
514
+ websearch_apikey=resolved_websearch_apikey,
328
515
  )
329
516
  client = AgentkitToolsClient(
330
517
  region=region,
@@ -340,10 +527,14 @@ def create_tool(
340
527
  "name": final_tool.name or request.name,
341
528
  "status": final_tool.status or TOOL_READY_STATUS,
342
529
  "model_provider": resolved_model_provider,
530
+ "model_base_url": resolved_model_base_url,
531
+ "role_name": resolved_role_name,
532
+ "websearch_apikey_set": bool(resolved_websearch_apikey),
343
533
  }
344
534
 
345
535
 
346
536
  def create_command(
537
+ ctx: typer.Context,
347
538
  tool_type: str = typer.Option(
348
539
  DEFAULT_CREATE_TOOL_TYPE,
349
540
  "--tool-type",
@@ -357,10 +548,7 @@ def create_command(
357
548
  tos_bucket: Optional[str] = typer.Option(
358
549
  None,
359
550
  "--tos-bucket",
360
- help=(
361
- "TOS bucket to mount. "
362
- "Omit to create the tool without a TOS mount."
363
- ),
551
+ help=("TOS bucket to mount. Omit to create the tool without a TOS mount."),
364
552
  ),
365
553
  tos_mount: Optional[str] = typer.Option(
366
554
  None,
@@ -392,14 +580,38 @@ def create_command(
392
580
  "and ANTHROPIC_AUTH_TOKEN when creating a tool."
393
581
  ),
394
582
  ),
395
- model_provider: ModelProviderType = typer.Option(
396
- ModelProviderType.MODEL_SQUARE,
583
+ model_provider: Optional[str] = typer.Option(
584
+ None,
397
585
  "--model-provider",
398
586
  help="Model provider to use for base URLs, defaults, and model catalog.",
399
587
  ),
588
+ model_base_url: Optional[str] = typer.Option(
589
+ None,
590
+ "--model-base-url",
591
+ help=(
592
+ "Custom model base URL to inject into OPENCODE_BASE_URL, "
593
+ "CODEX_BASE_URL, MODEL_BASE_URL, and ANTHROPIC_BASE_URL."
594
+ ),
595
+ ),
596
+ websearch_apikey: Optional[str] = typer.Option(
597
+ None,
598
+ "--websearch-apikey",
599
+ help=(
600
+ "Web search API key to inject as WEB_SEARCH_API_KEY env. "
601
+ f"Mutually exclusive with {SKILL_ROLE_NAME_OPTION}. "
602
+ "Use --disable-websearch-apikey in exec to disable it per session."
603
+ ),
604
+ ),
400
605
  ) -> None:
401
- """Create an AgentKit Tool with optional TOS mount."""
606
+ """Create an AgentKit Tool with optional TOS mount.
607
+
608
+ Extra option:
609
+ - --skill-role-name ROLE_NAME: reuse the role if it exists, otherwise create it
610
+ - --skill-role-name: create a role with an auto-generated name
611
+ """
612
+ result = None
402
613
  try:
614
+ skill_role_name, skill_role_name_provided = _resolve_create_extra_args(ctx)
403
615
  if tos_mount is not None and not (tos_bucket or "").strip():
404
616
  error("--tos-mount requires --tos-bucket")
405
617
  result = create_tool(
@@ -410,7 +622,11 @@ def create_command(
410
622
  cpu=cpu,
411
623
  model_name=model_name,
412
624
  model_api_key=model_api_key,
413
- model_provider=model_provider.value,
625
+ model_provider=model_provider,
626
+ model_base_url=model_base_url,
627
+ skill_role_name=skill_role_name,
628
+ skill_role_name_provided=skill_role_name_provided,
629
+ websearch_apikey=websearch_apikey,
414
630
  )
415
631
  save_tool_result(str(result["tool_type"]), result)
416
632
  except (typer.Abort, typer.Exit):
@@ -421,3 +637,10 @@ def create_command(
421
637
  typer.echo("工具创建成功")
422
638
  typer.echo(f"工具ID:{result['tool_id']}")
423
639
  typer.echo(f"状态:{result['status']}")
640
+ if result.get("role_name"):
641
+ typer.echo(f"角色名:{result['role_name']}")
642
+ if not result.get("role_name") and not result.get("websearch_apikey_set"):
643
+ typer.echo(
644
+ "提示:未配置 WebSearch(可通过 --skill-role-name 配置 Role 或 "
645
+ "--websearch-apikey 配置 API Key 来启用)"
646
+ )