diff --git a/backend/app/contracts/__init__.py b/backend/app/contracts/__init__.py index 0aabd6fd..37ecf0e2 100644 --- a/backend/app/contracts/__init__.py +++ b/backend/app/contracts/__init__.py @@ -68,6 +68,19 @@ ApiKeyStatus, ApiKeysListResponse, ) +from .provider_types import ( + ALL_PROVIDER_TYPES, + LEGACY_GEMINI, + PROVIDER_ANTHROPIC, + PROVIDER_CUSTOM, + PROVIDER_GITHUB_COPILOT, + PROVIDER_GOOGLE, + PROVIDER_OLLAMA, + PROVIDER_OLLAMA_CLOUD, + PROVIDER_OPENAI, + PROVIDER_OPENAI_COMPATIBLE, + ProviderType, +) from .report_v3 import ( Claim, ChangeRecommendation, @@ -136,6 +149,17 @@ "RunSummary", "SegmentReach", "VoiceRegister", + "ALL_PROVIDER_TYPES", + "LEGACY_GEMINI", + "PROVIDER_ANTHROPIC", + "PROVIDER_CUSTOM", + "PROVIDER_GITHUB_COPILOT", + "PROVIDER_GOOGLE", + "PROVIDER_OLLAMA", + "PROVIDER_OLLAMA_CLOUD", + "PROVIDER_OPENAI", + "PROVIDER_OPENAI_COMPATIBLE", + "ProviderType", # ReportV3 — 11 Pflichtabschnitt-DTOs "Claim", "ChangeRecommendation", diff --git a/backend/app/contracts/llm_profile_contract.py b/backend/app/contracts/llm_profile_contract.py index e087ac84..e9ebde4e 100644 --- a/backend/app/contracts/llm_profile_contract.py +++ b/backend/app/contracts/llm_profile_contract.py @@ -6,13 +6,13 @@ from __future__ import annotations from datetime import datetime -from typing import Literal, Optional +from typing import Optional from pydantic import BaseModel, ConfigDict, Field -_STRICT = ConfigDict(extra="forbid", populate_by_name=True) +from .provider_types import ProviderType -ProviderLiteral = Literal["ollama", "openai", "gemini", "anthropic", "custom"] +_STRICT = ConfigDict(extra="forbid", populate_by_name=True) class LlmProfile(BaseModel): @@ -20,7 +20,7 @@ class LlmProfile(BaseModel): id: str = Field(..., description="UUID, server-generiert") name: str = Field(..., min_length=1, max_length=80) - provider: ProviderLiteral + provider: ProviderType base_url: str = Field(..., min_length=1) model_name: str = Field(..., min_length=1) # None = nicht gesetzt (Update lässt Feld weg). "" = explizit geleert. @@ -39,7 +39,7 @@ class LlmProfileCreateRequest(BaseModel): model_config = _STRICT name: str = Field(..., min_length=1, max_length=80) - provider: ProviderLiteral + provider: ProviderType base_url: str = Field(..., min_length=1) model_name: str = Field(..., min_length=1) # None = nicht gesetzt (Update lässt Feld weg). "" = explizit geleert. diff --git a/backend/app/contracts/llm_routing_contract.py b/backend/app/contracts/llm_routing_contract.py index 8f6571c8..29072489 100644 --- a/backend/app/contracts/llm_routing_contract.py +++ b/backend/app/contracts/llm_routing_contract.py @@ -6,6 +6,7 @@ from typing import Dict, Literal, Optional, Any, List from pydantic import BaseModel, ConfigDict, Field, model_validator +from .provider_types import ProviderType _STRICT = ConfigDict(extra="forbid") @@ -71,7 +72,7 @@ class ProviderDescriptor(BaseModel): id: str label: str - type: Literal["ollama_cloud", "openai", "google", "openai_compatible", "github_copilot"] + type: ProviderType base_url: Optional[str] = None api_key_ref: Optional[str] = None supports_models_endpoint: bool = False diff --git a/backend/app/contracts/provider_types.py b/backend/app/contracts/provider_types.py new file mode 100644 index 00000000..0bfdee01 --- /dev/null +++ b/backend/app/contracts/provider_types.py @@ -0,0 +1,41 @@ +from typing import Literal + +PROVIDER_OLLAMA = "ollama" +PROVIDER_OPENAI = "openai" +PROVIDER_GOOGLE = "google" +PROVIDER_ANTHROPIC = "anthropic" +PROVIDER_CUSTOM = "custom" +PROVIDER_OLLAMA_CLOUD = "ollama_cloud" +PROVIDER_OPENAI_COMPATIBLE = "openai_compatible" +PROVIDER_GITHUB_COPILOT = "github_copilot" +PROVIDER_CLOUD = "cloud" +PROVIDER_UNKNOWN = "unknown" + +# Legacy alias +LEGACY_GEMINI = "gemini" + +ProviderType = Literal[ + "ollama", + "openai", + "google", + "anthropic", + "custom", + "ollama_cloud", + "openai_compatible", + "github_copilot", + "cloud", + "unknown", +] + +ALL_PROVIDER_TYPES: tuple[ProviderType, ...] = ( + PROVIDER_OLLAMA, + PROVIDER_OPENAI, + PROVIDER_GOOGLE, + PROVIDER_ANTHROPIC, + PROVIDER_CUSTOM, + PROVIDER_OLLAMA_CLOUD, + PROVIDER_OPENAI_COMPATIBLE, + PROVIDER_GITHUB_COPILOT, + PROVIDER_CLOUD, + PROVIDER_UNKNOWN, +) diff --git a/backend/app/contracts/report_v3.py b/backend/app/contracts/report_v3.py index 7e7bff90..e50b4c9e 100644 --- a/backend/app/contracts/report_v3.py +++ b/backend/app/contracts/report_v3.py @@ -15,6 +15,8 @@ from pydantic import BaseModel, ConfigDict, Field +from .provider_types import ProviderType + _STRICT = ConfigDict(extra="forbid", str_strip_whitespace=True) @@ -236,7 +238,7 @@ class ModelAttribution(BaseModel): model_config = _STRICT stage: ModelAttributionStage - provider: str = Field(min_length=1, description="z. B. 'ollama', 'openai', 'gemini'") + provider: ProviderType = Field(description="z. B. 'ollama', 'openai', 'google'") model_id: str = Field(min_length=1, description="Backend-Modell-ID, z. B. 'qwen2.5:32b'") prompt_tokens: int | None = Field(default=None, ge=0) completion_tokens: int | None = Field(default=None, ge=0) diff --git a/backend/app/services/llm_profiles_store.py b/backend/app/services/llm_profiles_store.py index 305a508c..939cbd36 100644 --- a/backend/app/services/llm_profiles_store.py +++ b/backend/app/services/llm_profiles_store.py @@ -14,7 +14,15 @@ from pathlib import Path from typing import Optional -from ..contracts.llm_profile_contract import LlmProfile, LlmProfileCreateRequest +from ..contracts import ( + PROVIDER_ANTHROPIC, + PROVIDER_CUSTOM, + PROVIDER_GOOGLE, + PROVIDER_OLLAMA, + PROVIDER_OPENAI, + LlmProfile, + LlmProfileCreateRequest, +) def _now() -> datetime: @@ -77,15 +85,15 @@ def _bootstrap_profile() -> Optional[dict]: if not model: return None if "openai.com" in base_url: - provider = "openai" + provider = PROVIDER_OPENAI elif "googleapis.com" in base_url: - provider = "gemini" + provider = PROVIDER_GOOGLE elif "anthropic.com" in base_url: - provider = "anthropic" + provider = PROVIDER_ANTHROPIC elif any(h in base_url for h in ("localhost", "127.0.0.1", "host.docker.internal")): - provider = "ollama" + provider = PROVIDER_OLLAMA else: - provider = "custom" + provider = PROVIDER_CUSTOM return dict( id=uuid.uuid4().hex, name="Standard", diff --git a/backend/app/services/llm_provider_registry.py b/backend/app/services/llm_provider_registry.py index 8b3f9137..6cb7c594 100644 --- a/backend/app/services/llm_provider_registry.py +++ b/backend/app/services/llm_provider_registry.py @@ -4,6 +4,13 @@ """ from typing import List, Optional +from ..contracts import ( + PROVIDER_GITHUB_COPILOT, + PROVIDER_GOOGLE, + PROVIDER_OLLAMA_CLOUD, + PROVIDER_OPENAI, + PROVIDER_OPENAI_COMPATIBLE, +) from ..contracts.llm_routing_contract import ProviderDescriptor class LlmProviderRegistry: @@ -20,7 +27,7 @@ def get_providers(self, session_api_keys: Optional[dict] = None) -> List[Provide ProviderDescriptor( id="ollama_cloud", label="Ollama (Cloud)", - type="ollama_cloud", + type=PROVIDER_OLLAMA_CLOUD, # OpenAI-kompatibler Ollama-Cloud-Endpoint. Auth via Bearer-Token # (``OLLAMA_API_KEY``). Doku: https://docs.ollama.com/cloud base_url="https://ollama.com/v1", @@ -36,7 +43,7 @@ def get_providers(self, session_api_keys: Optional[dict] = None) -> List[Provide ProviderDescriptor( id="openai", label="OpenAI", - type="openai", + type=PROVIDER_OPENAI, base_url="https://api.openai.com/v1", api_key_ref="OPENAI_API_KEY", supports_models_endpoint=True, @@ -45,7 +52,7 @@ def get_providers(self, session_api_keys: Optional[dict] = None) -> List[Provide ProviderDescriptor( id="google", label="Google Gemini", - type="google", + type=PROVIDER_GOOGLE, base_url="https://generativelanguage.googleapis.com/v1beta/openai/", api_key_ref="GOOGLE_API_KEY", supports_models_endpoint=True, @@ -54,7 +61,7 @@ def get_providers(self, session_api_keys: Optional[dict] = None) -> List[Provide ProviderDescriptor( id="openai_compatible", label="OpenAI Compatible", - type="openai_compatible", + type=PROVIDER_OPENAI_COMPATIBLE, api_key_ref="LLM_API_KEY", supports_models_endpoint=True, fallback_models=[], @@ -62,7 +69,7 @@ def get_providers(self, session_api_keys: Optional[dict] = None) -> List[Provide ProviderDescriptor( id="github_copilot", label="GitHub Copilot", - type="github_copilot", + type=PROVIDER_GITHUB_COPILOT, base_url="https://api.githubcopilot.com", api_key_ref="GH_AUTH_TOKEN", supports_models_endpoint=False, diff --git a/backend/app/services/llm_runtime.py b/backend/app/services/llm_runtime.py index f1425d08..08157573 100644 --- a/backend/app/services/llm_runtime.py +++ b/backend/app/services/llm_runtime.py @@ -11,18 +11,25 @@ from typing import Any, Mapping, Optional from urllib.parse import urlparse +from ..contracts import ( + LEGACY_GEMINI, + PROVIDER_CUSTOM, + PROVIDER_GOOGLE, + PROVIDER_OPENAI, + PROVIDER_OPENAI_COMPATIBLE, +) PROVIDER_DEFAULT_BASE_URLS = { - "google": "https://generativelanguage.googleapis.com/v1beta/openai/", - "openai": "https://api.openai.com/v1", + PROVIDER_GOOGLE: "https://generativelanguage.googleapis.com/v1beta/openai/", + PROVIDER_OPENAI: "https://api.openai.com/v1", } _PROVIDER_ALIASES = { "default": "default", "server": "default", - "google": "google", - "gemini": "google", - "openai": "openai", + "google": PROVIDER_GOOGLE, + LEGACY_GEMINI: PROVIDER_GOOGLE, + "openai": PROVIDER_OPENAI, "custom": "custom_openai", "custom_openai": "custom_openai", "openai_compatible": "custom_openai", @@ -33,8 +40,8 @@ # beliebigem Format (``custom_openai``, ``ollama_cloud``, ``github_copilot``) # stehen NICHT drin — die werden nicht validiert. _KEY_PREFIX_BY_PROVIDER: dict[str, tuple[str, ...]] = { - "openai": ("sk-",), - "google": ("AIzaSy",), + PROVIDER_OPENAI: ("sk-",), + PROVIDER_GOOGLE: ("AIzaSy",), } @@ -116,7 +123,7 @@ def parse_runtime_llm_config(data: Mapping[str, Any]) -> RuntimeLlmConfig: provider = _PROVIDER_ALIASES.get(provider_raw) if provider is None: raise ValueError( - "llm_provider.provider must be one of: default, google, openai, custom_openai" + f"llm_provider.provider must be one of: default, {PROVIDER_GOOGLE}, {PROVIDER_OPENAI}, custom_openai" ) if provider == "default": return RuntimeLlmConfig() diff --git a/backend/app/services/model_catalog_service.py b/backend/app/services/model_catalog_service.py index 1ccbc1ba..2a84eccf 100644 --- a/backend/app/services/model_catalog_service.py +++ b/backend/app/services/model_catalog_service.py @@ -17,6 +17,14 @@ import urllib3 from pydantic import BaseModel, ConfigDict +from ..contracts import ( + PROVIDER_GOOGLE, + PROVIDER_OLLAMA_CLOUD, + PROVIDER_OPENAI, + PROVIDER_OPENAI_COMPATIBLE, + PROVIDER_GITHUB_COPILOT, +) + from ..utils.logger import get_logger logger = get_logger("agora.model_catalog") @@ -122,11 +130,11 @@ def get_models(self, provider_id: str, provider_type: str, base_url: str, api_ke def _fetch_live(self, provider_type: str, base_url: str, api_key: Optional[str]) -> List[str]: """Discovery implementation per provider type.""" - if provider_type == "github_copilot": + if provider_type == PROVIDER_GITHUB_COPILOT: # Phase 1: kein Live-Discovery — statische Liste über _get_fallbacks. return [] - if provider_type == "ollama_cloud": + if provider_type == PROVIDER_OLLAMA_CLOUD: # Ollama Cloud (ollama.com) BRAUCHT Bearer-Token am /v1/models-Endpoint. # Vorher fehlte der Header — Ergebnis war ein stiller 401 → leere Liste # (PR #528 Follow-up). @@ -143,7 +151,7 @@ def _fetch_live(self, provider_type: str, base_url: str, api_key: Optional[str]) return [m["name"] for m in data.get("models", [])] return [] - if provider_type in ("openai", "google", "openai_compatible"): + if provider_type in (PROVIDER_OPENAI, PROVIDER_GOOGLE, PROVIDER_OPENAI_COMPATIBLE): # OpenAI-shape: data[].id. Erst /models, dann /v1/models als Fallback, # falls die base_url nicht schon /v1 enthält. data = _http_get_json(f"{base_url.rstrip('/')}/models", api_key=api_key) @@ -161,13 +169,13 @@ def _get_fallbacks(self, provider_type: str) -> List[str]: # zwingend installiert (User-Bericht 2026-05-16: halluzinierte Einträge # im Dashboard-Picker). Lieber leeres Catalog + sichtbarer Fehlerzustand # als Ehrlichkeits-Lüge mit nicht-existenten Modellen. - if provider_type == "ollama_cloud": + if provider_type == PROVIDER_OLLAMA_CLOUD: return [] - if provider_type == "openai": + if provider_type == PROVIDER_OPENAI: return ["gpt-4o", "gpt-4o-mini", "o1-preview"] - if provider_type == "google": + if provider_type == PROVIDER_GOOGLE: return ["gemini-1.5-pro", "gemini-1.5-flash"] - if provider_type == "github_copilot": + if provider_type == PROVIDER_GITHUB_COPILOT: from .llm_providers.github_copilot import GITHUB_COPILOT_MODELS return list(GITHUB_COPILOT_MODELS) return [] diff --git a/backend/app/services/model_event_bus.py b/backend/app/services/model_event_bus.py index 368a6012..6c6b19c5 100644 --- a/backend/app/services/model_event_bus.py +++ b/backend/app/services/model_event_bus.py @@ -28,6 +28,7 @@ from typing import Any, Generator, Iterator, Literal from pydantic import BaseModel, ConfigDict +from ..contracts import ProviderType from ..utils.logger import get_logger @@ -61,16 +62,7 @@ class ModelActiveEvent(BaseModel): "graph", "unknown", ] - provider: Literal[ - "ollama", - "cloud", - "openai", - "google", - "ollama_cloud", - "openai_compatible", - "github_copilot", - "unknown", - ] + provider: ProviderType ts: float extra: dict[str, Any] | None = None diff --git a/backend/app/services/runtime_run_config.py b/backend/app/services/runtime_run_config.py index c5ce0635..bd2e015c 100644 --- a/backend/app/services/runtime_run_config.py +++ b/backend/app/services/runtime_run_config.py @@ -8,6 +8,12 @@ import tempfile from urllib.parse import urlparse, urlunparse from typing import Optional, Dict, Any +from ..contracts import ( + PROVIDER_GOOGLE, + PROVIDER_OLLAMA_CLOUD, + PROVIDER_OPENAI, + PROVIDER_OPENAI_COMPATIBLE, +) from ..contracts.llm_routing_contract import RuntimeLlmRouting, StageLLMRoute, StageId from ..utils.artifact_locator import ArtifactLocator from ..utils.logger import get_logger @@ -47,12 +53,12 @@ def _detect_default_provider_id(base_url: Optional[str], model_name: Optional[st hostname = (parsed.hostname or "").lower() if parsed else "" if normalized_model.endswith(":cloud") or hostname == "ollama.com": - return "ollama_cloud" + return PROVIDER_OLLAMA_CLOUD if hostname == "generativelanguage.googleapis.com" or "gemini" in normalized_model: - return "google" + return PROVIDER_GOOGLE if hostname in {"api.openai.com", "openai.com"}: - return "openai" - return "openai_compatible" + return PROVIDER_OPENAI + return PROVIDER_OPENAI_COMPATIBLE class RuntimeRunConfig: diff --git a/backend/app/services/secret_resolver.py b/backend/app/services/secret_resolver.py index 689c63d5..2f883345 100644 --- a/backend/app/services/secret_resolver.py +++ b/backend/app/services/secret_resolver.py @@ -38,6 +38,13 @@ from typing import Optional, Dict from ..config import Config +from ..contracts import ( + PROVIDER_GOOGLE, + PROVIDER_OLLAMA_CLOUD, + PROVIDER_OPENAI, + PROVIDER_OPENAI_COMPATIBLE, + PROVIDER_GITHUB_COPILOT, +) from .llm_provider_secrets_store import get_llm_provider_secrets_store logger = logging.getLogger("agora.secret_resolver") @@ -81,9 +88,9 @@ def _format_valid(value: Optional[str], provider_type: str) -> bool: return False if v.lower() in _TOXIC_KEY_LITERALS: return False - if provider_type == "openai": + if provider_type == PROVIDER_OPENAI: return bool(_OPENAI_KEY_RE.match(v)) - if provider_type == "google": + if provider_type == PROVIDER_GOOGLE: return bool(_GOOGLE_KEY_RE.match(v)) return True @@ -136,16 +143,16 @@ def get_api_key(self, provider_id: str, provider_type: str) -> Optional[str]: # 3. Provider-spezifische Environment-Variablen (mit Format-Sanity-Check) env_candidates: list[tuple[str, Optional[str]]] = [] - if provider_type == "openai": + if provider_type == PROVIDER_OPENAI: env_candidates.append(("env:OPENAI_API_KEY", os.environ.get("OPENAI_API_KEY"))) - elif provider_type == "google": + elif provider_type == PROVIDER_GOOGLE: env_candidates.append(("env:GOOGLE_API_KEY", os.environ.get("GOOGLE_API_KEY"))) env_candidates.append(("env:GEMINI_API_KEY", os.environ.get("GEMINI_API_KEY"))) - elif provider_type == "ollama_cloud": + elif provider_type == PROVIDER_OLLAMA_CLOUD: env_candidates.append(("env:OLLAMA_API_KEY", os.environ.get("OLLAMA_API_KEY"))) - elif provider_type == "openai_compatible": + elif provider_type == PROVIDER_OPENAI_COMPATIBLE: env_candidates.append(("env:LLM_API_KEY", os.environ.get("LLM_API_KEY"))) - elif provider_type == "github_copilot": + elif provider_type == PROVIDER_GITHUB_COPILOT: # GitHub Copilot: Token-Auflösung über separates Modul (Slice B). try: from .llm_providers.github_copilot import resolve_copilot_token @@ -174,7 +181,7 @@ def get_api_key(self, provider_id: str, provider_type: str) -> Optional[str]: # 4. Globaler Fallback Config.LLM_API_KEY fallback = Config.LLM_API_KEY if fallback: - if provider_type in ("openai", "google"): + if provider_type in (PROVIDER_OPENAI, PROVIDER_GOOGLE): if _format_valid(fallback, provider_type): self.last_source = "config_fallback" return fallback @@ -184,7 +191,7 @@ def get_api_key(self, provider_id: str, provider_type: str) -> Optional[str]: "im UI (Settings → LLM-Provider) oder via ENV %s.", provider_type, _mask_for_log(fallback), - "OPENAI_API_KEY" if provider_type == "openai" else "GOOGLE_API_KEY", + "OPENAI_API_KEY" if provider_type == PROVIDER_OPENAI else "GOOGLE_API_KEY", ) return None # Provider-Type ohne striktes Format (ollama_cloud, openai_compatible, …): diff --git a/backend/app/utils/llm_profile_resolver.py b/backend/app/utils/llm_profile_resolver.py index faac3f13..88fe3da5 100644 --- a/backend/app/utils/llm_profile_resolver.py +++ b/backend/app/utils/llm_profile_resolver.py @@ -16,13 +16,14 @@ from typing import Any, Mapping +from ..contracts import LEGACY_GEMINI, PROVIDER_GOOGLE from ..services.llm_profiles_store import get_llm_profiles_store _PROFILE_PREFIX = "profile:" _PROFILE_PROVIDER_TO_RUNTIME = { "openai": "openai", - "gemini": "google", + LEGACY_GEMINI: PROVIDER_GOOGLE, "ollama": "custom_openai", "anthropic": "custom_openai", "custom": "custom_openai", diff --git a/backend/tests/contracts/test_provider_types.py b/backend/tests/contracts/test_provider_types.py new file mode 100644 index 00000000..854244cf --- /dev/null +++ b/backend/tests/contracts/test_provider_types.py @@ -0,0 +1,58 @@ +import re +from pathlib import Path + +def test_no_gemini_literals_in_code(): + """ + Regression gate: Ensure 'gemini' literal is not used for provider identification + outside of comments, docstrings, and the provider_types.py mapping. + """ + app_root = Path(__file__).parents[3] / "backend" / "app" + + # Regex to find "gemini" (with quotes) + gemini_re = re.compile(r'"gemini"') + + forbidden_lines = [] + + # Files/directories to skip completely + skip_files = {"provider_types.py"} + + # Legitimate substrings to allow on a line containing "gemini" + legitimate_substrings = [ + "fallback_models", + "gemini-1.5", + "gemini-3", + "VISION_MODEL_NAME", + "normalized_model", + "LEGACY_GEMINI" + ] + + for path in app_root.rglob("*.py"): + if path.name in skip_files: + continue + + with open(path, "r", encoding="utf-8") as f: + lines = f.readlines() + + for i, line in enumerate(lines, 1): + if gemini_re.search(line): + # Check for comments: if # appears before "gemini" + comment_idx = line.find("#") + gemini_idx = line.find('"gemini"') + if comment_idx != -1 and comment_idx < gemini_idx: + continue + + # Rough docstring check + if '"""' in line or "'''" in line: + continue + + # Check for legitimate usage + if any(sub in line for sub in legitimate_substrings): + continue + + forbidden_lines.append(f"{path.relative_to(app_root.parent)}:{i}: {line.strip()}") + + if forbidden_lines: + print("\nFound forbidden 'gemini' literals:") + for fl in forbidden_lines: + print(fl) + assert not forbidden_lines, f"Found {len(forbidden_lines)} forbidden 'gemini' literals." diff --git a/frontend/src/contracts/llmProfileContract.ts b/frontend/src/contracts/llmProfileContract.ts index 3888f953..7b8b6f1a 100644 --- a/frontend/src/contracts/llmProfileContract.ts +++ b/frontend/src/contracts/llmProfileContract.ts @@ -7,9 +7,15 @@ import { z } from "zod"; export const LlmProviderSchema = z.enum([ "ollama", "openai", + "google", "gemini", "anthropic", "custom", + "ollama_cloud", + "openai_compatible", + "github_copilot", + "cloud", + "unknown", ]); export type LlmProvider = z.infer; diff --git a/schemas/llm-profile-create-request.schema.json b/schemas/llm-profile-create-request.schema.json index 0dd2bb1d..33e52846 100644 --- a/schemas/llm-profile-create-request.schema.json +++ b/schemas/llm-profile-create-request.schema.json @@ -40,9 +40,14 @@ "enum": [ "ollama", "openai", - "gemini", + "google", "anthropic", - "custom" + "custom", + "ollama_cloud", + "openai_compatible", + "github_copilot", + "cloud", + "unknown" ], "title": "Provider", "type": "string" diff --git a/schemas/llm-profile-list-response.schema.json b/schemas/llm-profile-list-response.schema.json index f4593e97..b29c10e4 100644 --- a/schemas/llm-profile-list-response.schema.json +++ b/schemas/llm-profile-list-response.schema.json @@ -50,9 +50,14 @@ "enum": [ "ollama", "openai", - "gemini", + "google", "anthropic", - "custom" + "custom", + "ollama_cloud", + "openai_compatible", + "github_copilot", + "cloud", + "unknown" ], "title": "Provider", "type": "string" diff --git a/schemas/llm-profile.schema.json b/schemas/llm-profile.schema.json index 2c5c2753..6cf85df7 100644 --- a/schemas/llm-profile.schema.json +++ b/schemas/llm-profile.schema.json @@ -50,9 +50,14 @@ "enum": [ "ollama", "openai", - "gemini", + "google", "anthropic", - "custom" + "custom", + "ollama_cloud", + "openai_compatible", + "github_copilot", + "cloud", + "unknown" ], "title": "Provider", "type": "string" diff --git a/schemas/llm-provider-descriptor.schema.json b/schemas/llm-provider-descriptor.schema.json index dcc026f6..460379b3 100644 --- a/schemas/llm-provider-descriptor.schema.json +++ b/schemas/llm-provider-descriptor.schema.json @@ -50,11 +50,16 @@ }, "type": { "enum": [ - "ollama_cloud", + "ollama", "openai", "google", + "anthropic", + "custom", + "ollama_cloud", "openai_compatible", - "github_copilot" + "github_copilot", + "cloud", + "unknown" ], "title": "Type", "type": "string" diff --git a/schemas/report-v3.schema.json b/schemas/report-v3.schema.json index ccdd3533..20ebcf5b 100644 --- a/schemas/report-v3.schema.json +++ b/schemas/report-v3.schema.json @@ -369,8 +369,19 @@ "title": "Prompt Tokens" }, "provider": { - "description": "z. B. 'ollama', 'openai', 'gemini'", - "minLength": 1, + "description": "z. B. 'ollama', 'openai', 'google'", + "enum": [ + "ollama", + "openai", + "google", + "anthropic", + "custom", + "ollama_cloud", + "openai_compatible", + "github_copilot", + "cloud", + "unknown" + ], "title": "Provider", "type": "string" },