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:
parent
5294745704
commit
f2954af400
2 changed files with 24 additions and 12 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
IntegrationConfig.provider.in_(list(AI_PRESETS.keys()))
|
||||
)
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue