{"repo": "await-d/fluxcaption", "files": {"backend/app/core/init_ai_providers.py": {"f814aebbfde35185cc19d52cd7100fc81e441610": {"t": 1785745458.744579, "n": 10914, "h": "6c79d03d97d7fbac", "c": "\"\"\"\nAI Provider Initialization Service.\n\nAutomatically initializes AI provider configurations from environment variables.\n\"\"\"\n\nfrom datetime import UTC, datetime\n\nimport sqlalchemy as sa\nfrom sqlalchemy.orm import Session\n\nfrom app.core.config import settings\nfrom app.core.logging import get_logger\nfrom app.models.ai_model_config import AIModelConfig\nfrom app.models.ai_provider_config import AIProviderConfig\nfrom app.models.ai_provider_usage import AIProviderQuota\n\nlogger = get_logger(__name__)\n\n\ndef init_ai_providers(session: Session) -> None:\n \"\"\"\n Initialize AI provider configurations from environment variables.\n\n This function checks for API keys in environment and creates/updates\n provider configurations accordingly.\n\n Args:\n session: Database session\n \"\"\"\n logger.info(\"Initializing AI provider configurations...\")\n\n # Define provider configurations\n providers = [\n {\n \"provider_name\": \"ollama\",\n \"display_name\": \"Ollama (Local)\",\n \"is_enabled\": True, # Always enable Ollama\n \"api_key\": None,\n \"base_url\": settings.ollama_base_url,\n \"timeout\": settings.ollama_timeout,\n \"default_model\": settings.default_mt_model,\n \"priority\": 1,\n \"description\": \"Local LLM deployment with Ollama - Free and privacy-friendly\",\n \"quota\": None, # No quota for local deployment\n },\n {\n \"provider_name\": \"openai\",\n \"display_name\": \"OpenAI\",\n \"is_enabled\": bool(settings.openai_api_key),\n \"api_key\": settings.openai_api_key,\n \"base_url\": \"https://api.openai.com/v1\",\n \"timeout\": 300,\n \"default_model\": \"gpt-3.5-turbo\",\n \"priority\": 2,\n \"description\": \"OpenAI GPT models (GPT-4, GPT-3.5, etc.)\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n {\n \"provider_name\": \"deepseek\",\n \"display_name\": \"DeepSeek\",\n \"is_enabled\": bool(settings.deepseek_api_key),\n \"api_key\": settings.deepseek_api_key,\n \"base_url\": \"https://api.deepseek.com/v1\",\n \"timeout\": 300,\n \"default_model\": \"deepseek-chat\",\n \"priority\": 3,\n \"description\": \"DeepSeek AI models - Affordable and powerful\",\n \"quota\": {\"daily_limit\": 5.0, \"monthly_limit\": 50.0},\n },\n {\n \"provider_name\": \"deeplx\",\n \"display_name\": \"DeepLX\",\n \"is_enabled\": bool(settings.deeplx_base_url),\n \"api_key\": settings.deeplx_api_key,\n \"base_url\": settings.deeplx_base_url or \"http://localhost:1188\",\n \"timeout\": 300,\n \"default_model\": \"translate\",\n \"priority\": 4,\n \"description\": \"DeepLX translation service - DeepL-compatible self-hosted translator\",\n \"quota\": None,\n },\n {\n \"provider_name\": \"claude\",\n \"display_name\": \"Claude (Anthropic)\",\n \"is_enabled\": bool(settings.claude_api_key),\n \"api_key\": settings.claude_api_key,\n \"base_url\": \"https://api.anthropic.com/v1\",\n \"timeout\": 300,\n \"default_model\": \"claude-3-5-sonnet-20241022\",\n \"priority\": 5,\n \"description\": \"Anthropic Claude models (Opus, Sonnet, Haiku)\",\n \"quota\": {\"daily_limit\": 20.0, \"monthly_limit\": 200.0},\n },\n {\n \"provider_name\": \"gemini\",\n \"display_name\": \"Gemini (Google)\",\n \"is_enabled\": bool(settings.gemini_api_key),\n \"api_key\": settings.gemini_api_key,\n \"base_url\": \"https://generativelanguage.googleapis.com/v1beta\",\n \"timeout\": 300,\n \"default_model\": \"gemini-pro\",\n \"priority\": 6,\n \"description\": \"Google Gemini models\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n {\n \"provider_name\": \"zhipu\",\n \"display_name\": \"智谱AI (GLM)\",\n \"is_enabled\": bool(settings.zhipu_api_key),\n \"api_key\": settings.zhipu_api_key,\n \"base_url\": \"https://open.bigmodel.cn/api/paas/v4\",\n \"timeout\": 300,\n \"default_model\": \"glm-4\",\n \"priority\": 7,\n \"description\": \"智谱AI GLM models - Chinese-optimized\",\n \"quota\": {\"daily_limit\": 5.0, \"monthly_limit\": 50.0},\n },\n {\n \"provider_name\": \"moonshot\",\n \"display_name\": \"Moonshot AI (Kimi)\",\n \"is_enabled\": bool(settings.moonshot_api_key),\n \"api_key\": settings.moonshot_api_key,\n \"base_url\": \"https://api.moonshot.cn/v1\",\n \"timeout\": 300,\n \"default_model\": \"moonshot-v1-32k\",\n \"priority\": 8,\n \"description\": \"Moonshot Kimi models - Super long context\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n {\n \"provider_name\": \"custom_openai\",\n \"display_name\": \"Custom OpenAI Compatible\",\n \"is_enabled\": bool(settings.custom_openai_api_key and settings.custom_openai_base_url),\n \"api_key\": settings.custom_openai_api_key,\n \"base_url\": settings.custom_openai_base_url,\n \"timeout\": 300,\n \"default_model\": None,\n \"priority\": 9,\n \"description\": \"Custom OpenAI-compatible endpoint (OpenRouter, LocalAI, vLLM, etc.)\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n ]\n\n created_count = 0\n updated_count = 0\n skipped_count = 0\n\n for provider_data in providers:\n provider_name = provider_data[\"provider_name\"]\n\n # Check if exists\n stmt = sa.select(AIProviderConfig).where(AIProviderConfig.provider_name == provider_name)\n existing = session.execute(stmt).scalar_one_or_none()\n\n if existing:\n # Only update if API key is provided and different, or if it's Ollama\n should_update = False\n\n if provider_name == \"ollama\":\n # Always update Ollama config\n should_update = True\n elif provider_data[\"api_key\"] and existing.api_key != provider_data[\"api_key\"]:\n # Update if API key changed\n should_update = True\n\n if should_update:\n existing.display_name = provider_data[\"display_name\"]\n existing.is_enabled = provider_data[\"is_enabled\"]\n if provider_data[\"api_key\"]:\n existing.api_key = provider_data[\"api_key\"]\n if provider_data[\"base_url\"]:\n existing.base_url = provider_data[\"base_url\"]\n existing.timeout = provider_data[\"timeout\"]\n existing.default_model = provider_data[\"default_model\"]\n existing.priority = provider_data[\"priority\"]\n existing.description = provider_data[\"description\"]\n\n updated_count += 1\n logger.info(f\"Updated provider config: {provider_name}\")\n else:\n skipped_count += 1\n else:\n # Create new provider config\n new_config = AIProviderConfig(\n provider_name=provider_data[\"provider_name\"],\n display_name=provider_data[\"display_name\"],\n is_enabled=provider_data[\"is_enabled\"],\n api_key=provider_data[\"api_key\"],\n base_url=provider_data[\"base_url\"],\n timeout=provider_data[\"timeout\"],\n default_model=provider_data[\"default_model\"],\n priority=provider_data[\"priority\"],\n description=provider_data[\"description\"],\n )\n session.add(new_config)\n created_count += 1\n logger.info(f\"Created provider config: {provider_name}\")\n\n # Initialize quota for cloud providers\n if provider_data[\"quota\"] and provider_name != \"ollama\":\n _init_provider_quota(session, provider_name, provider_data[\"quota\"])\n\n if provider_name == \"deeplx\":\n _ensure_deeplx_model(session)\n\n session.commit()\n\n logger.info(\n f\"AI provider initialization complete: \"\n f\"{created_count} created, {updated_count} updated, {skipped_count} skipped\"\n )\n\n\ndef _init_provider_quota(\n session: Session,\n provider_name: str,\n quota_config: dict,\n) -> None:\n \"\"\"\n Initialize quota configuration for a provider.\n\n Args:\n session: Database session\n provider_name: Provider name\n quota_config: Quota configuration dict\n \"\"\"\n # Check if quota exists\n stmt = sa.select(AIProviderQuota).where(AIProviderQuota.provider_name == provider_name)\n existing_quota = session.execute(stmt).scalar_one_or_none()\n\n if not existing_quota:\n # Create new quota\n quota = AIProviderQuota(\n provider_name=provider_name,\n daily_limit=quota_config.get(\"daily_limit\"),\n monthly_limit=quota_config.get(\"monthly_limit\"),\n daily_token_limit=quota_config.get(\"daily_token_limit\"),\n monthly_token_limit=quota_config.get(\"monthly_token_limit\"),\n alert_threshold_percent=80,\n auto_disable_on_limit=True,\n daily_reset_at=datetime.now(UTC),\n monthly_reset_at=datetime.now(UTC),\n )\n session.add(quota)\n logger.info(\n f\"Created quota for {provider_name}: \"\n f\"${quota_config.get('daily_limit')}/day, \"\n f\"${quota_config.get('monthly_limit')}/month\"\n )\n\n\ndef _ensure_deeplx_model(session: Session) -> None:\n stmt = sa.select(AIModelConfig).where(\n AIModelConfig.provider_name == \"deeplx\",\n AIModelConfig.model_name == \"translate\",\n )\n existing = session.execute(stmt).scalar_one_or_none()\n\n if existing:\n existing.display_name = \"DeepLX Translate\"\n existing.is_enabled = True\n existing.model_type = \"translation\"\n existing.input_price = 0.0\n existing.output_price = 0.0\n existing.description = \"Built-in DeepLX translation endpoint\"\n existing.tags = '[\"builtin\", \"translation\"]'\n existing.is_default = True\n existing.is_available = True\n existing.priority = max(existing.priority, 100)\n return\n\n session.add(\n AIModelConfig(\n provider_name=\"deeplx\",\n model_name=\"translate\",\n display_name=\"DeepLX Translate\",\n is_enabled=True,\n model_type=\"translation\",\n input_price=0.0,\n output_price=0.0,\n description=\"Built-in DeepLX translation endpoint\",\n tags='[\"builtin\", \"translation\"]',\n is_default=True,\n priority=100,\n is_available=True,\n )\n )\n"}, "92238a0eb72a1b5c662c726f9d852c789e71e3cd": {"t": 1785753486.9311588, "n": 10914, "h": "6c79d03d97d7fbac", "c": "\"\"\"\nAI Provider Initialization Service.\n\nAutomatically initializes AI provider configurations from environment variables.\n\"\"\"\n\nfrom datetime import UTC, datetime\n\nimport sqlalchemy as sa\nfrom sqlalchemy.orm import Session\n\nfrom app.core.config import settings\nfrom app.core.logging import get_logger\nfrom app.models.ai_model_config import AIModelConfig\nfrom app.models.ai_provider_config import AIProviderConfig\nfrom app.models.ai_provider_usage import AIProviderQuota\n\nlogger = get_logger(__name__)\n\n\ndef init_ai_providers(session: Session) -> None:\n \"\"\"\n Initialize AI provider configurations from environment variables.\n\n This function checks for API keys in environment and creates/updates\n provider configurations accordingly.\n\n Args:\n session: Database session\n \"\"\"\n logger.info(\"Initializing AI provider configurations...\")\n\n # Define provider configurations\n providers = [\n {\n \"provider_name\": \"ollama\",\n \"display_name\": \"Ollama (Local)\",\n \"is_enabled\": True, # Always enable Ollama\n \"api_key\": None,\n \"base_url\": settings.ollama_base_url,\n \"timeout\": settings.ollama_timeout,\n \"default_model\": settings.default_mt_model,\n \"priority\": 1,\n \"description\": \"Local LLM deployment with Ollama - Free and privacy-friendly\",\n \"quota\": None, # No quota for local deployment\n },\n {\n \"provider_name\": \"openai\",\n \"display_name\": \"OpenAI\",\n \"is_enabled\": bool(settings.openai_api_key),\n \"api_key\": settings.openai_api_key,\n \"base_url\": \"https://api.openai.com/v1\",\n \"timeout\": 300,\n \"default_model\": \"gpt-3.5-turbo\",\n \"priority\": 2,\n \"description\": \"OpenAI GPT models (GPT-4, GPT-3.5, etc.)\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n {\n \"provider_name\": \"deepseek\",\n \"display_name\": \"DeepSeek\",\n \"is_enabled\": bool(settings.deepseek_api_key),\n \"api_key\": settings.deepseek_api_key,\n \"base_url\": \"https://api.deepseek.com/v1\",\n \"timeout\": 300,\n \"default_model\": \"deepseek-chat\",\n \"priority\": 3,\n \"description\": \"DeepSeek AI models - Affordable and powerful\",\n \"quota\": {\"daily_limit\": 5.0, \"monthly_limit\": 50.0},\n },\n {\n \"provider_name\": \"deeplx\",\n \"display_name\": \"DeepLX\",\n \"is_enabled\": bool(settings.deeplx_base_url),\n \"api_key\": settings.deeplx_api_key,\n \"base_url\": settings.deeplx_base_url or \"http://localhost:1188\",\n \"timeout\": 300,\n \"default_model\": \"translate\",\n \"priority\": 4,\n \"description\": \"DeepLX translation service - DeepL-compatible self-hosted translator\",\n \"quota\": None,\n },\n {\n \"provider_name\": \"claude\",\n \"display_name\": \"Claude (Anthropic)\",\n \"is_enabled\": bool(settings.claude_api_key),\n \"api_key\": settings.claude_api_key,\n \"base_url\": \"https://api.anthropic.com/v1\",\n \"timeout\": 300,\n \"default_model\": \"claude-3-5-sonnet-20241022\",\n \"priority\": 5,\n \"description\": \"Anthropic Claude models (Opus, Sonnet, Haiku)\",\n \"quota\": {\"daily_limit\": 20.0, \"monthly_limit\": 200.0},\n },\n {\n \"provider_name\": \"gemini\",\n \"display_name\": \"Gemini (Google)\",\n \"is_enabled\": bool(settings.gemini_api_key),\n \"api_key\": settings.gemini_api_key,\n \"base_url\": \"https://generativelanguage.googleapis.com/v1beta\",\n \"timeout\": 300,\n \"default_model\": \"gemini-pro\",\n \"priority\": 6,\n \"description\": \"Google Gemini models\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n {\n \"provider_name\": \"zhipu\",\n \"display_name\": \"智谱AI (GLM)\",\n \"is_enabled\": bool(settings.zhipu_api_key),\n \"api_key\": settings.zhipu_api_key,\n \"base_url\": \"https://open.bigmodel.cn/api/paas/v4\",\n \"timeout\": 300,\n \"default_model\": \"glm-4\",\n \"priority\": 7,\n \"description\": \"智谱AI GLM models - Chinese-optimized\",\n \"quota\": {\"daily_limit\": 5.0, \"monthly_limit\": 50.0},\n },\n {\n \"provider_name\": \"moonshot\",\n \"display_name\": \"Moonshot AI (Kimi)\",\n \"is_enabled\": bool(settings.moonshot_api_key),\n \"api_key\": settings.moonshot_api_key,\n \"base_url\": \"https://api.moonshot.cn/v1\",\n \"timeout\": 300,\n \"default_model\": \"moonshot-v1-32k\",\n \"priority\": 8,\n \"description\": \"Moonshot Kimi models - Super long context\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n {\n \"provider_name\": \"custom_openai\",\n \"display_name\": \"Custom OpenAI Compatible\",\n \"is_enabled\": bool(settings.custom_openai_api_key and settings.custom_openai_base_url),\n \"api_key\": settings.custom_openai_api_key,\n \"base_url\": settings.custom_openai_base_url,\n \"timeout\": 300,\n \"default_model\": None,\n \"priority\": 9,\n \"description\": \"Custom OpenAI-compatible endpoint (OpenRouter, LocalAI, vLLM, etc.)\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n ]\n\n created_count = 0\n updated_count = 0\n skipped_count = 0\n\n for provider_data in providers:\n provider_name = provider_data[\"provider_name\"]\n\n # Check if exists\n stmt = sa.select(AIProviderConfig).where(AIProviderConfig.provider_name == provider_name)\n existing = session.execute(stmt).scalar_one_or_none()\n\n if existing:\n # Only update if API key is provided and different, or if it's Ollama\n should_update = False\n\n if provider_name == \"ollama\":\n # Always update Ollama config\n should_update = True\n elif provider_data[\"api_key\"] and existing.api_key != provider_data[\"api_key\"]:\n # Update if API key changed\n should_update = True\n\n if should_update:\n existing.display_name = provider_data[\"display_name\"]\n existing.is_enabled = provider_data[\"is_enabled\"]\n if provider_data[\"api_key\"]:\n existing.api_key = provider_data[\"api_key\"]\n if provider_data[\"base_url\"]:\n existing.base_url = provider_data[\"base_url\"]\n existing.timeout = provider_data[\"timeout\"]\n existing.default_model = provider_data[\"default_model\"]\n existing.priority = provider_data[\"priority\"]\n existing.description = provider_data[\"description\"]\n\n updated_count += 1\n logger.info(f\"Updated provider config: {provider_name}\")\n else:\n skipped_count += 1\n else:\n # Create new provider config\n new_config = AIProviderConfig(\n provider_name=provider_data[\"provider_name\"],\n display_name=provider_data[\"display_name\"],\n is_enabled=provider_data[\"is_enabled\"],\n api_key=provider_data[\"api_key\"],\n base_url=provider_data[\"base_url\"],\n timeout=provider_data[\"timeout\"],\n default_model=provider_data[\"default_model\"],\n priority=provider_data[\"priority\"],\n description=provider_data[\"description\"],\n )\n session.add(new_config)\n created_count += 1\n logger.info(f\"Created provider config: {provider_name}\")\n\n # Initialize quota for cloud providers\n if provider_data[\"quota\"] and provider_name != \"ollama\":\n _init_provider_quota(session, provider_name, provider_data[\"quota\"])\n\n if provider_name == \"deeplx\":\n _ensure_deeplx_model(session)\n\n session.commit()\n\n logger.info(\n f\"AI provider initialization complete: \"\n f\"{created_count} created, {updated_count} updated, {skipped_count} skipped\"\n )\n\n\ndef _init_provider_quota(\n session: Session,\n provider_name: str,\n quota_config: dict,\n) -> None:\n \"\"\"\n Initialize quota configuration for a provider.\n\n Args:\n session: Database session\n provider_name: Provider name\n quota_config: Quota configuration dict\n \"\"\"\n # Check if quota exists\n stmt = sa.select(AIProviderQuota).where(AIProviderQuota.provider_name == provider_name)\n existing_quota = session.execute(stmt).scalar_one_or_none()\n\n if not existing_quota:\n # Create new quota\n quota = AIProviderQuota(\n provider_name=provider_name,\n daily_limit=quota_config.get(\"daily_limit\"),\n monthly_limit=quota_config.get(\"monthly_limit\"),\n daily_token_limit=quota_config.get(\"daily_token_limit\"),\n monthly_token_limit=quota_config.get(\"monthly_token_limit\"),\n alert_threshold_percent=80,\n auto_disable_on_limit=True,\n daily_reset_at=datetime.now(UTC),\n monthly_reset_at=datetime.now(UTC),\n )\n session.add(quota)\n logger.info(\n f\"Created quota for {provider_name}: \"\n f\"${quota_config.get('daily_limit')}/day, \"\n f\"${quota_config.get('monthly_limit')}/month\"\n )\n\n\ndef _ensure_deeplx_model(session: Session) -> None:\n stmt = sa.select(AIModelConfig).where(\n AIModelConfig.provider_name == \"deeplx\",\n AIModelConfig.model_name == \"translate\",\n )\n existing = session.execute(stmt).scalar_one_or_none()\n\n if existing:\n existing.display_name = \"DeepLX Translate\"\n existing.is_enabled = True\n existing.model_type = \"translation\"\n existing.input_price = 0.0\n existing.output_price = 0.0\n existing.description = \"Built-in DeepLX translation endpoint\"\n existing.tags = '[\"builtin\", \"translation\"]'\n existing.is_default = True\n existing.is_available = True\n existing.priority = max(existing.priority, 100)\n return\n\n session.add(\n AIModelConfig(\n provider_name=\"deeplx\",\n model_name=\"translate\",\n display_name=\"DeepLX Translate\",\n is_enabled=True,\n model_type=\"translation\",\n input_price=0.0,\n output_price=0.0,\n description=\"Built-in DeepLX translation endpoint\",\n tags='[\"builtin\", \"translation\"]',\n is_default=True,\n priority=100,\n is_available=True,\n )\n )\n"}, "554a84c06ad5038ad7a318a5157ec5c6e8297668": {"t": 1785753488.7214434, "n": 9083, "h": "aef893503a838521", "c": "\"\"\"\nAI Provider Initialization Service.\n\nAutomatically initializes AI provider configurations from environment variables.\n\"\"\"\n\nfrom datetime import timezone\n\nimport sqlalchemy as sa\nfrom sqlalchemy.orm import Session\n\nfrom app.core.config import settings\nfrom app.core.logging import get_logger\nfrom app.models.ai_provider_config import AIProviderConfig\nfrom app.models.ai_provider_usage import AIProviderQuota\n\nlogger = get_logger(__name__)\n\n\ndef init_ai_providers(session: Session) -> None:\n \"\"\"\n Initialize AI provider configurations from environment variables.\n\n This function checks for API keys in environment and creates/updates\n provider configurations accordingly.\n\n Args:\n session: Database session\n \"\"\"\n logger.info(\"Initializing AI provider configurations...\")\n\n # Define provider configurations\n providers = [\n {\n \"provider_name\": \"ollama\",\n \"display_name\": \"Ollama (Local)\",\n \"is_enabled\": True, # Always enable Ollama\n \"api_key\": None,\n \"base_url\": settings.ollama_base_url,\n \"timeout\": settings.ollama_timeout,\n \"default_model\": settings.default_mt_model,\n \"priority\": 1,\n \"description\": \"Local LLM deployment with Ollama - Free and privacy-friendly\",\n \"quota\": None, # No quota for local deployment\n },\n {\n \"provider_name\": \"openai\",\n \"display_name\": \"OpenAI\",\n \"is_enabled\": bool(settings.openai_api_key),\n \"api_key\": settings.openai_api_key,\n \"base_url\": \"https://api.openai.com/v1\",\n \"timeout\": 300,\n \"default_model\": \"gpt-3.5-turbo\",\n \"priority\": 2,\n \"description\": \"OpenAI GPT models (GPT-4, GPT-3.5, etc.)\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n {\n \"provider_name\": \"deepseek\",\n \"display_name\": \"DeepSeek\",\n \"is_enabled\": bool(settings.deepseek_api_key),\n \"api_key\": settings.deepseek_api_key,\n \"base_url\": \"https://api.deepseek.com/v1\",\n \"timeout\": 300,\n \"default_model\": \"deepseek-chat\",\n \"priority\": 3,\n \"description\": \"DeepSeek AI models - Affordable and powerful\",\n \"quota\": {\"daily_limit\": 5.0, \"monthly_limit\": 50.0},\n },\n {\n \"provider_name\": \"claude\",\n \"display_name\": \"Claude (Anthropic)\",\n \"is_enabled\": bool(settings.claude_api_key),\n \"api_key\": settings.claude_api_key,\n \"base_url\": \"https://api.anthropic.com/v1\",\n \"timeout\": 300,\n \"default_model\": \"claude-3-5-sonnet-20241022\",\n \"priority\": 4,\n \"description\": \"Anthropic Claude models (Opus, Sonnet, Haiku)\",\n \"quota\": {\"daily_limit\": 20.0, \"monthly_limit\": 200.0},\n },\n {\n \"provider_name\": \"gemini\",\n \"display_name\": \"Gemini (Google)\",\n \"is_enabled\": bool(settings.gemini_api_key),\n \"api_key\": settings.gemini_api_key,\n \"base_url\": \"https://generativelanguage.googleapis.com/v1beta\",\n \"timeout\": 300,\n \"default_model\": \"gemini-pro\",\n \"priority\": 5,\n \"description\": \"Google Gemini models\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n {\n \"provider_name\": \"zhipu\",\n \"display_name\": \"智谱AI (GLM)\",\n \"is_enabled\": bool(settings.zhipu_api_key),\n \"api_key\": settings.zhipu_api_key,\n \"base_url\": \"https://open.bigmodel.cn/api/paas/v4\",\n \"timeout\": 300,\n \"default_model\": \"glm-4\",\n \"priority\": 6,\n \"description\": \"智谱AI GLM models - Chinese-optimized\",\n \"quota\": {\"daily_limit\": 5.0, \"monthly_limit\": 50.0},\n },\n {\n \"provider_name\": \"moonshot\",\n \"display_name\": \"Moonshot AI (Kimi)\",\n \"is_enabled\": bool(settings.moonshot_api_key),\n \"api_key\": settings.moonshot_api_key,\n \"base_url\": \"https://api.moonshot.cn/v1\",\n \"timeout\": 300,\n \"default_model\": \"moonshot-v1-32k\",\n \"priority\": 7,\n \"description\": \"Moonshot Kimi models - Super long context\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n {\n \"provider_name\": \"custom_openai\",\n \"display_name\": \"Custom OpenAI Compatible\",\n \"is_enabled\": bool(settings.custom_openai_api_key and settings.custom_openai_base_url),\n \"api_key\": settings.custom_openai_api_key,\n \"base_url\": settings.custom_openai_base_url,\n \"timeout\": 300,\n \"default_model\": None,\n \"priority\": 8,\n \"description\": \"Custom OpenAI-compatible endpoint (OpenRouter, LocalAI, vLLM, etc.)\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n ]\n\n created_count = 0\n updated_count = 0\n skipped_count = 0\n\n for provider_data in providers:\n provider_name = provider_data[\"provider_name\"]\n\n # Check if exists\n stmt = sa.select(AIProviderConfig).where(AIProviderConfig.provider_name == provider_name)\n existing = session.execute(stmt).scalar_one_or_none()\n\n if existing:\n # Only update if API key is provided and different, or if it's Ollama\n should_update = False\n\n if provider_name == \"ollama\":\n # Always update Ollama config\n should_update = True\n elif provider_data[\"api_key\"] and existing.api_key != provider_data[\"api_key\"]:\n # Update if API key changed\n should_update = True\n\n if should_update:\n existing.display_name = provider_data[\"display_name\"]\n existing.is_enabled = provider_data[\"is_enabled\"]\n if provider_data[\"api_key\"]:\n existing.api_key = provider_data[\"api_key\"]\n if provider_data[\"base_url\"]:\n existing.base_url = provider_data[\"base_url\"]\n existing.timeout = provider_data[\"timeout\"]\n existing.default_model = provider_data[\"default_model\"]\n existing.priority = provider_data[\"priority\"]\n existing.description = provider_data[\"description\"]\n\n updated_count += 1\n logger.info(f\"Updated provider config: {provider_name}\")\n else:\n skipped_count += 1\n else:\n # Create new provider config\n new_config = AIProviderConfig(\n provider_name=provider_data[\"provider_name\"],\n display_name=provider_data[\"display_name\"],\n is_enabled=provider_data[\"is_enabled\"],\n api_key=provider_data[\"api_key\"],\n base_url=provider_data[\"base_url\"],\n timeout=provider_data[\"timeout\"],\n default_model=provider_data[\"default_model\"],\n priority=provider_data[\"priority\"],\n description=provider_data[\"description\"],\n )\n session.add(new_config)\n created_count += 1\n logger.info(f\"Created provider config: {provider_name}\")\n\n # Initialize quota for cloud providers\n if provider_data[\"quota\"] and provider_name != \"ollama\":\n _init_provider_quota(session, provider_name, provider_data[\"quota\"])\n\n session.commit()\n\n logger.info(\n f\"AI provider initialization complete: \"\n f\"{created_count} created, {updated_count} updated, {skipped_count} skipped\"\n )\n\n\ndef _init_provider_quota(\n session: Session,\n provider_name: str,\n quota_config: dict,\n) -> None:\n \"\"\"\n Initialize quota configuration for a provider.\n\n Args:\n session: Database session\n provider_name: Provider name\n quota_config: Quota configuration dict\n \"\"\"\n from datetime import datetime\n\n # Check if quota exists\n stmt = sa.select(AIProviderQuota).where(AIProviderQuota.provider_name == provider_name)\n existing_quota = session.execute(stmt).scalar_one_or_none()\n\n if not existing_quota:\n # Create new quota\n quota = AIProviderQuota(\n provider_name=provider_name,\n daily_limit=quota_config.get(\"daily_limit\"),\n monthly_limit=quota_config.get(\"monthly_limit\"),\n daily_token_limit=quota_config.get(\"daily_token_limit\"),\n monthly_token_limit=quota_config.get(\"monthly_token_limit\"),\n alert_threshold_percent=80,\n auto_disable_on_limit=True,\n daily_reset_at=datetime.now(timezone.utc),\n monthly_reset_at=datetime.now(timezone.utc),\n )\n session.add(quota)\n logger.info(\n f\"Created quota for {provider_name}: \"\n f\"${quota_config.get('daily_limit')}/day, \"\n f\"${quota_config.get('monthly_limit')}/month\"\n )\n"}, "2ac9f8fa9b9cbbf0f5fa6c737ec60fa55d44aa97": {"t": 1785753489.8867538, "n": 9060, "h": "d67bb173ff4820e8", "c": "\"\"\"\nAI Provider Initialization Service.\n\nAutomatically initializes AI provider configurations from environment variables.\n\"\"\"\n\nfrom datetime import UTC\n\nimport sqlalchemy as sa\nfrom sqlalchemy.orm import Session\n\nfrom app.core.config import settings\nfrom app.core.logging import get_logger\nfrom app.models.ai_provider_config import AIProviderConfig\nfrom app.models.ai_provider_usage import AIProviderQuota\n\nlogger = get_logger(__name__)\n\n\ndef init_ai_providers(session: Session) -> None:\n \"\"\"\n Initialize AI provider configurations from environment variables.\n\n This function checks for API keys in environment and creates/updates\n provider configurations accordingly.\n\n Args:\n session: Database session\n \"\"\"\n logger.info(\"Initializing AI provider configurations...\")\n\n # Define provider configurations\n providers = [\n {\n \"provider_name\": \"ollama\",\n \"display_name\": \"Ollama (Local)\",\n \"is_enabled\": True, # Always enable Ollama\n \"api_key\": None,\n \"base_url\": settings.ollama_base_url,\n \"timeout\": settings.ollama_timeout,\n \"default_model\": settings.default_mt_model,\n \"priority\": 1,\n \"description\": \"Local LLM deployment with Ollama - Free and privacy-friendly\",\n \"quota\": None, # No quota for local deployment\n },\n {\n \"provider_name\": \"openai\",\n \"display_name\": \"OpenAI\",\n \"is_enabled\": bool(settings.openai_api_key),\n \"api_key\": settings.openai_api_key,\n \"base_url\": \"https://api.openai.com/v1\",\n \"timeout\": 300,\n \"default_model\": \"gpt-3.5-turbo\",\n \"priority\": 2,\n \"description\": \"OpenAI GPT models (GPT-4, GPT-3.5, etc.)\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n {\n \"provider_name\": \"deepseek\",\n \"display_name\": \"DeepSeek\",\n \"is_enabled\": bool(settings.deepseek_api_key),\n \"api_key\": settings.deepseek_api_key,\n \"base_url\": \"https://api.deepseek.com/v1\",\n \"timeout\": 300,\n \"default_model\": \"deepseek-chat\",\n \"priority\": 3,\n \"description\": \"DeepSeek AI models - Affordable and powerful\",\n \"quota\": {\"daily_limit\": 5.0, \"monthly_limit\": 50.0},\n },\n {\n \"provider_name\": \"claude\",\n \"display_name\": \"Claude (Anthropic)\",\n \"is_enabled\": bool(settings.claude_api_key),\n \"api_key\": settings.claude_api_key,\n \"base_url\": \"https://api.anthropic.com/v1\",\n \"timeout\": 300,\n \"default_model\": \"claude-3-5-sonnet-20241022\",\n \"priority\": 4,\n \"description\": \"Anthropic Claude models (Opus, Sonnet, Haiku)\",\n \"quota\": {\"daily_limit\": 20.0, \"monthly_limit\": 200.0},\n },\n {\n \"provider_name\": \"gemini\",\n \"display_name\": \"Gemini (Google)\",\n \"is_enabled\": bool(settings.gemini_api_key),\n \"api_key\": settings.gemini_api_key,\n \"base_url\": \"https://generativelanguage.googleapis.com/v1beta\",\n \"timeout\": 300,\n \"default_model\": \"gemini-pro\",\n \"priority\": 5,\n \"description\": \"Google Gemini models\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n {\n \"provider_name\": \"zhipu\",\n \"display_name\": \"智谱AI (GLM)\",\n \"is_enabled\": bool(settings.zhipu_api_key),\n \"api_key\": settings.zhipu_api_key,\n \"base_url\": \"https://open.bigmodel.cn/api/paas/v4\",\n \"timeout\": 300,\n \"default_model\": \"glm-4\",\n \"priority\": 6,\n \"description\": \"智谱AI GLM models - Chinese-optimized\",\n \"quota\": {\"daily_limit\": 5.0, \"monthly_limit\": 50.0},\n },\n {\n \"provider_name\": \"moonshot\",\n \"display_name\": \"Moonshot AI (Kimi)\",\n \"is_enabled\": bool(settings.moonshot_api_key),\n \"api_key\": settings.moonshot_api_key,\n \"base_url\": \"https://api.moonshot.cn/v1\",\n \"timeout\": 300,\n \"default_model\": \"moonshot-v1-32k\",\n \"priority\": 7,\n \"description\": \"Moonshot Kimi models - Super long context\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n {\n \"provider_name\": \"custom_openai\",\n \"display_name\": \"Custom OpenAI Compatible\",\n \"is_enabled\": bool(settings.custom_openai_api_key and settings.custom_openai_base_url),\n \"api_key\": settings.custom_openai_api_key,\n \"base_url\": settings.custom_openai_base_url,\n \"timeout\": 300,\n \"default_model\": None,\n \"priority\": 8,\n \"description\": \"Custom OpenAI-compatible endpoint (OpenRouter, LocalAI, vLLM, etc.)\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n ]\n\n created_count = 0\n updated_count = 0\n skipped_count = 0\n\n for provider_data in providers:\n provider_name = provider_data[\"provider_name\"]\n\n # Check if exists\n stmt = sa.select(AIProviderConfig).where(AIProviderConfig.provider_name == provider_name)\n existing = session.execute(stmt).scalar_one_or_none()\n\n if existing:\n # Only update if API key is provided and different, or if it's Ollama\n should_update = False\n\n if provider_name == \"ollama\":\n # Always update Ollama config\n should_update = True\n elif provider_data[\"api_key\"] and existing.api_key != provider_data[\"api_key\"]:\n # Update if API key changed\n should_update = True\n\n if should_update:\n existing.display_name = provider_data[\"display_name\"]\n existing.is_enabled = provider_data[\"is_enabled\"]\n if provider_data[\"api_key\"]:\n existing.api_key = provider_data[\"api_key\"]\n if provider_data[\"base_url\"]:\n existing.base_url = provider_data[\"base_url\"]\n existing.timeout = provider_data[\"timeout\"]\n existing.default_model = provider_data[\"default_model\"]\n existing.priority = provider_data[\"priority\"]\n existing.description = provider_data[\"description\"]\n\n updated_count += 1\n logger.info(f\"Updated provider config: {provider_name}\")\n else:\n skipped_count += 1\n else:\n # Create new provider config\n new_config = AIProviderConfig(\n provider_name=provider_data[\"provider_name\"],\n display_name=provider_data[\"display_name\"],\n is_enabled=provider_data[\"is_enabled\"],\n api_key=provider_data[\"api_key\"],\n base_url=provider_data[\"base_url\"],\n timeout=provider_data[\"timeout\"],\n default_model=provider_data[\"default_model\"],\n priority=provider_data[\"priority\"],\n description=provider_data[\"description\"],\n )\n session.add(new_config)\n created_count += 1\n logger.info(f\"Created provider config: {provider_name}\")\n\n # Initialize quota for cloud providers\n if provider_data[\"quota\"] and provider_name != \"ollama\":\n _init_provider_quota(session, provider_name, provider_data[\"quota\"])\n\n session.commit()\n\n logger.info(\n f\"AI provider initialization complete: \"\n f\"{created_count} created, {updated_count} updated, {skipped_count} skipped\"\n )\n\n\ndef _init_provider_quota(\n session: Session,\n provider_name: str,\n quota_config: dict,\n) -> None:\n \"\"\"\n Initialize quota configuration for a provider.\n\n Args:\n session: Database session\n provider_name: Provider name\n quota_config: Quota configuration dict\n \"\"\"\n from datetime import datetime\n\n # Check if quota exists\n stmt = sa.select(AIProviderQuota).where(AIProviderQuota.provider_name == provider_name)\n existing_quota = session.execute(stmt).scalar_one_or_none()\n\n if not existing_quota:\n # Create new quota\n quota = AIProviderQuota(\n provider_name=provider_name,\n daily_limit=quota_config.get(\"daily_limit\"),\n monthly_limit=quota_config.get(\"monthly_limit\"),\n daily_token_limit=quota_config.get(\"daily_token_limit\"),\n monthly_token_limit=quota_config.get(\"monthly_token_limit\"),\n alert_threshold_percent=80,\n auto_disable_on_limit=True,\n daily_reset_at=datetime.now(UTC),\n monthly_reset_at=datetime.now(UTC),\n )\n session.add(quota)\n logger.info(\n f\"Created quota for {provider_name}: \"\n f\"${quota_config.get('daily_limit')}/day, \"\n f\"${quota_config.get('monthly_limit')}/month\"\n )\n"}, "fcec414f027b15b937fd02f9bfaf8b0eb9b20359": {"t": 1785753491.0980422, "n": 9138, "h": "b3a41ccdf96ce125", "c": "\"\"\"\nAI Provider Initialization Service.\n\nAutomatically initializes AI provider configurations from environment variables.\n\"\"\"\n\nimport json\nfrom typing import Optional\nfrom sqlalchemy.orm import Session\nimport sqlalchemy as sa\n\nfrom app.core.config import settings\nfrom app.core.logging import get_logger\nfrom app.models.ai_provider_config import AIProviderConfig\nfrom app.models.ai_provider_usage import AIProviderQuota\n\nlogger = get_logger(__name__)\n\n\ndef init_ai_providers(session: Session) -> None:\n \"\"\"\n Initialize AI provider configurations from environment variables.\n\n This function checks for API keys in environment and creates/updates\n provider configurations accordingly.\n\n Args:\n session: Database session\n \"\"\"\n logger.info(\"Initializing AI provider configurations...\")\n\n # Define provider configurations\n providers = [\n {\n \"provider_name\": \"ollama\",\n \"display_name\": \"Ollama (Local)\",\n \"is_enabled\": True, # Always enable Ollama\n \"api_key\": None,\n \"base_url\": settings.ollama_base_url,\n \"timeout\": settings.ollama_timeout,\n \"default_model\": settings.default_mt_model,\n \"priority\": 1,\n \"description\": \"Local LLM deployment with Ollama - Free and privacy-friendly\",\n \"quota\": None, # No quota for local deployment\n },\n {\n \"provider_name\": \"openai\",\n \"display_name\": \"OpenAI\",\n \"is_enabled\": bool(settings.openai_api_key),\n \"api_key\": settings.openai_api_key,\n \"base_url\": \"https://api.openai.com/v1\",\n \"timeout\": 300,\n \"default_model\": \"gpt-3.5-turbo\",\n \"priority\": 2,\n \"description\": \"OpenAI GPT models (GPT-4, GPT-3.5, etc.)\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n {\n \"provider_name\": \"deepseek\",\n \"display_name\": \"DeepSeek\",\n \"is_enabled\": bool(settings.deepseek_api_key),\n \"api_key\": settings.deepseek_api_key,\n \"base_url\": \"https://api.deepseek.com/v1\",\n \"timeout\": 300,\n \"default_model\": \"deepseek-chat\",\n \"priority\": 3,\n \"description\": \"DeepSeek AI models - Affordable and powerful\",\n \"quota\": {\"daily_limit\": 5.0, \"monthly_limit\": 50.0},\n },\n {\n \"provider_name\": \"claude\",\n \"display_name\": \"Claude (Anthropic)\",\n \"is_enabled\": bool(settings.claude_api_key),\n \"api_key\": settings.claude_api_key,\n \"base_url\": \"https://api.anthropic.com/v1\",\n \"timeout\": 300,\n \"default_model\": \"claude-3-5-sonnet-20241022\",\n \"priority\": 4,\n \"description\": \"Anthropic Claude models (Opus, Sonnet, Haiku)\",\n \"quota\": {\"daily_limit\": 20.0, \"monthly_limit\": 200.0},\n },\n {\n \"provider_name\": \"gemini\",\n \"display_name\": \"Gemini (Google)\",\n \"is_enabled\": bool(settings.gemini_api_key),\n \"api_key\": settings.gemini_api_key,\n \"base_url\": \"https://generativelanguage.googleapis.com/v1beta\",\n \"timeout\": 300,\n \"default_model\": \"gemini-pro\",\n \"priority\": 5,\n \"description\": \"Google Gemini models\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n {\n \"provider_name\": \"zhipu\",\n \"display_name\": \"智谱AI (GLM)\",\n \"is_enabled\": bool(settings.zhipu_api_key),\n \"api_key\": settings.zhipu_api_key,\n \"base_url\": \"https://open.bigmodel.cn/api/paas/v4\",\n \"timeout\": 300,\n \"default_model\": \"glm-4\",\n \"priority\": 6,\n \"description\": \"智谱AI GLM models - Chinese-optimized\",\n \"quota\": {\"daily_limit\": 5.0, \"monthly_limit\": 50.0},\n },\n {\n \"provider_name\": \"moonshot\",\n \"display_name\": \"Moonshot AI (Kimi)\",\n \"is_enabled\": bool(settings.moonshot_api_key),\n \"api_key\": settings.moonshot_api_key,\n \"base_url\": \"https://api.moonshot.cn/v1\",\n \"timeout\": 300,\n \"default_model\": \"moonshot-v1-32k\",\n \"priority\": 7,\n \"description\": \"Moonshot Kimi models - Super long context\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n {\n \"provider_name\": \"custom_openai\",\n \"display_name\": \"Custom OpenAI Compatible\",\n \"is_enabled\": bool(settings.custom_openai_api_key and settings.custom_openai_base_url),\n \"api_key\": settings.custom_openai_api_key,\n \"base_url\": settings.custom_openai_base_url,\n \"timeout\": 300,\n \"default_model\": None,\n \"priority\": 8,\n \"description\": \"Custom OpenAI-compatible endpoint (OpenRouter, LocalAI, vLLM, etc.)\",\n \"quota\": {\"daily_limit\": 10.0, \"monthly_limit\": 100.0},\n },\n ]\n\n created_count = 0\n updated_count = 0\n skipped_count = 0\n\n for provider_data in providers:\n provider_name = provider_data[\"provider_name\"]\n\n # Check if exists\n stmt = sa.select(AIProviderConfig).where(\n AIProviderConfig.provider_name == provider_name\n )\n existing = session.execute(stmt).scalar_one_or_none()\n\n if existing:\n # Only update if API key is provided and different, or if it's Ollama\n should_update = False\n\n if provider_name == \"ollama\":\n # Always update Ollama config\n should_update = True\n elif provider_data[\"api_key\"] and existing.api_key != provider_data[\"api_key\"]:\n # Update if API key changed\n should_update = True\n\n if should_update:\n existing.display_name = provider_data[\"display_name\"]\n existing.is_enabled = provider_data[\"is_enabled\"]\n if provider_data[\"api_key\"]:\n existing.api_key = provider_data[\"api_key\"]\n if provider_data[\"base_url\"]:\n existing.base_url = provider_data[\"base_url\"]\n existing.timeout = provider_data[\"timeout\"]\n existing.default_model = provider_data[\"default_model\"]\n existing.priority = provider_data[\"priority\"]\n existing.description = provider_data[\"description\"]\n\n updated_count += 1\n logger.info(f\"Updated provider config: {provider_name}\")\n else:\n skipped_count += 1\n else:\n # Create new provider config\n new_config = AIProviderConfig(\n provider_name=provider_data[\"provider_name\"],\n display_name=provider_data[\"display_name\"],\n is_enabled=provider_data[\"is_enabled\"],\n api_key=provider_data[\"api_key\"],\n base_url=provider_data[\"base_url\"],\n timeout=provider_data[\"timeout\"],\n default_model=provider_data[\"default_model\"],\n priority=provider_data[\"priority\"],\n description=provider_data[\"description\"],\n )\n session.add(new_config)\n created_count += 1\n logger.info(f\"Created provider config: {provider_name}\")\n\n # Initialize quota for cloud providers\n if provider_data[\"quota\"] and provider_name != \"ollama\":\n _init_provider_quota(session, provider_name, provider_data[\"quota\"])\n\n session.commit()\n\n logger.info(\n f\"AI provider initialization complete: \"\n f\"{created_count} created, {updated_count} updated, {skipped_count} skipped\"\n )\n\n\ndef _init_provider_quota(\n session: Session,\n provider_name: str,\n quota_config: dict,\n) -> None:\n \"\"\"\n Initialize quota configuration for a provider.\n\n Args:\n session: Database session\n provider_name: Provider name\n quota_config: Quota configuration dict\n \"\"\"\n from datetime import datetime, timezone\n\n # Check if quota exists\n stmt = sa.select(AIProviderQuota).where(\n AIProviderQuota.provider_name == provider_name\n )\n existing_quota = session.execute(stmt).scalar_one_or_none()\n\n if not existing_quota:\n # Create new quota\n quota = AIProviderQuota(\n provider_name=provider_name,\n daily_limit=quota_config.get(\"daily_limit\"),\n monthly_limit=quota_config.get(\"monthly_limit\"),\n daily_token_limit=quota_config.get(\"daily_token_limit\"),\n monthly_token_limit=quota_config.get(\"monthly_token_limit\"),\n alert_threshold_percent=80,\n auto_disable_on_limit=True,\n daily_reset_at=datetime.now(timezone.utc),\n monthly_reset_at=datetime.now(timezone.utc),\n )\n session.add(quota)\n logger.info(\n f\"Created quota for {provider_name}: \"\n f\"${quota_config.get('daily_limit')}/day, \"\n f\"${quota_config.get('monthly_limit')}/month\"\n )\n"}}}}