mirror of
https://github.com/meta-llama/llama-stack.git
synced 2025-10-03 19:57:35 +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
|
@ -14,7 +14,7 @@ NVIDIA's dataset I/O provider for accessing datasets from NVIDIA's data platform
|
|||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `api_key` | `str \| None` | No | | The NVIDIA API key. |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The NVIDIA API key. |
|
||||
| `dataset_namespace` | `str \| None` | No | default | The NVIDIA dataset namespace. |
|
||||
| `project_id` | `str \| None` | No | test-project | The NVIDIA project ID. |
|
||||
| `datasets_url` | `<class 'str'>` | No | http://nemo.test | Base URL for the NeMo Dataset API |
|
||||
|
|
|
@ -17,7 +17,7 @@ AWS S3-based file storage provider for scalable cloud file management with metad
|
|||
| `bucket_name` | `<class 'str'>` | No | | S3 bucket name to store files |
|
||||
| `region` | `<class 'str'>` | No | us-east-1 | AWS region where the bucket is located |
|
||||
| `aws_access_key_id` | `str \| None` | No | | AWS access key ID (optional if using IAM roles) |
|
||||
| `aws_secret_access_key` | `<class 'pydantic.types.SecretStr'>` | No | | AWS secret access key (optional if using IAM roles) |
|
||||
| `aws_secret_access_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | AWS secret access key (optional if using IAM roles) |
|
||||
| `endpoint_url` | `str \| None` | No | | Custom S3 endpoint URL (for MinIO, LocalStack, etc.) |
|
||||
| `auto_create_bucket` | `<class 'bool'>` | No | False | Automatically create the S3 bucket if it doesn't exist |
|
||||
| `metadata_store` | `utils.sqlstore.sqlstore.SqliteSqlStoreConfig \| utils.sqlstore.sqlstore.PostgresSqlStoreConfig` | No | sqlite | SQL store configuration for file metadata |
|
||||
|
|
|
@ -14,7 +14,7 @@ Anthropic inference provider for accessing Claude models and Anthropic's AI serv
|
|||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | API key for Anthropic models |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | API key for Anthropic models |
|
||||
|
||||
## Sample Configuration
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ https://learn.microsoft.com/en-us/azure/ai-foundry/openai/overview
|
|||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | Azure API key for Azure |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | Azure API key for Azure |
|
||||
| `api_base` | `<class 'pydantic.networks.HttpUrl'>` | No | | Azure API base for Azure (e.g., https://your-resource-name.openai.azure.com) |
|
||||
| `api_version` | `str \| None` | No | | Azure API version for Azure (e.g., 2024-12-01-preview) |
|
||||
| `api_type` | `str \| None` | No | azure | Azure API type for Azure (e.g., azure) |
|
||||
|
|
|
@ -15,8 +15,8 @@ AWS Bedrock inference provider for accessing various AI models through AWS's man
|
|||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `aws_access_key_id` | `str \| None` | No | | The AWS access key to use. Default use environment variable: AWS_ACCESS_KEY_ID |
|
||||
| `aws_secret_access_key` | `<class 'pydantic.types.SecretStr'>` | No | | The AWS secret access key to use. Default use environment variable: AWS_SECRET_ACCESS_KEY |
|
||||
| `aws_session_token` | `<class 'pydantic.types.SecretStr'>` | No | | The AWS session token to use. Default use environment variable: AWS_SESSION_TOKEN |
|
||||
| `aws_secret_access_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The AWS secret access key to use. Default use environment variable: AWS_SECRET_ACCESS_KEY |
|
||||
| `aws_session_token` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The AWS session token to use. Default use environment variable: AWS_SESSION_TOKEN |
|
||||
| `region_name` | `str \| None` | No | | The default AWS Region to use, for example, us-west-1 or us-west-2.Default use environment variable: AWS_DEFAULT_REGION |
|
||||
| `profile_name` | `str \| None` | No | | The profile name that contains credentials to use.Default use environment variable: AWS_PROFILE |
|
||||
| `total_max_attempts` | `int \| None` | No | | An integer representing the maximum number of attempts that will be made for a single request, including the initial attempt. Default use environment variable: AWS_MAX_ATTEMPTS |
|
||||
|
|
|
@ -15,7 +15,7 @@ Cerebras inference provider for running models on Cerebras Cloud platform.
|
|||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `base_url` | `<class 'str'>` | No | https://api.cerebras.ai | Base URL for the Cerebras API |
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | Cerebras API Key |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | Cerebras API Key |
|
||||
|
||||
## Sample Configuration
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ Databricks inference provider for running models on Databricks' unified analytic
|
|||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `url` | `<class 'str'>` | No | | The URL for the Databricks model serving endpoint |
|
||||
| `api_token` | `<class 'pydantic.types.SecretStr'>` | No | | The Databricks API token |
|
||||
| `api_token` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The Databricks API token |
|
||||
|
||||
## Sample Configuration
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ Fireworks AI inference provider for Llama models and other AI models on the Fire
|
|||
|-------|------|----------|---------|-------------|
|
||||
| `allowed_models` | `list[str \| None` | No | | List of models that should be registered with the model registry. If None, all models are allowed. |
|
||||
| `url` | `<class 'str'>` | No | https://api.fireworks.ai/inference/v1 | The URL for the Fireworks server |
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | The Fireworks.ai API Key |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The Fireworks.ai API Key |
|
||||
|
||||
## Sample Configuration
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ Google Gemini inference provider for accessing Gemini models and Google's AI ser
|
|||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | API key for Gemini models |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | API key for Gemini models |
|
||||
|
||||
## Sample Configuration
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ Groq inference provider for ultra-fast inference using Groq's LPU technology.
|
|||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | The Groq API key |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The Groq API key |
|
||||
| `url` | `<class 'str'>` | No | https://api.groq.com | The URL for the Groq AI server |
|
||||
|
||||
## Sample Configuration
|
||||
|
|
|
@ -15,7 +15,7 @@ HuggingFace Inference Endpoints provider for dedicated model serving.
|
|||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `endpoint_name` | `<class 'str'>` | No | | 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` | `pydantic.types.SecretStr \| None` | No | | Your Hugging Face user access token (will default to locally saved token if not provided) |
|
||||
| `api_token` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | Your Hugging Face user access token (will default to locally saved token if not provided) |
|
||||
|
||||
## Sample Configuration
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ HuggingFace Inference API serverless provider for on-demand model inference.
|
|||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `huggingface_repo` | `<class 'str'>` | No | | The model ID of the model on the Hugging Face Hub (e.g. 'meta-llama/Meta-Llama-3.1-70B-Instruct') |
|
||||
| `api_token` | `pydantic.types.SecretStr \| None` | No | | Your Hugging Face user access token (will default to locally saved token if not provided) |
|
||||
| `api_token` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | Your Hugging Face user access token (will default to locally saved token if not provided) |
|
||||
|
||||
## Sample Configuration
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ Llama OpenAI-compatible provider for using Llama models with OpenAI API format.
|
|||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | The Llama API key |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The Llama API key |
|
||||
| `openai_compat_api_base` | `<class 'str'>` | No | https://api.llama.com/compat/v1/ | The URL for the Llama API server |
|
||||
|
||||
## Sample Configuration
|
||||
|
|
|
@ -15,7 +15,7 @@ NVIDIA inference provider for accessing NVIDIA NIM models and AI services.
|
|||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `url` | `<class 'str'>` | No | https://integrate.api.nvidia.com | A base url for accessing the NVIDIA NIM |
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | The NVIDIA API key, only needed of using the hosted service |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The NVIDIA API key, only needed of using the hosted service |
|
||||
| `timeout` | `<class 'int'>` | No | 60 | Timeout for the HTTP requests |
|
||||
| `append_api_version` | `<class 'bool'>` | No | True | When set to false, the API version will not be appended to the base_url. By default, it is true. |
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ OpenAI inference provider for accessing GPT models and other OpenAI services.
|
|||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | API key for OpenAI models |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | API key for OpenAI models |
|
||||
| `base_url` | `<class 'str'>` | No | https://api.openai.com/v1 | Base URL for OpenAI API |
|
||||
|
||||
## Sample Configuration
|
||||
|
|
|
@ -15,7 +15,7 @@ Passthrough inference provider for connecting to any external inference service
|
|||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `url` | `<class 'str'>` | No | | The URL for the passthrough endpoint |
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | API Key for the passthrouth endpoint |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | API Key for the passthrouth endpoint |
|
||||
|
||||
## Sample Configuration
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ RunPod inference provider for running models on RunPod's cloud GPU platform.
|
|||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `url` | `str \| None` | No | | The URL for the Runpod model serving endpoint |
|
||||
| `api_token` | `<class 'pydantic.types.SecretStr'>` | No | | The API token |
|
||||
| `api_token` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The API token |
|
||||
|
||||
## Sample Configuration
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ SambaNova inference provider for running models on SambaNova's dataflow architec
|
|||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `url` | `<class 'str'>` | No | https://api.sambanova.ai/v1 | The URL for the SambaNova AI server |
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | The SambaNova cloud API Key |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The SambaNova cloud API Key |
|
||||
|
||||
## Sample Configuration
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ Together AI inference provider for open-source models and collaborative AI devel
|
|||
|-------|------|----------|---------|-------------|
|
||||
| `allowed_models` | `list[str \| None` | No | | List of models that should be registered with the model registry. If None, all models are allowed. |
|
||||
| `url` | `<class 'str'>` | No | https://api.together.xyz/v1 | The URL for the Together AI server |
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | The Together AI API Key |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The Together AI API Key |
|
||||
|
||||
## Sample Configuration
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ Remote vLLM inference provider for connecting to vLLM servers.
|
|||
|-------|------|----------|---------|-------------|
|
||||
| `url` | `str \| None` | No | | The URL for the vLLM model serving endpoint |
|
||||
| `max_tokens` | `<class 'int'>` | No | 4096 | Maximum number of tokens to generate. |
|
||||
| `api_token` | `<class 'pydantic.types.SecretStr'>` | No | | The API token |
|
||||
| `api_token` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The API token |
|
||||
| `tls_verify` | `bool \| str` | No | True | Whether to verify TLS certificates. Can be a boolean or a path to a CA certificate file. |
|
||||
| `refresh_models` | `<class 'bool'>` | No | False | Whether to refresh models periodically |
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ IBM WatsonX inference provider for accessing AI models on IBM's WatsonX platform
|
|||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `url` | `<class 'str'>` | No | https://us-south.ml.cloud.ibm.com | A base url for accessing the watsonx.ai |
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | The watsonx API key |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The watsonx API key |
|
||||
| `project_id` | `str \| None` | No | | The Project ID key |
|
||||
| `timeout` | `<class 'int'>` | No | 60 | Timeout for the HTTP requests |
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ NVIDIA's post-training provider for fine-tuning models on NVIDIA's platform.
|
|||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `api_key` | `str \| None` | No | | The NVIDIA API key. |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The NVIDIA API key. |
|
||||
| `dataset_namespace` | `str \| None` | No | default | The NVIDIA dataset namespace. |
|
||||
| `project_id` | `str \| None` | No | test-example-model@v1 | The NVIDIA project ID. |
|
||||
| `customizer_url` | `str \| None` | No | | Base URL for the NeMo Customizer API |
|
||||
|
|
|
@ -15,8 +15,8 @@ AWS Bedrock safety provider for content moderation using AWS's safety services.
|
|||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `aws_access_key_id` | `str \| None` | No | | The AWS access key to use. Default use environment variable: AWS_ACCESS_KEY_ID |
|
||||
| `aws_secret_access_key` | `<class 'pydantic.types.SecretStr'>` | No | | The AWS secret access key to use. Default use environment variable: AWS_SECRET_ACCESS_KEY |
|
||||
| `aws_session_token` | `<class 'pydantic.types.SecretStr'>` | No | | The AWS session token to use. Default use environment variable: AWS_SESSION_TOKEN |
|
||||
| `aws_secret_access_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The AWS secret access key to use. Default use environment variable: AWS_SECRET_ACCESS_KEY |
|
||||
| `aws_session_token` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The AWS session token to use. Default use environment variable: AWS_SESSION_TOKEN |
|
||||
| `region_name` | `str \| None` | No | | The default AWS Region to use, for example, us-west-1 or us-west-2.Default use environment variable: AWS_DEFAULT_REGION |
|
||||
| `profile_name` | `str \| None` | No | | The profile name that contains credentials to use.Default use environment variable: AWS_PROFILE |
|
||||
| `total_max_attempts` | `int \| None` | No | | An integer representing the maximum number of attempts that will be made for a single request, including the initial attempt. Default use environment variable: AWS_MAX_ATTEMPTS |
|
||||
|
|
|
@ -15,7 +15,7 @@ SambaNova's safety provider for content moderation and safety filtering.
|
|||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `url` | `<class 'str'>` | No | https://api.sambanova.ai/v1 | The URL for the SambaNova AI server |
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | The SambaNova cloud API Key |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The SambaNova cloud API Key |
|
||||
|
||||
## Sample Configuration
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ Braintrust scoring provider for evaluation and scoring using the Braintrust plat
|
|||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `openai_api_key` | `<class 'pydantic.types.SecretStr'>` | No | | The OpenAI API Key |
|
||||
| `openai_api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The OpenAI API Key |
|
||||
|
||||
## Sample Configuration
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ Bing Search tool for web search capabilities using Microsoft's search engine.
|
|||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | The Bing API key |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The Bing API key |
|
||||
| `top_k` | `<class 'int'>` | No | 3 | |
|
||||
|
||||
## Sample Configuration
|
||||
|
|
|
@ -14,7 +14,7 @@ Brave Search tool for web search capabilities with privacy-focused results.
|
|||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | The Brave Search API Key |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The Brave Search API Key |
|
||||
| `max_results` | `<class 'int'>` | No | 3 | The maximum number of results to return |
|
||||
|
||||
## Sample Configuration
|
||||
|
|
|
@ -14,7 +14,7 @@ Tavily Search tool for AI-optimized web search with structured results.
|
|||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `api_key` | `<class 'pydantic.types.SecretStr'>` | No | | The Tavily Search API Key |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The Tavily Search API Key |
|
||||
| `max_results` | `<class 'int'>` | No | 3 | The maximum number of results to return |
|
||||
|
||||
## Sample Configuration
|
||||
|
|
|
@ -14,7 +14,7 @@ Wolfram Alpha tool for computational knowledge and mathematical calculations.
|
|||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `api_key` | `str \| None` | No | | |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The WolframAlpha API Key |
|
||||
|
||||
## Sample Configuration
|
||||
|
||||
|
|
|
@ -217,7 +217,7 @@ See [PGVector's documentation](https://github.com/pgvector/pgvector) for more de
|
|||
| `port` | `int \| None` | No | 5432 | |
|
||||
| `db` | `str \| None` | No | postgres | |
|
||||
| `user` | `str \| None` | No | postgres | |
|
||||
| `password` | `<class 'pydantic.types.SecretStr'>` | No | ********** | |
|
||||
| `password` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | ********** | |
|
||||
| `kvstore` | `utils.kvstore.config.RedisKVStoreConfig \| utils.kvstore.config.SqliteKVStoreConfig \| utils.kvstore.config.PostgresKVStoreConfig \| utils.kvstore.config.MongoDBKVStoreConfig, annotation=NoneType, required=False, default='sqlite', discriminator='type'` | No | | Config for KV store backend (SQLite only for now) |
|
||||
|
||||
## Sample Configuration
|
||||
|
|
|
@ -22,7 +22,7 @@ Please refer to the inline provider documentation.
|
|||
| `grpc_port` | `<class 'int'>` | No | 6334 | |
|
||||
| `prefer_grpc` | `<class 'bool'>` | No | False | |
|
||||
| `https` | `bool \| None` | No | | |
|
||||
| `api_key` | `str \| None` | No | | |
|
||||
| `api_key` | `<class 'llama_stack.core.secret_types.MySecretStr'>` | No | | The API key for the Qdrant instance |
|
||||
| `prefix` | `str \| None` | No | | |
|
||||
| `timeout` | `int \| None` | No | | |
|
||||
| `host` | `str \| None` | No | | |
|
||||
|
|
|
@ -216,7 +216,7 @@ def run_stack_build_command(args: argparse.Namespace) -> None:
|
|||
with open(args.config) as f:
|
||||
try:
|
||||
contents = yaml.safe_load(f)
|
||||
contents = replace_env_vars(contents)
|
||||
contents = replace_env_vars(contents, provider_registry=get_provider_registry())
|
||||
build_config = BuildConfig(**contents)
|
||||
if args.image_type:
|
||||
build_config.image_type = args.image_type
|
||||
|
|
|
@ -165,7 +165,7 @@ def upgrade_from_routing_table(
|
|||
def parse_and_maybe_upgrade_config(config_dict: dict[str, Any]) -> StackRunConfig:
|
||||
version = config_dict.get("version", None)
|
||||
if version == LLAMA_STACK_RUN_CONFIG_VERSION:
|
||||
processed_config_dict = replace_env_vars(config_dict)
|
||||
processed_config_dict = replace_env_vars(config_dict, provider_registry=get_provider_registry())
|
||||
return StackRunConfig(**cast_image_name_to_string(processed_config_dict))
|
||||
|
||||
if "routing_table" in config_dict:
|
||||
|
@ -177,5 +177,5 @@ def parse_and_maybe_upgrade_config(config_dict: dict[str, Any]) -> StackRunConfi
|
|||
if not config_dict.get("external_providers_dir", None):
|
||||
config_dict["external_providers_dir"] = EXTERNAL_PROVIDERS_DIR
|
||||
|
||||
processed_config_dict = replace_env_vars(config_dict)
|
||||
processed_config_dict = replace_env_vars(config_dict, provider_registry=get_provider_registry())
|
||||
return StackRunConfig(**cast_image_name_to_string(processed_config_dict))
|
||||
|
|
|
@ -33,6 +33,7 @@ from termcolor import cprint
|
|||
from llama_stack.core.build import print_pip_install_help
|
||||
from llama_stack.core.configure import parse_and_maybe_upgrade_config
|
||||
from llama_stack.core.datatypes import Api, BuildConfig, BuildProvider, DistributionSpec
|
||||
from llama_stack.core.distribution import get_provider_registry
|
||||
from llama_stack.core.request_headers import (
|
||||
PROVIDER_DATA_VAR,
|
||||
request_provider_data_context,
|
||||
|
@ -220,7 +221,9 @@ class AsyncLlamaStackAsLibraryClient(AsyncLlamaStackClient):
|
|||
config_path = Path(config_path_or_distro_name)
|
||||
if not config_path.exists():
|
||||
raise ValueError(f"Config file {config_path} does not exist")
|
||||
config_dict = replace_env_vars(yaml.safe_load(config_path.read_text()))
|
||||
config_dict = replace_env_vars(
|
||||
yaml.safe_load(config_path.read_text()), provider_registry=get_provider_registry()
|
||||
)
|
||||
config = parse_and_maybe_upgrade_config(config_dict)
|
||||
else:
|
||||
# distribution
|
||||
|
|
21
llama_stack/core/secret_types.py
Normal file
21
llama_stack/core/secret_types.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the terms described in the LICENSE file in
|
||||
# the root directory of this source tree.
|
||||
|
||||
from pydantic.types import SecretStr
|
||||
|
||||
|
||||
class MySecretStr(SecretStr):
|
||||
"""A SecretStr that can accept None values to avoid mypy type errors.
|
||||
|
||||
This is useful for optional secret fields where you want to avoid
|
||||
explicit None checks in consuming code.
|
||||
|
||||
We chose to not use the SecretStr from pydantic because it does not allow None values and will
|
||||
let the provider's library fail if the secret is not provided.
|
||||
"""
|
||||
|
||||
def __init__(self, secret_value: str | None = None) -> None:
|
||||
SecretStr.__init__(self, secret_value) # type: ignore[arg-type]
|
|
@ -43,7 +43,7 @@ from llama_stack.core.datatypes import (
|
|||
StackRunConfig,
|
||||
process_cors_config,
|
||||
)
|
||||
from llama_stack.core.distribution import builtin_automatically_routed_apis
|
||||
from llama_stack.core.distribution import builtin_automatically_routed_apis, get_provider_registry
|
||||
from llama_stack.core.external import load_external_apis
|
||||
from llama_stack.core.request_headers import (
|
||||
PROVIDER_DATA_VAR,
|
||||
|
@ -371,7 +371,7 @@ def create_app(
|
|||
logger.error(f"Error: {str(e)}")
|
||||
raise ValueError(f"Invalid environment variable format: {env_pair}") from e
|
||||
|
||||
config = replace_env_vars(config_contents)
|
||||
config = replace_env_vars(config_contents, provider_registry=get_provider_registry())
|
||||
config = StackRunConfig(**cast_image_name_to_string(config))
|
||||
|
||||
_log_run_config(run_config=config)
|
||||
|
@ -524,7 +524,10 @@ def main(args: argparse.Namespace | None = None):
|
|||
env_vars=args.env,
|
||||
)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
|
||||
logger.error(f"Error creating app: {str(e)}")
|
||||
logger.error(f"Stack trace:\n{traceback.format_exc()}")
|
||||
sys.exit(1)
|
||||
|
||||
config_file = resolve_config_or_distro(config_or_distro, Mode.RUN)
|
||||
|
@ -534,7 +537,9 @@ def main(args: argparse.Namespace | None = None):
|
|||
logger_config = LoggingConfig(**cfg)
|
||||
else:
|
||||
logger_config = None
|
||||
config = StackRunConfig(**cast_image_name_to_string(replace_env_vars(config_contents)))
|
||||
config = StackRunConfig(
|
||||
**cast_image_name_to_string(replace_env_vars(config_contents, provider_registry=get_provider_registry()))
|
||||
)
|
||||
|
||||
import uvicorn
|
||||
|
||||
|
|
|
@ -141,12 +141,19 @@ class EnvVarError(Exception):
|
|||
)
|
||||
|
||||
|
||||
def replace_env_vars(config: Any, path: str = "") -> Any:
|
||||
def replace_env_vars(
|
||||
config: Any,
|
||||
path: str = "",
|
||||
provider_registry: dict[Api, dict[str, Any]] | None = None,
|
||||
current_provider_context: dict[str, Any] | None = None,
|
||||
) -> Any:
|
||||
if isinstance(config, dict):
|
||||
result = {}
|
||||
for k, v in config.items():
|
||||
try:
|
||||
result[k] = replace_env_vars(v, f"{path}.{k}" if path else k)
|
||||
result[k] = replace_env_vars(
|
||||
v, f"{path}.{k}" if path else k, provider_registry, current_provider_context
|
||||
)
|
||||
except EnvVarError as e:
|
||||
raise EnvVarError(e.var_name, e.path) from None
|
||||
return result
|
||||
|
@ -159,7 +166,9 @@ def replace_env_vars(config: Any, path: str = "") -> Any:
|
|||
# is disabled so that we can skip config env variable expansion and avoid validation errors
|
||||
if isinstance(v, dict) and "provider_id" in v:
|
||||
try:
|
||||
resolved_provider_id = replace_env_vars(v["provider_id"], f"{path}[{i}].provider_id")
|
||||
resolved_provider_id = replace_env_vars(
|
||||
v["provider_id"], f"{path}[{i}].provider_id", provider_registry, current_provider_context
|
||||
)
|
||||
if resolved_provider_id == "__disabled__":
|
||||
logger.debug(
|
||||
f"Skipping config env variable expansion for disabled provider: {v.get('provider_id', '')}"
|
||||
|
@ -167,13 +176,19 @@ def replace_env_vars(config: Any, path: str = "") -> Any:
|
|||
# Create a copy with resolved provider_id but original config
|
||||
disabled_provider = v.copy()
|
||||
disabled_provider["provider_id"] = resolved_provider_id
|
||||
result.append(disabled_provider)
|
||||
continue
|
||||
except EnvVarError:
|
||||
# If we can't resolve the provider_id, continue with normal processing
|
||||
pass
|
||||
|
||||
# Set up provider context for config processing
|
||||
provider_context = current_provider_context
|
||||
if isinstance(v, dict) and "provider_id" in v and "provider_type" in v and provider_registry:
|
||||
provider_context = _get_provider_context(v, provider_registry)
|
||||
|
||||
# Normal processing for non-disabled providers
|
||||
result.append(replace_env_vars(v, f"{path}[{i}]"))
|
||||
result.append(replace_env_vars(v, f"{path}[{i}]", provider_registry, provider_context))
|
||||
except EnvVarError as e:
|
||||
raise EnvVarError(e.var_name, e.path) from None
|
||||
return result
|
||||
|
@ -228,7 +243,7 @@ def replace_env_vars(config: Any, path: str = "") -> Any:
|
|||
result = re.sub(pattern, get_env_var, config)
|
||||
# Only apply type conversion if substitution actually happened
|
||||
if result != config:
|
||||
return _convert_string_to_proper_type(result)
|
||||
return _convert_string_to_proper_type_with_config(result, path, current_provider_context)
|
||||
return result
|
||||
except EnvVarError as e:
|
||||
raise EnvVarError(e.var_name, e.path) from None
|
||||
|
@ -236,12 +251,107 @@ def replace_env_vars(config: Any, path: str = "") -> Any:
|
|||
return config
|
||||
|
||||
|
||||
def _get_provider_context(
|
||||
provider_dict: dict[str, Any], provider_registry: dict[Api, dict[str, Any]]
|
||||
) -> dict[str, Any] | None:
|
||||
"""Get provider context information including config class for type conversion."""
|
||||
try:
|
||||
provider_type = provider_dict.get("provider_type")
|
||||
if not provider_type:
|
||||
return None
|
||||
|
||||
for api, providers in provider_registry.items():
|
||||
if provider_type in providers:
|
||||
provider_spec = providers[provider_type]
|
||||
|
||||
config_class = instantiate_class_type(provider_spec.config_class)
|
||||
|
||||
return {
|
||||
"api": api,
|
||||
"provider_type": provider_type,
|
||||
"config_class": config_class,
|
||||
"provider_spec": provider_spec,
|
||||
}
|
||||
except Exception as e:
|
||||
logger.debug(f"Failed to get provider context: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def _convert_string_to_proper_type_with_config(value: str, path: str, provider_context: dict[str, Any] | None) -> Any:
|
||||
"""Convert string to proper type using provider config class field information."""
|
||||
if not provider_context or not provider_context.get("config_class"):
|
||||
# best effort conversion if we don't have the config class
|
||||
return _convert_string_to_proper_type(value)
|
||||
|
||||
try:
|
||||
# Extract field name from path (e.g., "providers.inference[0].config.api_key" -> "api_key")
|
||||
field_name = path.split(".")[-1] if "." in path else path
|
||||
|
||||
config_class = provider_context["config_class"]
|
||||
|
||||
if hasattr(config_class, "model_fields") and field_name in config_class.model_fields:
|
||||
field_info = config_class.model_fields[field_name]
|
||||
field_type = field_info.annotation
|
||||
return _convert_value_by_field_type(value, field_type)
|
||||
else:
|
||||
return _convert_string_to_proper_type(value)
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"Failed to convert using config class: {e}")
|
||||
return _convert_string_to_proper_type(value)
|
||||
|
||||
|
||||
def _convert_value_by_field_type(value: str, field_type: Any) -> Any:
|
||||
"""Convert string value based on Pydantic field type annotation."""
|
||||
import typing
|
||||
from typing import get_args, get_origin
|
||||
|
||||
if value == "":
|
||||
if field_type is None or (hasattr(typing, "get_origin") and get_origin(field_type) is type(None)):
|
||||
return None
|
||||
if hasattr(typing, "get_origin") and get_origin(field_type) is typing.Union:
|
||||
args = get_args(field_type)
|
||||
if type(None) in args:
|
||||
return None
|
||||
return ""
|
||||
|
||||
if field_type is bool or (hasattr(typing, "get_origin") and get_origin(field_type) is bool):
|
||||
lowered = value.lower()
|
||||
if lowered == "true":
|
||||
return True
|
||||
elif lowered == "false":
|
||||
return False
|
||||
else:
|
||||
return value
|
||||
|
||||
if field_type is int or (hasattr(typing, "get_origin") and get_origin(field_type) is int):
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
return value
|
||||
|
||||
if field_type is float or (hasattr(typing, "get_origin") and get_origin(field_type) is float):
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
return value
|
||||
|
||||
if hasattr(typing, "get_origin") and get_origin(field_type) is typing.Union:
|
||||
args = get_args(field_type)
|
||||
# Try to convert to the first non-None type
|
||||
for arg in args:
|
||||
if arg is not type(None):
|
||||
try:
|
||||
return _convert_value_by_field_type(value, arg)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def _convert_string_to_proper_type(value: str) -> Any:
|
||||
# This might be tricky depending on what the config type is, if 'str | None' we are
|
||||
# good, if 'str' we need to keep the empty string... 'str | None' is more common and
|
||||
# providers config should be typed this way.
|
||||
# TODO: we could try to load the config class and see if the config has a field with type 'str | None'
|
||||
# and then convert the empty string to None or not
|
||||
# Fallback function for when provider config class is not available
|
||||
# The main type conversion logic is now in _convert_string_to_proper_type_with_config
|
||||
if value == "":
|
||||
return None
|
||||
|
||||
|
@ -416,7 +526,7 @@ def get_stack_run_config_from_distro(distro: str) -> StackRunConfig:
|
|||
raise ValueError(f"Distribution '{distro}' not found at {distro_path}")
|
||||
run_config = yaml.safe_load(path.open())
|
||||
|
||||
return StackRunConfig(**replace_env_vars(run_config))
|
||||
return StackRunConfig(**replace_env_vars(run_config, provider_registry=get_provider_registry()))
|
||||
|
||||
|
||||
def run_config_from_adhoc_config_spec(
|
||||
|
@ -452,7 +562,9 @@ def run_config_from_adhoc_config_spec(
|
|||
|
||||
# call method "sample_run_config" on the provider spec config class
|
||||
provider_config_type = instantiate_class_type(provider_spec.config_class)
|
||||
provider_config = replace_env_vars(provider_config_type.sample_run_config(__distro_dir__=distro_dir))
|
||||
provider_config = replace_env_vars(
|
||||
provider_config_type.sample_run_config(__distro_dir__=distro_dir), provider_registry=provider_registry
|
||||
)
|
||||
|
||||
provider_configs_by_api[api_str] = [
|
||||
Provider(
|
||||
|
|
|
@ -5,15 +5,16 @@
|
|||
# the root directory of this source tree.
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, SecretStr
|
||||
from pydantic import BaseModel
|
||||
|
||||
from llama_stack.core.datatypes import Api
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
|
||||
from .config import BraintrustScoringConfig
|
||||
|
||||
|
||||
class BraintrustProviderDataValidator(BaseModel):
|
||||
openai_api_key: SecretStr
|
||||
openai_api_key: MySecretStr
|
||||
|
||||
|
||||
async def get_provider_impl(
|
||||
|
|
|
@ -17,7 +17,7 @@ from autoevals.ragas import (
|
|||
ContextRelevancy,
|
||||
Faithfulness,
|
||||
)
|
||||
from pydantic import BaseModel, SecretStr
|
||||
from pydantic import BaseModel
|
||||
|
||||
from llama_stack.apis.datasetio import DatasetIO
|
||||
from llama_stack.apis.datasets import Datasets
|
||||
|
@ -31,6 +31,7 @@ from llama_stack.apis.scoring import (
|
|||
from llama_stack.apis.scoring_functions import ScoringFn, ScoringFnParams
|
||||
from llama_stack.core.datatypes import Api
|
||||
from llama_stack.core.request_headers import NeedsRequestProviderData
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.providers.datatypes import ScoringFunctionsProtocolPrivate
|
||||
from llama_stack.providers.utils.common.data_schema_validator import (
|
||||
get_valid_schemas,
|
||||
|
@ -152,7 +153,7 @@ class BraintrustScoringImpl(
|
|||
raise ValueError(
|
||||
'Pass OpenAI API Key in the header X-LlamaStack-Provider-Data as { "openai_api_key": <your api key>}'
|
||||
)
|
||||
self.config.openai_api_key = SecretStr(provider_data.openai_api_key)
|
||||
self.config.openai_api_key = MySecretStr(provider_data.openai_api_key)
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = self.config.openai_api_key.get_secret_value()
|
||||
|
||||
|
|
|
@ -5,12 +5,13 @@
|
|||
# the root directory of this source tree.
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
|
||||
|
||||
class BraintrustScoringConfig(BaseModel):
|
||||
openai_api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
openai_api_key: MySecretStr = Field(
|
||||
description="The OpenAI API Key",
|
||||
)
|
||||
|
||||
|
|
|
@ -65,8 +65,7 @@ class ConsoleSpanProcessor(SpanProcessor):
|
|||
if key.startswith("__") or key in ["message", "severity"]:
|
||||
continue
|
||||
|
||||
str_value = str(value)
|
||||
logger.info(f"[dim]{key}[/dim]: {str_value}")
|
||||
logger.info(f"[dim]{key}[/dim]: {value}")
|
||||
|
||||
def shutdown(self) -> None:
|
||||
"""Shutdown the processor."""
|
||||
|
|
|
@ -10,12 +10,14 @@ from typing import Any
|
|||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
|
||||
|
||||
class NvidiaDatasetIOConfig(BaseModel):
|
||||
"""Configuration for NVIDIA DatasetIO implementation."""
|
||||
|
||||
api_key: str | None = Field(
|
||||
default_factory=lambda: os.getenv("NVIDIA_API_KEY"),
|
||||
api_key: MySecretStr = Field(
|
||||
default_factory=lambda: MySecretStr(os.getenv("NVIDIA_API_KEY", "")),
|
||||
description="The NVIDIA 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.providers.utils.sqlstore.sqlstore import SqliteSqlStoreConfig, SqlStoreConfig
|
||||
|
||||
|
||||
|
@ -17,9 +18,7 @@ class S3FilesImplConfig(BaseModel):
|
|||
bucket_name: str = Field(description="S3 bucket name to store files")
|
||||
region: str = Field(default="us-east-1", description="AWS region where the bucket is located")
|
||||
aws_access_key_id: str | None = Field(default=None, description="AWS access key ID (optional if using IAM roles)")
|
||||
aws_secret_access_key: SecretStr = Field(
|
||||
default=SecretStr(""), description="AWS secret access key (optional if using IAM roles)"
|
||||
)
|
||||
aws_secret_access_key: MySecretStr = Field(description="AWS secret access key (optional if using IAM roles)")
|
||||
endpoint_url: str | None = Field(default=None, description="Custom S3 endpoint URL (for MinIO, LocalStack, etc.)")
|
||||
auto_create_bucket: bool = Field(
|
||||
default=False, description="Automatically create the S3 bucket if it doesn't exist"
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -9,14 +9,16 @@ from typing import Any
|
|||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
|
||||
# TODO: add default values for all fields
|
||||
|
||||
|
||||
class NvidiaPostTrainingConfig(BaseModel):
|
||||
"""Configuration for NVIDIA Post Training implementation."""
|
||||
|
||||
api_key: str | None = Field(
|
||||
default_factory=lambda: os.getenv("NVIDIA_API_KEY"),
|
||||
api_key: MySecretStr = Field(
|
||||
default_factory=lambda: MySecretStr(os.getenv("NVIDIA_API_KEY", "")),
|
||||
description="The NVIDIA API key.",
|
||||
)
|
||||
|
||||
|
|
|
@ -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 SambaNovaSafetyConfig(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",
|
||||
)
|
||||
|
||||
|
|
|
@ -6,14 +6,15 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
|
||||
|
||||
class BingSearchToolConfig(BaseModel):
|
||||
"""Configuration for Bing Search Tool Runtime"""
|
||||
|
||||
api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
api_key: MySecretStr = Field(
|
||||
description="The Bing API key",
|
||||
)
|
||||
top_k: int = 3
|
||||
|
|
|
@ -6,12 +6,13 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
|
||||
|
||||
class BraveSearchToolConfig(BaseModel):
|
||||
api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
api_key: MySecretStr = Field(
|
||||
description="The Brave Search API Key",
|
||||
)
|
||||
max_results: int = Field(
|
||||
|
|
|
@ -6,12 +6,13 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
|
||||
|
||||
class TavilySearchToolConfig(BaseModel):
|
||||
api_key: SecretStr = Field(
|
||||
default=SecretStr(""),
|
||||
api_key: MySecretStr = Field(
|
||||
description="The Tavily Search API Key",
|
||||
)
|
||||
max_results: int = Field(
|
||||
|
|
|
@ -6,13 +6,17 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
|
||||
|
||||
class WolframAlphaToolConfig(BaseModel):
|
||||
"""Configuration for WolframAlpha Tool Runtime"""
|
||||
|
||||
api_key: str | None = None
|
||||
api_key: MySecretStr = Field(
|
||||
description="The WolframAlpha API Key",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def sample_run_config(cls, __distro_dir__: str, **kwargs: Any) -> dict[str, Any]:
|
||||
|
|
|
@ -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.providers.utils.kvstore.config import (
|
||||
KVStoreConfig,
|
||||
SqliteKVStoreConfig,
|
||||
|
@ -21,7 +22,7 @@ class PGVectorVectorIOConfig(BaseModel):
|
|||
port: int | None = Field(default=5432)
|
||||
db: str | None = Field(default="postgres")
|
||||
user: str | None = Field(default="postgres")
|
||||
password: SecretStr = Field(default=SecretStr("mysecretpassword"))
|
||||
password: MySecretStr = Field(default=MySecretStr("mysecretpassword"))
|
||||
kvstore: KVStoreConfig | None = Field(description="Config for KV store backend (SQLite only for now)", default=None)
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.providers.utils.kvstore.config import (
|
||||
KVStoreConfig,
|
||||
SqliteKVStoreConfig,
|
||||
|
@ -23,7 +24,9 @@ class QdrantVectorIOConfig(BaseModel):
|
|||
grpc_port: int = 6334
|
||||
prefer_grpc: bool = False
|
||||
https: bool | None = None
|
||||
api_key: str | None = None
|
||||
api_key: MySecretStr = Field(
|
||||
description="The API key for the Qdrant instance",
|
||||
)
|
||||
prefix: str | None = None
|
||||
timeout: int | None = None
|
||||
host: str | None = None
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
import os
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
|
||||
|
||||
class BedrockBaseConfig(BaseModel):
|
||||
|
@ -14,12 +16,12 @@ class BedrockBaseConfig(BaseModel):
|
|||
default_factory=lambda: os.getenv("AWS_ACCESS_KEY_ID"),
|
||||
description="The AWS access key to use. Default use environment variable: AWS_ACCESS_KEY_ID",
|
||||
)
|
||||
aws_secret_access_key: SecretStr = Field(
|
||||
default_factory=lambda: SecretStr(os.getenv("AWS_SECRET_ACCESS_KEY", "")),
|
||||
aws_secret_access_key: MySecretStr = Field(
|
||||
default_factory=lambda: MySecretStr(os.getenv("AWS_SECRET_ACCESS_KEY", "")),
|
||||
description="The AWS secret access key to use. Default use environment variable: AWS_SECRET_ACCESS_KEY",
|
||||
)
|
||||
aws_session_token: SecretStr = Field(
|
||||
default_factory=lambda: SecretStr(os.getenv("AWS_SESSION_TOKEN", "")),
|
||||
aws_session_token: MySecretStr = Field(
|
||||
default_factory=lambda: MySecretStr(os.getenv("AWS_SESSION_TOKEN", "")),
|
||||
description="The AWS session token to use. Default use environment variable: AWS_SESSION_TOKEN",
|
||||
)
|
||||
region_name: str | None = Field(
|
||||
|
|
|
@ -8,7 +8,6 @@ from collections.abc import AsyncGenerator, AsyncIterator
|
|||
from typing import Any
|
||||
|
||||
import litellm
|
||||
from pydantic import SecretStr
|
||||
|
||||
from llama_stack.apis.common.content_types import (
|
||||
InterleavedContent,
|
||||
|
@ -40,6 +39,7 @@ from llama_stack.apis.inference import (
|
|||
ToolPromptFormat,
|
||||
)
|
||||
from llama_stack.core.request_headers import NeedsRequestProviderData
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.log import get_logger
|
||||
from llama_stack.providers.utils.inference.model_registry import ModelRegistryHelper, ProviderModelEntry
|
||||
from llama_stack.providers.utils.inference.openai_compat import (
|
||||
|
@ -69,7 +69,7 @@ class LiteLLMOpenAIMixin(
|
|||
def __init__(
|
||||
self,
|
||||
litellm_provider_name: str,
|
||||
api_key_from_config: SecretStr,
|
||||
api_key_from_config: MySecretStr,
|
||||
provider_data_api_key_field: str,
|
||||
model_entries: list[ProviderModelEntry] | None = None,
|
||||
openai_compat_api_base: str | None = None,
|
||||
|
@ -255,7 +255,7 @@ class LiteLLMOpenAIMixin(
|
|||
**get_sampling_options(request.sampling_params),
|
||||
}
|
||||
|
||||
def get_api_key(self) -> SecretStr:
|
||||
def get_api_key(self) -> MySecretStr:
|
||||
provider_data = self.get_request_provider_data()
|
||||
key_field = self.provider_data_api_key_field
|
||||
if provider_data and getattr(provider_data, key_field, None):
|
||||
|
|
|
@ -11,7 +11,6 @@ from collections.abc import AsyncIterator
|
|||
from typing import Any
|
||||
|
||||
from openai import NOT_GIVEN, AsyncOpenAI
|
||||
from pydantic import SecretStr
|
||||
|
||||
from llama_stack.apis.inference import (
|
||||
Model,
|
||||
|
@ -25,6 +24,7 @@ from llama_stack.apis.inference import (
|
|||
OpenAIResponseFormatParam,
|
||||
)
|
||||
from llama_stack.apis.models import ModelType
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.log import get_logger
|
||||
from llama_stack.providers.utils.inference.model_registry import ModelRegistryHelper
|
||||
from llama_stack.providers.utils.inference.openai_compat import prepare_openai_completion_params
|
||||
|
@ -71,14 +71,14 @@ class OpenAIMixin(ModelRegistryHelper, ABC):
|
|||
allowed_models: list[str] = []
|
||||
|
||||
@abstractmethod
|
||||
def get_api_key(self) -> SecretStr:
|
||||
def get_api_key(self) -> MySecretStr:
|
||||
"""
|
||||
Get the API key.
|
||||
|
||||
This method must be implemented by child classes to provide the API key
|
||||
for authenticating with the OpenAI API or compatible endpoints.
|
||||
|
||||
:return: The API key as a SecretStr
|
||||
:return: The API key as a MySecretStr
|
||||
"""
|
||||
pass
|
||||
|
||||
|
@ -114,7 +114,7 @@ class OpenAIMixin(ModelRegistryHelper, ABC):
|
|||
implemented by child classes.
|
||||
"""
|
||||
return AsyncOpenAI(
|
||||
api_key=self.get_api_key(),
|
||||
api_key=self.get_api_key().get_secret_value(),
|
||||
base_url=self.get_base_url(),
|
||||
**self.get_extra_client_params(),
|
||||
)
|
||||
|
|
|
@ -8,8 +8,9 @@ import re
|
|||
from enum import Enum
|
||||
from typing import Annotated, Literal
|
||||
|
||||
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.core.utils.config_dirs import RUNTIME_BASE_DIR
|
||||
|
||||
|
||||
|
@ -74,7 +75,7 @@ class PostgresKVStoreConfig(CommonConfig):
|
|||
port: int = 5432
|
||||
db: str = "llamastack"
|
||||
user: str
|
||||
password: SecretStr = SecretStr("")
|
||||
password: MySecretStr = MySecretStr("")
|
||||
ssl_mode: str | None = None
|
||||
ca_cert_path: str | None = None
|
||||
table_name: str = "llamastack_kvstore"
|
||||
|
@ -118,7 +119,7 @@ class MongoDBKVStoreConfig(CommonConfig):
|
|||
port: int = 27017
|
||||
db: str = "llamastack"
|
||||
user: str | None = None
|
||||
password: SecretStr = SecretStr("")
|
||||
password: MySecretStr = MySecretStr("")
|
||||
collection_name: str = "llamastack_kvstore"
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -9,8 +9,9 @@ from enum import StrEnum
|
|||
from pathlib import Path
|
||||
from typing import Annotated, Literal
|
||||
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.core.utils.config_dirs import RUNTIME_BASE_DIR
|
||||
|
||||
from .api import SqlStore
|
||||
|
@ -63,7 +64,7 @@ class PostgresSqlStoreConfig(SqlAlchemySqlStoreConfig):
|
|||
port: int = 5432
|
||||
db: str = "llamastack"
|
||||
user: str
|
||||
password: SecretStr = SecretStr("")
|
||||
password: MySecretStr = MySecretStr("")
|
||||
|
||||
@property
|
||||
def engine_str(self) -> str:
|
||||
|
|
|
@ -8,19 +8,20 @@ import json
|
|||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.request_headers import request_provider_data_context
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
from llama_stack.providers.utils.inference.litellm_openai_mixin import LiteLLMOpenAIMixin
|
||||
|
||||
|
||||
# Test fixtures and helper classes
|
||||
class TestConfig(BaseModel):
|
||||
api_key: SecretStr | None = Field(default=None)
|
||||
api_key: MySecretStr | None = Field(default=None)
|
||||
|
||||
|
||||
class TestProviderDataValidator(BaseModel):
|
||||
test_api_key: SecretStr | None = Field(default=None)
|
||||
test_api_key: MySecretStr | None = Field(default=None)
|
||||
|
||||
|
||||
class TestLiteLLMAdapter(LiteLLMOpenAIMixin):
|
||||
|
@ -36,7 +37,7 @@ class TestLiteLLMAdapter(LiteLLMOpenAIMixin):
|
|||
@pytest.fixture
|
||||
def adapter_with_config_key():
|
||||
"""Fixture to create adapter with API key in config"""
|
||||
config = TestConfig(api_key=SecretStr("config-api-key"))
|
||||
config = TestConfig(api_key=MySecretStr("config-api-key"))
|
||||
adapter = TestLiteLLMAdapter(config)
|
||||
adapter.__provider_spec__ = MagicMock()
|
||||
adapter.__provider_spec__.provider_data_validator = (
|
||||
|
|
|
@ -7,9 +7,14 @@
|
|||
import os
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from pydantic import SecretStr
|
||||
from llama_stack.core.secret_types import MySecretStr
|
||||
|
||||
|
||||
# Wrapper for backward compatibility in tests
|
||||
def replace_env_vars_compat(config, path=""):
|
||||
return replace_env_vars_compat(config, path, None, None)
|
||||
|
||||
|
||||
from llama_stack.core.stack import replace_env_vars
|
||||
from llama_stack.providers.remote.inference.openai.config import OpenAIConfig
|
||||
from llama_stack.providers.remote.inference.openai.openai import OpenAIInferenceAdapter
|
||||
|
||||
|
@ -37,7 +42,7 @@ class TestOpenAIBaseURLConfig:
|
|||
"""Test that the adapter uses base URL from OPENAI_BASE_URL environment variable."""
|
||||
# Use sample_run_config which has proper environment variable syntax
|
||||
config_data = OpenAIConfig.sample_run_config(api_key="test-key")
|
||||
processed_config = replace_env_vars(config_data)
|
||||
processed_config = replace_env_vars_compat(config_data)
|
||||
config = OpenAIConfig.model_validate(processed_config)
|
||||
adapter = OpenAIInferenceAdapter(config)
|
||||
|
||||
|
@ -61,14 +66,14 @@ class TestOpenAIBaseURLConfig:
|
|||
adapter = OpenAIInferenceAdapter(config)
|
||||
|
||||
# Mock the get_api_key method since it's delegated to LiteLLMOpenAIMixin
|
||||
adapter.get_api_key = MagicMock(return_value=SecretStr("test-key"))
|
||||
adapter.get_api_key = MagicMock(return_value=MySecretStr("test-key"))
|
||||
|
||||
# Access the client property to trigger AsyncOpenAI initialization
|
||||
_ = adapter.client
|
||||
|
||||
# Verify AsyncOpenAI was called with the correct base_url
|
||||
mock_openai_class.assert_called_once_with(
|
||||
api_key=SecretStr("test-key"),
|
||||
api_key=MySecretStr("test-key"),
|
||||
base_url=custom_url,
|
||||
)
|
||||
|
||||
|
@ -80,7 +85,7 @@ class TestOpenAIBaseURLConfig:
|
|||
adapter = OpenAIInferenceAdapter(config)
|
||||
|
||||
# Mock the get_api_key method
|
||||
adapter.get_api_key = MagicMock(return_value=SecretStr("test-key"))
|
||||
adapter.get_api_key = MagicMock(return_value=MySecretStr("test-key"))
|
||||
|
||||
# Mock a model object that will be returned by models.list()
|
||||
mock_model = MagicMock()
|
||||
|
@ -103,7 +108,7 @@ class TestOpenAIBaseURLConfig:
|
|||
|
||||
# Verify the client was created with the custom URL
|
||||
mock_openai_class.assert_called_with(
|
||||
api_key=SecretStr("test-key"),
|
||||
api_key=MySecretStr("test-key"),
|
||||
base_url=custom_url,
|
||||
)
|
||||
|
||||
|
@ -116,12 +121,12 @@ class TestOpenAIBaseURLConfig:
|
|||
"""Test that setting OPENAI_BASE_URL environment variable affects where model availability is checked."""
|
||||
# Use sample_run_config which has proper environment variable syntax
|
||||
config_data = OpenAIConfig.sample_run_config(api_key="test-key")
|
||||
processed_config = replace_env_vars(config_data)
|
||||
processed_config = replace_env_vars_compat(config_data)
|
||||
config = OpenAIConfig.model_validate(processed_config)
|
||||
adapter = OpenAIInferenceAdapter(config)
|
||||
|
||||
# Mock the get_api_key method
|
||||
adapter.get_api_key = MagicMock(return_value=SecretStr("test-key"))
|
||||
adapter.get_api_key = MagicMock(return_value=MySecretStr("test-key"))
|
||||
|
||||
# Mock a model object that will be returned by models.list()
|
||||
mock_model = MagicMock()
|
||||
|
@ -144,6 +149,6 @@ class TestOpenAIBaseURLConfig:
|
|||
|
||||
# Verify the client was created with the environment variable URL
|
||||
mock_openai_class.assert_called_with(
|
||||
api_key=SecretStr("test-key"),
|
||||
api_key=MySecretStr("test-key"),
|
||||
base_url="https://proxy.openai.com/v1",
|
||||
)
|
||||
|
|
|
@ -8,7 +8,10 @@ import os
|
|||
|
||||
import pytest
|
||||
|
||||
from llama_stack.core.stack import replace_env_vars
|
||||
|
||||
# Wrapper for backward compatibility in tests
|
||||
def replace_env_vars_compat(config, path=""):
|
||||
return replace_env_vars_compat(config, path, None, None)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
@ -32,52 +35,54 @@ def setup_env_vars():
|
|||
|
||||
|
||||
def test_simple_replacement(setup_env_vars):
|
||||
assert replace_env_vars("${env.TEST_VAR}") == "test_value"
|
||||
assert replace_env_vars_compat("${env.TEST_VAR}") == "test_value"
|
||||
|
||||
|
||||
def test_default_value_when_not_set(setup_env_vars):
|
||||
assert replace_env_vars("${env.NOT_SET:=default}") == "default"
|
||||
assert replace_env_vars_compat("${env.NOT_SET:=default}") == "default"
|
||||
|
||||
|
||||
def test_default_value_when_set(setup_env_vars):
|
||||
assert replace_env_vars("${env.TEST_VAR:=default}") == "test_value"
|
||||
assert replace_env_vars_compat("${env.TEST_VAR:=default}") == "test_value"
|
||||
|
||||
|
||||
def test_default_value_when_empty(setup_env_vars):
|
||||
assert replace_env_vars("${env.EMPTY_VAR:=default}") == "default"
|
||||
assert replace_env_vars_compat("${env.EMPTY_VAR:=default}") == "default"
|
||||
|
||||
|
||||
def test_none_value_when_empty(setup_env_vars):
|
||||
assert replace_env_vars("${env.EMPTY_VAR:=}") is None
|
||||
assert replace_env_vars_compat("${env.EMPTY_VAR:=}") is None
|
||||
|
||||
|
||||
def test_value_when_set(setup_env_vars):
|
||||
assert replace_env_vars("${env.TEST_VAR:=}") == "test_value"
|
||||
assert replace_env_vars_compat("${env.TEST_VAR:=}") == "test_value"
|
||||
|
||||
|
||||
def test_empty_var_no_default(setup_env_vars):
|
||||
assert replace_env_vars("${env.EMPTY_VAR_NO_DEFAULT:+}") is None
|
||||
assert replace_env_vars_compat("${env.EMPTY_VAR_NO_DEFAULT:+}") is None
|
||||
|
||||
|
||||
def test_conditional_value_when_set(setup_env_vars):
|
||||
assert replace_env_vars("${env.TEST_VAR:+conditional}") == "conditional"
|
||||
assert replace_env_vars_compat("${env.TEST_VAR:+conditional}") == "conditional"
|
||||
|
||||
|
||||
def test_conditional_value_when_not_set(setup_env_vars):
|
||||
assert replace_env_vars("${env.NOT_SET:+conditional}") is None
|
||||
assert replace_env_vars_compat("${env.NOT_SET:+conditional}") is None
|
||||
|
||||
|
||||
def test_conditional_value_when_empty(setup_env_vars):
|
||||
assert replace_env_vars("${env.EMPTY_VAR:+conditional}") is None
|
||||
assert replace_env_vars_compat("${env.EMPTY_VAR:+conditional}") is None
|
||||
|
||||
|
||||
def test_conditional_value_with_zero(setup_env_vars):
|
||||
assert replace_env_vars("${env.ZERO_VAR:+conditional}") == "conditional"
|
||||
assert replace_env_vars_compat("${env.ZERO_VAR:+conditional}") == "conditional"
|
||||
|
||||
|
||||
def test_mixed_syntax(setup_env_vars):
|
||||
assert replace_env_vars("${env.TEST_VAR:=default} and ${env.NOT_SET:+conditional}") == "test_value and "
|
||||
assert replace_env_vars("${env.NOT_SET:=default} and ${env.TEST_VAR:+conditional}") == "default and conditional"
|
||||
assert replace_env_vars_compat("${env.TEST_VAR:=default} and ${env.NOT_SET:+conditional}") == "test_value and "
|
||||
assert (
|
||||
replace_env_vars_compat("${env.NOT_SET:=default} and ${env.TEST_VAR:+conditional}") == "default and conditional"
|
||||
)
|
||||
|
||||
|
||||
def test_nested_structures(setup_env_vars):
|
||||
|
@ -87,11 +92,11 @@ def test_nested_structures(setup_env_vars):
|
|||
"key3": {"nested": "${env.NOT_SET:+conditional}"},
|
||||
}
|
||||
expected = {"key1": "test_value", "key2": ["default", "conditional"], "key3": {"nested": None}}
|
||||
assert replace_env_vars(data) == expected
|
||||
assert replace_env_vars_compat(data) == expected
|
||||
|
||||
|
||||
def test_explicit_strings_preserved(setup_env_vars):
|
||||
# Explicit strings that look like numbers/booleans should remain strings
|
||||
data = {"port": "8080", "enabled": "true", "count": "123", "ratio": "3.14"}
|
||||
expected = {"port": "8080", "enabled": "true", "count": "123", "ratio": "3.14"}
|
||||
assert replace_env_vars(data) == expected
|
||||
assert replace_env_vars_compat(data) == expected
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue