Made refiner, referencer and builder less strict
This commit is contained in:
@@ -39,6 +39,8 @@
|
||||
|
||||
`ids` должны содержать только ID событий из `present`, во время которых нужно
|
||||
искать содержимое. ID указывай в хронологическом порядке и без повторов.
|
||||
Если нужный фрагмент пересекает границу окна, укажи только его ID из `present`;
|
||||
если таких ID нет, не создавай ссылку.
|
||||
|
||||
`text` — короткая конкретная инструкция следующему этапу:
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
import logging
|
||||
import traceback
|
||||
from typing import Annotated, Literal
|
||||
|
||||
@@ -180,6 +181,49 @@ class StructureBuilder:
|
||||
"pending.event_ids must reference events from the current window"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _keep_window_references(
|
||||
result: _BuildResult,
|
||||
window: Window[Event],
|
||||
) -> _BuildResult:
|
||||
"""Drop impossible image references and repair technical pending IDs."""
|
||||
visual_ids = {
|
||||
event.id
|
||||
for event in window.past + window.present
|
||||
if event.type == "vis"
|
||||
}
|
||||
elements: list[StructureElement] = []
|
||||
for element in result.elements:
|
||||
if isinstance(element, ImageElement) and element.event_id not in visual_ids:
|
||||
logging.warning("Ignoring image outside the current window: id=%s", element.event_id)
|
||||
continue
|
||||
elements.append(element)
|
||||
|
||||
context = result.context
|
||||
if context.pending is not None:
|
||||
window_ids = {
|
||||
event.id
|
||||
for event in window.past + window.present + window.future
|
||||
}
|
||||
pending_ids = [
|
||||
event_id
|
||||
for event_id in context.pending.event_ids
|
||||
if event_id in window_ids
|
||||
]
|
||||
if pending_ids != context.pending.event_ids:
|
||||
logging.warning(
|
||||
"Trimming pending IDs to the current window: %s -> %s",
|
||||
context.pending.event_ids,
|
||||
pending_ids,
|
||||
)
|
||||
pending = (
|
||||
context.pending.model_copy(update={"event_ids": pending_ids})
|
||||
if pending_ids else None
|
||||
)
|
||||
context = context.model_copy(update={"pending": pending})
|
||||
|
||||
return result.model_copy(update={"elements": elements, "context": context})
|
||||
|
||||
def _build_window(self, window: Window[Event]) -> _BuildResult:
|
||||
"""Build document elements from a single timeline window."""
|
||||
messages = [
|
||||
@@ -211,6 +255,7 @@ class StructureBuilder:
|
||||
|
||||
try:
|
||||
result = _BuildResult.model_validate_json(response)
|
||||
result = self._keep_window_references(result, window)
|
||||
self._validate_result(result, window)
|
||||
return result
|
||||
except (ValidationError, ValueError, TypeError):
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, ValidationError, model_validator
|
||||
@@ -58,6 +59,25 @@ class StructureRefiner:
|
||||
if isinstance(element, ImageElement) and element.event_id not in input_image_ids:
|
||||
raise ValueError("image.event_id must come from present")
|
||||
|
||||
@staticmethod
|
||||
def _keep_present_images(
|
||||
result: _RefineResult,
|
||||
window: Window[StructureElement],
|
||||
) -> _RefineResult:
|
||||
"""Discard image elements invented from context windows."""
|
||||
input_image_ids = {
|
||||
element.event_id
|
||||
for element in window.present
|
||||
if isinstance(element, ImageElement)
|
||||
}
|
||||
elements: list[StructureElement] = []
|
||||
for element in result.elements:
|
||||
if isinstance(element, ImageElement) and element.event_id not in input_image_ids:
|
||||
logging.warning("Ignoring image outside present window: id=%s", element.event_id)
|
||||
continue
|
||||
elements.append(element)
|
||||
return result.model_copy(update={"elements": elements})
|
||||
|
||||
def _refine_window(self, window: Window[StructureElement]) -> _RefineResult:
|
||||
messages = [
|
||||
self._system_prompt,
|
||||
@@ -91,6 +111,7 @@ class StructureRefiner:
|
||||
|
||||
try:
|
||||
result = _RefineResult.model_validate_json(response)
|
||||
result = self._keep_present_images(result, window)
|
||||
self._validate_result(result, window)
|
||||
return result
|
||||
except (ValidationError, ValueError, TypeError):
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
import logging
|
||||
import traceback
|
||||
from typing import Literal
|
||||
|
||||
@@ -99,6 +100,47 @@ class VideoReferenceBuilder:
|
||||
raise ValueError("Duplicate reference in a single window")
|
||||
seen.add(key)
|
||||
|
||||
@staticmethod
|
||||
def _keep_present_references(
|
||||
result: _WindowResult,
|
||||
window: Window[VideoReferenceEvent],
|
||||
) -> _WindowResult:
|
||||
"""Keep the usable part of references that cross a window boundary."""
|
||||
present_positions = {
|
||||
event.id: position
|
||||
for position, event in enumerate(window.present)
|
||||
}
|
||||
references: list[UnresolvedReference] = []
|
||||
seen: set[tuple[tuple[int, ...], str]] = set()
|
||||
|
||||
for reference in result.unresolved:
|
||||
ids = sorted(
|
||||
{event_id for event_id in reference.ids if event_id in present_positions},
|
||||
key=present_positions.__getitem__,
|
||||
)
|
||||
if not ids:
|
||||
logging.warning(
|
||||
"Ignoring reference outside present window: ids=%s",
|
||||
reference.ids,
|
||||
)
|
||||
continue
|
||||
if ids != reference.ids:
|
||||
logging.warning(
|
||||
"Trimming reference to present window: ids=%s -> %s",
|
||||
reference.ids,
|
||||
ids,
|
||||
)
|
||||
|
||||
normalized = reference.model_copy(update={"ids": ids})
|
||||
key = (tuple(ids), normalized.type)
|
||||
if key in seen:
|
||||
logging.warning("Ignoring duplicate reference: ids=%s", ids)
|
||||
continue
|
||||
seen.add(key)
|
||||
references.append(normalized)
|
||||
|
||||
return result.model_copy(update={"unresolved": references})
|
||||
|
||||
def _build_window(
|
||||
self,
|
||||
window: Window[VideoReferenceEvent],
|
||||
@@ -135,6 +177,7 @@ class VideoReferenceBuilder:
|
||||
|
||||
try:
|
||||
result = _WindowResult.model_validate_json(response)
|
||||
result = self._keep_present_references(result, window)
|
||||
self._validate_result(result, window)
|
||||
return result
|
||||
except (ValidationError, ValueError, TypeError):
|
||||
|
||||
Reference in New Issue
Block a user