diff --git a/.env.example b/.env.example index bdffa3ee8..b28b3507e 100644 --- a/.env.example +++ b/.env.example @@ -41,8 +41,10 @@ AGENT_DEBUG_MODE=false # Note: OpenRouter does not currently support embedding or reranker models. OPENROUTER_API_KEY= -# Get your API key from: https://ai.azure.com/ TODO: will support in next PR. +# Get your API key from: https://ai.azure.com/ AZURE_OPENAI_API_KEY= +AZURE_OPENAI_ENDPOINT= +AZURE_OPENAI_API_VERSION="2024-10-21" # Get your API key from: https://aistudio.google.com/ GOOGLE_API_KEY= diff --git a/python/configs/config.yaml b/python/configs/config.yaml index e652dc147..9600321f6 100644 --- a/python/configs/config.yaml +++ b/python/configs/config.yaml @@ -38,6 +38,11 @@ models: openai-compatible: config_file: "providers/openai-compatible.yaml" api_key_env: "OPENAI_COMPATIBLE_API_KEY" + + azure: + config_file: "providers/azure.yaml" + api_key_env: "AZURE_OPENAI_API_KEY" + endpoint_env: "AZURE_OPENAI_ENDPOINT" # Agent Configuration agents: diff --git a/python/configs/providers/azure.yaml b/python/configs/providers/azure.yaml new file mode 100644 index 000000000..765f5c360 --- /dev/null +++ b/python/configs/providers/azure.yaml @@ -0,0 +1,91 @@ +# ============================================ +# Azure OpenAI Provider Configuration +# ============================================ +name: "Azure OpenAI" +provider_type: "azure" + +enabled: true # Default is true if not specified + +# Connection Configuration +# Azure OpenAI requires: +# - AZURE_OPENAI_API_KEY: API key from Azure Portal +# - AZURE_OPENAI_ENDPOINT: Endpoint URL (e.g., https://{resource-name}.openai.azure.com/) +connection: + api_key_env: "AZURE_OPENAI_API_KEY" + endpoint_env: "AZURE_OPENAI_ENDPOINT" + base_url: "AZURE_OPENAI_ENDPOINT" + +# API Version Configuration +# Azure OpenAI uses versioned APIs +# Supported api_version: 2024-10-21 (recommended), 2024-08-01, 2024-06-01, etc. +api_version: "2024-10-21" + +# Default model (deployment name in Azure) +# Note: Deployment names in Azure are NOT necessarily the same as model IDs +# Update to match your Azure deployment names +default_model: "gpt-5-2025-08-07" + +# Model Parameters Defaults +defaults: + temperature: 0.7 + +# Available Models +# These are mapped from Azure deployment names to OpenAI model equivalents +# In Azure, you create "deployments" with specific names - update these to match your Azure deployments +models: + - id: "gpt-5-2025-08-07" + name: "GPT-5 2025-08-07" + deployment_name: "gpt-5-2025-08-07" # Azure deployment name (can differ from id) + context_length: 400000 + max_output_tokens: 128000 + description: "Most capable GPT-5 model with 400K context window" + supported_inputs: ["text"] + supported_outputs: ["text"] + + - id: "gpt-5-mini-2025-08-07" + name: "GPT-5 Mini 2025-08-07" + deployment_name: "gpt-5-mini-2025-08-07" + context_length: 400000 + max_output_tokens: 128000 + description: "Lightweight GPT-5 model optimized for efficiency" + supported_inputs: ["text"] + supported_outputs: ["text"] + + + +# ============================================ +# Embedding Models Configuration +# ============================================ +embedding: + # Default embedding model + default_model: "text-embedding-3-small" + + # Default parameters + defaults: + encoding_format: "float" + + # Available embedding models + models: + - id: "text-embedding-3-small" + name: "Text Embedding 3 Small" + deployment_name: "text-embedding-3-small" + dimensions: 1536 + max_input: 8192 + performance_mteb: 62.3 + description: "Small embedding model with good performance and efficiency" + + - id: "text-embedding-3-large" + name: "Text Embedding 3 Large" + deployment_name: "text-embedding-3-large" + dimensions: 3072 + max_input: 8192 + performance_mteb: 64.6 + description: "Large embedding model with higher performance" + +# ============================================ +# Environment Variable Overrides +# ============================================ +# Map environment variable names to config paths +env_overrides: + AZURE_OPENAI_API_VERSION: "api_version" + AZURE_OPENAI_DEFAULT_MODEL: "default_model" diff --git a/python/valuecell/adapters/models/factory.py b/python/valuecell/adapters/models/factory.py index 69f499338..850f9cc3e 100644 --- a/python/valuecell/adapters/models/factory.py +++ b/python/valuecell/adapters/models/factory.py @@ -169,35 +169,126 @@ def create_embedder(self, model_id: Optional[str] = None, **kwargs): class AzureProvider(ModelProvider): - """Azure OpenAI model provider""" + """Azure OpenAI model provider + + Azure OpenAI is a managed service providing OpenAI models deployed on Azure infrastructure. + It uses deployment names instead of model IDs, and requires both API key and endpoint. + + Configuration: + - AZURE_OPENAI_API_KEY: API key from Azure Portal + - AZURE_OPENAI_ENDPOINT: Azure OpenAI endpoint URL + - api_version: API version for Azure OpenAI REST API (default: 2024-10-21) + """ def create_model(self, model_id: Optional[str] = None, **kwargs): - """Create Azure OpenAI model""" + """Create Azure OpenAI model + + Args: + model_id: Deployment name in Azure (uses default if None) + **kwargs: Additional model parameters + + Returns: + AzureOpenAI model instance + """ try: - # Try to import from agno first from agno.models.azure import AzureOpenAI except ImportError: - raise ImportError("No Azure OpenAI library found") + raise ImportError( + "agno package with Azure support not installed. " + "Install with: pip install agno[azure]" + ) - model_id = model_id or self.config.default_model + # Use provided model_id or default + deployment_name = model_id or self.config.default_model + + # Merge parameters: provider defaults < kwargs params = {**self.config.parameters, **kwargs} + # Get API version from config (can be overridden by env var) + # The api_version field in azure.yaml is handled by env_overrides api_version = self.config.extra_config.get("api_version", "2024-10-21") - logger.info(f"Creating Azure OpenAI model: {model_id}") + logger.info( + f"Creating Azure OpenAI model: deployment={deployment_name}, " + f"endpoint={self.config.base_url}, api_version={api_version}" + ) return AzureOpenAI( - deployment_name=model_id, + deployment_name=deployment_name, api_key=self.config.api_key, azure_endpoint=self.config.base_url, api_version=api_version, temperature=params.get("temperature"), max_tokens=params.get("max_tokens"), + top_p=params.get("top_p"), + frequency_penalty=params.get("frequency_penalty"), + presence_penalty=params.get("presence_penalty"), + ) + + def create_embedder(self, model_id: Optional[str] = None, **kwargs): + """Create embedder via Azure OpenAI (OpenAI-compatible) + + Args: + model_id: Embedding deployment name in Azure (uses default if None) + **kwargs: Additional embedder parameters + + Returns: + OpenAIEmbedder instance configured for Azure + """ + try: + from agno.knowledge.embedder.openai import OpenAIEmbedder + except ImportError: + raise ImportError("agno package not installed") + + # Use provided model_id or default embedding model + deployment_name = model_id or self.config.default_embedding_model + + if not deployment_name: + raise ValueError( + f"No embedding model specified for provider '{self.config.name}'. " + "Set 'embedding.default_model' in azure.yaml" + ) + + # Merge parameters: provider embedding defaults < kwargs + params = {**self.config.embedding_parameters, **kwargs} + + logger.info( + f"Creating Azure OpenAI embedder: deployment={deployment_name}, " + f"endpoint={self.config.base_url}" + ) + + return OpenAIEmbedder( + id=deployment_name, + api_key=self.config.api_key, + base_url=self.config.base_url, + dimensions=int(params.get("dimensions", 1536)) + if params.get("dimensions") + else None, + encoding_format=params.get("encoding_format", "float"), ) def is_available(self) -> bool: - """Azure needs both API key and endpoint""" - return bool(self.config.api_key and self.config.base_url) + """Azure needs both API key and endpoint to be available + + Returns: + True if both API key and endpoint are configured + """ + has_key = bool(self.config.api_key) + has_endpoint = bool(self.config.base_url) + + if not has_key: + logger.warning( + "Azure OpenAI API key not configured. " + "Set AZURE_OPENAI_API_KEY environment variable." + ) + + if not has_endpoint: + logger.warning( + "Azure OpenAI endpoint not configured. " + "Set AZURE_OPENAI_ENDPOINT environment variable." + ) + + return has_key and has_endpoint class SiliconFlowProvider(ModelProvider): diff --git a/python/valuecell/config/manager.py b/python/valuecell/config/manager.py index 42333162d..e12830266 100644 --- a/python/valuecell/config/manager.py +++ b/python/valuecell/config/manager.py @@ -134,6 +134,7 @@ def primary_provider(self) -> str: "google", "openai", "openai-compatible", + "azure", ] for preferred in preferred_order: