Pipeline/backend/app/models/provider_credentials.py

57 lines
2.4 KiB
Python
Raw Permalink Normal View History

"""Provider credential records — API keys and endpoints for AI providers.
Each row stores one named account for a single provider (Anthropic, OpenAI,
Ollama, etc.). An organization can have multiple rows per provider, enabling
separate cost tracking for e.g. "work" and "personal" OpenAI accounts.
Token storage follows the same pattern as ForgejoConnection and Gateway:
plaintext in the trusted DB, last-four chars available for verification without
exposing the full key.
"""
from __future__ import annotations
from datetime import datetime
from uuid import UUID, uuid4
from sqlmodel import Field
from app.core.time import utcnow
from app.models.base import QueryModel
RUNTIME_ANNOTATION_TYPES = (datetime,)
SUPPORTED_PROVIDERS = frozenset({"anthropic", "openai", "ollama", "google", "other"})
class ProviderCredential(QueryModel, table=True):
"""One named AI-provider account for an organisation."""
__tablename__ = "provider_credentials" # pyright: ignore[reportAssignmentType]
id: UUID = Field(default_factory=uuid4, primary_key=True)
organization_id: UUID = Field(foreign_key="organizations.id", index=True)
# Provider identity
provider: str = Field(index=True) # "anthropic", "openai", "ollama", …
account_key: str = Field(index=True) # user label: "work", "personal", "local"
display_name: str = Field(default="") # human-readable name shown in the UI
# Credentials (not all providers need both)
api_key: str | None = Field(default=None) # full key — never returned in API
api_key_last_four: str | None = Field(default=None) # shown in UI for verification
base_url: str | None = Field(default=None) # Ollama, Azure, custom endpoints
2026-05-21 03:29:35 -05:00
# Session / OAuth token for subscription-usage endpoints.
# Anthropic: claude.ai sessionKey cookie (sk-ant-sid-...)
# OpenAI Codex: ChatGPT bearer token from browser session
# This is a different credential from the API key — it authenticates against
# the provider's subscription system rather than the generation API.
session_key: str | None = Field(default=None) # full token — never returned in API
session_key_last_four: str | None = Field(default=None)
active: bool = Field(default=True, index=True)
created_at: datetime = Field(default_factory=utcnow)
updated_at: datetime = Field(default_factory=utcnow)