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

+125
View File
@@ -0,0 +1,125 @@
"""Small fixed-endpoint client for optional OpenRouter Jev relevance judgments."""
from __future__ import annotations
import json
from dataclasses import dataclass
from typing import Any, cast
from urllib.error import HTTPError, URLError
from urllib.request import HTTPRedirectHandler, Request, build_opener
from gewu.domain.contracts import IngestReviewCreate, ProjectRecord, ResearchLens
ENDPOINT = "https://openrouter.ai/api/alpha/decisions"
MODEL = "typesafe/jev-1.13"
MAX_RESPONSE_BYTES = 32_000
@dataclass(frozen=True, slots=True)
class JevAssessment:
relation_probability: float
lens: ResearchLens
lens_confidence: float
model: str
class JevUnavailable(RuntimeError):
"""Jev is not configured or returned an unusable response."""
class _NoRedirect(HTTPRedirectHandler):
def redirect_request(
self, request: Any, response: Any, code: int, message: str, headers: Any, new_url: str
) -> None:
return None
class OpenRouterJev:
def __init__(self, api_key: str, *, model: str = MODEL) -> None:
if not api_key or len(api_key) > 512 or any(char.isspace() for char in api_key):
raise ValueError("OpenRouter API key is invalid")
if model not in {MODEL, "~typesafe/jev-latest"}:
raise ValueError("Unsupported Jev model identifier")
self._api_key = api_key
self._model = model
def assess(self, item: IngestReviewCreate, project: ProjectRecord) -> JevAssessment:
state = {
"project_title": project.title,
"project_north_star": project.north_star,
"project_questions": project.key_questions,
"candidate_lens": item.lens,
"candidate_summary": item.summary,
"candidate_relevance_rationale": item.relevance_rationale,
"source_excerpt": item.excerpt,
}
criteria = {
"technical": "Engineering, implementation, technology, or operational practice.",
"conceptual": "Ideas, theory, design concepts, or analytical frameworks.",
"cultural-historical": "Cultural, artistic, historical, social, or audience context.",
"financial-market": "Costs, revenues, business models, markets, or financing.",
}
payload = json.dumps(
{
"model": self._model,
"state": state,
"questions": {
"project_relation": {
"type": "noul",
"instructions": (
"Does this passage help answer a project question or explain "
"the project's technical, conceptual, cultural-historical, or "
"financial-market context? Treat indirect context as relevant when "
"the connection is plausible."
),
"criteria": {
"true": "A plausible, specific connection to the project exists.",
"false": "The passage has no plausible project connection.",
},
},
"best_lens": {
"type": "choice",
"instructions": "Which project research lens best describes this passage?",
"criteria": criteria,
},
},
},
ensure_ascii=False,
separators=(",", ":"),
).encode("utf-8")
request = Request(
ENDPOINT,
data=payload,
headers={
"Authorization": f"Bearer {self._api_key}",
"Content-Type": "application/json",
"Accept": "application/json",
},
method="POST",
)
try:
with build_opener(_NoRedirect()).open(request, timeout=12) as response:
if response.status != 200:
raise JevUnavailable("Jev request failed")
raw = response.read(MAX_RESPONSE_BYTES + 1)
except (HTTPError, URLError, TimeoutError, OSError) as error:
raise JevUnavailable("Jev request failed") from error
if len(raw) > MAX_RESPONSE_BYTES:
raise JevUnavailable("Jev response exceeded its size limit")
try:
result = json.loads(raw)
answers = result["answers"]
relation = float(answers["project_relation"]["noul"])
lens = answers["best_lens"]["choice"]
confidence = float(answers["best_lens"]["confidence"])
served_model = str(result["model"])
except (KeyError, TypeError, ValueError, json.JSONDecodeError) as error:
raise JevUnavailable("Jev response did not match its typed decision schema") from error
if (
not 0 <= relation <= 1
or not 0 <= confidence <= 1
or lens not in criteria
or len(served_model) > 100
):
raise JevUnavailable("Jev response contained an invalid decision")
return JevAssessment(relation, cast(ResearchLens, lens), confidence, served_model)