feat: add ingest review flags and Jev scoring
CI / quality (push) Canceled after 0s

This commit is contained in:
vasily committed 2026-10-14 20:35:48 +08:00
1 parent 1c61c7ad04
commit 7b2c43ddaf
18 files changed
+1648 -2

No files matched your search

+90
View File
@@ -19,6 +19,18 @@ from pydantic import (
)
SCHEMA_VERSION = 1
ResearchLens = Literal[
"technical",
"conceptual",
"cultural-historical",
"financial-market",
]
DEFAULT_RESEARCH_LENSES: tuple[ResearchLens, ...] = (
"technical",
"conceptual",
"cultural-historical",
"financial-market",
)
RecordId = Annotated[
str,
@@ -58,6 +70,7 @@ class ProjectRecord(StrictRecord):
owners: tuple[RecordId, ...] = ()
north_star: str = Field(min_length=1, max_length=4000)
key_questions: tuple[str, ...] = Field(min_length=1, max_length=30)
research_lenses: tuple[ResearchLens, ...] = DEFAULT_RESEARCH_LENSES
watch_list: tuple[str, ...] = Field(default=(), max_length=100)
default_includes: tuple[RecordId, ...] = ("shared",)
taxonomy_extensions: dict[str, tuple[str, ...]] = Field(default_factory=dict, max_length=100)
@@ -71,20 +84,97 @@ class ProjectRecord(StrictRecord):
return value
class IngestGuidance(StrictRecord):
guidance_id: RecordId
lens: ResearchLens
rule: str = Field(min_length=1, max_length=1000)
created_at: datetime
@field_validator("created_at")
@classmethod
def guidance_timestamp_is_utc(cls, value: datetime) -> datetime:
offset = value.utcoffset()
if offset is None or offset.total_seconds() != 0:
raise ValueError("guidance timestamp must be timezone-aware UTC")
return value
class ProjectLens(StrictRecord):
project_id: RecordId
title: str = Field(min_length=1, max_length=200)
status: ProjectStatus
north_star: str = Field(min_length=1, max_length=4000)
key_questions: tuple[str, ...] = Field(min_length=1, max_length=30)
research_lenses: tuple[ResearchLens, ...] = DEFAULT_RESEARCH_LENSES
watch_list: tuple[str, ...] = Field(default=(), max_length=100)
taxonomy_extensions: dict[str, tuple[str, ...]] = Field(default_factory=dict, max_length=100)
ingest_guidance: tuple[IngestGuidance, ...] = Field(default=(), max_length=100)
class ProjectContextResponse(StrictRecord):
lens: ProjectLens
class IngestReviewItem(StrictRecord):
review_id: RecordId
project_id: RecordId
run_id: RecordId
source_revision_id: RecordId
extraction_id: Sha256
locator: Locator
excerpt: str = Field(min_length=1, max_length=4000)
lens: ResearchLens
summary: str = Field(min_length=1, max_length=2000)
relevance_rationale: str = Field(min_length=1, max_length=2000)
relevance: Literal["borderline", "low"] = "borderline"
jev_relation_probability: FiniteFloat | None = Field(default=None, ge=0, le=1)
jev_lens: ResearchLens | None = None
jev_lens_confidence: FiniteFloat | None = Field(default=None, ge=0, le=1)
jev_model: str | None = Field(default=None, max_length=100)
status: Literal["needs-user-review", "included", "excluded", "deferred"] = "needs-user-review"
submitted_by: RecordId
created_at: datetime
@field_validator("created_at")
@classmethod
def review_timestamp_is_utc(cls, value: datetime) -> datetime:
offset = value.utcoffset()
if offset is None or offset.total_seconds() != 0:
raise ValueError("review timestamp must be timezone-aware UTC")
return value
class IngestReviewCreate(StrictRecord):
review_id: RecordId
project_id: RecordId
run_id: RecordId
source_revision_id: RecordId
extraction_id: Sha256
locator: Locator
excerpt: str = Field(min_length=1, max_length=4000)
lens: ResearchLens
summary: str = Field(min_length=1, max_length=2000)
relevance_rationale: str = Field(min_length=1, max_length=2000)
relevance: Literal["borderline", "low"] = "borderline"
class IngestReviewDecision(StrictRecord):
decision_id: RecordId
review_id: RecordId
decision: Literal["include", "exclude", "defer"]
rationale: str = Field(min_length=1, max_length=2000)
save_as_guidance: bool = False
guidance_rule: str | None = Field(default=None, max_length=1000)
@model_validator(mode="after")
def guidance_requires_explicit_rule(self) -> IngestReviewDecision:
if self.save_as_guidance and not (self.guidance_rule or "").strip():
raise ValueError("saving project guidance requires a non-empty guidance rule")
if not self.save_as_guidance and self.guidance_rule is not None:
raise ValueError("guidance_rule requires explicit save_as_guidance")
return self
class ReliabilityGrade(StrEnum):
LOW = "low"
MEDIUM = "medium"
+3
View File
@@ -50,6 +50,8 @@ OPERATIONS: Final[tuple[Operation, ...]] = (
Operation("project_archive", "project:admin", frozenset({Transport.HTTP})),
Operation("list_projects", "knowledge:read", frozenset({Transport.HTTP, Transport.MCP})),
Operation("get_project_context", "knowledge:read", frozenset({Transport.MCP})),
Operation("ingest_review_list", "ingest:review", frozenset({Transport.HTTP})),
Operation("ingest_review_decide", "ingest:review", frozenset({Transport.HTTP})),
Operation("search", "knowledge:read", frozenset({Transport.HTTP, Transport.MCP})),
Operation("list_sources", "knowledge:read", frozenset({Transport.HTTP, Transport.MCP})),
Operation("get_node", "knowledge:read", frozenset({Transport.HTTP, Transport.MCP})),
@@ -73,6 +75,7 @@ OPERATIONS: Final[tuple[Operation, ...]] = (
Operation("run_create", "run:write", frozenset({Transport.MCP})),
Operation("run_checkpoint", "run:write", frozenset({Transport.MCP})),
Operation("run_status", "run:read", frozenset({Transport.MCP})),
Operation("ingest_review_create", "ingest:review", frozenset({Transport.MCP})),
Operation("owner_challenge_get", "owner:review", frozenset({Transport.OWNER_MCP})),
Operation("owner_diff_get", "owner:review", frozenset({Transport.OWNER_MCP})),
Operation("owner_decide", "owner:decide", frozenset({Transport.OWNER_MCP})),