Fix: ai_provider reads actual DB schema (config_key/config_value rows)

Root cause: ai_provider.py assumed a single JSON blob in an
'encrypted_value' column, but IntegrationConfig stores each credential
as a separate row with config_key + config_value columns.

Fixes:
- Query groups rows by provider, decrypts each config_value separately
- Correct decrypt_value(secret_key, ciphertext) argument order
- Filter by user_id so providers are per-user
- Callers (ai_chat.py, chat()) now pass user_id through
This commit is contained in:
Claude 2026-06-22 00:27:19 +00:00
parent 5294745704
commit f2954af400
2 changed files with 24 additions and 12 deletions

View file

@ -54,7 +54,7 @@ async def ai_status(
db: AsyncSession = Depends(get_db),
):
"""Check which AI provider is configured."""
provider = await ai_provider.get_active_ai_provider(db)
provider = await ai_provider.get_active_ai_provider(db, user_id=user.id)
if provider:
return {
"configured": True,

View file

@ -55,22 +55,34 @@ RULES:
- Never fabricate data only reference what's in the context."""
async def get_active_ai_provider(db: AsyncSession) -> dict | None:
"""Find the first configured AI provider."""
result = await db.execute(
select(IntegrationConfig).where(
async def get_active_ai_provider(db: AsyncSession, user_id=None) -> dict | None:
"""Find the first configured AI provider for the given user."""
query = select(IntegrationConfig).where(
IntegrationConfig.provider.in_(list(AI_PRESETS.keys()))
)
)
configs = result.scalars().all()
if user_id is not None:
query = query.where(IntegrationConfig.user_id == user_id)
for config in configs:
provider_name = config.provider
result = await db.execute(query)
rows = result.scalars().all()
# Group rows by provider: {"gemini": {"api_key": "decrypted_value"}}
providers: dict[str, dict[str, str]] = {}
for row in rows:
if row.provider not in providers:
providers[row.provider] = {}
try:
creds = json.loads(decrypt_value(config.encrypted_value, settings.secret_key))
providers[row.provider][row.config_key] = decrypt_value(
settings.secret_key, row.config_value
)
except Exception:
continue
# Return the first provider that has at least one credential
for provider_name, creds in providers.items():
if not creds:
continue
preset = AI_PRESETS.get(provider_name, AI_PRESETS["custom_ai"])
api_key = creds.get("api_key", "")
base_url = creds.get("endpoint_url", preset["base_url"]) or preset["base_url"]
@ -127,7 +139,7 @@ async def chat(
db: AsyncSession,
) -> AsyncGenerator[str, None]:
"""Route chat to the configured AI provider with streaming."""
provider = await get_active_ai_provider(db)
provider = await get_active_ai_provider(db, user_id=user_id)
if not provider:
yield "No AI provider configured. Go to Settings → Integrations to connect one (OpenAI, OpenRouter, Ollama, etc.)."
return