feat(ccip): matcher reads the incremental prototype store (#1317, m138 step 4)
match_image now sources character references from character_prototype via a per-character in-process cache (_load_prototypes) that reloads ONLY the characters whose ccip_prototype_state.updated_at advanced — no request-path rebuild, so the per-accept ~4s stall is gone once the store is populated. Cold start (store empty pre-first-refresh) falls back to the legacy on-the-fly reference build, so character suggestions work immediately post-deploy and the background refresh populates the store within ~15 min. Match math + grounding are unchanged; existing tests exercise the legacy fallback, and a new test covers matching from the populated prototype store. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
This commit is contained in:
+25
-1
@@ -3,7 +3,13 @@ model needed, so it runs in CI with synthetic CCIP vectors."""
|
||||
import pytest
|
||||
from sqlalchemy import select
|
||||
|
||||
from backend.app.models import ImageRecord, ImageRegion, TagKind
|
||||
from backend.app.models import (
|
||||
CcipPrototypeState,
|
||||
CharacterPrototype,
|
||||
ImageRecord,
|
||||
ImageRegion,
|
||||
TagKind,
|
||||
)
|
||||
from backend.app.models.tag import image_tag
|
||||
from backend.app.services.ml.ccip import match_image
|
||||
from backend.app.services.tag_service import TagService
|
||||
@@ -61,6 +67,24 @@ async def test_matches_same_character_across_images(db):
|
||||
assert m["grounding"]["kind"] == "figure"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_matches_from_prototype_store(db):
|
||||
# Once the prototype store is populated (#1317), match_image reads it (the
|
||||
# fast incremental path) instead of rebuilding references on the request —
|
||||
# same match + grounding as the legacy build.
|
||||
raven = await TagService(db).find_or_create("Raven", TagKind.character)
|
||||
db.add(CharacterPrototype(tag_id=raven.id, ccip_embedding=_ccip(0)))
|
||||
db.add(CcipPrototypeState(tag_id=raven.id, fingerprint="1:1"))
|
||||
query = await _img(db, "n" * 64)
|
||||
await _figure(db, query.id, _ccip(0)) # query figure matches the prototype
|
||||
await db.commit()
|
||||
|
||||
matches = await match_image(db, query.id)
|
||||
m = next(x for x in matches if x["tag_id"] == raven.id)
|
||||
assert m["source"] == "ccip" and m["score"] > 0.9
|
||||
assert m["grounding"]["kind"] == "figure"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_no_match_for_different_character(db):
|
||||
raven = await TagService(db).find_or_create("Raven", TagKind.character)
|
||||
|
||||
Reference in New Issue
Block a user