feat(ai): enforce local-first routing

Keep external providers behind server consent, task, and prompt-cost gates while persisting actual provider provenance.
This commit is contained in:
cesnimda
2026-08-09 12:30:11 +02:00
parent c3f4a57195
commit 5eb9b3cb96
29 changed files with 967 additions and 145 deletions
+7 -2
View File
@@ -43,6 +43,7 @@ services:
# External processing requires this admin gate AND a per-user opt-in. Default is local-only.
- Ai__ExternalProcessingEnabled=${EXTERNAL_AI_ENABLED:-false}
- Ai__ExternalProvider=${AI_PROVIDER:-ollama}
- Ai__RoutingMode=${AI_ROUTING_MODE:-local_first}
# Shared secret for calls to ai-service. Must match AI_SERVICE_TOKEN below.
# Quoted: the `:?` message contains a colon-space, which YAML would otherwise read as a map.
- "Ai__ServiceToken=${AI_SERVICE_TOKEN:?AI_SERVICE_TOKEN must be set - generate one with python -c 'import secrets; print(secrets.token_hex(32))'}"
@@ -151,10 +152,14 @@ services:
# and no duplicate Ollama container is created.
- OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://ollama:11434}
- OLLAMA_MODEL=${OLLAMA_MODEL:-qwen2.5:7b}
# AI provider for heavy /cv/* calls: ollama (default) | gemini | groq.
# Set AI_PROVIDER=gemini + GEMINI_API_KEY in prod to offload a weak local GPU.
# External fallback provider for heavy /cv/* calls. Ollama remains primary by default.
- AI_PROVIDER=${AI_PROVIDER:-ollama}
- EXTERNAL_AI_ENABLED=${EXTERNAL_AI_ENABLED:-false}
- AI_ROUTING_MODE=${AI_ROUTING_MODE:-local_first}
- EXTERNAL_AI_ALLOWED_TASKS=${EXTERNAL_AI_ALLOWED_TASKS:-cv-normalize,cv-classify,cv-rewrite}
- EXTERNAL_AI_MAX_PROMPT_CHARS=${EXTERNAL_AI_MAX_PROMPT_CHARS:-24000}
- LOCAL_AI_CIRCUIT_FAILURE_THRESHOLD=${LOCAL_AI_CIRCUIT_FAILURE_THRESHOLD:-3}
- LOCAL_AI_CIRCUIT_OPEN_SECONDS=${LOCAL_AI_CIRCUIT_OPEN_SECONDS:-30}
- GEMINI_API_KEY=${GEMINI_API_KEY:-}
- GEMINI_MODEL=${GEMINI_MODEL:-gemini-2.0-flash}
- GROQ_API_KEY=${GROQ_API_KEY:-}