precedentai / api /contracts.py
lothnic's picture
Release code against the existing immutable corpus
66bab44 verified
Raw History Blame Contribute Delete
15 kB
"""Public wire records shared by HTTP, SSE and generated frontend types.
Optional metadata stays absent when unavailable. Additional provenance/tool
fields are retained for compatibility; required response and citation fields are
validated. These contracts describe shape, not legal support or authenticity.
"""
from typing import Any, Literal, NotRequired, Required
from pydantic import ConfigDict, TypeAdapter, ValidationError
from typing_extensions import TypedDict
CONTRACT_VERSION = "2.0"
class PublicRecord(TypedDict, total=False):
__pydantic_config__ = ConfigDict(extra="allow", strict=True)
class FeedbackContext(PublicRecord):
question: Required[str]
answer: Required[str]
source_ids: Required[list[str]]
class FeedbackReport(PublicRecord):
category: Required[Literal["answer", "sources", "coverage", "interface", "signin", "other"]]
details: Required[str]
consent: Required[bool]
share_context: Required[bool]
context: Required[FeedbackContext | None]
class FeedbackReceipt(PublicRecord):
id: Required[str]
created_at: Required[str]
status: Required[Literal["received", "withdrawn"]]
already_received: Required[bool]
retention_days: Required[int]
class SubmittedFeedback(FeedbackReceipt):
report: Required[FeedbackReport]
class FeedbackList(PublicRecord):
reports: Required[list[SubmittedFeedback]]
class Evaluation(PublicRecord, total=False):
passed: bool
notes: list[str]
class KeyQuote(PublicRecord, total=False):
quote: str
text: str
offset: int
class SummaryProvenance(PublicRecord, total=False):
summarized_from: str
parent_id: str
class Summary(PublicRecord, total=False):
variant: str
case_name: str | None
facts: str | None
facts_llm: str
issues: str | None
holding: str | None
sections: dict[str, str]
key_quotes: list[KeyQuote]
llm_status: str
llm_windows: int
provenance: SummaryProvenance
class Outcome(PublicRecord, total=False):
action: str
question: str
document: str
note: str
review_notice: str
class Source(PublicRecord, total=False):
title: str
source_id: str
court: str
year: int | str
text: str
score: float
citation: str
decision_date: str
document_type: str
edition_as_of: str
article_number: str
provision_status: str
source_section: str
chunk_index: int
parent_id: str
case_id: str
case_number: str
case_number_status: str
case_number_original: str
proceeding_id: str | None
proceeding_basis: Literal["source_reported_docket", "pdf_checked_docket"]
collection: str
source_url: str
source_origin: Literal["web"]
provider: str
fetched_at: str
extraction_kind: Literal["provider_excerpts", "provider_full_content"]
original_pdf_url: str
page_start: int
page_end: int
paragraph_start: int
paragraph_end: int
paragraph_locator_kind: str
section_heading: str
passage_role: str
opinion_role: str
metadata_date_status: str
pdf_header_date: str
source_pdf_path: str
context_kind: str
text_start: int
chunk_offset: int
document_id: str
pdf_sha256: str
retrieval_signals: list[Literal["semantic", "bm25", "exact"]]
retrieval_ranks: dict[Literal["semantic", "bm25", "exact"], int]
candidate_reservations: list[Literal["semantic", "bm25", "exact"]]
class Judgment(Source, total=False):
parent_id: Required[str]
text: Required[str]
characters: Required[int]
judge: str
class ReviewPrompt(PublicRecord, total=False):
note: str
task: str
approval_id: str
expires_at: str
class ApprovalReceipt(PublicRecord):
approval_id: Required[str]
approved: Required[bool]
status: Required[Literal["accepted", "completed", "uncertain"]]
created_at: Required[str]
settled_at: Required[str | None]
expires_at: Required[str]
retention_days: Required[int]
class DecisionDocument(Source, total=False):
document_id: Required[str]
parent_id: Required[str]
title: Required[str]
decision_date: Required[str]
class DecisionScope(TypedDict):
__pydantic_config__ = ConfigDict(extra="forbid", strict=True)
start: str | None
end: str | None
court: str | None
class DecisionPage(PublicRecord, total=False):
kind: Literal["decision_list"]
documents: Required[list[DecisionDocument]]
total: Required[int]
limit: Required[int]
offset: Required[int]
next_offset: Required[int | None]
scope: Required[DecisionScope]
date_basis: Required[Literal["source_reported_decision_date"]]
date_status_counts: Required[dict[str, int]]
conflicting_dates_excluded: Required[int]
unknown_dates_excluded: Required[int]
complete_court_inventory: Required[Literal[False]]
class Citation(PublicRecord, total=False):
id: Required[int]
quote: Required[str]
chunk_offset: Required[int]
document_offset: int
class AnswerParagraph(PublicRecord, total=False):
heading: str
text: Required[str]
citations: Required[list[Citation]]
class EvidencePassage(Source, total=False):
id: Required[int]
source_id: Required[str]
parent_id: Required[str]
title: Required[str]
citation: Required[str]
text: Required[str]
chunk_offset: Required[int]
locator_scope: Literal["source_chunk"]
class EvidenceContext(PublicRecord, total=False):
status: Required[str]
documents_requested: int
documents_read: int
candidate_passages: int
included_passages: int
focused_passages: int
requested_facets: list[str]
selected_facets: list[str]
bounded_scan_truncated: list[str]
note: str
class Claim(PublicRecord, total=False):
claim_id: Required[int]
paragraph_index: Required[int]
text: Required[str]
evidence_ids: Required[list[int]]
consequential: Required[bool]
status: Required[
Literal["references_checked", "rejected", "model_supported", "unsupported", "uncertain"]
]
reason: Required[str]
review_evidence_ids: list[int]
paragraph_text: str
standalone: bool
class ClaimCheck(PublicRecord, total=False):
status: Required[
Literal["not_run", "references_checked", "model_reviewed", "review_unavailable"]
]
review_calls: Required[int]
omitted_paragraphs: int
partially_retained_paragraphs: int
verification_scope: Required[str]
claims: Required[list[Claim]]
failure_reason: str | None
repair: dict[str, Any]
class TreatmentFinding(PublicRecord, total=False):
statement: Required[Literal["overruled", "affirmed", "distinguished", "followed"]]
identity_basis: Required[str]
quote: Required[str]
source: Required[Source]
evidence_id: int
class TreatmentCheck(PublicRecord, total=False):
target_title: Required[str]
target_citation: str
status: Required[Literal["explicit_statement_found", "unknown"]]
reason: Required[str]
findings: Required[list[TreatmentFinding]]
class TreatmentCoverage(PublicRecord, total=False):
year_start: int | None
year_end: int | None
class Treatment(PublicRecord, total=False):
status: Required[str]
current_status: Required[Literal["unknown"]]
coverage: Required[TreatmentCoverage]
note: Required[str]
checks: Required[list[TreatmentCheck]]
class ResearchAnswer(PublicRecord, total=False):
message: str
kind: Literal["decision_list"]
documents: list[DecisionDocument]
mode: Required[Literal["synthesis", "excerpts", "insufficient_evidence"]]
llm_status: Required[str]
paragraphs: Required[list[AnswerParagraph]]
evidence: Required[list[EvidencePassage]]
gaps: Required[list[str]]
evidence_context: EvidenceContext
claim_check: ClaimCheck
treatment: Treatment
citation_validation: dict[str, Any]
class WebResearch(PublicRecord, total=False):
provider: Required[Literal["parallel_mcp"]]
status: Required[str]
searched: Required[bool]
documents_fetched: Required[int]
requests: Required[int]
queries: list[str]
note: Required[str]
class ToolResult(PublicRecord, total=False):
tool: str
summary: Summary
outcome: Outcome
answer: ResearchAnswer
discovery: "DecisionPage | CaseResolution"
web_research: WebResearch
sources: list[Source]
class ChatResult(PublicRecord, total=False):
# Optional to preserve readable previously saved transcripts; new server
# responses always include this version.
contract_version: Literal["2.0"]
interrupted: Required[bool]
thread_id: Required[str]
routed_to: str | None
evaluation: Evaluation | None
result: ToolResult
sources: list[Source] | None
review: ReviewPrompt
class RecoverySettings(PublicRecord):
enabled: Required[bool]
generation: Required[str | None]
threads: Required[int]
bytes: Required[int]
retention_hours: Required[int]
max_threads: Required[int]
max_bytes: Required[int]
max_thread_bytes: Required[int]
class RecoveryThread(PublicRecord):
thread_id: Required[str]
updated_at: Required[str]
expires_at: Required[str]
class RecoveryThreads(PublicRecord):
threads: Required[list[RecoveryThread]]
class RecoveryExport(PublicRecord):
version: Required[int]
encoding: Required[str]
retention_hours: Required[int]
checkpoints: Required[list[dict[str, Any]]]
class RecoveryTurn(PublicRecord):
role: Required[Literal["user", "assistant"]]
content: Required[str]
class RecoveryConversation(PublicRecord):
thread_id: Required[str]
request: Required[str]
status: Required[Literal["completed", "review_required", "review_expired", "decision_consumed", "interrupted"]]
history: Required[list[RecoveryTurn]]
result: Required[ChatResult | None]
class HealthReport(TypedDict):
status: str
llm: str
web_fallback: str
backend: str
index: str
lexical_index: str
class ReadinessComponent(TypedDict, total=False):
name: Required[str]
status: Required[Literal["ready", "failed", "disabled", "configured", "missing_credentials"]]
code: str
probe_ms: float
class ReadinessResources(TypedDict, total=False):
process_peak_rss_bytes: int
runtime_filesystem_free_bytes: int
container_memory_limit_bytes: int
container_memory_used_bytes: int
class ReadinessReport(TypedDict):
status: Literal["ready", "retrieval_only", "warming", "degraded", "unavailable"]
detail: str
checked_at: str | None
components: list[ReadinessComponent]
generation: Literal["configured", "disabled", "missing_credentials", "not_probed"]
verification_scope: str
resources: NotRequired[ReadinessResources]
class HistoryTurn(PublicRecord, total=False):
role: Required[Literal["user", "assistant"]]
content: Required[str]
class HistoryResponse(PublicRecord, total=False):
thread_id: Required[str]
history: Required[list[HistoryTurn]]
status: str | None
class SupervisorResponse(PublicRecord, total=False):
sources: list[Source]
summary: Summary
evaluation: Evaluation
class CancelResponse(TypedDict):
stopped: bool
class UsageReport(PublicRecord, total=False):
calls: Required[int]
failed_calls: Required[int]
prompt_tokens: Required[int]
completion_tokens: Required[int]
total_tokens: Required[int]
cost_usd_estimate: Required[float]
by_model: dict[str, Any]
class LimitWindow(PublicRecord, total=False):
limit: Required[int]
remaining: Required[int]
used: Required[int]
window_seconds: Required[int]
next_expiry_at: str | None
retry_after: int
class ProviderMinuteLimits(TypedDict):
__pydantic_config__ = ConfigDict(extra="forbid", strict=True)
model_provider_calls: LimitWindow
model_provider_tokens: LimitWindow
class ProviderWindow(TypedDict):
provider: str
model: str
limits: ProviderMinuteLimits
class ServiceLimitsReport(PublicRecord, total=False):
persistence: Required[Literal["disabled", "memory", "postgres"]]
counted_at: str
limits: Required[dict[str, LimitWindow]]
provider_windows: list[ProviderWindow]
class CoverageSource(PublicRecord, total=False):
provider: Required[str]
url: Required[str]
license: str
attribution: str
class CoverageReport(PublicRecord, total=False):
snapshot_id: Required[str]
documents: Required[int]
built_at: Required[str]
decision_year_counts: Required[dict[str, int]]
court_counts: Required[dict[str, int]]
date_status_counts: Required[dict[str, int]]
document_kind_counts: Required[dict[str, int]]
limitations: Required[list[str]]
sources: Required[list[CoverageSource]]
class CaseResolution(PublicRecord, total=False):
kind: Literal["case_resolution"]
status: Required[Literal["resolved", "ambiguous", "no_match"]]
match_kind: Required[Literal["identity", "party_fragment", "none"]]
documents: Required[list[Source]]
class HistorySettings(TypedDict):
enabled: bool
threads: int
bytes: int
max_threads: int
max_bytes: int
max_thread_bytes: int
retention_days: int
class IdentityResponse(TypedDict):
id: str
persistence: Literal["postgres"]
class SavedThread(PublicRecord, total=False):
id: Required[str]
title: Required[str]
createdAt: Required[int]
updatedAt: Required[int]
messages: Required[list[dict[str, Any]]]
revision: Required[int]
class SavedThreads(TypedDict):
threads: list[SavedThread]
class HistoryExport(SavedThreads):
version: Literal[1]
class StreamNode(TypedDict):
event: Literal["node"]
name: str
class StreamDone(ChatResult, total=False):
event: Required[Literal["done"]]
class StreamError(TypedDict):
event: Literal["error"]
code: str
detail: str
class ErrorResponse(PublicRecord, total=False):
detail: Required[str | list[dict[str, Any]]]
code: str
category: str
StreamEvent = StreamNode | StreamDone | StreamError
chat_adapter = TypeAdapter(ChatResult)
stream_adapter = TypeAdapter(StreamEvent)
limits_adapter = TypeAdapter(ServiceLimitsReport)
class ContractError(RuntimeError):
"""Sanitized failure; never include a private prompt or source payload."""
def _validate(adapter: TypeAdapter, payload: Any):
try:
return adapter.validate_python(payload)
except ValidationError:
raise ContractError("The research response was incomplete. Please try again.") from None
def public_chat(payload: dict[str, Any]) -> ChatResult:
return _validate(chat_adapter, {**payload, "contract_version": CONTRACT_VERSION})
def public_limits(payload: dict[str, Any]) -> ServiceLimitsReport:
return _validate(limits_adapter, payload)
def stream_frame(payload: dict[str, Any]) -> str:
checked = _validate(stream_adapter, payload)
return f"data: {stream_adapter.dump_json(checked).decode()}\n\n"