fix: network-share readers, model unload off the loop, bad save requests (#620)

Every read-only SQLite open now uses one URI helper, so a library on a Windows
network share works in backups, restores, moves and history reads. Unloading a
model no longer blocks the server. A malformed /save body gets a 400, not a 500.
This commit is contained in:
Vito Sansevero
2026-11-02 10:23:26 -08:00
parent f6432fec74
commit d0ab5680d9
13 changed files with 141 additions and 38 deletions
+3 -1
View File
@@ -30,8 +30,10 @@ from .jobs import (
)
try:
from ...database.sqlite_uri import readonly_uri
from ...utils.parallel import map_parallel
except ImportError:
from database.sqlite_uri import readonly_uri
from utils.parallel import map_parallel
try:
@@ -399,7 +401,7 @@ def _inspect_restore_upload(path):
Returns:
None when acceptable, otherwise a human-readable reason (blocking).
"""
uri = Path(os.path.abspath(path)).as_uri() + "?mode=ro"
uri = readonly_uri(path)
try:
conn = sqlite3.connect(uri, uri=True)
except sqlite3.Error as e:
+2 -1
View File
@@ -1411,7 +1411,8 @@ class AutotagRoutesMixin:
)
model_type = service.get_loaded_model_type()
service.unload_model()
# Off the loop; a dropped request keeps the gate until it ends.
await model_gate.run(owner, service.unload_model)
finally:
model_gate.release(owner)
+3 -2
View File
@@ -45,7 +45,6 @@ import os
import sqlite3
import tempfile
from contextlib import closing
from pathlib import Path
from aiohttp import web
@@ -56,6 +55,7 @@ from .maintenance_plans import PlanExpired, PlanUsed
from .restore_plans import RestorePlanStore
try:
from ...database.sqlite_uri import readonly_uri
from ...database import restore_stage
from ...database.operations import PromptDatabase
from ...database.safety_backup import (
@@ -66,6 +66,7 @@ try:
safety_temp,
)
except ImportError:
from database.sqlite_uri import readonly_uri
from database import restore_stage
from database.operations import PromptDatabase
from database.safety_backup import (
@@ -186,7 +187,7 @@ def snapshot_corruption(path):
with open(path, "rb") as handle:
if handle.read(len(SQLITE_HEADER)) != SQLITE_HEADER:
return "Not a SQLite database file"
uri = Path(os.path.abspath(path)).as_uri() + "?mode=ro"
uri = readonly_uri(path)
with closing(sqlite3.connect(uri, uri=True)) as conn:
row = conn.execute("PRAGMA integrity_check").fetchone()
except (OSError, sqlite3.Error) as error:
+9 -1
View File
@@ -727,7 +727,15 @@ class PromptRoutesMixin:
async def save_prompt(self, request):
"""Save a new prompt with metadata and duplicate detection."""
try:
data = await request.json()
data, error = await self._read_json_body(request)
if error is not None:
return error
for field in ("text", "category", "notes"):
if data.get(field) is not None and not isinstance(data[field], str):
return web.json_response(
{"success": False, "error": f"{field} must be text"},
status=400,
)
text = (data.get("text") or "").strip()
if not text: