feat(retrieval): a menu line is a name, its kind and System, and the whole passage that matched (#4364)
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 58s
CI & Build / Python tests (push) Successful in 1m36s
CI & Build / Build & push image (push) Successful in 26s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 58s
CI & Build / Python tests (push) Successful in 1m36s
CI & Build / Build & push image (push) Successful in 26s
Injected lines rendered a snippet's or lesson's title, which carries its whole trigger by construction (the embedding shape) and ran past 1,500 characters -- again on every `seen` repeat. The passage under a line was cut to 200 chars from the middle, keeping its head (the title again) and losing where the match was. Now, on both the prompt menu and the write-path prior-art menu: - the line shows the record's NAME (snippet data.name / lesson subject), with its kind and System (`[issue (done) · Plugin & hooks]`); - the passage is the whole matched chunk, title prefix stripped, on one line so the blockquote holds; a title-only match hands over the trigger; - a `seen` record is a one-line pointer to what is already in context. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -310,6 +310,39 @@ async def list_record_systems(user_id: int, note_id: int) -> list[System]:
|
||||
return list(result.scalars().all())
|
||||
|
||||
|
||||
async def system_names_for(note_ids: set[int]) -> dict[int, list[str]]:
|
||||
"""{note_id: [system name, …]} in one query, for records ALREADY read.
|
||||
|
||||
For decorating a result set the caller was allowed to see — an injected
|
||||
menu line says which part of the project a record is about, so the reader
|
||||
can place it without opening it (#4364). No access check here for that
|
||||
reason: the ids come from a search that applied one, and a system name is
|
||||
metadata of the record, not a record of its own.
|
||||
|
||||
Fails soft, like `access.owner_names_for`: a menu without its system labels
|
||||
is a cosmetic downgrade, and failing the whole injection over one is not.
|
||||
"""
|
||||
if not note_ids:
|
||||
return {}
|
||||
try:
|
||||
async with async_session() as session:
|
||||
rows = (
|
||||
await session.execute(
|
||||
select(RecordSystem.note_id, System.name)
|
||||
.join(System, System.id == RecordSystem.system_id)
|
||||
.where(RecordSystem.note_id.in_(note_ids), System.deleted_at.is_(None))
|
||||
.order_by(System.order_index.asc(), System.name.asc())
|
||||
)
|
||||
).all()
|
||||
except Exception:
|
||||
logger.warning("System-name lookup failed; menu lines go unlabelled", exc_info=True)
|
||||
return {}
|
||||
out: dict[int, list[str]] = {}
|
||||
for note_id, name in rows:
|
||||
out.setdefault(int(note_id), []).append(name)
|
||||
return out
|
||||
|
||||
|
||||
async def list_records_for_system(
|
||||
user_id: int, system_id: int, kind: str | None = None, open_only: bool = False
|
||||
) -> list[Note]:
|
||||
|
||||
Reference in New Issue
Block a user