SurfSense/surfsense_backend/app/services/provider_registry.py
Thibault Jaigu 2ff7ea4cb6 feat: add Requesty as a model provider
Add Requesty (https://requesty.ai), an OpenAI-compatible LLM router, as a
model provider by mirroring the existing OpenRouter integration.

Backend:
- app/services/requesty_model_normalizer.py: normalizes Requesty's /v1/models
  catalogue, mapping its flat capability booleans (supports_tool_calling/
  supports_vision/supports_image_generation) and context_window field onto the
  shared normalized shape (Requesty differs from OpenRouter's architecture +
  supported_parameters + context_length layout)
- provider_registry.py: Requesty ProviderSpec (OpenAI-compatible, base URL
  https://router.requesty.ai/v1, REQUESTY_API_KEY bearer auth)
- model_connection_service.py: key verification + live model discovery
- quality_score.py: Requesty score entry
- unit tests mirroring the OpenRouter normalizer coverage

Frontend:
- Requesty provider icon + registration, metadata entry, and base-url hint

Signed-off-by: Thibault Jaigu <thibault.jaigu@gmail.com>
2026-07-13 09:42:30 +01:00

136 lines
3.3 KiB
Python

"""Provider registry for model connections.
The provider string is the single public identity axis. This registry only
describes providers whose behavior differs from LiteLLM's native default.
"""
from __future__ import annotations
from dataclasses import dataclass
from enum import StrEnum
from typing import Literal
class Transport(StrEnum):
NATIVE = "NATIVE"
OPENAI_COMPATIBLE = "OPENAI_COMPATIBLE"
OLLAMA = "OLLAMA"
DiscoveryKind = Literal[
"ollama",
"openai_models",
"anthropic_models",
"bedrock_models",
"openrouter",
"requesty",
"static",
"none",
]
AuthStyle = Literal["bearer", "x-api-key", "none", "native"]
@dataclass(frozen=True)
class ProviderSpec:
transport: Transport
litellm_prefix: str | None
discovery: DiscoveryKind
default_base_url: str | None
base_url_required: bool
auth_style: AuthStyle
display_name: str | None = None
REGISTRY: dict[str, ProviderSpec] = {
"openai": ProviderSpec(
Transport.NATIVE, "openai", "openai_models", None, False, "bearer", "OpenAI"
),
"anthropic": ProviderSpec(
Transport.NATIVE,
"anthropic",
"anthropic_models",
None,
False,
"x-api-key",
"Anthropic",
),
"azure": ProviderSpec(
Transport.NATIVE, "azure", "static", None, True, "native", "Azure OpenAI"
),
"vertex_ai": ProviderSpec(
Transport.NATIVE, "vertex_ai", "static", None, False, "native", "Vertex AI"
),
"bedrock": ProviderSpec(
Transport.NATIVE,
"bedrock",
"bedrock_models",
None,
False,
"native",
"Amazon Bedrock",
),
"openrouter": ProviderSpec(
Transport.OPENAI_COMPATIBLE,
"openrouter",
"openrouter",
"https://openrouter.ai/api/v1",
False,
"bearer",
"OpenRouter",
),
"requesty": ProviderSpec(
Transport.OPENAI_COMPATIBLE,
"openai",
"requesty",
"https://router.requesty.ai/v1",
False,
"bearer",
"Requesty",
),
"openai_compatible": ProviderSpec(
Transport.OPENAI_COMPATIBLE,
"openai",
"openai_models",
None,
True,
"bearer",
"OpenAI-compatible provider",
),
"lm_studio": ProviderSpec(
Transport.OPENAI_COMPATIBLE,
"openai",
"openai_models",
"http://host.docker.internal:1234/v1",
True,
"bearer",
"LM Studio",
),
"ollama_chat": ProviderSpec(
Transport.OLLAMA,
"ollama_chat",
"ollama",
"http://host.docker.internal:11434",
True,
"none",
"Ollama",
),
}
def spec_for(provider: str | None) -> ProviderSpec:
provider_key = (provider or "").strip()
return REGISTRY.get(provider_key) or ProviderSpec(
Transport.NATIVE, provider_key or "openai", "static", None, False, "native"
)
def provider_label(provider: str | None) -> str:
provider_key = (provider or "").strip()
spec = spec_for(provider_key)
if spec.display_name:
return spec.display_name
return provider_key.replace("_", " ").title() if provider_key else "Provider"
__all__ = ["REGISTRY", "ProviderSpec", "Transport", "provider_label", "spec_for"]