diff --git a/docs/docs/providers/datasetio/remote_nvidia.mdx b/docs/docs/providers/datasetio/remote_nvidia.mdx index 35a7dacee..ba5522fce 100644 --- a/docs/docs/providers/datasetio/remote_nvidia.mdx +++ b/docs/docs/providers/datasetio/remote_nvidia.mdx @@ -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` | `` | 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` | `` | No | http://nemo.test | Base URL for the NeMo Dataset API | diff --git a/docs/docs/providers/files/remote_s3.mdx b/docs/docs/providers/files/remote_s3.mdx index adf4bced0..9de31df44 100644 --- a/docs/docs/providers/files/remote_s3.mdx +++ b/docs/docs/providers/files/remote_s3.mdx @@ -17,7 +17,7 @@ AWS S3-based file storage provider for scalable cloud file management with metad | `bucket_name` | `` | No | | S3 bucket name to store files | | `region` | `` | 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` | `` | No | | AWS secret access key (optional if using IAM roles) | +| `aws_secret_access_key` | `` | 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` | `` | 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 | diff --git a/docs/docs/providers/inference/remote_anthropic.mdx b/docs/docs/providers/inference/remote_anthropic.mdx index f795ad3f1..72deb298c 100644 --- a/docs/docs/providers/inference/remote_anthropic.mdx +++ b/docs/docs/providers/inference/remote_anthropic.mdx @@ -14,7 +14,7 @@ Anthropic inference provider for accessing Claude models and Anthropic's AI serv | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| -| `api_key` | `` | No | | API key for Anthropic models | +| `api_key` | `` | No | | API key for Anthropic models | ## Sample Configuration diff --git a/docs/docs/providers/inference/remote_azure.mdx b/docs/docs/providers/inference/remote_azure.mdx index 0eb0ea755..1085e4ad4 100644 --- a/docs/docs/providers/inference/remote_azure.mdx +++ b/docs/docs/providers/inference/remote_azure.mdx @@ -21,7 +21,7 @@ https://learn.microsoft.com/en-us/azure/ai-foundry/openai/overview | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| -| `api_key` | `` | No | | Azure API key for Azure | +| `api_key` | `` | No | | Azure API key for Azure | | `api_base` | `` | 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) | diff --git a/docs/docs/providers/inference/remote_bedrock.mdx b/docs/docs/providers/inference/remote_bedrock.mdx index ff1ed5ad8..627be48e5 100644 --- a/docs/docs/providers/inference/remote_bedrock.mdx +++ b/docs/docs/providers/inference/remote_bedrock.mdx @@ -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` | `` | No | | The AWS secret access key to use. Default use environment variable: AWS_SECRET_ACCESS_KEY | -| `aws_session_token` | `` | No | | The AWS session token to use. Default use environment variable: AWS_SESSION_TOKEN | +| `aws_secret_access_key` | `` | No | | The AWS secret access key to use. Default use environment variable: AWS_SECRET_ACCESS_KEY | +| `aws_session_token` | `` | 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 | diff --git a/docs/docs/providers/inference/remote_cerebras.mdx b/docs/docs/providers/inference/remote_cerebras.mdx index d9cc93aef..7c96e5115 100644 --- a/docs/docs/providers/inference/remote_cerebras.mdx +++ b/docs/docs/providers/inference/remote_cerebras.mdx @@ -15,7 +15,7 @@ Cerebras inference provider for running models on Cerebras Cloud platform. | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `base_url` | `` | No | https://api.cerebras.ai | Base URL for the Cerebras API | -| `api_key` | `` | No | | Cerebras API Key | +| `api_key` | `` | No | | Cerebras API Key | ## Sample Configuration diff --git a/docs/docs/providers/inference/remote_databricks.mdx b/docs/docs/providers/inference/remote_databricks.mdx index 7f736db9d..fb3783720 100644 --- a/docs/docs/providers/inference/remote_databricks.mdx +++ b/docs/docs/providers/inference/remote_databricks.mdx @@ -15,7 +15,7 @@ Databricks inference provider for running models on Databricks' unified analytic | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `url` | `` | No | | The URL for the Databricks model serving endpoint | -| `api_token` | `` | No | | The Databricks API token | +| `api_token` | `` | No | | The Databricks API token | ## Sample Configuration diff --git a/docs/docs/providers/inference/remote_fireworks.mdx b/docs/docs/providers/inference/remote_fireworks.mdx index 0f37ccbc2..73698f28c 100644 --- a/docs/docs/providers/inference/remote_fireworks.mdx +++ b/docs/docs/providers/inference/remote_fireworks.mdx @@ -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` | `` | No | https://api.fireworks.ai/inference/v1 | The URL for the Fireworks server | -| `api_key` | `` | No | | The Fireworks.ai API Key | +| `api_key` | `` | No | | The Fireworks.ai API Key | ## Sample Configuration diff --git a/docs/docs/providers/inference/remote_gemini.mdx b/docs/docs/providers/inference/remote_gemini.mdx index d9a2f3e8d..538a9f29b 100644 --- a/docs/docs/providers/inference/remote_gemini.mdx +++ b/docs/docs/providers/inference/remote_gemini.mdx @@ -14,7 +14,7 @@ Google Gemini inference provider for accessing Gemini models and Google's AI ser | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| -| `api_key` | `` | No | | API key for Gemini models | +| `api_key` | `` | No | | API key for Gemini models | ## Sample Configuration diff --git a/docs/docs/providers/inference/remote_groq.mdx b/docs/docs/providers/inference/remote_groq.mdx index b6d29496e..2f80ab7ca 100644 --- a/docs/docs/providers/inference/remote_groq.mdx +++ b/docs/docs/providers/inference/remote_groq.mdx @@ -14,7 +14,7 @@ Groq inference provider for ultra-fast inference using Groq's LPU technology. | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| -| `api_key` | `` | No | | The Groq API key | +| `api_key` | `` | No | | The Groq API key | | `url` | `` | No | https://api.groq.com | The URL for the Groq AI server | ## Sample Configuration diff --git a/docs/docs/providers/inference/remote_hf_endpoint.mdx b/docs/docs/providers/inference/remote_hf_endpoint.mdx index 771b24f8d..93468fdbc 100644 --- a/docs/docs/providers/inference/remote_hf_endpoint.mdx +++ b/docs/docs/providers/inference/remote_hf_endpoint.mdx @@ -15,7 +15,7 @@ HuggingFace Inference Endpoints provider for dedicated model serving. | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `endpoint_name` | `` | 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` | `` | No | | Your Hugging Face user access token (will default to locally saved token if not provided) | ## Sample Configuration diff --git a/docs/docs/providers/inference/remote_hf_serverless.mdx b/docs/docs/providers/inference/remote_hf_serverless.mdx index 1a89b8e3e..80753ed05 100644 --- a/docs/docs/providers/inference/remote_hf_serverless.mdx +++ b/docs/docs/providers/inference/remote_hf_serverless.mdx @@ -15,7 +15,7 @@ HuggingFace Inference API serverless provider for on-demand model inference. | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `huggingface_repo` | `` | 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` | `` | No | | Your Hugging Face user access token (will default to locally saved token if not provided) | ## Sample Configuration diff --git a/docs/docs/providers/inference/remote_llama-openai-compat.mdx b/docs/docs/providers/inference/remote_llama-openai-compat.mdx index 491774844..478b7dadb 100644 --- a/docs/docs/providers/inference/remote_llama-openai-compat.mdx +++ b/docs/docs/providers/inference/remote_llama-openai-compat.mdx @@ -14,7 +14,7 @@ Llama OpenAI-compatible provider for using Llama models with OpenAI API format. | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| -| `api_key` | `` | No | | The Llama API key | +| `api_key` | `` | No | | The Llama API key | | `openai_compat_api_base` | `` | No | https://api.llama.com/compat/v1/ | The URL for the Llama API server | ## Sample Configuration diff --git a/docs/docs/providers/inference/remote_nvidia.mdx b/docs/docs/providers/inference/remote_nvidia.mdx index 9aee22e9a..c51da9547 100644 --- a/docs/docs/providers/inference/remote_nvidia.mdx +++ b/docs/docs/providers/inference/remote_nvidia.mdx @@ -15,7 +15,7 @@ NVIDIA inference provider for accessing NVIDIA NIM models and AI services. | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `url` | `` | No | https://integrate.api.nvidia.com | A base url for accessing the NVIDIA NIM | -| `api_key` | `` | No | | The NVIDIA API key, only needed of using the hosted service | +| `api_key` | `` | No | | The NVIDIA API key, only needed of using the hosted service | | `timeout` | `` | No | 60 | Timeout for the HTTP requests | | `append_api_version` | `` | No | True | When set to false, the API version will not be appended to the base_url. By default, it is true. | diff --git a/docs/docs/providers/inference/remote_openai.mdx b/docs/docs/providers/inference/remote_openai.mdx index f82bea154..79e9d9ccc 100644 --- a/docs/docs/providers/inference/remote_openai.mdx +++ b/docs/docs/providers/inference/remote_openai.mdx @@ -14,7 +14,7 @@ OpenAI inference provider for accessing GPT models and other OpenAI services. | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| -| `api_key` | `` | No | | API key for OpenAI models | +| `api_key` | `` | No | | API key for OpenAI models | | `base_url` | `` | No | https://api.openai.com/v1 | Base URL for OpenAI API | ## Sample Configuration diff --git a/docs/docs/providers/inference/remote_passthrough.mdx b/docs/docs/providers/inference/remote_passthrough.mdx index efaa01d04..f35f746db 100644 --- a/docs/docs/providers/inference/remote_passthrough.mdx +++ b/docs/docs/providers/inference/remote_passthrough.mdx @@ -15,7 +15,7 @@ Passthrough inference provider for connecting to any external inference service | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `url` | `` | No | | The URL for the passthrough endpoint | -| `api_key` | `` | No | | API Key for the passthrouth endpoint | +| `api_key` | `` | No | | API Key for the passthrouth endpoint | ## Sample Configuration diff --git a/docs/docs/providers/inference/remote_runpod.mdx b/docs/docs/providers/inference/remote_runpod.mdx index 1def462f6..8028f0228 100644 --- a/docs/docs/providers/inference/remote_runpod.mdx +++ b/docs/docs/providers/inference/remote_runpod.mdx @@ -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` | `` | No | | The API token | +| `api_token` | `` | No | | The API token | ## Sample Configuration diff --git a/docs/docs/providers/inference/remote_sambanova.mdx b/docs/docs/providers/inference/remote_sambanova.mdx index b5d64e6d9..2a63cab16 100644 --- a/docs/docs/providers/inference/remote_sambanova.mdx +++ b/docs/docs/providers/inference/remote_sambanova.mdx @@ -15,7 +15,7 @@ SambaNova inference provider for running models on SambaNova's dataflow architec | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `url` | `` | No | https://api.sambanova.ai/v1 | The URL for the SambaNova AI server | -| `api_key` | `` | No | | The SambaNova cloud API Key | +| `api_key` | `` | No | | The SambaNova cloud API Key | ## Sample Configuration diff --git a/docs/docs/providers/inference/remote_together.mdx b/docs/docs/providers/inference/remote_together.mdx index 0669a1112..912cf0291 100644 --- a/docs/docs/providers/inference/remote_together.mdx +++ b/docs/docs/providers/inference/remote_together.mdx @@ -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` | `` | No | https://api.together.xyz/v1 | The URL for the Together AI server | -| `api_key` | `` | No | | The Together AI API Key | +| `api_key` | `` | No | | The Together AI API Key | ## Sample Configuration diff --git a/docs/docs/providers/inference/remote_vllm.mdx b/docs/docs/providers/inference/remote_vllm.mdx index 5f9af4db4..4db289376 100644 --- a/docs/docs/providers/inference/remote_vllm.mdx +++ b/docs/docs/providers/inference/remote_vllm.mdx @@ -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` | `` | No | 4096 | Maximum number of tokens to generate. | -| `api_token` | `` | No | | The API token | +| `api_token` | `` | 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` | `` | No | False | Whether to refresh models periodically | diff --git a/docs/docs/providers/inference/remote_watsonx.mdx b/docs/docs/providers/inference/remote_watsonx.mdx index c1ab57a7b..2584a78ac 100644 --- a/docs/docs/providers/inference/remote_watsonx.mdx +++ b/docs/docs/providers/inference/remote_watsonx.mdx @@ -15,7 +15,7 @@ IBM WatsonX inference provider for accessing AI models on IBM's WatsonX platform | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `url` | `` | No | https://us-south.ml.cloud.ibm.com | A base url for accessing the watsonx.ai | -| `api_key` | `` | No | | The watsonx API key | +| `api_key` | `` | No | | The watsonx API key | | `project_id` | `str \| None` | No | | The Project ID key | | `timeout` | `` | No | 60 | Timeout for the HTTP requests | diff --git a/docs/docs/providers/post_training/remote_nvidia.mdx b/docs/docs/providers/post_training/remote_nvidia.mdx index 448ac4c75..451b1b258 100644 --- a/docs/docs/providers/post_training/remote_nvidia.mdx +++ b/docs/docs/providers/post_training/remote_nvidia.mdx @@ -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` | `` | 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 | diff --git a/docs/docs/providers/safety/remote_bedrock.mdx b/docs/docs/providers/safety/remote_bedrock.mdx index e068f1fed..d5362ccd9 100644 --- a/docs/docs/providers/safety/remote_bedrock.mdx +++ b/docs/docs/providers/safety/remote_bedrock.mdx @@ -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` | `` | No | | The AWS secret access key to use. Default use environment variable: AWS_SECRET_ACCESS_KEY | -| `aws_session_token` | `` | No | | The AWS session token to use. Default use environment variable: AWS_SESSION_TOKEN | +| `aws_secret_access_key` | `` | No | | The AWS secret access key to use. Default use environment variable: AWS_SECRET_ACCESS_KEY | +| `aws_session_token` | `` | 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 | diff --git a/docs/docs/providers/safety/remote_sambanova.mdx b/docs/docs/providers/safety/remote_sambanova.mdx index 3a5a0db7d..c63e2160c 100644 --- a/docs/docs/providers/safety/remote_sambanova.mdx +++ b/docs/docs/providers/safety/remote_sambanova.mdx @@ -15,7 +15,7 @@ SambaNova's safety provider for content moderation and safety filtering. | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `url` | `` | No | https://api.sambanova.ai/v1 | The URL for the SambaNova AI server | -| `api_key` | `` | No | | The SambaNova cloud API Key | +| `api_key` | `` | No | | The SambaNova cloud API Key | ## Sample Configuration diff --git a/docs/docs/providers/scoring/inline_braintrust.mdx b/docs/docs/providers/scoring/inline_braintrust.mdx index 93779b7ac..b035c259a 100644 --- a/docs/docs/providers/scoring/inline_braintrust.mdx +++ b/docs/docs/providers/scoring/inline_braintrust.mdx @@ -14,7 +14,7 @@ Braintrust scoring provider for evaluation and scoring using the Braintrust plat | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| -| `openai_api_key` | `` | No | | The OpenAI API Key | +| `openai_api_key` | `` | No | | The OpenAI API Key | ## Sample Configuration diff --git a/docs/docs/providers/tool_runtime/remote_bing-search.mdx b/docs/docs/providers/tool_runtime/remote_bing-search.mdx index b7dabdd47..02ed6af2c 100644 --- a/docs/docs/providers/tool_runtime/remote_bing-search.mdx +++ b/docs/docs/providers/tool_runtime/remote_bing-search.mdx @@ -14,7 +14,7 @@ Bing Search tool for web search capabilities using Microsoft's search engine. | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| -| `api_key` | `` | No | | The Bing API key | +| `api_key` | `` | No | | The Bing API key | | `top_k` | `` | No | 3 | | ## Sample Configuration diff --git a/docs/docs/providers/tool_runtime/remote_brave-search.mdx b/docs/docs/providers/tool_runtime/remote_brave-search.mdx index 084ae20f5..ccb7f3e23 100644 --- a/docs/docs/providers/tool_runtime/remote_brave-search.mdx +++ b/docs/docs/providers/tool_runtime/remote_brave-search.mdx @@ -14,7 +14,7 @@ Brave Search tool for web search capabilities with privacy-focused results. | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| -| `api_key` | `` | No | | The Brave Search API Key | +| `api_key` | `` | No | | The Brave Search API Key | | `max_results` | `` | No | 3 | The maximum number of results to return | ## Sample Configuration diff --git a/docs/docs/providers/tool_runtime/remote_tavily-search.mdx b/docs/docs/providers/tool_runtime/remote_tavily-search.mdx index 1c3429983..7947c42c8 100644 --- a/docs/docs/providers/tool_runtime/remote_tavily-search.mdx +++ b/docs/docs/providers/tool_runtime/remote_tavily-search.mdx @@ -14,7 +14,7 @@ Tavily Search tool for AI-optimized web search with structured results. | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| -| `api_key` | `` | No | | The Tavily Search API Key | +| `api_key` | `` | No | | The Tavily Search API Key | | `max_results` | `` | No | 3 | The maximum number of results to return | ## Sample Configuration diff --git a/docs/docs/providers/tool_runtime/remote_wolfram-alpha.mdx b/docs/docs/providers/tool_runtime/remote_wolfram-alpha.mdx index 96bc41789..2dd58f043 100644 --- a/docs/docs/providers/tool_runtime/remote_wolfram-alpha.mdx +++ b/docs/docs/providers/tool_runtime/remote_wolfram-alpha.mdx @@ -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` | `` | No | | The WolframAlpha API Key | ## Sample Configuration diff --git a/docs/docs/providers/vector_io/remote_pgvector.mdx b/docs/docs/providers/vector_io/remote_pgvector.mdx index 6d3157753..2853cddeb 100644 --- a/docs/docs/providers/vector_io/remote_pgvector.mdx +++ b/docs/docs/providers/vector_io/remote_pgvector.mdx @@ -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` | `` | No | ********** | | +| `password` | `` | 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 diff --git a/docs/docs/providers/vector_io/remote_qdrant.mdx b/docs/docs/providers/vector_io/remote_qdrant.mdx index c44a2b937..c344dfe2e 100644 --- a/docs/docs/providers/vector_io/remote_qdrant.mdx +++ b/docs/docs/providers/vector_io/remote_qdrant.mdx @@ -22,7 +22,7 @@ Please refer to the inline provider documentation. | `grpc_port` | `` | No | 6334 | | | `prefer_grpc` | `` | No | False | | | `https` | `bool \| None` | No | | | -| `api_key` | `str \| None` | No | | | +| `api_key` | `` | No | | The API key for the Qdrant instance | | `prefix` | `str \| None` | No | | | | `timeout` | `int \| None` | No | | | | `host` | `str \| None` | No | | | diff --git a/llama_stack/cli/stack/_build.py b/llama_stack/cli/stack/_build.py index b14e6fe55..1992ba292 100644 --- a/llama_stack/cli/stack/_build.py +++ b/llama_stack/cli/stack/_build.py @@ -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 diff --git a/llama_stack/core/configure.py b/llama_stack/core/configure.py index 64473c053..92ec66c49 100644 --- a/llama_stack/core/configure.py +++ b/llama_stack/core/configure.py @@ -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)) diff --git a/llama_stack/core/library_client.py b/llama_stack/core/library_client.py index e722e4de6..6f0f3fb2e 100644 --- a/llama_stack/core/library_client.py +++ b/llama_stack/core/library_client.py @@ -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 diff --git a/llama_stack/core/secret_types.py b/llama_stack/core/secret_types.py new file mode 100644 index 000000000..e1700d783 --- /dev/null +++ b/llama_stack/core/secret_types.py @@ -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] diff --git a/llama_stack/core/server/server.py b/llama_stack/core/server/server.py index 7d119c139..1c5a79f1c 100644 --- a/llama_stack/core/server/server.py +++ b/llama_stack/core/server/server.py @@ -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 diff --git a/llama_stack/core/stack.py b/llama_stack/core/stack.py index 3e14328a3..3b93f9913 100644 --- a/llama_stack/core/stack.py +++ b/llama_stack/core/stack.py @@ -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( diff --git a/llama_stack/providers/inline/scoring/braintrust/__init__.py b/llama_stack/providers/inline/scoring/braintrust/__init__.py index 2f3dce966..77c09c615 100644 --- a/llama_stack/providers/inline/scoring/braintrust/__init__.py +++ b/llama_stack/providers/inline/scoring/braintrust/__init__.py @@ -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( diff --git a/llama_stack/providers/inline/scoring/braintrust/braintrust.py b/llama_stack/providers/inline/scoring/braintrust/braintrust.py index 7f2b1e205..8d80b5920 100644 --- a/llama_stack/providers/inline/scoring/braintrust/braintrust.py +++ b/llama_stack/providers/inline/scoring/braintrust/braintrust.py @@ -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": }' ) - 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() diff --git a/llama_stack/providers/inline/scoring/braintrust/config.py b/llama_stack/providers/inline/scoring/braintrust/config.py index a2a52d610..3520ffb08 100644 --- a/llama_stack/providers/inline/scoring/braintrust/config.py +++ b/llama_stack/providers/inline/scoring/braintrust/config.py @@ -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", ) diff --git a/llama_stack/providers/inline/telemetry/meta_reference/console_span_processor.py b/llama_stack/providers/inline/telemetry/meta_reference/console_span_processor.py index 8beb3a841..a2eb896ea 100644 --- a/llama_stack/providers/inline/telemetry/meta_reference/console_span_processor.py +++ b/llama_stack/providers/inline/telemetry/meta_reference/console_span_processor.py @@ -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.""" diff --git a/llama_stack/providers/remote/datasetio/nvidia/config.py b/llama_stack/providers/remote/datasetio/nvidia/config.py index addce6c1f..aa1ac163d 100644 --- a/llama_stack/providers/remote/datasetio/nvidia/config.py +++ b/llama_stack/providers/remote/datasetio/nvidia/config.py @@ -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.", ) diff --git a/llama_stack/providers/remote/files/s3/config.py b/llama_stack/providers/remote/files/s3/config.py index b7935902d..e8fc452d8 100644 --- a/llama_stack/providers/remote/files/s3/config.py +++ b/llama_stack/providers/remote/files/s3/config.py @@ -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" diff --git a/llama_stack/providers/remote/inference/anthropic/config.py b/llama_stack/providers/remote/inference/anthropic/config.py index c28f05d24..eb77b328f 100644 --- a/llama_stack/providers/remote/inference/anthropic/config.py +++ b/llama_stack/providers/remote/inference/anthropic/config.py @@ -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", ) diff --git a/llama_stack/providers/remote/inference/azure/config.py b/llama_stack/providers/remote/inference/azure/config.py index fe9d61d53..ab53e85c2 100644 --- a/llama_stack/providers/remote/inference/azure/config.py +++ b/llama_stack/providers/remote/inference/azure/config.py @@ -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( diff --git a/llama_stack/providers/remote/inference/cerebras/config.py b/llama_stack/providers/remote/inference/cerebras/config.py index 0b737ea6c..5c5de27a5 100644 --- a/llama_stack/providers/remote/inference/cerebras/config.py +++ b/llama_stack/providers/remote/inference/cerebras/config.py @@ -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", ) diff --git a/llama_stack/providers/remote/inference/databricks/config.py b/llama_stack/providers/remote/inference/databricks/config.py index 67cd0480c..92b250034 100644 --- a/llama_stack/providers/remote/inference/databricks/config.py +++ b/llama_stack/providers/remote/inference/databricks/config.py @@ -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", ) diff --git a/llama_stack/providers/remote/inference/fireworks/config.py b/llama_stack/providers/remote/inference/fireworks/config.py index 5bf0fcd88..14f2dffc1 100644 --- a/llama_stack/providers/remote/inference/fireworks/config.py +++ b/llama_stack/providers/remote/inference/fireworks/config.py @@ -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", ) diff --git a/llama_stack/providers/remote/inference/gemini/config.py b/llama_stack/providers/remote/inference/gemini/config.py index 4a69bd064..a90fb93cb 100644 --- a/llama_stack/providers/remote/inference/gemini/config.py +++ b/llama_stack/providers/remote/inference/gemini/config.py @@ -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", ) diff --git a/llama_stack/providers/remote/inference/groq/config.py b/llama_stack/providers/remote/inference/groq/config.py index efc1f437b..c3c3fd580 100644 --- a/llama_stack/providers/remote/inference/groq/config.py +++ b/llama_stack/providers/remote/inference/groq/config.py @@ -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", ) diff --git a/llama_stack/providers/remote/inference/llama_openai_compat/config.py b/llama_stack/providers/remote/inference/llama_openai_compat/config.py index d20fed8e0..863988dd6 100644 --- a/llama_stack/providers/remote/inference/llama_openai_compat/config.py +++ b/llama_stack/providers/remote/inference/llama_openai_compat/config.py @@ -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", ) diff --git a/llama_stack/providers/remote/inference/nvidia/config.py b/llama_stack/providers/remote/inference/nvidia/config.py index 9ca2ffdd6..2f6dfe9f3 100644 --- a/llama_stack/providers/remote/inference/nvidia/config.py +++ b/llama_stack/providers/remote/inference/nvidia/config.py @@ -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( diff --git a/llama_stack/providers/remote/inference/openai/config.py b/llama_stack/providers/remote/inference/openai/config.py index b6f5f1a55..c8d6be9c2 100644 --- a/llama_stack/providers/remote/inference/openai/config.py +++ b/llama_stack/providers/remote/inference/openai/config.py @@ -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( diff --git a/llama_stack/providers/remote/inference/passthrough/config.py b/llama_stack/providers/remote/inference/passthrough/config.py index f57ce0e8a..dee3b9173 100644 --- a/llama_stack/providers/remote/inference/passthrough/config.py +++ b/llama_stack/providers/remote/inference/passthrough/config.py @@ -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", ) diff --git a/llama_stack/providers/remote/inference/runpod/config.py b/llama_stack/providers/remote/inference/runpod/config.py index 3c6c5afe9..eaabe4e33 100644 --- a/llama_stack/providers/remote/inference/runpod/config.py +++ b/llama_stack/providers/remote/inference/runpod/config.py @@ -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", ) diff --git a/llama_stack/providers/remote/inference/sambanova/config.py b/llama_stack/providers/remote/inference/sambanova/config.py index 4637cb49e..10e28c3ac 100644 --- a/llama_stack/providers/remote/inference/sambanova/config.py +++ b/llama_stack/providers/remote/inference/sambanova/config.py @@ -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", ) diff --git a/llama_stack/providers/remote/inference/tgi/config.py b/llama_stack/providers/remote/inference/tgi/config.py index 55136c8ba..a1f4c19f3 100644 --- a/llama_stack/providers/remote/inference/tgi/config.py +++ b/llama_stack/providers/remote/inference/tgi/config.py @@ -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)", ) diff --git a/llama_stack/providers/remote/inference/tgi/tgi.py b/llama_stack/providers/remote/inference/tgi/tgi.py index 27597900f..239fdce4e 100644 --- a/llama_stack/providers/remote/inference/tgi/tgi.py +++ b/llama_stack/providers/remote/inference/tgi/tgi.py @@ -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): diff --git a/llama_stack/providers/remote/inference/together/config.py b/llama_stack/providers/remote/inference/together/config.py index c15d42140..a2fa8f76e 100644 --- a/llama_stack/providers/remote/inference/together/config.py +++ b/llama_stack/providers/remote/inference/together/config.py @@ -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", ) diff --git a/llama_stack/providers/remote/inference/vertexai/vertexai.py b/llama_stack/providers/remote/inference/vertexai/vertexai.py index 581a00f29..dd1971df4 100644 --- a/llama_stack/providers/remote/inference/vertexai/vertexai.py +++ b/llama_stack/providers/remote/inference/vertexai/vertexai.py @@ -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: """ diff --git a/llama_stack/providers/remote/inference/vllm/__init__.py b/llama_stack/providers/remote/inference/vllm/__init__.py index 5b3ec6d5b..e47597c8f 100644 --- a/llama_stack/providers/remote/inference/vllm/__init__.py +++ b/llama_stack/providers/remote/inference/vllm/__init__.py @@ -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", ) diff --git a/llama_stack/providers/remote/inference/vllm/config.py b/llama_stack/providers/remote/inference/vllm/config.py index 708a39be1..7cc3ebebb 100644 --- a/llama_stack/providers/remote/inference/vllm/config.py +++ b/llama_stack/providers/remote/inference/vllm/config.py @@ -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( diff --git a/llama_stack/providers/remote/inference/watsonx/config.py b/llama_stack/providers/remote/inference/watsonx/config.py index a28de1226..64421c856 100644 --- a/llama_stack/providers/remote/inference/watsonx/config.py +++ b/llama_stack/providers/remote/inference/watsonx/config.py @@ -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( diff --git a/llama_stack/providers/remote/post_training/nvidia/config.py b/llama_stack/providers/remote/post_training/nvidia/config.py index 83d7b49e6..1730b779f 100644 --- a/llama_stack/providers/remote/post_training/nvidia/config.py +++ b/llama_stack/providers/remote/post_training/nvidia/config.py @@ -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.", ) diff --git a/llama_stack/providers/remote/safety/sambanova/config.py b/llama_stack/providers/remote/safety/sambanova/config.py index 814e5b1e5..ff58decfd 100644 --- a/llama_stack/providers/remote/safety/sambanova/config.py +++ b/llama_stack/providers/remote/safety/sambanova/config.py @@ -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", ) diff --git a/llama_stack/providers/remote/tool_runtime/bing_search/config.py b/llama_stack/providers/remote/tool_runtime/bing_search/config.py index 8ac4358ba..54b3a9d52 100644 --- a/llama_stack/providers/remote/tool_runtime/bing_search/config.py +++ b/llama_stack/providers/remote/tool_runtime/bing_search/config.py @@ -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 diff --git a/llama_stack/providers/remote/tool_runtime/brave_search/config.py b/llama_stack/providers/remote/tool_runtime/brave_search/config.py index ddc711c12..8fbc42154 100644 --- a/llama_stack/providers/remote/tool_runtime/brave_search/config.py +++ b/llama_stack/providers/remote/tool_runtime/brave_search/config.py @@ -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( diff --git a/llama_stack/providers/remote/tool_runtime/tavily_search/config.py b/llama_stack/providers/remote/tool_runtime/tavily_search/config.py index c0de93114..c2e31e5d3 100644 --- a/llama_stack/providers/remote/tool_runtime/tavily_search/config.py +++ b/llama_stack/providers/remote/tool_runtime/tavily_search/config.py @@ -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( diff --git a/llama_stack/providers/remote/tool_runtime/wolfram_alpha/config.py b/llama_stack/providers/remote/tool_runtime/wolfram_alpha/config.py index 457661c06..eea5ffdd2 100644 --- a/llama_stack/providers/remote/tool_runtime/wolfram_alpha/config.py +++ b/llama_stack/providers/remote/tool_runtime/wolfram_alpha/config.py @@ -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]: diff --git a/llama_stack/providers/remote/vector_io/pgvector/config.py b/llama_stack/providers/remote/vector_io/pgvector/config.py index 1c6d0ed52..ee4ef40d0 100644 --- a/llama_stack/providers/remote/vector_io/pgvector/config.py +++ b/llama_stack/providers/remote/vector_io/pgvector/config.py @@ -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 diff --git a/llama_stack/providers/remote/vector_io/qdrant/config.py b/llama_stack/providers/remote/vector_io/qdrant/config.py index ff5506236..58044e248 100644 --- a/llama_stack/providers/remote/vector_io/qdrant/config.py +++ b/llama_stack/providers/remote/vector_io/qdrant/config.py @@ -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 diff --git a/llama_stack/providers/utils/bedrock/config.py b/llama_stack/providers/utils/bedrock/config.py index 2a5c8e882..ae4018b80 100644 --- a/llama_stack/providers/utils/bedrock/config.py +++ b/llama_stack/providers/utils/bedrock/config.py @@ -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( diff --git a/llama_stack/providers/utils/inference/litellm_openai_mixin.py b/llama_stack/providers/utils/inference/litellm_openai_mixin.py index 8bdfbbbc9..a5d320e41 100644 --- a/llama_stack/providers/utils/inference/litellm_openai_mixin.py +++ b/llama_stack/providers/utils/inference/litellm_openai_mixin.py @@ -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): diff --git a/llama_stack/providers/utils/inference/openai_mixin.py b/llama_stack/providers/utils/inference/openai_mixin.py index 7fbd62ef6..4bb7a28c5 100644 --- a/llama_stack/providers/utils/inference/openai_mixin.py +++ b/llama_stack/providers/utils/inference/openai_mixin.py @@ -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(), ) diff --git a/llama_stack/providers/utils/kvstore/config.py b/llama_stack/providers/utils/kvstore/config.py index baab4e372..a8dd1a99a 100644 --- a/llama_stack/providers/utils/kvstore/config.py +++ b/llama_stack/providers/utils/kvstore/config.py @@ -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 diff --git a/llama_stack/providers/utils/sqlstore/sqlstore.py b/llama_stack/providers/utils/sqlstore/sqlstore.py index 6eaafccfe..3bcd8f40d 100644 --- a/llama_stack/providers/utils/sqlstore/sqlstore.py +++ b/llama_stack/providers/utils/sqlstore/sqlstore.py @@ -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: diff --git a/tests/unit/providers/inference/test_litellm_openai_mixin.py b/tests/unit/providers/inference/test_litellm_openai_mixin.py index cf7623dd1..48bf5ce38 100644 --- a/tests/unit/providers/inference/test_litellm_openai_mixin.py +++ b/tests/unit/providers/inference/test_litellm_openai_mixin.py @@ -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 = ( diff --git a/tests/unit/providers/inference/test_openai_base_url_config.py b/tests/unit/providers/inference/test_openai_base_url_config.py index 7bc908069..40c789073 100644 --- a/tests/unit/providers/inference/test_openai_base_url_config.py +++ b/tests/unit/providers/inference/test_openai_base_url_config.py @@ -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", ) diff --git a/tests/unit/server/test_replace_env_vars.py b/tests/unit/server/test_replace_env_vars.py index 14b3b7231..0c9bec860 100644 --- a/tests/unit/server/test_replace_env_vars.py +++ b/tests/unit/server/test_replace_env_vars.py @@ -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