From 0e61b40ad85939f795e486feeef36af3c4edf19a Mon Sep 17 00:00:00 2001 From: Xie Zhihao Date: Fri, 31 Jul 2026 16:30:41 +0800 Subject: [PATCH 01/11] feat: add model gateway SDK Signed-off-by: Xie Zhihao --- agentkit/sdk/model_gateway/__init__.py | 101 +++++++ agentkit/sdk/model_gateway/client.py | 191 ++++++++++++ agentkit/sdk/model_gateway/types.py | 391 +++++++++++++++++++++++++ 3 files changed, 683 insertions(+) create mode 100644 agentkit/sdk/model_gateway/__init__.py create mode 100644 agentkit/sdk/model_gateway/client.py create mode 100644 agentkit/sdk/model_gateway/types.py diff --git a/agentkit/sdk/model_gateway/__init__.py b/agentkit/sdk/model_gateway/__init__.py new file mode 100644 index 00000000..501ac15f --- /dev/null +++ b/agentkit/sdk/model_gateway/__init__.py @@ -0,0 +1,101 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .client import AgentkitModelGatewayClient +from .types import ( + ApiKeysForModelGateway, + AuthzConfigForModelGateway, + ConsumerForCreateModelGateway, + ConsumersForModelGateway, + CreateModelGatewayRequest, + CreateModelGatewayConsumerRequest, + CreateModelGatewayConsumerResponse, + CreateModelGatewayProviderRequest, + CreateModelGatewayProviderResponse, + CreateModelGatewayResponse, + CredentialsForModelGateway, + DeleteModelGatewayConsumerRequest, + DeleteModelGatewayConsumerResponse, + DeleteModelGatewayProviderRequest, + DeleteModelGatewayProviderResponse, + GetModelGatewayConsumerRequest, + GetModelGatewayConsumerResponse, + GetModelGatewayProviderRequest, + GetModelGatewayProviderResponse, + ListModelGatewayConsumersRequest, + ListModelGatewayConsumersResponse, + ListModelGatewaysRequest, + ListModelGatewaysResponse, + ListModelGatewayProvidersRequest, + ListModelGatewayProvidersResponse, + ModelGatewaysForModelGateway, + ProviderAuthzConfigsForModelGateway, + ProviderModelsForModelGateway, + ProvidersItemForCreateModelGateway, + ProvidersForModelGateway, + ProviderSpecForModelGateway, + RetryPolicyForModelGateway, + TagFiltersForModelGateway, + TagsForModelGateway, + TokenRateLimitConfigForModelGateway, + TokenRateLimitRulesForModelGateway, + UpdateModelGatewayConsumerRequest, + UpdateModelGatewayConsumerResponse, + UpdateModelGatewayProviderRequest, + UpdateModelGatewayProviderResponse, +) + +__all__ = [ + "AgentkitModelGatewayClient", + "ApiKeysForModelGateway", + "AuthzConfigForModelGateway", + "ConsumerForCreateModelGateway", + "ConsumersForModelGateway", + "CreateModelGatewayRequest", + "CreateModelGatewayConsumerRequest", + "CreateModelGatewayConsumerResponse", + "CreateModelGatewayProviderRequest", + "CreateModelGatewayProviderResponse", + "CreateModelGatewayResponse", + "CredentialsForModelGateway", + "DeleteModelGatewayConsumerRequest", + "DeleteModelGatewayConsumerResponse", + "DeleteModelGatewayProviderRequest", + "DeleteModelGatewayProviderResponse", + "GetModelGatewayConsumerRequest", + "GetModelGatewayConsumerResponse", + "GetModelGatewayProviderRequest", + "GetModelGatewayProviderResponse", + "ListModelGatewayConsumersRequest", + "ListModelGatewayConsumersResponse", + "ListModelGatewaysRequest", + "ListModelGatewaysResponse", + "ListModelGatewayProvidersRequest", + "ListModelGatewayProvidersResponse", + "ModelGatewaysForModelGateway", + "ProviderAuthzConfigsForModelGateway", + "ProviderModelsForModelGateway", + "ProvidersItemForCreateModelGateway", + "ProvidersForModelGateway", + "ProviderSpecForModelGateway", + "RetryPolicyForModelGateway", + "TagFiltersForModelGateway", + "TagsForModelGateway", + "TokenRateLimitConfigForModelGateway", + "TokenRateLimitRulesForModelGateway", + "UpdateModelGatewayConsumerRequest", + "UpdateModelGatewayConsumerResponse", + "UpdateModelGatewayProviderRequest", + "UpdateModelGatewayProviderResponse", +] diff --git a/agentkit/sdk/model_gateway/client.py b/agentkit/sdk/model_gateway/client.py new file mode 100644 index 00000000..dd8c496f --- /dev/null +++ b/agentkit/sdk/model_gateway/client.py @@ -0,0 +1,191 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Auto-generated from API JSON definition +# Do not edit manually + +from __future__ import annotations + +from typing import Dict + +from agentkit.client import BaseAgentkitClient + +from .types import ( + CreateModelGatewayRequest, + CreateModelGatewayConsumerRequest, + CreateModelGatewayConsumerResponse, + CreateModelGatewayProviderRequest, + CreateModelGatewayProviderResponse, + CreateModelGatewayResponse, + DeleteModelGatewayConsumerRequest, + DeleteModelGatewayConsumerResponse, + DeleteModelGatewayProviderRequest, + DeleteModelGatewayProviderResponse, + GetModelGatewayConsumerRequest, + GetModelGatewayConsumerResponse, + GetModelGatewayProviderRequest, + GetModelGatewayProviderResponse, + ListModelGatewayConsumersRequest, + ListModelGatewayConsumersResponse, + ListModelGatewaysRequest, + ListModelGatewaysResponse, + ListModelGatewayProvidersRequest, + ListModelGatewayProvidersResponse, + UpdateModelGatewayConsumerRequest, + UpdateModelGatewayConsumerResponse, + UpdateModelGatewayProviderRequest, + UpdateModelGatewayProviderResponse, +) + + +class AgentkitModelGatewayClient(BaseAgentkitClient): + """AgentKit Model Gateway Management Service""" + + API_ACTIONS: Dict[str, str] = { + "CreateModelGateway": "CreateModelGateway", + "CreateModelGatewayConsumer": "CreateModelGatewayConsumer", + "CreateModelGatewayProvider": "CreateModelGatewayProvider", + "DeleteModelGatewayConsumer": "DeleteModelGatewayConsumer", + "DeleteModelGatewayProvider": "DeleteModelGatewayProvider", + "GetModelGatewayConsumer": "GetModelGatewayConsumer", + "GetModelGatewayProvider": "GetModelGatewayProvider", + "ListModelGatewayConsumers": "ListModelGatewayConsumers", + "ListModelGateways": "ListModelGateways", + "ListModelGatewayProviders": "ListModelGatewayProviders", + "UpdateModelGatewayConsumer": "UpdateModelGatewayConsumer", + "UpdateModelGatewayProvider": "UpdateModelGatewayProvider", + } + + def __init__( + self, + access_key: str = "", + secret_key: str = "", + region: str = "", + session_token: str = "", + ) -> None: + super().__init__( + access_key=access_key, + secret_key=secret_key, + region=region, + session_token=session_token, + service_name="model_gateway", + ) + + def create_model_gateway_consumer( + self, request: CreateModelGatewayConsumerRequest + ) -> CreateModelGatewayConsumerResponse: + return self._invoke_api( + api_action="CreateModelGatewayConsumer", + request=request, + response_type=CreateModelGatewayConsumerResponse, + ) + + def create_model_gateway( + self, request: CreateModelGatewayRequest + ) -> CreateModelGatewayResponse: + return self._invoke_api( + api_action="CreateModelGateway", + request=request, + response_type=CreateModelGatewayResponse, + ) + + def create_model_gateway_provider( + self, request: CreateModelGatewayProviderRequest + ) -> CreateModelGatewayProviderResponse: + return self._invoke_api( + api_action="CreateModelGatewayProvider", + request=request, + response_type=CreateModelGatewayProviderResponse, + ) + + def delete_model_gateway_consumer( + self, request: DeleteModelGatewayConsumerRequest + ) -> DeleteModelGatewayConsumerResponse: + return self._invoke_api( + api_action="DeleteModelGatewayConsumer", + request=request, + response_type=DeleteModelGatewayConsumerResponse, + ) + + def delete_model_gateway_provider( + self, request: DeleteModelGatewayProviderRequest + ) -> DeleteModelGatewayProviderResponse: + return self._invoke_api( + api_action="DeleteModelGatewayProvider", + request=request, + response_type=DeleteModelGatewayProviderResponse, + ) + + def get_model_gateway_consumer( + self, request: GetModelGatewayConsumerRequest + ) -> GetModelGatewayConsumerResponse: + return self._invoke_api( + api_action="GetModelGatewayConsumer", + request=request, + response_type=GetModelGatewayConsumerResponse, + ) + + def get_model_gateway_provider( + self, request: GetModelGatewayProviderRequest + ) -> GetModelGatewayProviderResponse: + return self._invoke_api( + api_action="GetModelGatewayProvider", + request=request, + response_type=GetModelGatewayProviderResponse, + ) + + def list_model_gateway_consumers( + self, request: ListModelGatewayConsumersRequest + ) -> ListModelGatewayConsumersResponse: + return self._invoke_api( + api_action="ListModelGatewayConsumers", + request=request, + response_type=ListModelGatewayConsumersResponse, + ) + + def list_model_gateways( + self, request: ListModelGatewaysRequest + ) -> ListModelGatewaysResponse: + return self._invoke_api( + api_action="ListModelGateways", + request=request, + response_type=ListModelGatewaysResponse, + ) + + def list_model_gateway_providers( + self, request: ListModelGatewayProvidersRequest + ) -> ListModelGatewayProvidersResponse: + return self._invoke_api( + api_action="ListModelGatewayProviders", + request=request, + response_type=ListModelGatewayProvidersResponse, + ) + + def update_model_gateway_consumer( + self, request: UpdateModelGatewayConsumerRequest + ) -> UpdateModelGatewayConsumerResponse: + return self._invoke_api( + api_action="UpdateModelGatewayConsumer", + request=request, + response_type=UpdateModelGatewayConsumerResponse, + ) + + def update_model_gateway_provider( + self, request: UpdateModelGatewayProviderRequest + ) -> UpdateModelGatewayProviderResponse: + return self._invoke_api( + api_action="UpdateModelGatewayProvider", + request=request, + response_type=UpdateModelGatewayProviderResponse, + ) diff --git a/agentkit/sdk/model_gateway/types.py b/agentkit/sdk/model_gateway/types.py new file mode 100644 index 00000000..5d93c321 --- /dev/null +++ b/agentkit/sdk/model_gateway/types.py @@ -0,0 +1,391 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Auto-generated from API JSON definition +# Do not edit manually + +from __future__ import annotations + +from typing import Optional + +from pydantic import AliasChoices, BaseModel, Field + + +class ModelGatewayBaseModel(BaseModel): + """AgentKit auto-generated base model""" + + model_config = {"populate_by_name": True, "arbitrary_types_allowed": True} + + +# Data Types +class ApiKeysForModelGateway(ModelGatewayBaseModel): + name: Optional[str] = Field(default=None, alias="Name") + value: Optional[str] = Field(default=None, alias="Value") + + +class AuthzConfigForModelGateway(ModelGatewayBaseModel): + allow_all: Optional[bool] = Field(default=None, alias="AllowAll") + provider_authz_configs: Optional[list[ProviderAuthzConfigsForModelGateway]] = Field( + default=None, alias="ProviderAuthzConfigs" + ) + + +class ConsumerForCreateModelGateway(ModelGatewayBaseModel): + consumer_name: str = Field(..., alias="ConsumerName") + + +class ConsumersForModelGateway(ModelGatewayBaseModel): + consumer_id: Optional[str] = Field(default=None, alias="ConsumerId") + consumer_name: Optional[str] = Field(default=None, alias="ConsumerName") + model_gateway_id: Optional[str] = Field(default=None, alias="ModelGatewayId") + api_keys: Optional[list[str]] = Field(default=None, alias="ApiKeys") + authz_config: Optional[AuthzConfigForModelGateway] = Field( + default=None, alias="AuthzConfig" + ) + token_rate_limit_config: Optional[TokenRateLimitConfigForModelGateway] = Field( + default=None, alias="TokenRateLimitConfig" + ) + comments: Optional[str] = Field(default=None, alias="Comments") + tags: Optional[list[TagsForModelGateway]] = Field(default=None, alias="Tags") + created_at: Optional[str] = Field(default=None, alias="CreatedAt") + updated_at: Optional[str] = Field(default=None, alias="UpdatedAt") + + +class CredentialsForModelGateway(ModelGatewayBaseModel): + type: Optional[str] = Field(default=None, alias="Type") + api_keys: Optional[list[ApiKeysForModelGateway]] = Field( + default=None, alias="ApiKeys" + ) + + +class ModelGatewaysForModelGateway(ModelGatewayBaseModel): + model_gateway_id: Optional[str] = Field( + default=None, + validation_alias=AliasChoices("ModelGatewayId", "ModelGatewayID"), + serialization_alias="ModelGatewayId", + ) + retry_policy: Optional[RetryPolicyForModelGateway] = Field( + default=None, alias="RetryPolicy" + ) + project_name: Optional[str] = Field(default=None, alias="ProjectName") + comments: Optional[str] = Field(default=None, alias="Comments") + tags: Optional[list[TagsForModelGateway]] = Field(default=None, alias="Tags") + status: Optional[str] = Field(default=None, alias="Status") + message: Optional[str] = Field(default=None, alias="Message") + created_at: Optional[str] = Field(default=None, alias="CreatedAt") + updated_at: Optional[str] = Field(default=None, alias="UpdatedAt") + + +class ProviderAuthzConfigsForModelGateway(ModelGatewayBaseModel): + provider_id: Optional[str] = Field(default=None, alias="ProviderId") + allow_all: Optional[bool] = Field(default=None, alias="AllowAll") + allowed_provider_model_ids: Optional[list[str]] = Field( + default=None, alias="AllowedProviderModelIds" + ) + + +class ProviderModelsForModelGateway(ModelGatewayBaseModel): + model_name: Optional[str] = Field(default=None, alias="ModelName") + + +class ProviderSpecForModelGateway(ModelGatewayBaseModel): + base_url: Optional[str] = Field(default=None, alias="BaseURL") + + +class ProvidersForModelGateway(ModelGatewayBaseModel): + provider_id: Optional[str] = Field( + default=None, + validation_alias=AliasChoices("ProviderId", "ProviderID"), + serialization_alias="ProviderId", + ) + provider_name: Optional[str] = Field(default=None, alias="ProviderName") + model_gateway_id: Optional[str] = Field( + default=None, + validation_alias=AliasChoices("ModelGatewayId", "ModelGatewayID"), + serialization_alias="ModelGatewayId", + ) + base_url: Optional[str] = Field(default=None, alias="BaseUrl") + provider_type: Optional[str] = Field(default=None, alias="ProviderType") + protocols: Optional[list[str]] = Field(default=None, alias="Protocols") + provider_source: Optional[str] = Field(default=None, alias="ProviderSource") + provider_spec: Optional[ProviderSpecForModelGateway] = Field( + default=None, alias="ProviderSpec" + ) + credentials: Optional[CredentialsForModelGateway] = Field( + default=None, alias="Credentials" + ) + provider_models: Optional[list[ProviderModelsForModelGateway]] = Field( + default=None, alias="ProviderModels" + ) + fallback_provider_model_names: Optional[list[str]] = Field( + default=None, alias="FallbackProviderModelNames" + ) + comments: Optional[str] = Field(default=None, alias="Comments") + tags: Optional[list[TagsForModelGateway]] = Field(default=None, alias="Tags") + created_at: Optional[str] = Field(default=None, alias="CreatedAt") + updated_at: Optional[str] = Field(default=None, alias="UpdatedAt") + + +class RetryPolicyForModelGateway(ModelGatewayBaseModel): + enable: Optional[bool] = Field(default=None, alias="Enable") + max_retries: Optional[int] = Field(default=None, alias="MaxRetries") + + +class TagFiltersForModelGateway(ModelGatewayBaseModel): + key: Optional[str] = Field(default=None, alias="Key") + values: Optional[list[str]] = Field(default=None, alias="Values") + + +class TagsForModelGateway(ModelGatewayBaseModel): + key: str = Field(..., alias="Key") + value: Optional[str] = Field(default=None, alias="Value") + + +class TokenRateLimitConfigForModelGateway(ModelGatewayBaseModel): + enable: Optional[bool] = Field(default=None, alias="Enable") + rules: Optional[list[TokenRateLimitRulesForModelGateway]] = Field( + default=None, alias="Rules" + ) + + +class TokenRateLimitRulesForModelGateway(ModelGatewayBaseModel): + enable: Optional[bool] = Field(default=None, alias="Enable") + time_window: Optional[int] = Field(default=None, alias="TimeWindow") + value: Optional[int] = Field(default=None, alias="Value") + + +# CreateModelGateway - Request +class ProvidersItemForCreateModelGateway(ModelGatewayBaseModel): + provider_type: Optional[str] = Field(default=None, alias="ProviderType") + provider_name: str = Field(..., alias="ProviderName") + protocols: Optional[list[str]] = Field(default=None, alias="Protocols") + provider_source: Optional[str] = Field(default=None, alias="ProviderSource") + provider_spec: Optional[ProviderSpecForModelGateway] = Field( + default=None, alias="ProviderSpec" + ) + credentials: Optional[CredentialsForModelGateway] = Field( + default=None, alias="Credentials" + ) + provider_models: Optional[list[ProviderModelsForModelGateway]] = Field( + default=None, alias="ProviderModels" + ) + fallback_provider_model_names: Optional[list[str]] = Field( + default=None, alias="FallbackProviderModelNames" + ) + + +class CreateModelGatewayRequest(ModelGatewayBaseModel): + type: str = Field(..., alias="Type") + apig_gateway_id: Optional[str] = Field(default=None, alias="ApigGatewayId") + providers: Optional[list[ProvidersItemForCreateModelGateway]] = Field( + default=None, alias="Providers" + ) + consumer: ConsumerForCreateModelGateway = Field(..., alias="Consumer") + retry_policy: Optional[RetryPolicyForModelGateway] = Field( + default=None, alias="RetryPolicy" + ) + comments: Optional[str] = Field(default=None, alias="Comments") + tags: Optional[list[TagsForModelGateway]] = Field(default=None, alias="Tags") + + +# CreateModelGateway - Response +class CreateModelGatewayResponse(ModelGatewayBaseModel): + model_gateway_id: Optional[str] = Field(default=None, alias="ModelGatewayId") + + +# ListModelGateways - Request +class ListModelGatewaysRequest(ModelGatewayBaseModel): + status: Optional[str] = Field(default=None, alias="Status") + page_number: Optional[int] = Field(default=None, alias="PageNumber") + page_size: Optional[int] = Field(default=None, alias="PageSize") + tag_filters: Optional[list[TagFiltersForModelGateway]] = Field( + default=None, alias="TagFilters" + ) + + +# ListModelGateways - Response +class ListModelGatewaysResponse(ModelGatewayBaseModel): + total: Optional[int] = Field(default=None, alias="Total") + model_gateways: Optional[list[ModelGatewaysForModelGateway]] = Field( + default=None, alias="ModelGateways" + ) + + +# CreateModelGatewayProvider - Request +class CreateModelGatewayProviderRequest(ModelGatewayBaseModel): + model_gateway_id: str = Field(..., alias="ModelGatewayId") + provider_type: Optional[str] = Field(default=None, alias="ProviderType") + provider_name: str = Field(..., alias="ProviderName") + protocols: Optional[list[str]] = Field(default=None, alias="Protocols") + provider_source: Optional[str] = Field(default=None, alias="ProviderSource") + provider_spec: Optional[ProviderSpecForModelGateway] = Field( + default=None, alias="ProviderSpec" + ) + credentials: Optional[CredentialsForModelGateway] = Field( + default=None, alias="Credentials" + ) + provider_models: Optional[list[ProviderModelsForModelGateway]] = Field( + default=None, alias="ProviderModels" + ) + fallback_provider_model_names: Optional[list[str]] = Field( + default=None, alias="FallbackProviderModelNames" + ) + comments: Optional[str] = Field(default=None, alias="Comments") + tags: Optional[list[TagsForModelGateway]] = Field(default=None, alias="Tags") + + +# CreateModelGatewayProvider - Response +class CreateModelGatewayProviderResponse(ModelGatewayBaseModel): + provider_id: Optional[str] = Field(default=None, alias="ProviderId") + + +# UpdateModelGatewayProvider - Request +class UpdateModelGatewayProviderRequest(ModelGatewayBaseModel): + provider_id: str = Field(..., alias="ProviderId") + provider_name: Optional[str] = Field(default=None, alias="ProviderName") + protocols: Optional[list[str]] = Field(default=None, alias="Protocols") + provider_source: Optional[str] = Field(default=None, alias="ProviderSource") + provider_spec: Optional[ProviderSpecForModelGateway] = Field( + default=None, alias="ProviderSpec" + ) + credentials: Optional[CredentialsForModelGateway] = Field( + default=None, alias="Credentials" + ) + provider_models: Optional[list[ProviderModelsForModelGateway]] = Field( + default=None, alias="ProviderModels" + ) + fallback_provider_model_names: Optional[list[str]] = Field( + default=None, alias="FallbackProviderModelNames" + ) + comments: Optional[str] = Field(default=None, alias="Comments") + + +# UpdateModelGatewayProvider - Response +class UpdateModelGatewayProviderResponse(ModelGatewayBaseModel): + provider_id: Optional[str] = Field(default=None, alias="ProviderId") + + +# DeleteModelGatewayProvider - Request +class DeleteModelGatewayProviderRequest(ModelGatewayBaseModel): + provider_id: str = Field(..., alias="ProviderId") + + +# DeleteModelGatewayProvider - Response +class DeleteModelGatewayProviderResponse(ModelGatewayBaseModel): + pass + + +# GetModelGatewayProvider - Request +class GetModelGatewayProviderRequest(ModelGatewayBaseModel): + provider_id: str = Field(..., alias="ProviderId") + + +# GetModelGatewayProvider - Response +class GetModelGatewayProviderResponse(ModelGatewayBaseModel): + provider: Optional[ProvidersForModelGateway] = Field(default=None, alias="Provider") + + +# ListModelGatewayProviders - Request +class ListModelGatewayProvidersRequest(ModelGatewayBaseModel): + provider_name: Optional[str] = Field(default=None, alias="ProviderName") + model_gateway_id: str = Field(..., alias="ModelGatewayId") + page_number: Optional[int] = Field(default=None, alias="PageNumber") + page_size: Optional[int] = Field(default=None, alias="PageSize") + tag_filters: Optional[list[TagFiltersForModelGateway]] = Field( + default=None, alias="TagFilters" + ) + + +# ListModelGatewayProviders - Response +class ListModelGatewayProvidersResponse(ModelGatewayBaseModel): + total: Optional[int] = Field(default=None, alias="Total") + providers: Optional[list[ProvidersForModelGateway]] = Field( + default=None, alias="Providers" + ) + + +# CreateModelGatewayConsumer - Request +class CreateModelGatewayConsumerRequest(ModelGatewayBaseModel): + consumer_name: str = Field(..., alias="ConsumerName") + model_gateway_id: str = Field(..., alias="ModelGatewayId") + authz_config: Optional[AuthzConfigForModelGateway] = Field( + default=None, alias="AuthzConfig" + ) + token_rate_limit_config: Optional[TokenRateLimitConfigForModelGateway] = Field( + default=None, alias="TokenRateLimitConfig" + ) + comments: Optional[str] = Field(default=None, alias="Comments") + tags: Optional[list[TagsForModelGateway]] = Field(default=None, alias="Tags") + + +# CreateModelGatewayConsumer - Response +class CreateModelGatewayConsumerResponse(ModelGatewayBaseModel): + consumer_id: Optional[str] = Field(default=None, alias="ConsumerId") + + +# UpdateModelGatewayConsumer - Request +class UpdateModelGatewayConsumerRequest(ModelGatewayBaseModel): + consumer_id: str = Field(..., alias="ConsumerId") + consumer_name: Optional[str] = Field(default=None, alias="ConsumerName") + authz_config: Optional[AuthzConfigForModelGateway] = Field( + default=None, alias="AuthzConfig" + ) + token_rate_limit_config: Optional[TokenRateLimitConfigForModelGateway] = Field( + default=None, alias="TokenRateLimitConfig" + ) + comments: Optional[str] = Field(default=None, alias="Comments") + + +# UpdateModelGatewayConsumer - Response +class UpdateModelGatewayConsumerResponse(ModelGatewayBaseModel): + consumer_id: Optional[str] = Field(default=None, alias="ConsumerId") + + +# DeleteModelGatewayConsumer - Request +class DeleteModelGatewayConsumerRequest(ModelGatewayBaseModel): + consumer_id: str = Field(..., alias="ConsumerId") + + +# DeleteModelGatewayConsumer - Response +class DeleteModelGatewayConsumerResponse(ModelGatewayBaseModel): + pass + + +# GetModelGatewayConsumer - Request +class GetModelGatewayConsumerRequest(ModelGatewayBaseModel): + consumer_id: str = Field(..., alias="ConsumerId") + + +# GetModelGatewayConsumer - Response +class GetModelGatewayConsumerResponse(ModelGatewayBaseModel): + consumer: Optional[ConsumersForModelGateway] = Field(default=None, alias="Consumer") + + +# ListModelGatewayConsumers - Request +class ListModelGatewayConsumersRequest(ModelGatewayBaseModel): + consumer_name: Optional[str] = Field(default=None, alias="ConsumerName") + model_gateway_id: str = Field(..., alias="ModelGatewayId") + page_number: Optional[int] = Field(default=None, alias="PageNumber") + page_size: Optional[int] = Field(default=None, alias="PageSize") + tag_filters: Optional[list[TagFiltersForModelGateway]] = Field( + default=None, alias="TagFilters" + ) + + +# ListModelGatewayConsumers - Response +class ListModelGatewayConsumersResponse(ModelGatewayBaseModel): + total: Optional[int] = Field(default=None, alias="Total") + consumers: Optional[list[ConsumersForModelGateway]] = Field( + default=None, alias="Consumers" + ) From 61334ec1b8038e5d6b5a467f13b8ee723a2dde0c Mon Sep 17 00:00:00 2001 From: Xie Zhihao Date: Mon, 3 Aug 2026 11:36:09 +0800 Subject: [PATCH 02/11] feat: add model gateway CLI Signed-off-by: Xie Zhihao --- agentkit/toolkit/cli/cli.py | 2 + agentkit/toolkit/cli/cli_model_gateway.py | 776 ++++++++++++++++++ .../1.model_gateway_quickstart.md | 136 +++ 3 files changed, 914 insertions(+) create mode 100644 agentkit/toolkit/cli/cli_model_gateway.py create mode 100644 docs/content/9.model-gateway/1.model_gateway_quickstart.md diff --git a/agentkit/toolkit/cli/cli.py b/agentkit/toolkit/cli/cli.py index 1d830863..93c7df15 100644 --- a/agentkit/toolkit/cli/cli.py +++ b/agentkit/toolkit/cli/cli.py @@ -33,6 +33,7 @@ from agentkit.toolkit.cli.cli_destroy import destroy_command from agentkit.toolkit.cli.cli_memory import memory_app from agentkit.toolkit.cli.cli_knowledge import knowledge_app +from agentkit.toolkit.cli.cli_model_gateway import model_gateway_app from agentkit.toolkit.cli.cli_tools import tools_app from agentkit.toolkit.cli.cli_runtime import runtime_app from agentkit.toolkit.cli.cli_skills import skills_app @@ -123,6 +124,7 @@ def main( app.add_typer(auth_app, name="auth") app.add_typer(memory_app, name="memory") app.add_typer(knowledge_app, name="knowledge") +app.add_typer(model_gateway_app, name="model-gateway") app.add_typer(tools_app, name="tools") app.add_typer(runtime_app, name="runtime") app.add_typer(skills_app, name="skills") diff --git a/agentkit/toolkit/cli/cli_model_gateway.py b/agentkit/toolkit/cli/cli_model_gateway.py new file mode 100644 index 00000000..53bd8cb6 --- /dev/null +++ b/agentkit/toolkit/cli/cli_model_gateway.py @@ -0,0 +1,776 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""AgentKit CLI - Model Gateway commands.""" + +from __future__ import annotations + +import json +import time +from typing import List, Optional + +import typer +from rich.console import Console +from rich.panel import Panel +from rich.table import Table + +from agentkit.sdk.model_gateway.client import AgentkitModelGatewayClient +from agentkit.sdk.model_gateway import types as mgw + +console = Console() + +MODEL_GATEWAY_RUNNING_STATUS = "Running" +MODEL_GATEWAY_FAILED_STATUSES = {"CreatedFailed", "UpdatedFailed", "DeletedFailed"} +MODEL_GATEWAY_ERROR_STATUSES = MODEL_GATEWAY_FAILED_STATUSES | {"Error"} + +model_gateway_app = typer.Typer( + name="model-gateway", + help="Manage AgentKit Model Gateway", + add_completion=False, +) +provider_app = typer.Typer( + name="provider", + help="Manage model gateway providers", + add_completion=False, +) +consumer_app = typer.Typer( + name="consumer", + help="Manage model gateway consumers", + add_completion=False, +) + + +def _print_api_error(action: str, exc: Exception) -> None: + msg = str(exc) + server_message = None + code = None + try: + start = msg.find("{") + end = msg.rfind("}") + if start != -1 and end > start: + payload = json.loads(msg[start : end + 1]) + err = payload.get("ResponseMetadata", {}).get("Error", {}) + code = err.get("Code") + server_message = err.get("Message") + except Exception: + pass + + lines = [] + if code: + lines.append(f"Code: [yellow]{code}[/yellow]") + lines.append(f"Message: [red]{server_message or msg}[/red]") + console.print( + Panel.fit( + "\n".join(lines), + title=f"{action} Error", + border_style="red", + ) + ) + + +def _client(region: Optional[str]) -> AgentkitModelGatewayClient: + return AgentkitModelGatewayClient(region=(region or "").strip()) + + +def _require_value(option_name: str, value: str) -> str: + normalized = value.strip() + if not normalized: + raise typer.BadParameter(f"{option_name} cannot be empty") + return normalized + + +def _require_values(option_name: str, values: List[str]) -> List[str]: + if not values: + raise typer.BadParameter(f"{option_name} requires at least one value") + return [_require_value(option_name, value) for value in values] + + +def _require_single_protocol(values: Optional[List[str]]) -> List[str]: + protocols = _require_values("--protocol", values or []) + if len(protocols) != 1: + raise typer.BadParameter("--protocol currently requires exactly one value") + return protocols + + +def _require_allow_models(values: Optional[List[str]]) -> Optional[List[str]]: + if not values: + return None + + normalized_values = _require_values("--allow-models", values) + for item in normalized_values: + if "/" not in item: + _require_value("--allow-models provider name", item) + continue + provider_name, model_name = item.split("/", 1) + _require_value("--allow-models provider name", provider_name) + _require_value("--allow-models model name", model_name) + return normalized_values + + +def _get_model_gateway( + client: AgentkitModelGatewayClient, + model_gateway_id: str, +) -> mgw.ModelGatewaysForModelGateway: + resp = client.list_model_gateways( + mgw.ListModelGatewaysRequest(page_number=1, page_size=100) + ) + for gateway in resp.model_gateways or []: + if gateway.model_gateway_id == model_gateway_id: + return gateway + raise RuntimeError(f"Model gateway not found: {model_gateway_id}") + + +def _wait_model_gateway_running( + client: AgentkitModelGatewayClient, + model_gateway_id: str, + action: str, + timeout_seconds: int = 120, + poll_interval_seconds: int = 2, +) -> mgw.ModelGatewaysForModelGateway: + time.sleep(1) + deadline = time.monotonic() + timeout_seconds + last_status = None + with console.status( + "[cyan]Waiting for ModelGateway status to become Running...[/cyan]", + spinner="dots", + ) as wait_status: + while True: + gateway = _get_model_gateway(client, model_gateway_id) + status = gateway.status or "" + if status != last_status: + last_status = status + wait_status.update( + f"[cyan]ModelGateway status: {status or 'Unknown'}[/cyan]" + ) + if status == MODEL_GATEWAY_RUNNING_STATUS: + return gateway + if status in MODEL_GATEWAY_ERROR_STATUSES: + message = f"ModelGateway status is {status}" + if gateway.message: + message = f"{message}: {gateway.message}" + raise RuntimeError(message) + if time.monotonic() >= deadline: + raise RuntimeError( + f"Timed out waiting for ModelGateway to become Running " + f"after {action}. Last status: {status or 'Unknown'}" + ) + time.sleep(poll_interval_seconds) + + +def _resolve_model_gateway_id( + client: AgentkitModelGatewayClient, +) -> str: + resp = client.list_model_gateways( + mgw.ListModelGatewaysRequest(page_number=1, page_size=100) + ) + gateways = resp.model_gateways or [] + if gateways and gateways[0].model_gateway_id: + return gateways[0].model_gateway_id + raise typer.BadParameter( + "No model gateway found. Run 'agentkit model-gateway activate' first." + ) + + +def _normalize_protocol(protocol: str) -> str: + value = protocol.strip() + aliases = { + "openai": "OpenAICompatible", + "openai-compatible": "OpenAICompatible", + "OpenAICompatible": "OpenAICompatible", + "anthropic": "AnthropicCompatible", + "anthropic-compatible": "AnthropicCompatible", + "AnthropicCompatible": "AnthropicCompatible", + } + if value not in aliases: + raise typer.BadParameter( + "--protocol must be one of: openai, anthropic, " + "OpenAICompatible, AnthropicCompatible" + ) + return aliases[value] + + +def _build_credentials(api_keys: List[str]) -> mgw.CredentialsForModelGateway: + return mgw.CredentialsForModelGateway( + type="APIKey", + api_keys=[ + mgw.ApiKeysForModelGateway(name=f"key_{idx}", value=value) + for idx, value in enumerate(api_keys, start=1) + ], + ) + + +def _provider_models(models: List[str]) -> List[mgw.ProviderModelsForModelGateway]: + return [mgw.ProviderModelsForModelGateway(model_name=model) for model in models] + + +def _provider_table(providers: List[mgw.ProvidersForModelGateway]) -> Table: + table = Table(title="Model Gateway Providers") + table.add_column("ProviderId", style="cyan") + table.add_column("Name", style="white") + table.add_column("BaseUrl", style="blue") + table.add_column("Models", style="green") + for item in providers: + models = ", ".join(m.model_name or "" for m in item.provider_models or []) + table.add_row( + item.provider_id or "", + item.provider_name or "", + item.base_url + or (item.provider_spec.base_url if item.provider_spec else ""), + models, + ) + return table + + +def _consumer_table(consumers: List[mgw.ConsumersForModelGateway]) -> Table: + table = Table(title="Model Gateway Consumers") + table.add_column("ConsumerId", style="cyan") + table.add_column("Name", style="white") + table.add_column("ApiKeys", style="yellow") + table.add_column("Authz", style="green") + table.add_column("TPM", style="magenta") + table.add_column("TPD", style="magenta") + for item in consumers: + authz = "" + if item.authz_config: + if item.authz_config.allow_all: + authz = "All" + elif item.authz_config.provider_authz_configs: + parts = [] + for config in item.authz_config.provider_authz_configs: + provider_id = config.provider_id or "" + if config.allow_all: + parts.append(f"{provider_id}:*") + else: + models = ", ".join(config.allowed_provider_model_ids or []) + parts.append(f"{provider_id}:{models}") + authz = "; ".join(parts) + + tpm = "" + tpd = "" + if item.token_rate_limit_config: + for rule in item.token_rate_limit_config.rules or []: + if rule.time_window == 60: + tpm = str(rule.value or "") + elif rule.time_window == 86400: + tpd = str(rule.value or "") + table.add_row( + item.consumer_id or "", + item.consumer_name or "", + ", ".join(item.api_keys or []), + authz, + tpm, + tpd, + ) + return table + + +def _find_provider_by_name( + client: AgentkitModelGatewayClient, + model_gateway_id: str, + name: str, +) -> mgw.ProvidersForModelGateway: + resp = client.list_model_gateway_providers( + mgw.ListModelGatewayProvidersRequest( + model_gateway_id=model_gateway_id, + provider_name=name, + page_number=1, + page_size=100, + ) + ) + matches = [p for p in resp.providers or [] if p.provider_name == name] + if len(matches) != 1 or not matches[0].provider_id: + raise typer.BadParameter(f"Provider not found or ambiguous: {name}") + return matches[0] + + +def _find_consumer_by_name( + client: AgentkitModelGatewayClient, + model_gateway_id: str, + name: str, +) -> mgw.ConsumersForModelGateway: + resp = client.list_model_gateway_consumers( + mgw.ListModelGatewayConsumersRequest( + model_gateway_id=model_gateway_id, + consumer_name=name, + page_number=1, + page_size=100, + ) + ) + matches = [c for c in resp.consumers or [] if c.consumer_name == name] + if len(matches) != 1 or not matches[0].consumer_id: + raise typer.BadParameter(f"Consumer not found or ambiguous: {name}") + return matches[0] + + +def _build_token_rate_limit_config( + tpm: Optional[int], + tpd: Optional[int], +) -> Optional[mgw.TokenRateLimitConfigForModelGateway]: + rules = [] + if tpm is not None: + rules.append(mgw.TokenRateLimitRulesForModelGateway(time_window=60, value=tpm)) + if tpd is not None: + rules.append( + mgw.TokenRateLimitRulesForModelGateway(time_window=86400, value=tpd) + ) + if not rules: + return None + return mgw.TokenRateLimitConfigForModelGateway(enable=True, rules=rules) + + +def _build_authz_config( + client: AgentkitModelGatewayClient, + model_gateway_id: str, + allow_models: Optional[List[str]], +) -> Optional[mgw.AuthzConfigForModelGateway]: + if not allow_models: + return None + + grouped: dict[str, list[str]] = {} + allow_all_provider_ids: set[str] = set() + for item in allow_models: + if "/" not in item: + provider_name = _require_value("--allow-models provider name", item) + provider = _find_provider_by_name(client, model_gateway_id, provider_name) + allow_all_provider_ids.add(provider.provider_id or "") + grouped.pop(provider.provider_id or "", None) + continue + provider_name, model_name = item.split("/", 1) + provider_name = _require_value("--allow-models provider name", provider_name) + model_name = _require_value("--allow-models model name", model_name) + provider = _find_provider_by_name(client, model_gateway_id, provider_name) + provider_id = provider.provider_id or "" + if provider_id not in allow_all_provider_ids: + grouped.setdefault(provider_id, []).append(model_name) + + provider_authz_configs = [ + mgw.ProviderAuthzConfigsForModelGateway( + provider_id=provider_id, + allow_all=True, + ) + for provider_id in allow_all_provider_ids + ] + provider_authz_configs.extend( + mgw.ProviderAuthzConfigsForModelGateway( + provider_id=provider_id, + allow_all=False, + allowed_provider_model_ids=models, + ) + for provider_id, models in grouped.items() + ) + + return mgw.AuthzConfigForModelGateway( + allow_all=False, + provider_authz_configs=provider_authz_configs, + ) + + +@model_gateway_app.command("activate") +def activate_command( + apig_gateway_id: Optional[str] = typer.Option( + None, "--apig-gateway-id", help="API Gateway instance ID" + ), + region: Optional[str] = typer.Option(None, "--region", help="Region override"), +): + """Create a model gateway with an initial consumer.""" + if apig_gateway_id is not None: + apig_gateway_id = _require_value("--apig-gateway-id", apig_gateway_id) + try: + client = _client(region) + existing = client.list_model_gateways( + mgw.ListModelGatewaysRequest(page_number=1, page_size=100) + ) + gateways = existing.model_gateways or [] + if gateways and gateways[0].model_gateway_id: + gateway = gateways[0] + status = gateway.status or "Unknown" + title = "ModelGateway Already Exists" + border_style = "green" + state = "[green]Already exists[/green]" + if status != MODEL_GATEWAY_RUNNING_STATUS: + border_style = ( + "red" if status in MODEL_GATEWAY_ERROR_STATUSES else "yellow" + ) + state = ( + "[red]Already exists with error status[/red]" + if status in MODEL_GATEWAY_ERROR_STATUSES + else "[yellow]Already exists but not Running[/yellow]" + ) + lines = [ + state, + f"ModelGatewayId: {gateway.model_gateway_id}", + f"Status: {status}", + ] + if gateway.message: + lines.append(f"Message: {gateway.message}") + console.print( + Panel.fit( + "\n".join(lines), + title=title, + border_style=border_style, + ) + ) + if status in MODEL_GATEWAY_ERROR_STATUSES: + raise typer.Exit(1) + return + + resp = client.create_model_gateway( + mgw.CreateModelGatewayRequest( + type="Standard" if apig_gateway_id else "Shared", + apig_gateway_id=apig_gateway_id, + consumer=mgw.ConsumerForCreateModelGateway(consumer_name="default"), + ) + ) + if resp.model_gateway_id: + _wait_model_gateway_running( + client, + resp.model_gateway_id, + "CreateModelGateway", + ) + console.print( + Panel.fit( + "[green]Activated[/green]\n" f"ModelGatewayId: {resp.model_gateway_id}", + title="CreateModelGateway", + border_style="green", + ) + ) + except typer.Exit: + raise + except Exception as e: + _print_api_error("CreateModelGateway", e) + raise typer.Exit(1) + + +@provider_app.command("add") +def provider_add_command( + name: str = typer.Option(..., "--name", help="Provider name"), + base_url: str = typer.Option(..., "--base-url", help="Provider base URL"), + api_keys: List[str] = typer.Option(..., "--api-keys", help="API key, repeatable"), + models: List[str] = typer.Option(..., "--models", help="Model name, repeatable"), + protocol: List[str] = typer.Option( + ["openai"], "--protocol", help="openai or anthropic, exactly once" + ), + region: Optional[str] = typer.Option(None, "--region", help="Region override"), +): + """Add a provider.""" + name = _require_value("--name", name) + base_url = _require_value("--base-url", base_url) + api_keys = _require_values("--api-keys", api_keys) + models = _require_values("--models", models) + protocol = _require_single_protocol(protocol) + try: + client = _client(region) + gateway_id = _resolve_model_gateway_id(client) + resp = client.create_model_gateway_provider( + mgw.CreateModelGatewayProviderRequest( + model_gateway_id=gateway_id, + provider_name=name, + protocols=[_normalize_protocol(p) for p in protocol], + provider_source="Domain", + provider_spec=mgw.ProviderSpecForModelGateway(base_url=base_url), + credentials=_build_credentials(api_keys), + provider_models=_provider_models(models), + ) + ) + _wait_model_gateway_running(client, gateway_id, "CreateModelGatewayProvider") + console.print( + Panel.fit( + f"[green]Added[/green]\nProviderId: {resp.provider_id}", + title="CreateModelGatewayProvider", + border_style="green", + ) + ) + except Exception as e: + _print_api_error("CreateModelGatewayProvider", e) + raise typer.Exit(1) + + +@provider_app.command("list") +def provider_list_command( + region: Optional[str] = typer.Option(None, "--region", help="Region override"), +): + """List providers.""" + try: + client = _client(region) + gateway_id = _resolve_model_gateway_id(client) + resp = client.list_model_gateway_providers( + mgw.ListModelGatewayProvidersRequest( + model_gateway_id=gateway_id, + page_number=1, + page_size=100, + ) + ) + console.print(_provider_table(resp.providers or [])) + except Exception as e: + _print_api_error("ListModelGatewayProviders", e) + raise typer.Exit(1) + + +@provider_app.command("show") +def provider_show_command( + name: str = typer.Option(..., "--name", help="Provider name"), + region: Optional[str] = typer.Option(None, "--region", help="Region override"), +): + """Show a provider by name.""" + name = _require_value("--name", name) + try: + client = _client(region) + gateway_id = _resolve_model_gateway_id(client) + provider = _find_provider_by_name(client, gateway_id, name) + resp = client.get_model_gateway_provider( + mgw.GetModelGatewayProviderRequest(provider_id=provider.provider_id) + ) + console.print(_provider_table([resp.provider] if resp.provider else [])) + except Exception as e: + _print_api_error("GetModelGatewayProvider", e) + raise typer.Exit(1) + + +@provider_app.command("update") +def provider_update_command( + name: str = typer.Option(..., "--name", help="Provider name"), + base_url: Optional[str] = typer.Option( + None, "--base-url", help="Provider base URL" + ), + api_keys: Optional[List[str]] = typer.Option( + None, "--api-keys", help="API key, repeatable" + ), + models: Optional[List[str]] = typer.Option( + None, "--models", help="Model name, repeatable" + ), + protocol: Optional[List[str]] = typer.Option( + None, "--protocol", help="openai or anthropic, repeatable" + ), + region: Optional[str] = typer.Option(None, "--region", help="Region override"), +): + """Update a provider by name.""" + name = _require_value("--name", name) + if base_url is not None: + base_url = _require_value("--base-url", base_url) + if api_keys is not None: + api_keys = _require_values("--api-keys", api_keys) + if models is not None: + models = _require_values("--models", models) + if protocol: + protocol = _require_single_protocol(protocol) + try: + client = _client(region) + gateway_id = _resolve_model_gateway_id(client) + provider = _find_provider_by_name(client, gateway_id, name) + request_kwargs = {} + if protocol: + request_kwargs["protocols"] = [_normalize_protocol(p) for p in protocol] + if base_url is not None: + request_kwargs["provider_source"] = "Domain" + request_kwargs["provider_spec"] = mgw.ProviderSpecForModelGateway( + base_url=base_url + ) + if api_keys is not None: + request_kwargs["credentials"] = _build_credentials(api_keys) + if models is not None: + request_kwargs["provider_models"] = _provider_models(models) + resp = client.update_model_gateway_provider( + mgw.UpdateModelGatewayProviderRequest( + provider_id=provider.provider_id, + **request_kwargs, + ) + ) + _wait_model_gateway_running(client, gateway_id, "UpdateModelGatewayProvider") + console.print( + Panel.fit( + f"[green]Updated[/green]\nProviderId: {resp.provider_id}", + title="UpdateModelGatewayProvider", + border_style="green", + ) + ) + except Exception as e: + _print_api_error("UpdateModelGatewayProvider", e) + raise typer.Exit(1) + + +@provider_app.command("delete") +def provider_delete_command( + name: str = typer.Option(..., "--name", help="Provider name"), + region: Optional[str] = typer.Option(None, "--region", help="Region override"), +): + """Delete a provider by name.""" + name = _require_value("--name", name) + try: + client = _client(region) + gateway_id = _resolve_model_gateway_id(client) + provider = _find_provider_by_name(client, gateway_id, name) + client.delete_model_gateway_provider( + mgw.DeleteModelGatewayProviderRequest(provider_id=provider.provider_id) + ) + _wait_model_gateway_running(client, gateway_id, "DeleteModelGatewayProvider") + console.print( + Panel.fit("[green]Deleted[/green]", title="DeleteModelGatewayProvider") + ) + except Exception as e: + _print_api_error("DeleteModelGatewayProvider", e) + raise typer.Exit(1) + + +@consumer_app.command("add") +def consumer_add_command( + name: str = typer.Option(..., "--name", help="Consumer name"), + allow_models: Optional[List[str]] = typer.Option( + None, + "--allow-models", + help="provider-name or provider-name/model-name, repeatable", + ), + tpm: Optional[int] = typer.Option(None, "--tpm", help="Tokens per minute"), + tpd: Optional[int] = typer.Option(None, "--tpd", help="Tokens per day"), + region: Optional[str] = typer.Option(None, "--region", help="Region override"), +): + """Add a consumer.""" + name = _require_value("--name", name) + allow_models = _require_allow_models(allow_models) + try: + client = _client(region) + gateway_id = _resolve_model_gateway_id(client) + resp = client.create_model_gateway_consumer( + mgw.CreateModelGatewayConsumerRequest( + consumer_name=name, + model_gateway_id=gateway_id, + authz_config=_build_authz_config(client, gateway_id, allow_models), + token_rate_limit_config=_build_token_rate_limit_config(tpm, tpd), + ) + ) + _wait_model_gateway_running(client, gateway_id, "CreateModelGatewayConsumer") + console.print( + Panel.fit( + f"[green]Added[/green]\nConsumerId: {resp.consumer_id}", + title="CreateModelGatewayConsumer", + border_style="green", + ) + ) + except Exception as e: + _print_api_error("CreateModelGatewayConsumer", e) + raise typer.Exit(1) + + +@consumer_app.command("list") +def consumer_list_command( + region: Optional[str] = typer.Option(None, "--region", help="Region override"), +): + """List consumers.""" + try: + client = _client(region) + gateway_id = _resolve_model_gateway_id(client) + resp = client.list_model_gateway_consumers( + mgw.ListModelGatewayConsumersRequest( + model_gateway_id=gateway_id, + page_number=1, + page_size=100, + ) + ) + console.print(_consumer_table(resp.consumers or [])) + except Exception as e: + _print_api_error("ListModelGatewayConsumers", e) + raise typer.Exit(1) + + +@consumer_app.command("show") +def consumer_show_command( + name: str = typer.Option(..., "--name", help="Consumer name"), + region: Optional[str] = typer.Option(None, "--region", help="Region override"), +): + """Show a consumer by name.""" + name = _require_value("--name", name) + try: + client = _client(region) + gateway_id = _resolve_model_gateway_id(client) + consumer = _find_consumer_by_name(client, gateway_id, name) + resp = client.get_model_gateway_consumer( + mgw.GetModelGatewayConsumerRequest(consumer_id=consumer.consumer_id) + ) + console.print(_consumer_table([resp.consumer] if resp.consumer else [])) + except Exception as e: + _print_api_error("GetModelGatewayConsumer", e) + raise typer.Exit(1) + + +@consumer_app.command("update") +def consumer_update_command( + name: str = typer.Option(..., "--name", help="Consumer name"), + allow_models: Optional[List[str]] = typer.Option( + None, + "--allow-models", + help="provider-name or provider-name/model-name, repeatable", + ), + tpm: Optional[int] = typer.Option(None, "--tpm", help="Tokens per minute"), + tpd: Optional[int] = typer.Option(None, "--tpd", help="Tokens per day"), + region: Optional[str] = typer.Option(None, "--region", help="Region override"), +): + """Update a consumer by name.""" + name = _require_value("--name", name) + allow_models = _require_allow_models(allow_models) + try: + client = _client(region) + gateway_id = _resolve_model_gateway_id(client) + consumer = _find_consumer_by_name(client, gateway_id, name) + request_kwargs = {} + if allow_models is not None: + request_kwargs["authz_config"] = _build_authz_config( + client, gateway_id, allow_models + ) + if tpm is not None or tpd is not None: + request_kwargs["token_rate_limit_config"] = _build_token_rate_limit_config( + tpm, tpd + ) + resp = client.update_model_gateway_consumer( + mgw.UpdateModelGatewayConsumerRequest( + consumer_id=consumer.consumer_id, + **request_kwargs, + ) + ) + _wait_model_gateway_running(client, gateway_id, "UpdateModelGatewayConsumer") + console.print( + Panel.fit( + f"[green]Updated[/green]\nConsumerId: {resp.consumer_id}", + title="UpdateModelGatewayConsumer", + border_style="green", + ) + ) + except Exception as e: + _print_api_error("UpdateModelGatewayConsumer", e) + raise typer.Exit(1) + + +@consumer_app.command("delete") +def consumer_delete_command( + name: str = typer.Option(..., "--name", help="Consumer name"), + region: Optional[str] = typer.Option(None, "--region", help="Region override"), +): + """Delete a consumer by name.""" + name = _require_value("--name", name) + try: + client = _client(region) + gateway_id = _resolve_model_gateway_id(client) + consumer = _find_consumer_by_name(client, gateway_id, name) + client.delete_model_gateway_consumer( + mgw.DeleteModelGatewayConsumerRequest(consumer_id=consumer.consumer_id) + ) + _wait_model_gateway_running(client, gateway_id, "DeleteModelGatewayConsumer") + console.print( + Panel.fit("[green]Deleted[/green]", title="DeleteModelGatewayConsumer") + ) + except Exception as e: + _print_api_error("DeleteModelGatewayConsumer", e) + raise typer.Exit(1) + + +model_gateway_app.add_typer(provider_app, name="provider") +model_gateway_app.add_typer(consumer_app, name="consumer") diff --git a/docs/content/9.model-gateway/1.model_gateway_quickstart.md b/docs/content/9.model-gateway/1.model_gateway_quickstart.md new file mode 100644 index 00000000..94c4e078 --- /dev/null +++ b/docs/content/9.model-gateway/1.model_gateway_quickstart.md @@ -0,0 +1,136 @@ +# AgentKit 模型网关快速开始指南 + +模型网关提供统一的模型访问调用入口。您可以将火山方舟等模型服务配置为供应商,再创建消费者获取访问密钥,最后通过模型网关的 `/chat/completions` 接口调用模型。 + +本文以火山方舟 `doubao-seed-evolving` 为例。 + +## 准备工作 + +请先完成 AgentKit CLI 安装,并配置火山引擎 AK/SK: + +```bash +export VOLCENGINE_ACCESS_KEY= +export VOLCENGINE_SECRET_KEY= +``` + +## 1. 激活模型网关 + +执行 `activate` 创建或复用当前账号下的模型网关。是否传入 `--apig-gateway-id` 决定模型网关类型: + +- 不传 `--apig-gateway-id`:创建共享型模型网关,适合快速启动和体验。 +- 传入 `--apig-gateway-id`:创建标准型模型网关,适合需要 VPC 内私网访问、自定义入站/出站网络规格,或对稳定性要求更高的生产场景。 + +快速启动可以直接创建共享型模型网关: + +```bash +agentkit model-gateway activate +``` + +如果需要标准型模型网关,请先准备 API 网关实例 ID,再执行: + +```bash +agentkit model-gateway activate --apig-gateway-id +``` + +如果已存在模型网关,CLI 会直接提示已存在,并展示 `ModelGatewayId` 和当前状态。只有状态为 `Running` 时,后续配置和调用才可正常进行。 + +## 2. 添加火山方舟供应商 + +使用火山方舟 OpenAI Compatible 接口作为供应商: + +```bash +agentkit model-gateway provider add \ + --name ark \ + --base-url https://ark.cn-beijing.volces.com/api/v3 \ + --api-keys \ + --models doubao-seed-evolving +``` + +说明: + +- `--name ark` 是供应商名称,后续消费者授权会用到。 +- `--base-url` 是火山方舟 OpenAI Compatible Base URL。 +- `--api-keys` 传入火山方舟 API Key。 +- `--models` 配置该供应商支持的模型名。 +- `--protocol` 默认使用 `openai`,通常无需显式传入。 + +供应商创建是异步操作。命令执行后 CLI 会等待模型网关回到 `Running` 状态。 + +## 3. 查看供应商 + +通过 `provider show` 确认供应商配置,重点检查 `BaseUrl` 和 `Models`: + +```bash +agentkit model-gateway provider show --name ark +``` + +输出会以表格展示,调用模型网关时使用 `BaseUrl` 列中的访问地址。 + +```text + Model Gateway Providers +┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┓ +┃ ProviderId ┃ Name ┃ BaseUrl ┃ Models ┃ +┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━┩ +│ p-ab*******************uv │ ark │ https://ark.cn-beijing.volces.com/api/v3 │ doubao-seed-evolving │ +└───────────────────────────┴──────┴──────────────────────────────────────────┴──────────────────────┘ +``` + +## 4. 创建消费者 + +创建消费者,并授权它访问 `ark` 供应商下的所有模型: + +```bash +agentkit model-gateway consumer add \ + --name quickstart \ + --allow-models ark +``` + +如果只想授权单个模型,也可以写成: + +```bash +agentkit model-gateway consumer add \ + --name quickstart \ + --allow-models ark/doubao-seed-evolving +``` + +消费者创建同样是异步操作。命令执行后 CLI 会等待模型网关回到 `Running` 状态。 + +## 5. 查看消费者密钥 + +通过 `consumer show` 获取消费者的 API Key: + +```bash +agentkit model-gateway consumer show --name quickstart +``` + +输出会以表格展示。调用模型网关时使用 `ApiKeys` 列中的密钥。 + +```text + Model Gateway Consumers +┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━┳━━━━━┓ +┃ ConsumerId ┃ Name ┃ ApiKeys ┃ Authz ┃ TPM ┃ TPD ┃ +┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━╇━━━━━┩ +│ c-ab*******************uv │ quickstart │ sk-01****************************ef │ All │ │ │ +└───────────────────────────┴────────────┴─────────────────────────────────────┴───────┴─────┴─────┘ +``` + +## 6. 调用模型网关 + +获取模型网关的访问地址后,调用 `/chat/completions`。访问地址通常是模型网关绑定的API网关域名。 + +```bash +curl "/chat/completions" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer " \ + -d '{ + "model": "doubao-seed-evolving", + "messages": [ + { + "role": "user", + "content": "你是谁?" + } + ] + }' +``` + +如果配置正确,模型网关会将请求转发到火山方舟,并返回响应。 From 5bf09a92e00db21ca69075d36df715c3622e6a8f Mon Sep 17 00:00:00 2001 From: Xie Zhihao Date: Mon, 3 Aug 2026 15:22:14 +0800 Subject: [PATCH 03/11] fix: add missing arguments Signed-off-by: Xie Zhihao --- agentkit/sdk/model_gateway/types.py | 2 +- agentkit/toolkit/cli/cli_model_gateway.py | 154 ++++++++++++++---- .../1.model_gateway_quickstart.md | 44 ++--- 3 files changed, 141 insertions(+), 59 deletions(-) diff --git a/agentkit/sdk/model_gateway/types.py b/agentkit/sdk/model_gateway/types.py index 5d93c321..ad3bb6ba 100644 --- a/agentkit/sdk/model_gateway/types.py +++ b/agentkit/sdk/model_gateway/types.py @@ -225,7 +225,7 @@ class ListModelGatewaysResponse(ModelGatewayBaseModel): # CreateModelGatewayProvider - Request class CreateModelGatewayProviderRequest(ModelGatewayBaseModel): model_gateway_id: str = Field(..., alias="ModelGatewayId") - provider_type: Optional[str] = Field(default=None, alias="ProviderType") + provider_type: str = Field(..., alias="ProviderType") provider_name: str = Field(..., alias="ProviderName") protocols: Optional[list[str]] = Field(default=None, alias="Protocols") provider_source: Optional[str] = Field(default=None, alias="ProviderSource") diff --git a/agentkit/toolkit/cli/cli_model_gateway.py b/agentkit/toolkit/cli/cli_model_gateway.py index 53bd8cb6..3dd3b92e 100644 --- a/agentkit/toolkit/cli/cli_model_gateway.py +++ b/agentkit/toolkit/cli/cli_model_gateway.py @@ -216,65 +216,139 @@ def _provider_models(models: List[str]) -> List[mgw.ProviderModelsForModelGatewa def _provider_table(providers: List[mgw.ProvidersForModelGateway]) -> Table: table = Table(title="Model Gateway Providers") - table.add_column("ProviderId", style="cyan") + table.add_column("Provider ID", style="cyan") table.add_column("Name", style="white") - table.add_column("BaseUrl", style="blue") + table.add_column("Access URL", style="blue") table.add_column("Models", style="green") for item in providers: models = ", ".join(m.model_name or "" for m in item.provider_models or []) table.add_row( item.provider_id or "", item.provider_name or "", - item.base_url - or (item.provider_spec.base_url if item.provider_spec else ""), + item.base_url or "", models, ) return table -def _consumer_table(consumers: List[mgw.ConsumersForModelGateway]) -> Table: +def _consumer_table( + consumers: List[mgw.ConsumersForModelGateway], + provider_names_by_id: Optional[dict[str, str]] = None, +) -> Table: + provider_names_by_id = provider_names_by_id or {} table = Table(title="Model Gateway Consumers") - table.add_column("ConsumerId", style="cyan") + table.add_column("Consumer ID", style="cyan") table.add_column("Name", style="white") - table.add_column("ApiKeys", style="yellow") - table.add_column("Authz", style="green") + table.add_column("Authorization", style="green") table.add_column("TPM", style="magenta") table.add_column("TPD", style="magenta") for item in consumers: - authz = "" - if item.authz_config: - if item.authz_config.allow_all: - authz = "All" - elif item.authz_config.provider_authz_configs: - parts = [] - for config in item.authz_config.provider_authz_configs: - provider_id = config.provider_id or "" - if config.allow_all: - parts.append(f"{provider_id}:*") - else: - models = ", ".join(config.allowed_provider_model_ids or []) - parts.append(f"{provider_id}:{models}") - authz = "; ".join(parts) - - tpm = "" - tpd = "" - if item.token_rate_limit_config: - for rule in item.token_rate_limit_config.rules or []: - if rule.time_window == 60: - tpm = str(rule.value or "") - elif rule.time_window == 86400: - tpd = str(rule.value or "") + tpm, tpd = _consumer_rate_limits(item) table.add_row( item.consumer_id or "", item.consumer_name or "", - ", ".join(item.api_keys or []), - authz, + _consumer_authz(item, provider_names_by_id), tpm, tpd, ) return table +def _print_fields(fields: List[tuple[str, str]]) -> None: + for key, value in fields: + typer.echo(f"{key}: {value}") + + +def _provider_show(provider: mgw.ProvidersForModelGateway) -> None: + models = ", ".join(m.model_name or "" for m in provider.provider_models or []) + base_url = provider.provider_spec.base_url if provider.provider_spec else "" + api_keys = ( + ", ".join(key.value or "" for key in provider.credentials.api_keys or []) + if provider.credentials + else "" + ) + _print_fields( + [ + ("Provider ID", provider.provider_id or ""), + ("Name", provider.provider_name or ""), + ("Access URL", provider.base_url or ""), + ("Base URL", base_url), + ("API Keys", api_keys), + ("Models", models), + ] + ) + + +def _consumer_authz( + consumer: mgw.ConsumersForModelGateway, + provider_names_by_id: dict[str, str], +) -> str: + if not consumer.authz_config: + return "" + if consumer.authz_config.allow_all: + return "All" + if not consumer.authz_config.provider_authz_configs: + return "" + + parts = [] + for config in consumer.authz_config.provider_authz_configs: + provider_id = config.provider_id or "" + provider_name = provider_names_by_id.get(provider_id, provider_id) + if config.allow_all: + parts.append(f"{provider_name}:*") + else: + for model in config.allowed_provider_model_ids or []: + parts.append(f"{provider_name}:{model}") + return "; ".join(parts) + + +def _consumer_rate_limits(consumer: mgw.ConsumersForModelGateway) -> tuple[str, str]: + tpm = "" + tpd = "" + if consumer.token_rate_limit_config: + for rule in consumer.token_rate_limit_config.rules or []: + if rule.time_window == 60: + tpm = str(rule.value or "") + elif rule.time_window == 86400: + tpd = str(rule.value or "") + return tpm, tpd + + +def _consumer_show( + consumer: mgw.ConsumersForModelGateway, + provider_names_by_id: dict[str, str], +) -> None: + tpm, tpd = _consumer_rate_limits(consumer) + _print_fields( + [ + ("Consumer ID", consumer.consumer_id or ""), + ("Name", consumer.consumer_name or ""), + ("API Keys", ", ".join(consumer.api_keys or [])), + ("Authorization", _consumer_authz(consumer, provider_names_by_id)), + ("TPM", tpm), + ("TPD", tpd), + ] + ) + + +def _provider_names_by_id( + client: AgentkitModelGatewayClient, + model_gateway_id: str, +) -> dict[str, str]: + resp = client.list_model_gateway_providers( + mgw.ListModelGatewayProvidersRequest( + model_gateway_id=model_gateway_id, + page_number=1, + page_size=100, + ) + ) + return { + item.provider_id: item.provider_name or item.provider_id + for item in resp.providers or [] + if item.provider_id + } + + def _find_provider_by_name( client: AgentkitModelGatewayClient, model_gateway_id: str, @@ -475,6 +549,7 @@ def provider_add_command( resp = client.create_model_gateway_provider( mgw.CreateModelGatewayProviderRequest( model_gateway_id=gateway_id, + provider_type="Custom", provider_name=name, protocols=[_normalize_protocol(p) for p in protocol], provider_source="Domain", @@ -531,7 +606,8 @@ def provider_show_command( resp = client.get_model_gateway_provider( mgw.GetModelGatewayProviderRequest(provider_id=provider.provider_id) ) - console.print(_provider_table([resp.provider] if resp.provider else [])) + if resp.provider: + _provider_show(resp.provider) except Exception as e: _print_api_error("GetModelGatewayProvider", e) raise typer.Exit(1) @@ -676,7 +752,12 @@ def consumer_list_command( page_size=100, ) ) - console.print(_consumer_table(resp.consumers or [])) + console.print( + _consumer_table( + resp.consumers or [], + _provider_names_by_id(client, gateway_id), + ) + ) except Exception as e: _print_api_error("ListModelGatewayConsumers", e) raise typer.Exit(1) @@ -696,7 +777,8 @@ def consumer_show_command( resp = client.get_model_gateway_consumer( mgw.GetModelGatewayConsumerRequest(consumer_id=consumer.consumer_id) ) - console.print(_consumer_table([resp.consumer] if resp.consumer else [])) + if resp.consumer: + _consumer_show(resp.consumer, _provider_names_by_id(client, gateway_id)) except Exception as e: _print_api_error("GetModelGatewayConsumer", e) raise typer.Exit(1) diff --git a/docs/content/9.model-gateway/1.model_gateway_quickstart.md b/docs/content/9.model-gateway/1.model_gateway_quickstart.md index 94c4e078..ede42739 100644 --- a/docs/content/9.model-gateway/1.model_gateway_quickstart.md +++ b/docs/content/9.model-gateway/1.model_gateway_quickstart.md @@ -40,7 +40,7 @@ agentkit model-gateway activate --apig-gateway-id ```bash agentkit model-gateway provider add \ - --name ark \ + --name quickstart \ --base-url https://ark.cn-beijing.volces.com/api/v3 \ --api-keys \ --models doubao-seed-evolving @@ -48,7 +48,7 @@ agentkit model-gateway provider add \ 说明: -- `--name ark` 是供应商名称,后续消费者授权会用到。 +- `--name quickstart` 是供应商名称,后续消费者授权会用到。 - `--base-url` 是火山方舟 OpenAI Compatible Base URL。 - `--api-keys` 传入火山方舟 API Key。 - `--models` 配置该供应商支持的模型名。 @@ -58,21 +58,21 @@ agentkit model-gateway provider add \ ## 3. 查看供应商 -通过 `provider show` 确认供应商配置,重点检查 `BaseUrl` 和 `Models`: +通过 `provider show` 确认供应商配置,重点检查 `Access URL`、`Base URL` 和 `Models`: ```bash -agentkit model-gateway provider show --name ark +agentkit model-gateway provider show --name quickstart ``` -输出会以表格展示,调用模型网关时使用 `BaseUrl` 列中的访问地址。 +输出会逐行展示。调用模型网关时使用 `Access URL`,它是模型网关代理后的访问地址;`Base URL` 是供应商真实地址。 ```text - Model Gateway Providers -┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┓ -┃ ProviderId ┃ Name ┃ BaseUrl ┃ Models ┃ -┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━┩ -│ p-ab*******************uv │ ark │ https://ark.cn-beijing.volces.com/api/v3 │ doubao-seed-evolving │ -└───────────────────────────┴──────┴──────────────────────────────────────────┴──────────────────────┘ +Provider ID: p-ab*******************uv +Name: quickstart +Access URL: https://sab****************uv.apigateway-cn-beijing.volceapi.com/p-ab*******************uv +Base URL: https://ark.cn-beijing.volces.com/api/v3 +API Keys: 01******-****-****-****-**********ef +Models: doubao-seed-evolving ``` ## 4. 创建消费者 @@ -82,7 +82,7 @@ agentkit model-gateway provider show --name ark ```bash agentkit model-gateway consumer add \ --name quickstart \ - --allow-models ark + --allow-models quickstart ``` 如果只想授权单个模型,也可以写成: @@ -90,7 +90,7 @@ agentkit model-gateway consumer add \ ```bash agentkit model-gateway consumer add \ --name quickstart \ - --allow-models ark/doubao-seed-evolving + --allow-models quickstart/doubao-seed-evolving ``` 消费者创建同样是异步操作。命令执行后 CLI 会等待模型网关回到 `Running` 状态。 @@ -103,23 +103,23 @@ agentkit model-gateway consumer add \ agentkit model-gateway consumer show --name quickstart ``` -输出会以表格展示。调用模型网关时使用 `ApiKeys` 列中的密钥。 +输出会逐行展示。调用模型网关时使用 `API Keys` 中的密钥。 ```text - Model Gateway Consumers -┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━┳━━━━━┓ -┃ ConsumerId ┃ Name ┃ ApiKeys ┃ Authz ┃ TPM ┃ TPD ┃ -┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━╇━━━━━┩ -│ c-ab*******************uv │ quickstart │ sk-01****************************ef │ All │ │ │ -└───────────────────────────┴────────────┴─────────────────────────────────────┴───────┴─────┴─────┘ +Consumer ID: c-ab*******************uv +Name: quickstart +API Keys: sk-01****************************ef +Authorization: quickstart:* +TPM: +TPD: ``` ## 6. 调用模型网关 -获取模型网关的访问地址后,调用 `/chat/completions`。访问地址通常是模型网关绑定的API网关域名。 +获取模型网关的 `Access URL` 后,调用 `/chat/completions`。访问地址通常是模型网关绑定的 API 网关域名。 ```bash -curl "/chat/completions" \ +curl "/chat/completions" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ From aca85679310697b044e653b292ce2f306b2ebd47 Mon Sep 17 00:00:00 2001 From: Xie Zhihao Date: Mon, 3 Aug 2026 15:49:22 +0800 Subject: [PATCH 04/11] feat: show example Signed-off-by: Xie Zhihao --- agentkit/toolkit/cli/cli_model_gateway.py | 104 ++++++++++++++++++ .../1.model_gateway_quickstart.md | 10 +- 2 files changed, 112 insertions(+), 2 deletions(-) diff --git a/agentkit/toolkit/cli/cli_model_gateway.py b/agentkit/toolkit/cli/cli_model_gateway.py index 3dd3b92e..efac318d 100644 --- a/agentkit/toolkit/cli/cli_model_gateway.py +++ b/agentkit/toolkit/cli/cli_model_gateway.py @@ -450,6 +450,110 @@ def _build_authz_config( ) +@model_gateway_app.command("show-example") +def show_example_command( + provider: str = typer.Option(..., "--provider", help="Provider name"), + consumer: str = typer.Option(..., "--consumer", help="Consumer name"), + region: Optional[str] = typer.Option(None, "--region", help="Region override"), +): + """Show a curl example for calling the model gateway.""" + provider = _require_value("--provider", provider) + consumer = _require_value("--consumer", consumer) + try: + client = _client(region) + gateway_id = _resolve_model_gateway_id(client) + provider_item = _find_provider_by_name(client, gateway_id, provider) + provider_resp = client.get_model_gateway_provider( + mgw.GetModelGatewayProviderRequest(provider_id=provider_item.provider_id) + ) + consumer_item = _find_consumer_by_name(client, gateway_id, consumer) + consumer_resp = client.get_model_gateway_consumer( + mgw.GetModelGatewayConsumerRequest(consumer_id=consumer_item.consumer_id) + ) + + if not provider_resp.provider: + raise typer.BadParameter(f"Provider not found: {provider}") + if not consumer_resp.consumer: + raise typer.BadParameter(f"Consumer not found: {consumer}") + + access_url = provider_resp.provider.base_url or "" + if not access_url: + raise typer.BadParameter(f"Provider has no Access URL: {provider}") + + protocols = provider_resp.provider.protocols or [] + protocol = protocols[0] if protocols else "OpenAICompatible" + if protocol == "OpenAICompatible": + path = "/chat/completions" + elif protocol == "AnthropicCompatible": + path = "/messages" + else: + raise typer.BadParameter(f"Unsupported provider protocol: {protocol}") + + provider_models = [ + item.model_name + for item in provider_resp.provider.provider_models or [] + if item.model_name + ] + if not provider_models: + raise typer.BadParameter(f"Provider has no model: {provider}") + + provider_id = provider_resp.provider.provider_id or provider_item.provider_id + authz_config = consumer_resp.consumer.authz_config + accessible_models: List[str] = [] + if authz_config and authz_config.allow_all: + accessible_models = provider_models + elif authz_config and authz_config.provider_authz_configs: + allow_all_models = False + allowed_model_names: set[str] = set() + for config in authz_config.provider_authz_configs: + if config.provider_id != provider_id: + continue + if config.allow_all: + allow_all_models = True + break + allowed_model_names.update(config.allowed_provider_model_ids or []) + if allow_all_models: + accessible_models = provider_models + else: + accessible_models = [ + model for model in provider_models if model in allowed_model_names + ] + if not accessible_models: + console.print( + Panel.fit( + f"No accessible model for consumer '{consumer}' " + f"on provider '{provider}'.", + title="ShowModelGatewayExample Warning", + border_style="yellow", + ) + ) + return + model = accessible_models[0] + + api_keys = consumer_resp.consumer.api_keys or [] + api_key = api_keys[0] if api_keys else "" + if not api_key: + raise typer.BadParameter(f"Consumer has no API key: {consumer}") + + typer.echo( + f'curl "{access_url}{path}" \\\n' + ' -H "Content-Type: application/json" \\\n' + f' -H "Authorization: Bearer {api_key}" \\\n' + " -d '{\n" + f' "model": "{model}",\n' + ' "messages": [\n' + " {\n" + ' "role": "user",\n' + ' "content": "你是谁?"\n' + " }\n" + " ]\n" + " }'" + ) + except Exception as e: + _print_api_error("ShowModelGatewayExample", e) + raise typer.Exit(1) + + @model_gateway_app.command("activate") def activate_command( apig_gateway_id: Optional[str] = typer.Option( diff --git a/docs/content/9.model-gateway/1.model_gateway_quickstart.md b/docs/content/9.model-gateway/1.model_gateway_quickstart.md index ede42739..34a4764b 100644 --- a/docs/content/9.model-gateway/1.model_gateway_quickstart.md +++ b/docs/content/9.model-gateway/1.model_gateway_quickstart.md @@ -52,7 +52,7 @@ agentkit model-gateway provider add \ - `--base-url` 是火山方舟 OpenAI Compatible Base URL。 - `--api-keys` 传入火山方舟 API Key。 - `--models` 配置该供应商支持的模型名。 -- `--protocol` 默认使用 `openai`,通常无需显式传入。 +- `--protocol` OpenAI 兼容协议使用 `openai`,Anthropic 兼容协议使用 `anthropic`,通常无需显式传入。 供应商创建是异步操作。命令执行后 CLI 会等待模型网关回到 `Running` 状态。 @@ -116,9 +116,15 @@ TPD: ## 6. 调用模型网关 -获取模型网关的 `Access URL` 后,调用 `/chat/completions`。访问地址通常是模型网关绑定的 API 网关域名。 +使用 `show-example` 生成 curl 调用示例: ```bash +agentkit model-gateway show-example --provider quickstart --consumer quickstart +``` + +OpenAI 兼容的供应商会使用 `/chat/completions`,Anthropic 兼容的供应商会使用 `/messages`。输出示例: + +```text curl "/chat/completions" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ From 8efff188a5704a743b8658ff6fbf97be2c3ac45b Mon Sep 17 00:00:00 2001 From: Xie Zhihao Date: Mon, 3 Aug 2026 15:57:20 +0800 Subject: [PATCH 05/11] fix: use provider model ID in consumer authz Signed-off-by: Xie Zhihao --- agentkit/sdk/model_gateway/types.py | 5 ++ agentkit/toolkit/cli/cli_model_gateway.py | 91 +++++++++++++++++------ 2 files changed, 75 insertions(+), 21 deletions(-) diff --git a/agentkit/sdk/model_gateway/types.py b/agentkit/sdk/model_gateway/types.py index ad3bb6ba..d0830a71 100644 --- a/agentkit/sdk/model_gateway/types.py +++ b/agentkit/sdk/model_gateway/types.py @@ -96,6 +96,11 @@ class ProviderAuthzConfigsForModelGateway(ModelGatewayBaseModel): class ProviderModelsForModelGateway(ModelGatewayBaseModel): + provider_model_id: Optional[str] = Field( + default=None, + validation_alias=AliasChoices("ProviderModelId", "ProviderModelID"), + serialization_alias="ProviderModelId", + ) model_name: Optional[str] = Field(default=None, alias="ModelName") diff --git a/agentkit/toolkit/cli/cli_model_gateway.py b/agentkit/toolkit/cli/cli_model_gateway.py index efac318d..e9d0fd14 100644 --- a/agentkit/toolkit/cli/cli_model_gateway.py +++ b/agentkit/toolkit/cli/cli_model_gateway.py @@ -234,8 +234,10 @@ def _provider_table(providers: List[mgw.ProvidersForModelGateway]) -> Table: def _consumer_table( consumers: List[mgw.ConsumersForModelGateway], provider_names_by_id: Optional[dict[str, str]] = None, + provider_model_names_by_id: Optional[dict[str, str]] = None, ) -> Table: provider_names_by_id = provider_names_by_id or {} + provider_model_names_by_id = provider_model_names_by_id or {} table = Table(title="Model Gateway Consumers") table.add_column("Consumer ID", style="cyan") table.add_column("Name", style="white") @@ -247,7 +249,7 @@ def _consumer_table( table.add_row( item.consumer_id or "", item.consumer_name or "", - _consumer_authz(item, provider_names_by_id), + _consumer_authz(item, provider_names_by_id, provider_model_names_by_id), tpm, tpd, ) @@ -282,6 +284,7 @@ def _provider_show(provider: mgw.ProvidersForModelGateway) -> None: def _consumer_authz( consumer: mgw.ConsumersForModelGateway, provider_names_by_id: dict[str, str], + provider_model_names_by_id: dict[str, str], ) -> str: if not consumer.authz_config: return "" @@ -297,8 +300,9 @@ def _consumer_authz( if config.allow_all: parts.append(f"{provider_name}:*") else: - for model in config.allowed_provider_model_ids or []: - parts.append(f"{provider_name}:{model}") + for model_id in config.allowed_provider_model_ids or []: + model_name = provider_model_names_by_id.get(model_id, model_id) + parts.append(f"{provider_name}:{model_name}") return "; ".join(parts) @@ -317,6 +321,7 @@ def _consumer_rate_limits(consumer: mgw.ConsumersForModelGateway) -> tuple[str, def _consumer_show( consumer: mgw.ConsumersForModelGateway, provider_names_by_id: dict[str, str], + provider_model_names_by_id: dict[str, str], ) -> None: tpm, tpd = _consumer_rate_limits(consumer) _print_fields( @@ -324,17 +329,24 @@ def _consumer_show( ("Consumer ID", consumer.consumer_id or ""), ("Name", consumer.consumer_name or ""), ("API Keys", ", ".join(consumer.api_keys or [])), - ("Authorization", _consumer_authz(consumer, provider_names_by_id)), + ( + "Authorization", + _consumer_authz( + consumer, + provider_names_by_id, + provider_model_names_by_id, + ), + ), ("TPM", tpm), ("TPD", tpd), ] ) -def _provider_names_by_id( +def _provider_authz_display_maps( client: AgentkitModelGatewayClient, model_gateway_id: str, -) -> dict[str, str]: +) -> tuple[dict[str, str], dict[str, str]]: resp = client.list_model_gateway_providers( mgw.ListModelGatewayProvidersRequest( model_gateway_id=model_gateway_id, @@ -342,11 +354,19 @@ def _provider_names_by_id( page_size=100, ) ) - return { - item.provider_id: item.provider_name or item.provider_id - for item in resp.providers or [] - if item.provider_id - } + provider_names_by_id = {} + provider_model_names_by_id = {} + for item in resp.providers or []: + if item.provider_id: + provider_names_by_id[item.provider_id] = ( + item.provider_name or item.provider_id + ) + for model in item.provider_models or []: + if model.provider_model_id: + provider_model_names_by_id[model.provider_model_id] = ( + model.model_name or model.provider_model_id + ) + return provider_names_by_id, provider_model_names_by_id def _find_provider_by_name( @@ -423,10 +443,24 @@ def _build_authz_config( provider_name, model_name = item.split("/", 1) provider_name = _require_value("--allow-models provider name", provider_name) model_name = _require_value("--allow-models model name", model_name) - provider = _find_provider_by_name(client, model_gateway_id, provider_name) + provider_item = _find_provider_by_name(client, model_gateway_id, provider_name) + provider_resp = client.get_model_gateway_provider( + mgw.GetModelGatewayProviderRequest(provider_id=provider_item.provider_id) + ) + provider = provider_resp.provider or provider_item provider_id = provider.provider_id or "" + provider_model_id = "" + for model in provider.provider_models or []: + if model.model_name == model_name: + provider_model_id = model.provider_model_id or "" + break + if not provider_model_id: + raise typer.BadParameter( + f"Model not found or has no ID for provider '{provider_name}': " + f"{model_name}" + ) if provider_id not in allow_all_provider_ids: - grouped.setdefault(provider_id, []).append(model_name) + grouped.setdefault(provider_id, []).append(provider_model_id) provider_authz_configs = [ mgw.ProviderAuthzConfigsForModelGateway( @@ -490,7 +524,7 @@ def show_example_command( raise typer.BadParameter(f"Unsupported provider protocol: {protocol}") provider_models = [ - item.model_name + (item.provider_model_id or "", item.model_name) for item in provider_resp.provider.provider_models or [] if item.model_name ] @@ -501,22 +535,24 @@ def show_example_command( authz_config = consumer_resp.consumer.authz_config accessible_models: List[str] = [] if authz_config and authz_config.allow_all: - accessible_models = provider_models + accessible_models = [model_name for _, model_name in provider_models] elif authz_config and authz_config.provider_authz_configs: allow_all_models = False - allowed_model_names: set[str] = set() + allowed_model_ids: set[str] = set() for config in authz_config.provider_authz_configs: if config.provider_id != provider_id: continue if config.allow_all: allow_all_models = True break - allowed_model_names.update(config.allowed_provider_model_ids or []) + allowed_model_ids.update(config.allowed_provider_model_ids or []) if allow_all_models: - accessible_models = provider_models + accessible_models = [model_name for _, model_name in provider_models] else: accessible_models = [ - model for model in provider_models if model in allowed_model_names + model_name + for model_id, model_name in provider_models + if model_id in allowed_model_ids ] if not accessible_models: console.print( @@ -856,10 +892,15 @@ def consumer_list_command( page_size=100, ) ) + provider_names_by_id, provider_model_names_by_id = _provider_authz_display_maps( + client, + gateway_id, + ) console.print( _consumer_table( resp.consumers or [], - _provider_names_by_id(client, gateway_id), + provider_names_by_id, + provider_model_names_by_id, ) ) except Exception as e: @@ -882,7 +923,15 @@ def consumer_show_command( mgw.GetModelGatewayConsumerRequest(consumer_id=consumer.consumer_id) ) if resp.consumer: - _consumer_show(resp.consumer, _provider_names_by_id(client, gateway_id)) + ( + provider_names_by_id, + provider_model_names_by_id, + ) = _provider_authz_display_maps(client, gateway_id) + _consumer_show( + resp.consumer, + provider_names_by_id, + provider_model_names_by_id, + ) except Exception as e: _print_api_error("GetModelGatewayConsumer", e) raise typer.Exit(1) From cb461262b07f7a86d8d993a7098792904ba958bc Mon Sep 17 00:00:00 2001 From: Xie Zhihao Date: Mon, 3 Aug 2026 15:59:24 +0800 Subject: [PATCH 06/11] fix: use provider model ID in updating provider Signed-off-by: Xie Zhihao --- agentkit/toolkit/cli/cli_model_gateway.py | 29 ++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/agentkit/toolkit/cli/cli_model_gateway.py b/agentkit/toolkit/cli/cli_model_gateway.py index e9d0fd14..07bb4570 100644 --- a/agentkit/toolkit/cli/cli_model_gateway.py +++ b/agentkit/toolkit/cli/cli_model_gateway.py @@ -795,7 +795,34 @@ def provider_update_command( if api_keys is not None: request_kwargs["credentials"] = _build_credentials(api_keys) if models is not None: - request_kwargs["provider_models"] = _provider_models(models) + provider_resp = client.get_model_gateway_provider( + mgw.GetModelGatewayProviderRequest(provider_id=provider.provider_id) + ) + current_provider = provider_resp.provider or provider + provider_models = [] + existing_models = { + item.model_name: item + for item in current_provider.provider_models or [] + if item.model_name + } + for model_name in models: + existing_model = existing_models.get(model_name) + if not existing_model: + provider_models.append( + mgw.ProviderModelsForModelGateway(model_name=model_name) + ) + continue + if not existing_model.provider_model_id: + raise typer.BadParameter( + f"Existing provider model has no ID: {model_name}" + ) + provider_models.append( + mgw.ProviderModelsForModelGateway( + provider_model_id=existing_model.provider_model_id, + model_name=model_name, + ) + ) + request_kwargs["provider_models"] = provider_models resp = client.update_model_gateway_provider( mgw.UpdateModelGatewayProviderRequest( provider_id=provider.provider_id, From ca42e1aa5581b9ce9392b909a1d5657649975331 Mon Sep 17 00:00:00 2001 From: Xie Zhihao Date: Mon, 3 Aug 2026 16:17:32 +0800 Subject: [PATCH 07/11] chore: change repeatable parameters to singular form Signed-off-by: Xie Zhihao --- agentkit/toolkit/cli/cli_model_gateway.py | 34 +++++++++---------- .../1.model_gateway_quickstart.md | 14 ++++---- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/agentkit/toolkit/cli/cli_model_gateway.py b/agentkit/toolkit/cli/cli_model_gateway.py index 07bb4570..fb5319fd 100644 --- a/agentkit/toolkit/cli/cli_model_gateway.py +++ b/agentkit/toolkit/cli/cli_model_gateway.py @@ -107,14 +107,14 @@ def _require_allow_models(values: Optional[List[str]]) -> Optional[List[str]]: if not values: return None - normalized_values = _require_values("--allow-models", values) + normalized_values = _require_values("--allow-model", values) for item in normalized_values: if "/" not in item: - _require_value("--allow-models provider name", item) + _require_value("--allow-model provider name", item) continue provider_name, model_name = item.split("/", 1) - _require_value("--allow-models provider name", provider_name) - _require_value("--allow-models model name", model_name) + _require_value("--allow-model provider name", provider_name) + _require_value("--allow-model model name", model_name) return normalized_values @@ -435,14 +435,14 @@ def _build_authz_config( allow_all_provider_ids: set[str] = set() for item in allow_models: if "/" not in item: - provider_name = _require_value("--allow-models provider name", item) + provider_name = _require_value("--allow-model provider name", item) provider = _find_provider_by_name(client, model_gateway_id, provider_name) allow_all_provider_ids.add(provider.provider_id or "") grouped.pop(provider.provider_id or "", None) continue provider_name, model_name = item.split("/", 1) - provider_name = _require_value("--allow-models provider name", provider_name) - model_name = _require_value("--allow-models model name", model_name) + provider_name = _require_value("--allow-model provider name", provider_name) + model_name = _require_value("--allow-model model name", model_name) provider_item = _find_provider_by_name(client, model_gateway_id, provider_name) provider_resp = client.get_model_gateway_provider( mgw.GetModelGatewayProviderRequest(provider_id=provider_item.provider_id) @@ -670,8 +670,8 @@ def activate_command( def provider_add_command( name: str = typer.Option(..., "--name", help="Provider name"), base_url: str = typer.Option(..., "--base-url", help="Provider base URL"), - api_keys: List[str] = typer.Option(..., "--api-keys", help="API key, repeatable"), - models: List[str] = typer.Option(..., "--models", help="Model name, repeatable"), + api_keys: List[str] = typer.Option(..., "--api-key", help="API key, repeatable"), + models: List[str] = typer.Option(..., "--model", help="Model name, repeatable"), protocol: List[str] = typer.Option( ["openai"], "--protocol", help="openai or anthropic, exactly once" ), @@ -680,8 +680,8 @@ def provider_add_command( """Add a provider.""" name = _require_value("--name", name) base_url = _require_value("--base-url", base_url) - api_keys = _require_values("--api-keys", api_keys) - models = _require_values("--models", models) + api_keys = _require_values("--api-key", api_keys) + models = _require_values("--model", models) protocol = _require_single_protocol(protocol) try: client = _client(region) @@ -760,10 +760,10 @@ def provider_update_command( None, "--base-url", help="Provider base URL" ), api_keys: Optional[List[str]] = typer.Option( - None, "--api-keys", help="API key, repeatable" + None, "--api-key", help="API key, repeatable" ), models: Optional[List[str]] = typer.Option( - None, "--models", help="Model name, repeatable" + None, "--model", help="Model name, repeatable" ), protocol: Optional[List[str]] = typer.Option( None, "--protocol", help="openai or anthropic, repeatable" @@ -775,9 +775,9 @@ def provider_update_command( if base_url is not None: base_url = _require_value("--base-url", base_url) if api_keys is not None: - api_keys = _require_values("--api-keys", api_keys) + api_keys = _require_values("--api-key", api_keys) if models is not None: - models = _require_values("--models", models) + models = _require_values("--model", models) if protocol: protocol = _require_single_protocol(protocol) try: @@ -870,7 +870,7 @@ def consumer_add_command( name: str = typer.Option(..., "--name", help="Consumer name"), allow_models: Optional[List[str]] = typer.Option( None, - "--allow-models", + "--allow-model", help="provider-name or provider-name/model-name, repeatable", ), tpm: Optional[int] = typer.Option(None, "--tpm", help="Tokens per minute"), @@ -969,7 +969,7 @@ def consumer_update_command( name: str = typer.Option(..., "--name", help="Consumer name"), allow_models: Optional[List[str]] = typer.Option( None, - "--allow-models", + "--allow-model", help="provider-name or provider-name/model-name, repeatable", ), tpm: Optional[int] = typer.Option(None, "--tpm", help="Tokens per minute"), diff --git a/docs/content/9.model-gateway/1.model_gateway_quickstart.md b/docs/content/9.model-gateway/1.model_gateway_quickstart.md index 34a4764b..f91c7804 100644 --- a/docs/content/9.model-gateway/1.model_gateway_quickstart.md +++ b/docs/content/9.model-gateway/1.model_gateway_quickstart.md @@ -42,16 +42,16 @@ agentkit model-gateway activate --apig-gateway-id agentkit model-gateway provider add \ --name quickstart \ --base-url https://ark.cn-beijing.volces.com/api/v3 \ - --api-keys \ - --models doubao-seed-evolving + --api-key \ + --model doubao-seed-evolving ``` 说明: - `--name quickstart` 是供应商名称,后续消费者授权会用到。 -- `--base-url` 是火山方舟 OpenAI Compatible Base URL。 -- `--api-keys` 传入火山方舟 API Key。 -- `--models` 配置该供应商支持的模型名。 +- `--base-url` 是火山方舟 OpenAI 兼容协议的 Base URL。 +- `--api-key` 传入火山方舟 API Key。 +- `--model` 配置该供应商支持的模型名。 - `--protocol` OpenAI 兼容协议使用 `openai`,Anthropic 兼容协议使用 `anthropic`,通常无需显式传入。 供应商创建是异步操作。命令执行后 CLI 会等待模型网关回到 `Running` 状态。 @@ -82,7 +82,7 @@ Models: doubao-seed-evolving ```bash agentkit model-gateway consumer add \ --name quickstart \ - --allow-models quickstart + --allow-model quickstart ``` 如果只想授权单个模型,也可以写成: @@ -90,7 +90,7 @@ agentkit model-gateway consumer add \ ```bash agentkit model-gateway consumer add \ --name quickstart \ - --allow-models quickstart/doubao-seed-evolving + --allow-model quickstart/doubao-seed-evolving ``` 消费者创建同样是异步操作。命令执行后 CLI 会等待模型网关回到 `Running` 状态。 From 5fccefaed0282d9b4f2eec8dc275d47c56e2ec55 Mon Sep 17 00:00:00 2001 From: Xie Zhihao Date: Mon, 3 Aug 2026 19:55:46 +0800 Subject: [PATCH 08/11] fix: API alias Signed-off-by: Xie Zhihao --- agentkit/sdk/model_gateway/types.py | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/agentkit/sdk/model_gateway/types.py b/agentkit/sdk/model_gateway/types.py index d0830a71..82e8032f 100644 --- a/agentkit/sdk/model_gateway/types.py +++ b/agentkit/sdk/model_gateway/types.py @@ -70,11 +70,7 @@ class CredentialsForModelGateway(ModelGatewayBaseModel): class ModelGatewaysForModelGateway(ModelGatewayBaseModel): - model_gateway_id: Optional[str] = Field( - default=None, - validation_alias=AliasChoices("ModelGatewayId", "ModelGatewayID"), - serialization_alias="ModelGatewayId", - ) + model_gateway_id: Optional[str] = Field(default=None, alias="ModelGatewayId") retry_policy: Optional[RetryPolicyForModelGateway] = Field( default=None, alias="RetryPolicy" ) @@ -96,30 +92,18 @@ class ProviderAuthzConfigsForModelGateway(ModelGatewayBaseModel): class ProviderModelsForModelGateway(ModelGatewayBaseModel): - provider_model_id: Optional[str] = Field( - default=None, - validation_alias=AliasChoices("ProviderModelId", "ProviderModelID"), - serialization_alias="ProviderModelId", - ) + provider_model_id: Optional[str] = Field(default=None, alias="ProviderModelId") model_name: Optional[str] = Field(default=None, alias="ModelName") class ProviderSpecForModelGateway(ModelGatewayBaseModel): - base_url: Optional[str] = Field(default=None, alias="BaseURL") + base_url: Optional[str] = Field(default=None, alias="BaseUrl") class ProvidersForModelGateway(ModelGatewayBaseModel): - provider_id: Optional[str] = Field( - default=None, - validation_alias=AliasChoices("ProviderId", "ProviderID"), - serialization_alias="ProviderId", - ) + provider_id: Optional[str] = Field(default=None, alias="ProviderId") provider_name: Optional[str] = Field(default=None, alias="ProviderName") - model_gateway_id: Optional[str] = Field( - default=None, - validation_alias=AliasChoices("ModelGatewayId", "ModelGatewayID"), - serialization_alias="ModelGatewayId", - ) + model_gateway_id: Optional[str] = Field(default=None, alias="ModelGatewayId") base_url: Optional[str] = Field(default=None, alias="BaseUrl") provider_type: Optional[str] = Field(default=None, alias="ProviderType") protocols: Optional[list[str]] = Field(default=None, alias="Protocols") From 818432256a0ab47977562a8850a7077a35813179 Mon Sep 17 00:00:00 2001 From: Xie Zhihao Date: Mon, 3 Aug 2026 20:35:57 +0800 Subject: [PATCH 09/11] feat: add more examples Signed-off-by: Xie Zhihao --- agentkit/toolkit/cli/cli_model_gateway.py | 111 ++++++++++++++++++---- 1 file changed, 91 insertions(+), 20 deletions(-) diff --git a/agentkit/toolkit/cli/cli_model_gateway.py b/agentkit/toolkit/cli/cli_model_gateway.py index fb5319fd..dfc0e02c 100644 --- a/agentkit/toolkit/cli/cli_model_gateway.py +++ b/agentkit/toolkit/cli/cli_model_gateway.py @@ -16,6 +16,7 @@ from __future__ import annotations +from enum import Enum import json import time from typing import List, Optional @@ -34,6 +35,14 @@ MODEL_GATEWAY_FAILED_STATUSES = {"CreatedFailed", "UpdatedFailed", "DeletedFailed"} MODEL_GATEWAY_ERROR_STATUSES = MODEL_GATEWAY_FAILED_STATUSES | {"Error"} + +class ModelGatewayExampleType(str, Enum): + curl = "curl" + openai = "openai" + anthropic = "anthropic" + agentkit = "agentkit" + + model_gateway_app = typer.Typer( name="model-gateway", help="Manage AgentKit Model Gateway", @@ -488,9 +497,14 @@ def _build_authz_config( def show_example_command( provider: str = typer.Option(..., "--provider", help="Provider name"), consumer: str = typer.Option(..., "--consumer", help="Consumer name"), + example: ModelGatewayExampleType = typer.Option( + ModelGatewayExampleType.curl, + "--example", + help="Example type: curl, openai, anthropic, or agentkit", + ), region: Optional[str] = typer.Option(None, "--region", help="Region override"), ): - """Show a curl example for calling the model gateway.""" + """Show an example for calling the model gateway.""" provider = _require_value("--provider", provider) consumer = _require_value("--consumer", consumer) try: @@ -514,6 +528,11 @@ def show_example_command( if not access_url: raise typer.BadParameter(f"Provider has no Access URL: {provider}") + api_keys = consumer_resp.consumer.api_keys or [] + api_key = api_keys[0] if api_keys else "" + if not api_key: + raise typer.BadParameter(f"Consumer has no API key: {consumer}") + protocols = provider_resp.provider.protocols or [] protocol = protocols[0] if protocols else "OpenAICompatible" if protocol == "OpenAICompatible": @@ -566,25 +585,77 @@ def show_example_command( return model = accessible_models[0] - api_keys = consumer_resp.consumer.api_keys or [] - api_key = api_keys[0] if api_keys else "" - if not api_key: - raise typer.BadParameter(f"Consumer has no API key: {consumer}") - - typer.echo( - f'curl "{access_url}{path}" \\\n' - ' -H "Content-Type: application/json" \\\n' - f' -H "Authorization: Bearer {api_key}" \\\n' - " -d '{\n" - f' "model": "{model}",\n' - ' "messages": [\n' - " {\n" - ' "role": "user",\n' - ' "content": "你是谁?"\n' - " }\n" - " ]\n" - " }'" - ) + if example == ModelGatewayExampleType.curl: + typer.echo( + f'curl "{access_url}{path}" \\\n' + ' -H "Content-Type: application/json" \\\n' + f' -H "Authorization: Bearer {api_key}" \\\n' + " -d '{\n" + f' "model": "{model}",\n' + ' "messages": [\n' + " {\n" + ' "role": "user",\n' + ' "content": "Hello, world"\n' + " }\n" + " ]\n" + " }'" + ) + elif example == ModelGatewayExampleType.openai: + if protocol != "OpenAICompatible": + raise typer.BadParameter( + f"OpenAI example requires OpenAI-Compatible protocol: {protocol}" + ) + typer.echo( + "from openai import OpenAI\n\n" + "client = OpenAI(\n" + f' api_key="{api_key}",\n' + f' base_url="{access_url}",\n' + ")\n\n" + "completion = client.chat.completions.create(\n" + f' model="{model}",\n' + " messages=[\n" + " {\n" + ' "role": "user",\n' + ' "content": "你是谁?",\n' + " }\n" + " ],\n" + ")\n\n" + "print(completion.choices[0].message)" + ) + elif example == ModelGatewayExampleType.anthropic: + if protocol != "AnthropicCompatible": + raise typer.BadParameter( + f"Anthropic example requires Anthropic-Compatible protocol: {protocol}" + ) + typer.echo( + "from anthropic import Anthropic\n\n" + "client = Anthropic(\n" + f' api_key="{api_key}",\n' + f' base_url="{access_url}",\n' + ")\n\n" + "for message in client.messages.create(\n" + " max_tokens=1024,\n" + " messages=[\n" + " {\n" + ' "role": "user",\n' + ' "content": "Hello, world",\n' + " }\n" + " ],\n" + f' model="{model}",\n' + "):\n" + " print(message)" + ) + elif example == ModelGatewayExampleType.agentkit: + if protocol != "OpenAICompatible": + raise typer.BadParameter( + f"AgentKit example requires OpenAI-Compatible protocol: {protocol}" + ) + typer.echo( + "agentkit config \n" + f" -e MODEL_AGENT_NAME={model} \\\n" + f" -e MODEL_AGENT_API_BASE={access_url} \\\n" + f" -e MODEL_AGENT_API_KEY={api_key}" + ) except Exception as e: _print_api_error("ShowModelGatewayExample", e) raise typer.Exit(1) From ddc7128128eb1ff1d1c6bf8b9dc8e98bbd62dbe4 Mon Sep 17 00:00:00 2001 From: Xie Zhihao Date: Tue, 4 Aug 2026 19:03:05 +0800 Subject: [PATCH 10/11] fix: API Signed-off-by: Xie Zhihao --- agentkit/sdk/model_gateway/types.py | 6 ++++-- agentkit/toolkit/cli/cli_model_gateway.py | 14 +++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/agentkit/sdk/model_gateway/types.py b/agentkit/sdk/model_gateway/types.py index 82e8032f..64c0d97d 100644 --- a/agentkit/sdk/model_gateway/types.py +++ b/agentkit/sdk/model_gateway/types.py @@ -35,7 +35,7 @@ class ApiKeysForModelGateway(ModelGatewayBaseModel): class AuthzConfigForModelGateway(ModelGatewayBaseModel): - allow_all: Optional[bool] = Field(default=None, alias="AllowAll") + allow_all_providers: Optional[bool] = Field(default=None, alias="AllowAllProviders") provider_authz_configs: Optional[list[ProviderAuthzConfigsForModelGateway]] = Field( default=None, alias="ProviderAuthzConfigs" ) @@ -85,7 +85,9 @@ class ModelGatewaysForModelGateway(ModelGatewayBaseModel): class ProviderAuthzConfigsForModelGateway(ModelGatewayBaseModel): provider_id: Optional[str] = Field(default=None, alias="ProviderId") - allow_all: Optional[bool] = Field(default=None, alias="AllowAll") + allow_all_provider_models: Optional[bool] = Field( + default=None, alias="AllowAllProviderModels" + ) allowed_provider_model_ids: Optional[list[str]] = Field( default=None, alias="AllowedProviderModelIds" ) diff --git a/agentkit/toolkit/cli/cli_model_gateway.py b/agentkit/toolkit/cli/cli_model_gateway.py index dfc0e02c..193117a9 100644 --- a/agentkit/toolkit/cli/cli_model_gateway.py +++ b/agentkit/toolkit/cli/cli_model_gateway.py @@ -297,7 +297,7 @@ def _consumer_authz( ) -> str: if not consumer.authz_config: return "" - if consumer.authz_config.allow_all: + if consumer.authz_config.allow_all_providers: return "All" if not consumer.authz_config.provider_authz_configs: return "" @@ -306,7 +306,7 @@ def _consumer_authz( for config in consumer.authz_config.provider_authz_configs: provider_id = config.provider_id or "" provider_name = provider_names_by_id.get(provider_id, provider_id) - if config.allow_all: + if config.allow_all_provider_models: parts.append(f"{provider_name}:*") else: for model_id in config.allowed_provider_model_ids or []: @@ -474,21 +474,21 @@ def _build_authz_config( provider_authz_configs = [ mgw.ProviderAuthzConfigsForModelGateway( provider_id=provider_id, - allow_all=True, + allow_all_provider_models=True, ) for provider_id in allow_all_provider_ids ] provider_authz_configs.extend( mgw.ProviderAuthzConfigsForModelGateway( provider_id=provider_id, - allow_all=False, + allow_all_provider_models=False, allowed_provider_model_ids=models, ) for provider_id, models in grouped.items() ) return mgw.AuthzConfigForModelGateway( - allow_all=False, + allow_all_providers=False, provider_authz_configs=provider_authz_configs, ) @@ -553,7 +553,7 @@ def show_example_command( provider_id = provider_resp.provider.provider_id or provider_item.provider_id authz_config = consumer_resp.consumer.authz_config accessible_models: List[str] = [] - if authz_config and authz_config.allow_all: + if authz_config and authz_config.allow_all_providers: accessible_models = [model_name for _, model_name in provider_models] elif authz_config and authz_config.provider_authz_configs: allow_all_models = False @@ -561,7 +561,7 @@ def show_example_command( for config in authz_config.provider_authz_configs: if config.provider_id != provider_id: continue - if config.allow_all: + if config.allow_all_provider_models: allow_all_models = True break allowed_model_ids.update(config.allowed_provider_model_ids or []) From 8a219ec55fd20387e106386f5dcf84d73351e0e7 Mon Sep 17 00:00:00 2001 From: Xie Zhihao Date: Tue, 4 Aug 2026 19:32:49 +0800 Subject: [PATCH 11/11] chore: typo Signed-off-by: Xie Zhihao --- agentkit/toolkit/cli/cli_model_gateway.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/agentkit/toolkit/cli/cli_model_gateway.py b/agentkit/toolkit/cli/cli_model_gateway.py index 193117a9..713d03f0 100644 --- a/agentkit/toolkit/cli/cli_model_gateway.py +++ b/agentkit/toolkit/cli/cli_model_gateway.py @@ -45,17 +45,17 @@ class ModelGatewayExampleType(str, Enum): model_gateway_app = typer.Typer( name="model-gateway", - help="Manage AgentKit Model Gateway", + help="Manage AgentKit Model Gateway.", add_completion=False, ) provider_app = typer.Typer( name="provider", - help="Manage model gateway providers", + help="Manage model gateway providers.", add_completion=False, ) consumer_app = typer.Typer( name="consumer", - help="Manage model gateway consumers", + help="Manage model gateway consumers.", add_completion=False, ) @@ -668,7 +668,7 @@ def activate_command( ), region: Optional[str] = typer.Option(None, "--region", help="Region override"), ): - """Create a model gateway with an initial consumer.""" + """Activate the model gateway.""" if apig_gateway_id is not None: apig_gateway_id = _require_value("--apig-gateway-id", apig_gateway_id) try: