1+ import asyncio
12import logging
23import mimetypes
34from typing import Literal
@@ -66,51 +67,54 @@ async def upload_file(
6667 error_messages : list [str ] = []
6768 uploaded_files : list [dict [str , str | None ]] = []
6869
70+ # 1. Read all file contents first (must happen before UploadFile objects close)
71+ file_payloads : list [tuple [str , bytes ]] = []
6972 for file in files :
70- file_content = None
7173 try :
72- # 1. Read the file content first
7374 file_content = await file .read ()
7475 if not file .filename :
7576 raise ValueError ("File has no filename" )
7677 if not file_content :
7778 raise ValueError ("File content is empty" )
79+ file_payloads .append ((file .filename , file_content ))
80+ except ValueError as ve :
81+ logger .error (f"File validation error for { file .filename } : { ve } " )
82+ error_messages .append (f"{ file .filename } : { ve } " )
7883
79- # 2. Upload the file content to OpenAI
84+ # 2. Upload all files to OpenAI and add to vector store in parallel
85+ async def process_file (filename : str , content : bytes ) -> dict [str , str | None ] | None :
86+ try :
8087 openai_file = await client .files .create (
81- file = (file . filename , file_content ),
88+ file = (filename , content ),
8289 purpose = purpose
8390 )
84-
85- # 3. Add the uploaded file to the vector store
8691 vs_file = await client .vector_stores .files .create (
8792 vector_store_id = vector_store_id ,
8893 file_id = openai_file .id
8994 )
90- logger .info (f"File { file . filename } uploaded to OpenAI and added to vector store." )
95+ logger .info (f"File { filename } uploaded to OpenAI and added to vector store." )
9196
92- # Track the uploaded file so we can include it in the response
93- # even if the list API hasn't caught up yet
94- uploaded_files .append ({
95- "id" : openai_file .id ,
96- "filename" : file .filename ,
97- "status" : vs_file .status ,
98- "last_error" : vs_file .last_error .message if vs_file .last_error else None
99- })
100-
101- # 4. Store the file locally using the same content
10297 try :
103- store_file (file . filename , file_content )
98+ store_file (filename , content )
10499 except Exception as e :
105- logger .error (f"Error storing file { file . filename } locally: { e } " )
106- error_messages .append (f"Error storing { file . filename } locally" )
100+ logger .error (f"Error storing file { filename } locally: { e } " )
101+ error_messages .append (f"Error storing { filename } locally" )
107102
108- except ValueError as ve :
109- logger .error (f"File validation error for { file .filename } : { ve } " )
110- error_messages .append (f"{ file .filename } : { ve } " )
103+ return {
104+ "id" : openai_file .id ,
105+ "filename" : filename ,
106+ "status" : vs_file .status ,
107+ "last_error" : vs_file .last_error .message if vs_file .last_error else None
108+ }
111109 except Exception as e :
112- logger .error (f"Error uploading file { file .filename } : { e } " )
113- error_messages .append (f"Error uploading { file .filename } " )
110+ logger .error (f"Error uploading file { filename } : { e } " )
111+ error_messages .append (f"Error uploading { filename } " )
112+ return None
113+
114+ results = await asyncio .gather (
115+ * (process_file (fn , content ) for fn , content in file_payloads )
116+ )
117+ uploaded_files = [r for r in results if r is not None ]
114118
115119 # Fetch the updated list of files and render the partial
116120 file_list : list [dict [str , str | None ]] = []
@@ -175,12 +179,11 @@ async def delete_all_files(
175179 break
176180 after = page .last_id
177181
178- # Delete each file
179- for vs_file in all_vs_files :
180- # Retrieve filename for local deletion
182+ # Delete each file in parallel
183+ async def delete_one (vs_file_id : str ) -> None :
181184 file_to_delete_name = None
182185 try :
183- retrieved_file = await client .files .retrieve (vs_file . id )
186+ retrieved_file = await client .files .retrieve (vs_file_id )
184187 if retrieved_file and retrieved_file .filename :
185188 file_to_delete_name = retrieved_file .filename
186189 except Exception :
@@ -194,18 +197,20 @@ async def delete_all_files(
194197
195198 try :
196199 deleted = await client .vector_stores .files .delete (
197- vector_store_id = vector_store_id , file_id = vs_file . id
200+ vector_store_id = vector_store_id , file_id = vs_file_id
198201 )
199202 if deleted .deleted :
200203 try :
201- await client .files .delete (file_id = vs_file . id )
204+ await client .files .delete (file_id = vs_file_id )
202205 except Exception as file_err :
203- logger .warning (f"Removed { vs_file . id } from vector store but failed to delete file object: { file_err } " )
206+ logger .warning (f"Removed { vs_file_id } from vector store but failed to delete file object: { file_err } " )
204207 else :
205- error_messages .append (f"Failed to remove { vs_file . id } from vector store" )
208+ error_messages .append (f"Failed to remove { vs_file_id } from vector store" )
206209 except Exception as del_err :
207- logger .error (f"Error deleting file { vs_file .id } : { del_err } " )
208- error_messages .append (f"Error deleting { vs_file .id } " )
210+ logger .error (f"Error deleting file { vs_file_id } : { del_err } " )
211+ error_messages .append (f"Error deleting { vs_file_id } " )
212+
213+ await asyncio .gather (* (delete_one (vs_file .id ) for vs_file in all_vs_files ))
209214
210215 except Exception as e :
211216 logger .error (f"Error during delete all: { e } " )
0 commit comments