mirror of
https://github.com/dartdavros/chatballs.git
synced 2026-10-14 21:19:04 +03:00
Add the Custom BYOK integration per ADR-HUB-0034 and SPEC-HUB-0024 §5: an organization can connect any OpenAI-compatible provider with three fields — endpoint (Base URL), API key and a free-text model — without a per-provider adapter. The OpenRouter integration stays a separate ready integration with its model catalog and is NOT absorbed by generic mode. Backend: - integrations/models.py: add CUSTOM to IntegrationProvider + PROVIDER_KIND (LLM_PROVIDER). TextChoices only affect validation; no DB migration needed (makemigrations --check: no changes detected). - integrations/services.py: _normalized_config stores base_url + default_model for CUSTOM; _CHECKS wires the new check_custom. - integrations/checks.py: check_custom verifies the endpoint speaks the OpenAI shape via GET /models with Authorization: Bearer. No catalog authority. - ai/provider/custom.py: CustomProvider reuses the OpenRouter HTTP layer verbatim (ADR-HUB-0034 §3) — the contract is identical; only the name differs. - ai/provider/routing.py: resolve_provider(_and_model) routes through Channel.provider_integration explicitly (ADR-HUB-0020:45), scoped to that one integration — never the first org-wide. The integration's default_model overrides AIAgent.model at runtime, closing the as-built gap where the «Модель по умолчанию» field was decorative (SPEC-HUB-0005:388, SPEC #2 #5). - ai/provider/factory.py: get_provider(channel) takes an optional channel; BYOK linked via provider_integration wins over the legacy global path. - ai/invocation.py: invoke_chat resolves provider+model through routing for a BYOK channel. Frontend: - integrations/model.ts: CUSTOM in the provider type and PROVIDERS map (hasModel/testable/checkable; secretLabel reuses the existing «API-ключ»). - integrations/IntegrationForm.tsx: the three Custom fields render through the existing meta; the OpenRouter-only model placeholder is scoped to OpenRouter. Tests: - integrations/tests.py: CustomIntegrationTests (6) — endpoint+model persistence, free-text model, check_custom openai-shape success/failure. - ai/tests.py: ByokRoutingTests (7) — CUSTOM→CustomProvider, OpenRouter mapping, default_model runtime override (SPEC #5), fallback, missing integration, end-to-end invoke_chat uses the integration model. Verified: 13/13 integrations tests, 47/47 ai tests, frontend typecheck + vitest. Acceptance: SPEC-HUB-0024 #1 (Custom with endpoint/key/model), #2 (model field is read at runtime), #3 (any OpenAI-compatible endpoint), #5 (default_model no longer decorative). #4 (OpenRouter catalog+check) untouched as-built. Scope boundary: the CustoAI Managed path, removal of the non-compliant factory._openrouter_from_integration side-effect token, and the credential_mode field land in track B (ADR-HUB-0033).