Task 011 implement evidence matrix and claim gate
This commit is contained in:
@@ -15,6 +15,10 @@ from src.domain.contracts import (
|
||||
AgentJobType,
|
||||
ArticleSummary,
|
||||
BoundaryQuestionSummary,
|
||||
ClaimRiskLevel,
|
||||
ClaimSupportStatus,
|
||||
ClaimSummary,
|
||||
EvidenceSummary,
|
||||
PlanReviewStatus,
|
||||
PlanSectionSummary,
|
||||
PlanSummary,
|
||||
@@ -49,6 +53,8 @@ class BackendRepository:
|
||||
self.boundary_questions = BoundaryQuestionsRepository(self)
|
||||
self.article_plans = ArticlePlansRepository(self)
|
||||
self.research_manifests = ResearchManifestsRepository(self)
|
||||
self.evidence_items = EvidenceItemsRepository(self)
|
||||
self.claims = ClaimsRepository(self)
|
||||
self.agent_jobs = AgentJobsRepository(self)
|
||||
self.script_config_versions = ScriptConfigVersionsRepository(self)
|
||||
self.script_config_version_events = ScriptConfigVersionAuditEventsRepository(self)
|
||||
@@ -1123,6 +1129,276 @@ class ResearchManifestsRepository:
|
||||
return _research_manifest_from_row(row)
|
||||
|
||||
|
||||
class EvidenceItemsRepository:
|
||||
def __init__(self, repository: BackendRepository) -> None:
|
||||
self._repository = repository
|
||||
|
||||
def create(
|
||||
self,
|
||||
*,
|
||||
article_id: UUID,
|
||||
source_title: str,
|
||||
source_url: str,
|
||||
source_type: str,
|
||||
source_quality_score: float,
|
||||
summary: str,
|
||||
supports_claims: list[UUID],
|
||||
artifact_manifest_id: UUID,
|
||||
retrieved_at: datetime,
|
||||
review_status: str = "PENDING",
|
||||
) -> EvidenceSummary:
|
||||
evidence_id = uuid4()
|
||||
placeholder = self._repository.placeholder()
|
||||
json_cast = self._repository.json_cast()
|
||||
with self._repository.connection() as connection:
|
||||
connection.execute(
|
||||
f"""
|
||||
INSERT INTO evidence_items (
|
||||
id,
|
||||
article_id,
|
||||
source_title,
|
||||
source_url,
|
||||
source_type,
|
||||
source_quality_score,
|
||||
summary,
|
||||
supports_claims,
|
||||
artifact_manifest_id,
|
||||
retrieved_at,
|
||||
review_status
|
||||
)
|
||||
VALUES (
|
||||
{placeholder},
|
||||
{placeholder},
|
||||
{placeholder},
|
||||
{placeholder},
|
||||
{placeholder},
|
||||
{placeholder},
|
||||
{placeholder},
|
||||
{placeholder}{json_cast},
|
||||
{placeholder},
|
||||
{placeholder},
|
||||
{placeholder}
|
||||
)
|
||||
""",
|
||||
(
|
||||
str(evidence_id),
|
||||
str(article_id),
|
||||
source_title,
|
||||
source_url,
|
||||
source_type,
|
||||
source_quality_score,
|
||||
summary,
|
||||
_json_value([str(claim_id) for claim_id in supports_claims]),
|
||||
str(artifact_manifest_id),
|
||||
_datetime_value(retrieved_at),
|
||||
review_status,
|
||||
),
|
||||
)
|
||||
return self.get(evidence_id)
|
||||
|
||||
def list_for_article(self, article_id: UUID) -> list[EvidenceSummary]:
|
||||
placeholder = self._repository.placeholder()
|
||||
with self._repository.connection() as connection:
|
||||
rows = connection.execute(
|
||||
f"""
|
||||
SELECT {self._select_columns()}
|
||||
FROM evidence_items
|
||||
WHERE article_id = {placeholder}
|
||||
ORDER BY retrieved_at, id
|
||||
""",
|
||||
(str(article_id),),
|
||||
).fetchall()
|
||||
return [_evidence_from_row(row) for row in rows]
|
||||
|
||||
def get(self, evidence_id: UUID) -> EvidenceSummary:
|
||||
placeholder = self._repository.placeholder()
|
||||
with self._repository.connection() as connection:
|
||||
row = connection.execute(
|
||||
f"""
|
||||
SELECT {self._select_columns()}
|
||||
FROM evidence_items
|
||||
WHERE id = {placeholder}
|
||||
""",
|
||||
(str(evidence_id),),
|
||||
).fetchone()
|
||||
if row is None:
|
||||
raise LookupError(f"Evidence not found: {evidence_id}")
|
||||
return _evidence_from_row(row)
|
||||
|
||||
def update_review_status(
|
||||
self,
|
||||
*,
|
||||
article_id: UUID,
|
||||
evidence_id: UUID,
|
||||
review_status: str,
|
||||
) -> EvidenceSummary:
|
||||
placeholder = self._repository.placeholder()
|
||||
with self._repository.connection() as connection:
|
||||
connection.execute(
|
||||
f"""
|
||||
UPDATE evidence_items
|
||||
SET review_status = {placeholder}
|
||||
WHERE article_id = {placeholder} AND id = {placeholder}
|
||||
""",
|
||||
(review_status, str(article_id), str(evidence_id)),
|
||||
)
|
||||
return self.get(evidence_id)
|
||||
|
||||
def delete(
|
||||
self,
|
||||
*,
|
||||
article_id: UUID,
|
||||
evidence_id: UUID,
|
||||
) -> None:
|
||||
placeholder = self._repository.placeholder()
|
||||
with self._repository.connection() as connection:
|
||||
connection.execute(
|
||||
f"""
|
||||
DELETE FROM evidence_items
|
||||
WHERE article_id = {placeholder} AND id = {placeholder}
|
||||
""",
|
||||
(str(article_id), str(evidence_id)),
|
||||
)
|
||||
|
||||
def _select_columns(self) -> str:
|
||||
return """
|
||||
id,
|
||||
article_id,
|
||||
source_title,
|
||||
source_url,
|
||||
source_type,
|
||||
source_quality_score,
|
||||
summary,
|
||||
supports_claims,
|
||||
artifact_manifest_id,
|
||||
retrieved_at,
|
||||
review_status
|
||||
"""
|
||||
|
||||
|
||||
class ClaimsRepository:
|
||||
def __init__(self, repository: BackendRepository) -> None:
|
||||
self._repository = repository
|
||||
|
||||
def create(
|
||||
self,
|
||||
*,
|
||||
article_id: UUID,
|
||||
section_id: UUID | None,
|
||||
claim_text: str,
|
||||
support_status: ClaimSupportStatus,
|
||||
risk_level: ClaimRiskLevel,
|
||||
evidence_item_ids: list[UUID],
|
||||
created_at: datetime,
|
||||
) -> ClaimSummary:
|
||||
claim_id = uuid4()
|
||||
placeholder = self._repository.placeholder()
|
||||
json_cast = self._repository.json_cast()
|
||||
with self._repository.connection() as connection:
|
||||
connection.execute(
|
||||
f"""
|
||||
INSERT INTO claims (
|
||||
id,
|
||||
article_id,
|
||||
section_id,
|
||||
claim_text,
|
||||
support_status,
|
||||
risk_level,
|
||||
evidence_item_ids,
|
||||
created_at
|
||||
)
|
||||
VALUES (
|
||||
{placeholder},
|
||||
{placeholder},
|
||||
{placeholder},
|
||||
{placeholder},
|
||||
{placeholder},
|
||||
{placeholder},
|
||||
{placeholder}{json_cast},
|
||||
{placeholder}
|
||||
)
|
||||
""",
|
||||
(
|
||||
str(claim_id),
|
||||
str(article_id),
|
||||
_uuid_value(section_id),
|
||||
claim_text,
|
||||
support_status.value,
|
||||
risk_level.value,
|
||||
_json_value([str(evidence_id) for evidence_id in evidence_item_ids]),
|
||||
_datetime_value(created_at),
|
||||
),
|
||||
)
|
||||
return self.get(claim_id)
|
||||
|
||||
def list_for_article(self, article_id: UUID) -> list[ClaimSummary]:
|
||||
placeholder = self._repository.placeholder()
|
||||
with self._repository.connection() as connection:
|
||||
rows = connection.execute(
|
||||
f"""
|
||||
SELECT
|
||||
id,
|
||||
article_id,
|
||||
section_id,
|
||||
claim_text,
|
||||
support_status,
|
||||
risk_level,
|
||||
evidence_item_ids
|
||||
FROM claims
|
||||
WHERE article_id = {placeholder}
|
||||
ORDER BY id
|
||||
""",
|
||||
(str(article_id),),
|
||||
).fetchall()
|
||||
return [_claim_from_row(row) for row in rows]
|
||||
|
||||
def get(self, claim_id: UUID) -> ClaimSummary:
|
||||
placeholder = self._repository.placeholder()
|
||||
with self._repository.connection() as connection:
|
||||
row = connection.execute(
|
||||
f"""
|
||||
SELECT
|
||||
id,
|
||||
article_id,
|
||||
section_id,
|
||||
claim_text,
|
||||
support_status,
|
||||
risk_level,
|
||||
evidence_item_ids
|
||||
FROM claims
|
||||
WHERE id = {placeholder}
|
||||
""",
|
||||
(str(claim_id),),
|
||||
).fetchone()
|
||||
if row is None:
|
||||
raise LookupError(f"Claim not found: {claim_id}")
|
||||
return _claim_from_row(row)
|
||||
|
||||
def update_evidence_links(
|
||||
self,
|
||||
*,
|
||||
claim_id: UUID,
|
||||
evidence_item_ids: list[UUID],
|
||||
support_status: ClaimSupportStatus,
|
||||
) -> ClaimSummary:
|
||||
placeholder = self._repository.placeholder()
|
||||
json_cast = self._repository.json_cast()
|
||||
with self._repository.connection() as connection:
|
||||
connection.execute(
|
||||
f"""
|
||||
UPDATE claims
|
||||
SET evidence_item_ids = {placeholder}{json_cast}, support_status = {placeholder}
|
||||
WHERE id = {placeholder}
|
||||
""",
|
||||
(
|
||||
_json_value([str(evidence_id) for evidence_id in evidence_item_ids]),
|
||||
support_status.value,
|
||||
str(claim_id),
|
||||
),
|
||||
)
|
||||
return self.get(claim_id)
|
||||
|
||||
|
||||
class AgentJobsRepository:
|
||||
def __init__(self, repository: BackendRepository) -> None:
|
||||
self._repository = repository
|
||||
@@ -1847,6 +2123,36 @@ def _research_manifest_from_row(row: Any) -> ResearchArtifactManifestSummary:
|
||||
)
|
||||
|
||||
|
||||
def _evidence_from_row(row: Any) -> EvidenceSummary:
|
||||
return EvidenceSummary(
|
||||
id=_row_value(row, "id"),
|
||||
article_id=_row_value(row, "article_id"),
|
||||
source_title=_row_value(row, "source_title"),
|
||||
source_url=_row_value(row, "source_url"),
|
||||
source_type=_row_value(row, "source_type"),
|
||||
source_quality_score=float(_row_value(row, "source_quality_score")),
|
||||
summary=_row_value(row, "summary"),
|
||||
supports_claims=[UUID(value) for value in _json_from_row(row, "supports_claims")],
|
||||
artifact_manifest_id=_row_value(row, "artifact_manifest_id"),
|
||||
retrieved_at=_row_value(row, "retrieved_at"),
|
||||
review_status=_row_value(row, "review_status"),
|
||||
)
|
||||
|
||||
|
||||
def _claim_from_row(row: Any) -> ClaimSummary:
|
||||
return ClaimSummary(
|
||||
id=_row_value(row, "id"),
|
||||
article_id=_row_value(row, "article_id"),
|
||||
section_id=_row_value(row, "section_id"),
|
||||
claim_text=_row_value(row, "claim_text"),
|
||||
support_status=_row_value(row, "support_status"),
|
||||
risk_level=_row_value(row, "risk_level"),
|
||||
evidence_item_ids=[
|
||||
UUID(value) for value in _json_from_row(row, "evidence_item_ids")
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def _agent_job_summary_from_row(row: Any) -> AgentJobSummary:
|
||||
return AgentJobSummary(
|
||||
id=_row_value(row, "id"),
|
||||
|
||||
Reference in New Issue
Block a user