Skip to content

Commit c4b814c

Browse files
committed
Merge branch 'feature/open-ai-agents' of https://github.com/UiPath/uipath-llamaindex-python into feature/open-ai-agents
2 parents 4abc7db + 58d2f2d commit c4b814c

2 files changed

Lines changed: 42 additions & 14 deletions

File tree

packages/uipath-openai-agents/src/uipath_openai_agents/runtime/factory.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def _remove_file_with_retry(self, path: str, max_attempts: int = 5) -> None:
7272
"""Remove file with retry logic for Windows file locking.
7373
7474
OpenAI SDK uses sync sqlite3 which doesn't immediately release file locks
75-
on Windows. This retry mechanism gives the OS time to release the lock.
75+
on Windows. This method ensures proper cleanup before deletion.
7676
7777
Args:
7878
path: Path to file to remove
@@ -81,8 +81,31 @@ def _remove_file_with_retry(self, path: str, max_attempts: int = 5) -> None:
8181
Raises:
8282
OSError: If file cannot be removed after all retries
8383
"""
84+
import gc
85+
import sqlite3
8486
import time
8587

88+
# Before attempting deletion, ensure the SQLite database is properly closed
89+
# by performing a WAL checkpoint to release all file locks
90+
try:
91+
conn = sqlite3.connect(path, timeout=1.0)
92+
try:
93+
# Commit any pending transactions
94+
conn.commit()
95+
# Force WAL checkpoint to release shared memory files
96+
conn.execute("PRAGMA wal_checkpoint(TRUNCATE)")
97+
conn.commit()
98+
finally:
99+
conn.close()
100+
# Force garbage collection to release file handles
101+
gc.collect()
102+
# Give OS time to release file locks (especially on Windows)
103+
time.sleep(0.1)
104+
except Exception:
105+
# If checkpoint fails, still try to delete
106+
pass
107+
108+
# Now attempt deletion with retry logic
86109
for attempt in range(max_attempts):
87110
try:
88111
os.remove(path)

packages/uipath-openai-agents/src/uipath_openai_agents/runtime/runtime.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,6 @@ async def _run_agent(
207207
Runtime events if stream_events=True, then final result
208208
"""
209209
agent_input = self._prepare_agent_input(input)
210-
is_resuming = bool(options and options.resume)
211210

212211
# Create session for state persistence (local to this run)
213212
# SQLiteSession automatically loads existing data from the database when created
@@ -232,20 +231,12 @@ async def _run_agent(
232231
)
233232
yield self._create_success_result(result.final_output)
234233

235-
except Exception:
236-
# Clean up session on error
237-
if session and self.storage_path and not is_resuming:
238-
# Delete incomplete session
239-
try:
240-
import os
241-
242-
if os.path.exists(self.storage_path):
243-
os.remove(self.storage_path)
244-
except Exception:
245-
pass # Best effort cleanup
246-
raise
247234
finally:
248235
# Always close session after run completes with proper WAL checkpoint
236+
# Note: We don't delete the database file on error because:
237+
# 1. Multiple sessions (different session_ids) coexist in the same file
238+
# 2. Deleting the file would delete ALL sessions, not just the failed one
239+
# 3. File deletion is handled at the factory level when starting new executions
249240
if session:
250241
self._close_session_with_checkpoint(session)
251242

@@ -497,6 +488,10 @@ def _close_session_with_checkpoint(self, session: SQLiteSession) -> None:
497488
Args:
498489
session: The SQLiteSession to close
499490
"""
491+
import gc
492+
import platform
493+
import time
494+
500495
try:
501496
# Get the underlying connection
502497
conn = session._get_connection()
@@ -525,6 +520,16 @@ def _close_session_with_checkpoint(self, session: SQLiteSession) -> None:
525520
except Exception:
526521
pass # Best effort
527522

523+
# Force garbage collection to close any open file handles
524+
# This is especially important on Windows where file handles
525+
# may remain open even after explicit close()
526+
gc.collect()
527+
528+
# On Windows, give the OS time to release file locks
529+
# Virus scanners and backup agents often interfere
530+
if platform.system() == "Windows":
531+
time.sleep(0.1)
532+
528533
async def dispose(self) -> None:
529534
"""Cleanup runtime resources."""
530535
# Sessions are closed immediately after each run in _run_agent()

0 commit comments

Comments
 (0)