mirror of
https://github.com/meta-llama/llama-stack.git
synced 2025-10-04 04:04:14 +00:00
feat: load config class when doing variable substitution
When using bash style substitution env variable in distribution template, we are processing the string and convert it to the type associated with the provider's config class. This allows us to return the proper type. This is crucial for api key since they are not strings anymore but SecretStr. If the key is unset we will get an empty string which will result in a Pydantic error like: ``` ERROR 2025-09-25 21:40:44,565 __main__:527 core::server: Error creating app: 1 validation error for AnthropicConfig api_key Input should be a valid string For further information visit https://errors.pydantic.dev/2.11/v/string_type ``` Signed-off-by: Sébastien Han <seb@redhat.com>
This commit is contained in:
parent
4af141292f
commit
bc64635835
79 changed files with 381 additions and 216 deletions
|
@ -6,22 +6,21 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
||||
class AnthropicProviderDataValidator(BaseModel):
|
||||
anthropic_api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
anthropic_api_key: MySecretStr = Field(
|
||||
description="API key for Anthropic models",
|
||||
)
|
||||
|
||||
|
||||
@json_schema_type
|
||||
class AnthropicConfig(BaseModel):
|
||||
api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
api_key: MySecretStr = Field(
|
||||
description="API key for Anthropic models",
|
||||
)
|
||||
|
||||
|
|
|
@ -7,13 +7,14 @@
|
|||
import os
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, HttpUrl, SecretStr
|
||||
from pydantic import BaseModel, Field, HttpUrl
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
||||
class AzureProviderDataValidator(BaseModel):
|
||||
azure_api_key: SecretStr = Field(
|
||||
azure_api_key: MySecretStr = Field(
|
||||
description="Azure API key for Azure",
|
||||
)
|
||||
azure_api_base: HttpUrl = Field(
|
||||
|
@ -31,7 +32,7 @@ class AzureProviderDataValidator(BaseModel):
|
|||
|
||||
@json_schema_type
|
||||
class AzureConfig(BaseModel):
|
||||
api_key: SecretStr = Field(
|
||||
api_key: MySecretStr = Field(
|
||||
description="Azure API key for Azure",
|
||||
)
|
||||
api_base: HttpUrl = Field(
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
import os
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
DEFAULT_BASE_URL = "https://api.cerebras.ai"
|
||||
|
@ -20,12 +21,8 @@ class CerebrasImplConfig(BaseModel):
|
|||
default=os.environ.get("CEREBRAS_BASE_URL", DEFAULT_BASE_URL),
|
||||
description="Base URL for the Cerebras API",
|
||||
)
|
||||
api_key: SecretStr = Field(
|
||||
<<<<<<< HEAD
|
||||
default=SecretStr(os.environ.get("CEREBRAS_API_KEY")),
|
||||
=======
|
||||
default=SecretStr(os.environ.get("CEREBRAS_API_KEY", "")),
|
||||
>>>>>>> a48f2009 (chore: use empty SecretStr values as default)
|
||||
api_key: MySecretStr = Field(
|
||||
default=MySecretStr(os.environ.get("CEREBRAS_API_KEY")),
|
||||
description="Cerebras API Key",
|
||||
)
|
||||
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
||||
|
@ -17,8 +18,7 @@ class DatabricksImplConfig(BaseModel):
|
|||
default=None,
|
||||
description="The URL for the Databricks model serving endpoint",
|
||||
)
|
||||
api_token: SecretStr = Field(
|
||||
default=SecretStr(None),
|
||||
api_token: MySecretStr = Field(
|
||||
description="The Databricks API token",
|
||||
)
|
||||
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import Field, SecretStr
|
||||
from pydantic import Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.providers.utils.inference.model_registry import RemoteInferenceProviderConfig
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
@ -18,8 +19,7 @@ class FireworksImplConfig(RemoteInferenceProviderConfig):
|
|||
default="https://api.fireworks.ai/inference/v1",
|
||||
description="The URL for the Fireworks server",
|
||||
)
|
||||
api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
api_key: MySecretStr = Field(
|
||||
description="The Fireworks.ai API Key",
|
||||
)
|
||||
|
||||
|
|
|
@ -6,22 +6,21 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
||||
class GeminiProviderDataValidator(BaseModel):
|
||||
gemini_api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
gemini_api_key: MySecretStr = Field(
|
||||
description="API key for Gemini models",
|
||||
)
|
||||
|
||||
|
||||
@json_schema_type
|
||||
class GeminiConfig(BaseModel):
|
||||
api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
api_key: MySecretStr = Field(
|
||||
description="API key for Gemini models",
|
||||
)
|
||||
|
||||
|
|
|
@ -6,23 +6,22 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
||||
class GroqProviderDataValidator(BaseModel):
|
||||
groq_api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
groq_api_key: MySecretStr = Field(
|
||||
description="API key for Groq models",
|
||||
)
|
||||
|
||||
|
||||
@json_schema_type
|
||||
class GroqConfig(BaseModel):
|
||||
api_key: SecretStr = Field(
|
||||
api_key: MySecretStr = Field(
|
||||
# The Groq client library loads the GROQ_API_KEY environment variable by default
|
||||
default=SecretStr(""),
|
||||
description="The Groq API key",
|
||||
)
|
||||
|
||||
|
|
|
@ -6,22 +6,21 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
||||
class LlamaProviderDataValidator(BaseModel):
|
||||
llama_api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
llama_api_key: MySecretStr = Field(
|
||||
description="API key for api.llama models",
|
||||
)
|
||||
|
||||
|
||||
@json_schema_type
|
||||
class LlamaCompatConfig(BaseModel):
|
||||
api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
api_key: MySecretStr = Field(
|
||||
description="The Llama API key",
|
||||
)
|
||||
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
import os
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
||||
|
@ -39,8 +40,8 @@ class NVIDIAConfig(BaseModel):
|
|||
default_factory=lambda: os.getenv("NVIDIA_BASE_URL", "https://integrate.api.nvidia.com"),
|
||||
description="A base url for accessing the NVIDIA NIM",
|
||||
)
|
||||
api_key: SecretStr = Field(
|
||||
default_factory=lambda: SecretStr(os.getenv("NVIDIA_API_KEY", "")),
|
||||
api_key: MySecretStr = Field(
|
||||
default_factory=lambda: MySecretStr(os.getenv("NVIDIA_API_KEY", "")),
|
||||
description="The NVIDIA API key, only needed of using the hosted service",
|
||||
)
|
||||
timeout: int = Field(
|
||||
|
|
|
@ -6,22 +6,21 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
||||
class OpenAIProviderDataValidator(BaseModel):
|
||||
openai_api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
openai_api_key: MySecretStr = Field(
|
||||
description="API key for OpenAI models",
|
||||
)
|
||||
|
||||
|
||||
@json_schema_type
|
||||
class OpenAIConfig(BaseModel):
|
||||
api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
api_key: MySecretStr = Field(
|
||||
description="API key for OpenAI models",
|
||||
)
|
||||
base_url: str = Field(
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
||||
|
@ -18,8 +19,7 @@ class PassthroughImplConfig(BaseModel):
|
|||
description="The URL for the passthrough endpoint",
|
||||
)
|
||||
|
||||
api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
api_key: MySecretStr = Field(
|
||||
description="API Key for the passthrouth endpoint",
|
||||
)
|
||||
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
||||
|
@ -17,8 +18,7 @@ class RunpodImplConfig(BaseModel):
|
|||
default=None,
|
||||
description="The URL for the Runpod model serving endpoint",
|
||||
)
|
||||
api_token: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
api_token: MySecretStr = Field(
|
||||
description="The API token",
|
||||
)
|
||||
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
||||
class SambaNovaProviderDataValidator(BaseModel):
|
||||
sambanova_api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
sambanova_api_key: MySecretStr = Field(
|
||||
description="Sambanova Cloud API key",
|
||||
)
|
||||
|
||||
|
@ -24,8 +24,7 @@ class SambaNovaImplConfig(BaseModel):
|
|||
default="https://api.sambanova.ai/v1",
|
||||
description="The URL for the SambaNova AI server",
|
||||
)
|
||||
api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
api_key: MySecretStr = Field(
|
||||
description="The SambaNova cloud API Key",
|
||||
)
|
||||
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
# the root directory of this source tree.
|
||||
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
||||
|
@ -32,8 +33,7 @@ class InferenceEndpointImplConfig(BaseModel):
|
|||
endpoint_name: str = Field(
|
||||
description="The name of the Hugging Face Inference Endpoint in the format of '{namespace}/{endpoint_name}' (e.g. 'my-cool-org/meta-llama-3-1-8b-instruct-rce'). Namespace is optional and will default to the user account if not provided.",
|
||||
)
|
||||
api_token: SecretStr | None = Field(
|
||||
default=None,
|
||||
api_token: MySecretStr = Field(
|
||||
description="Your Hugging Face user access token (will default to locally saved token if not provided)",
|
||||
)
|
||||
|
||||
|
@ -55,8 +55,7 @@ class InferenceAPIImplConfig(BaseModel):
|
|||
huggingface_repo: str = Field(
|
||||
description="The model ID of the model on the Hugging Face Hub (e.g. 'meta-llama/Meta-Llama-3.1-70B-Instruct')",
|
||||
)
|
||||
api_token: SecretStr | None = Field(
|
||||
default=None,
|
||||
api_token: MySecretStr = Field(
|
||||
description="Your Hugging Face user access token (will default to locally saved token if not provided)",
|
||||
)
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
from collections.abc import AsyncGenerator
|
||||
|
||||
from huggingface_hub import AsyncInferenceClient, HfApi
|
||||
from pydantic import SecretStr
|
||||
|
||||
from llama_stack.apis.common.content_types import (
|
||||
InterleavedContent,
|
||||
|
@ -35,6 +34,7 @@ from llama_stack.apis.inference import (
|
|||
)
|
||||
from llama_stack.apis.models import Model
|
||||
from llama_stack.apis.models.models import ModelType
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.log import get_logger
|
||||
from llama_stack.models.llama.sku_list import all_registered_models
|
||||
from llama_stack.providers.datatypes import ModelsProtocolPrivate
|
||||
|
@ -79,7 +79,7 @@ class _HfAdapter(
|
|||
ModelsProtocolPrivate,
|
||||
):
|
||||
url: str
|
||||
api_key: SecretStr
|
||||
api_key: MySecretStr
|
||||
|
||||
hf_client: AsyncInferenceClient
|
||||
max_tokens: int
|
||||
|
@ -337,7 +337,7 @@ class TGIAdapter(_HfAdapter):
|
|||
self.max_tokens = endpoint_info["max_total_tokens"]
|
||||
self.model_id = endpoint_info["model_id"]
|
||||
self.url = f"{config.url.rstrip('/')}/v1"
|
||||
self.api_key = SecretStr("NO_KEY")
|
||||
self.api_key = MySecretStr("NO_KEY")
|
||||
|
||||
|
||||
class InferenceAPIAdapter(_HfAdapter):
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import Field, SecretStr
|
||||
from pydantic import Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.providers.utils.inference.model_registry import RemoteInferenceProviderConfig
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
@ -18,8 +19,7 @@ class TogetherImplConfig(RemoteInferenceProviderConfig):
|
|||
default="https://api.together.xyz/v1",
|
||||
description="The URL for the Together AI server",
|
||||
)
|
||||
api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
api_key: MySecretStr = Field(
|
||||
description="The Together AI API Key",
|
||||
)
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@ from typing import Any
|
|||
|
||||
import google.auth.transport.requests
|
||||
from google.auth import default
|
||||
from pydantic import SecretStr
|
||||
|
||||
from llama_stack.apis.inference import ChatCompletionRequest
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.providers.utils.inference.litellm_openai_mixin import (
|
||||
LiteLLMOpenAIMixin,
|
||||
)
|
||||
|
@ -24,12 +24,12 @@ class VertexAIInferenceAdapter(OpenAIMixin, LiteLLMOpenAIMixin):
|
|||
LiteLLMOpenAIMixin.__init__(
|
||||
self,
|
||||
litellm_provider_name="vertex_ai",
|
||||
api_key_from_config=SecretStr(""), # Vertex AI uses ADC, not API keys
|
||||
api_key_from_config=MySecretStr(None), # Vertex AI uses ADC, not API keys
|
||||
provider_data_api_key_field="vertex_project", # Use project for validation
|
||||
)
|
||||
self.config = config
|
||||
|
||||
def get_api_key(self) -> SecretStr:
|
||||
def get_api_key(self) -> MySecretStr:
|
||||
"""
|
||||
Get an access token for Vertex AI using Application Default Credentials.
|
||||
|
||||
|
@ -40,11 +40,11 @@ class VertexAIInferenceAdapter(OpenAIMixin, LiteLLMOpenAIMixin):
|
|||
# Get default credentials - will read from GOOGLE_APPLICATION_CREDENTIALS
|
||||
credentials, _ = default(scopes=["https://www.googleapis.com/auth/cloud-platform"])
|
||||
credentials.refresh(google.auth.transport.requests.Request())
|
||||
return SecretStr(credentials.token)
|
||||
return MySecretStr(credentials.token)
|
||||
except Exception:
|
||||
# If we can't get credentials, return empty string to let LiteLLM handle it
|
||||
# This allows the LiteLLM mixin to work with ADC directly
|
||||
return SecretStr("")
|
||||
return MySecretStr("")
|
||||
|
||||
def get_base_url(self) -> str:
|
||||
"""
|
||||
|
|
|
@ -4,14 +4,15 @@
|
|||
# This source code is licensed under the terms described in the LICENSE file in
|
||||
# the root directory of this source tree.
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
|
||||
from .config import VLLMInferenceAdapterConfig
|
||||
|
||||
|
||||
class VLLMProviderDataValidator(BaseModel):
|
||||
vllm_api_token: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
vllm_api_token: MySecretStr = Field(
|
||||
description="API token for vLLM models",
|
||||
)
|
||||
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
|
||||
from pathlib import Path
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr, field_validator
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
||||
|
@ -21,8 +22,7 @@ class VLLMInferenceAdapterConfig(BaseModel):
|
|||
default=4096,
|
||||
description="Maximum number of tokens to generate.",
|
||||
)
|
||||
api_token: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
api_token: MySecretStr = Field(
|
||||
description="The API token",
|
||||
)
|
||||
tls_verify: bool | str = Field(
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
import os
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
||||
|
@ -24,8 +25,8 @@ class WatsonXConfig(BaseModel):
|
|||
default_factory=lambda: os.getenv("WATSONX_BASE_URL", "https://us-south.ml.cloud.ibm.com"),
|
||||
description="A base url for accessing the watsonx.ai",
|
||||
)
|
||||
api_key: SecretStr = Field(
|
||||
default_factory=lambda: SecretStr(os.getenv("WATSONX_API_KEY", "")),
|
||||
api_key: MySecretStr = Field(
|
||||
default_factory=lambda: MySecretStr(os.getenv("WATSONX_API_KEY", "")),
|
||||
description="The watsonx API key",
|
||||
)
|
||||
project_id: str | None = Field(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue