From fe5f0842bddb68ccb9862d86fd255af2694a8b52 Mon Sep 17 00:00:00 2001 From: YurenHao0426 Date: Fri, 13 Feb 2026 18:26:40 +0000 Subject: Fix file upload/delete using user API keys instead of env vars only The upload_file and delete_file endpoints were calling API clients without passing user-specific API keys, falling back only to environment variables. Now both endpoints resolve keys from the authenticated user's settings via get_user_api_key(), fixing "API key not found" errors for users who configured keys through the frontend. Co-Authored-By: Claude Opus 4.6 --- backend/app/main.py | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) (limited to 'backend/app') diff --git a/backend/app/main.py b/backend/app/main.py index a6f4f10..2c625a9 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -639,12 +639,13 @@ async def ensure_user_vector_store(user: str, client=None) -> str: json.dump({"id": vs_id}, f) return vs_id -async def ensure_openai_file_and_index(user: str, meta: FileMeta, path: str, llm_config: Optional[LLMConfig] = None) -> tuple[str, str]: +async def ensure_openai_file_and_index(user: str, meta: FileMeta, path: str, llm_config: Optional[LLMConfig] = None, api_key: str = None) -> tuple[str, str]: """ Ensure the file is uploaded to OpenAI Files and added to the user's vector store. Returns (openai_file_id, vector_store_id). """ - client = get_openai_client(llm_config.api_key if llm_config else None) + key = api_key or (llm_config.api_key if llm_config else None) + client = get_openai_client(key) vs_id = await ensure_user_vector_store(user, client) file_id = meta.openai_file_id or (meta.provider_file_id if meta.provider == "openai" else None) @@ -1108,7 +1109,10 @@ async def upload_file( file: UploadFile = File(...), provider: str = Form("local"), purpose: Optional[str] = Form(None), + current_user: User | None = Depends(get_current_user_optional), ): + if current_user: + user = current_user.username migrate_legacy_layout(user) items = load_files_index(user) file_id = str(uuid4()) @@ -1131,9 +1135,14 @@ async def upload_file( provider_file_id: Optional[str] = None provider_created_at: Optional[float] = None + # Resolve user API keys for all providers + openai_key = get_user_api_key(current_user, "openai") + google_key = get_user_api_key(current_user, "google") + anthropic_key = get_user_api_key(current_user, "claude") + if provider_normalized == "openai": try: - client = get_openai_client() + client = get_openai_client(openai_key) upload_purpose = purpose or OPENAI_DEFAULT_FILE_PURPOSE resp = await client.files.create( file=(file_name, content), @@ -1145,10 +1154,10 @@ async def upload_file( raise HTTPException(status_code=500, detail=f"OpenAI upload failed: {str(e)}") elif provider_normalized == "google": try: - key = os.getenv("GOOGLE_API_KEY") - if not key: + g_key = google_key + if not g_key: raise HTTPException(status_code=500, detail="Google API Key not found") - client = genai.Client(api_key=key) + client = genai.Client(api_key=g_key) # The Google GenAI SDK upload is synchronous; run in thread to avoid blocking the event loop. tmp_path = None try: @@ -1215,7 +1224,7 @@ async def upload_file( logger.debug("Skipping OpenAI vector store indexing for image file: %s", file_name) elif size <= OPENAI_MAX_FILE_SIZE: try: - openai_file_id, vs_id = await ensure_openai_file_and_index(user, meta, dest_path, None) + openai_file_id, vs_id = await ensure_openai_file_and_index(user, meta, dest_path, api_key=openai_key) meta.openai_file_id = openai_file_id meta.openai_vector_store_id = vs_id if provider_normalized == "openai" and not meta.provider_file_id: @@ -1226,12 +1235,12 @@ async def upload_file( logger.warning("Skipping OpenAI indexing for %s: exceeds 50MB", file_name) # Anthropic Files API upload (eager, like OpenAI) - anthropic_id = await ensure_anthropic_file_upload(meta, dest_path) + anthropic_id = await ensure_anthropic_file_upload(meta, dest_path, api_key=anthropic_key) if anthropic_id: meta.anthropic_file_id = anthropic_id # Google Files API upload (eager, like OpenAI) - google_uri = await ensure_google_file_upload(meta, dest_path) + google_uri = await ensure_google_file_upload(meta, dest_path, api_key=google_key) if google_uri: meta.google_file_uri = google_uri # Also backfill provider_file_id for legacy compat if provider was google @@ -1257,7 +1266,13 @@ def download_file(user: str = DEFAULT_USER, file_id: str = ""): @app.post("/api/files/delete") -async def delete_file(user: str = DEFAULT_USER, file_id: str = ""): +async def delete_file( + user: str = DEFAULT_USER, + file_id: str = "", + current_user: User | None = Depends(get_current_user_optional), +): + if current_user: + user = current_user.username migrate_legacy_layout(user) items = load_files_index(user) meta = next((i for i in items if i.id == file_id), None) @@ -1269,7 +1284,7 @@ async def delete_file(user: str = DEFAULT_USER, file_id: str = ""): await remove_file_from_vector_store(meta.openai_vector_store_id, meta.openai_file_id) if meta.provider == "openai" and meta.provider_file_id: try: - client = get_openai_client() + client = get_openai_client(get_user_api_key(current_user, "openai")) await client.files.delete(meta.provider_file_id) except Exception as e: raise HTTPException(status_code=500, detail=f"OpenAI delete failed: {str(e)}") @@ -1281,7 +1296,7 @@ async def delete_file(user: str = DEFAULT_USER, file_id: str = ""): google_refs.add(meta.google_file_uri) for g_ref in google_refs: try: - key = os.getenv("GOOGLE_API_KEY") + key = get_user_api_key(current_user, "google") if not key: logger.warning("Skipping Google file deletion: no API key") break @@ -1294,7 +1309,7 @@ async def delete_file(user: str = DEFAULT_USER, file_id: str = ""): # Delete from Anthropic Files API if present if meta.anthropic_file_id: try: - client = get_anthropic_client() + client = get_anthropic_client(get_user_api_key(current_user, "claude")) await asyncio.to_thread( client.beta.files.delete, meta.anthropic_file_id ) -- cgit v1.2.3