mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-17 10:15:27 -04:00
fix: archive browser model filter is suffix-only and drops matching models (#1709)
This commit is contained in:
@@ -618,7 +618,12 @@ def setup_session_routes(session_manager: SessionManager, config: dict, webhook_
|
|||||||
safe_search = search.replace('%', r'\%').replace('_', r'\_')
|
safe_search = search.replace('%', r'\%').replace('_', r'\_')
|
||||||
q = q.filter(DbSession.name.ilike(f"%{safe_search}%", escape='\\'))
|
q = q.filter(DbSession.name.ilike(f"%{safe_search}%", escape='\\'))
|
||||||
if model:
|
if model:
|
||||||
q = q.filter(DbSession.model.ilike(f"%{model}"))
|
# Contains match (mirrors the name filter above). The old
|
||||||
|
# f"%{model}" was a SUFFIX-only match, so filtering by "gpt-4"
|
||||||
|
# dropped "gpt-4o" and over-matched on shared suffixes; it also
|
||||||
|
# left LIKE wildcards in the user value unescaped.
|
||||||
|
safe_model = model.replace('%', r'\%').replace('_', r'\_')
|
||||||
|
q = q.filter(DbSession.model.ilike(f"%{safe_model}%", escape='\\'))
|
||||||
total = q.count()
|
total = q.count()
|
||||||
sort_map = {
|
sort_map = {
|
||||||
"recent": DbSession.updated_at.desc(),
|
"recent": DbSession.updated_at.desc(),
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
"""Archive browser model filter must be a CONTAINS match, not suffix-only.
|
||||||
|
|
||||||
|
list_archived_sessions filtered with DbSession.model.ilike(f"%{model}") - a
|
||||||
|
suffix match. Filtering by "gpt-4" therefore returned "openai/gpt-4" but
|
||||||
|
silently DROPPED "gpt-4o" (contains but does not end with the value), and
|
||||||
|
over-matched models that merely share the suffix. The sibling name filter
|
||||||
|
already uses a wildcard-escaped contains match.
|
||||||
|
"""
|
||||||
|
import tempfile
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from sqlalchemy.pool import NullPool
|
||||||
|
|
||||||
|
import core.database as cdb
|
||||||
|
from core.database import Session as DbSession
|
||||||
|
|
||||||
|
_TMPDB = tempfile.NamedTemporaryFile(suffix=".db", delete=False)
|
||||||
|
_ENGINE = create_engine(
|
||||||
|
f"sqlite:///{_TMPDB.name}",
|
||||||
|
connect_args={"check_same_thread": False},
|
||||||
|
poolclass=NullPool,
|
||||||
|
)
|
||||||
|
cdb.Base.metadata.create_all(_ENGINE)
|
||||||
|
_TS = sessionmaker(bind=_ENGINE, autoflush=False, autocommit=False)
|
||||||
|
|
||||||
|
|
||||||
|
def _route(router, path, method="GET"):
|
||||||
|
for r in router.routes:
|
||||||
|
if r.path == path and method in getattr(r, "methods", set()):
|
||||||
|
return r.endpoint
|
||||||
|
raise AssertionError(f"route not found: {path}")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def archived_endpoint(monkeypatch):
|
||||||
|
import routes.session_routes as sr
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
monkeypatch.setattr(sr, "SessionLocal", _TS)
|
||||||
|
monkeypatch.setattr(sr, "effective_user", lambda request: "alice")
|
||||||
|
router = sr.setup_session_routes(MagicMock(), {})
|
||||||
|
return _route(router, "/api/sessions/archived")
|
||||||
|
|
||||||
|
|
||||||
|
def _seed(owner, *models):
|
||||||
|
db = _TS()
|
||||||
|
try:
|
||||||
|
db.query(DbSession).delete()
|
||||||
|
for m in models:
|
||||||
|
db.add(DbSession(id=str(uuid.uuid4()), owner=owner, name=f"chat {m}",
|
||||||
|
model=m, archived=True))
|
||||||
|
db.commit()
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_contains_match_returns_all_models_sharing_the_substring(archived_endpoint):
|
||||||
|
_seed("alice", "openai/gpt-4", "gpt-4o", "claude-3")
|
||||||
|
res = archived_endpoint(request=None, model="gpt-4")
|
||||||
|
got = {s["model"] for s in res["sessions"]}
|
||||||
|
assert got == {"openai/gpt-4", "gpt-4o"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_exact_full_model_still_matches(archived_endpoint):
|
||||||
|
_seed("alice", "openai/gpt-4", "gpt-4o")
|
||||||
|
res = archived_endpoint(request=None, model="openai/gpt-4")
|
||||||
|
assert {s["model"] for s in res["sessions"]} == {"openai/gpt-4"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_wildcard_in_filter_is_escaped(archived_endpoint):
|
||||||
|
_seed("alice", "gpt-4o", "gpt_4o")
|
||||||
|
res = archived_endpoint(request=None, model="gpt_4")
|
||||||
|
assert {s["model"] for s in res["sessions"]} == {"gpt_4o"}
|
||||||
Reference in New Issue
Block a user