72 lines
3.3 KiB
Python
72 lines
3.3 KiB
Python
"""Endpunkte, API-Keys und Modelle (→ Konzept 8.1).
|
|
|
|
Global gepflegt vom Plattform-Admin. Ein Key gilt entweder für alle Teams
|
|
(team_id leer, z. B. OpenAI in der Entwicklung) oder für genau ein Team
|
|
(Gateway). Der Team-Key hat Vorrang.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from decimal import Decimal
|
|
|
|
from sqlalchemy import Boolean, ForeignKey, Index, Integer, Numeric, String, UniqueConstraint, text
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.core.database import Base, now
|
|
|
|
# Modellklassen (→ Konzept 8.1); zunächst zeigen alle auf dasselbe Modell
|
|
MODEL_CLASSES = ("standard", "reasoning", "fast")
|
|
|
|
|
|
class Provider(Base):
|
|
__tablename__ = "providers"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
name: Mapped[str] = mapped_column(String(100), unique=True)
|
|
# OpenAI-kompatibel mit Responses API: OpenAI direkt oder das AI Gateway
|
|
base_url: Mapped[str] = mapped_column(String(500))
|
|
active: Mapped[bool] = mapped_column(Boolean, default=True)
|
|
created: Mapped[datetime] = mapped_column(default=now)
|
|
|
|
|
|
class ProviderKey(Base):
|
|
__tablename__ = "provider_keys"
|
|
__table_args__ = (
|
|
UniqueConstraint("provider_id", "team_id"),
|
|
# Nur ein Key für alle Teams je Endpunkt (NULL zählt sonst nicht als doppelt)
|
|
Index("uq_provider_keys_default", "provider_id", unique=True, postgresql_where=text("team_id IS NULL")),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
provider_id: Mapped[int] = mapped_column(ForeignKey("providers.id", ondelete="CASCADE"))
|
|
team_id: Mapped[int | None] = mapped_column(ForeignKey("teams.id", ondelete="CASCADE"))
|
|
# Verschlüsselt (app.core.crypto); angezeigt werden nur die letzten Zeichen
|
|
secret: Mapped[str] = mapped_column(String(2000))
|
|
hint: Mapped[str] = mapped_column(String(10))
|
|
updated_by: Mapped[int] = mapped_column(ForeignKey("users.id"))
|
|
updated: Mapped[datetime] = mapped_column(default=now)
|
|
|
|
|
|
class Model(Base):
|
|
__tablename__ = "models"
|
|
__table_args__ = (
|
|
UniqueConstraint("provider_id", "name"),
|
|
Index("uq_models_default", "is_default", unique=True, postgresql_where=text("is_default")),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
provider_id: Mapped[int] = mapped_column(ForeignKey("providers.id", ondelete="CASCADE"))
|
|
# Name, wie ihn die API erwartet, z. B. gpt-6-luna
|
|
name: Mapped[str] = mapped_column(String(200))
|
|
label: Mapped[str] = mapped_column(String(200))
|
|
model_class: Mapped[str] = mapped_column(String(20), default="standard")
|
|
context_window: Mapped[int] = mapped_column(Integer, default=128000)
|
|
supports_tools: Mapped[bool] = mapped_column(Boolean, default=True)
|
|
supports_reasoning: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
# Preise je 1 Mio. Tokens in cost_currency; leer = Kosten werden nicht berechnet
|
|
input_price: Mapped[Decimal | None] = mapped_column(Numeric(12, 4))
|
|
cached_price: Mapped[Decimal | None] = mapped_column(Numeric(12, 4))
|
|
output_price: Mapped[Decimal | None] = mapped_column(Numeric(12, 4))
|
|
# Nur aktive Modelle sind für Workspaces wählbar
|
|
active: Mapped[bool] = mapped_column(Boolean, default=True)
|
|
is_default: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
created: Mapped[datetime] = mapped_column(default=now)
|