99 lines
2 KiB
Python
99 lines
2 KiB
Python
|
|
"""Immutable generic project, node, edge, and context contracts."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from dataclasses import asdict, dataclass
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass(frozen=True)
|
||
|
|
class Limits:
|
||
|
|
max_source_bytes: int = 1_000_000
|
||
|
|
max_nodes: int = 10_000
|
||
|
|
max_query_chars: int = 500
|
||
|
|
max_results: int = 100
|
||
|
|
max_traversal_depth: int = 8
|
||
|
|
max_context_tokens: int = 32_000
|
||
|
|
max_tool_output_chars: int = 200_000
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass(frozen=True)
|
||
|
|
class ContextProfile:
|
||
|
|
profile_id: str
|
||
|
|
families: tuple[str, ...]
|
||
|
|
statuses: tuple[str, ...]
|
||
|
|
required_nodes: tuple[str, ...]
|
||
|
|
token_budget: int
|
||
|
|
dependency_depth: int
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass(frozen=True)
|
||
|
|
class ProjectDescriptor:
|
||
|
|
schema_version: int
|
||
|
|
project_id: str
|
||
|
|
title: str
|
||
|
|
adapter: str
|
||
|
|
root: Path
|
||
|
|
descriptor_path: Path
|
||
|
|
descriptor_hash: str
|
||
|
|
content_roots: tuple[Path, ...]
|
||
|
|
authority_files: tuple[Path, ...]
|
||
|
|
cache_root: Path
|
||
|
|
index_path: Path
|
||
|
|
allowed_relations: tuple[str, ...]
|
||
|
|
profiles: tuple[ContextProfile, ...]
|
||
|
|
limits: Limits
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass(frozen=True)
|
||
|
|
class Node:
|
||
|
|
node_id: str
|
||
|
|
title: str
|
||
|
|
family: str
|
||
|
|
authority: str
|
||
|
|
status: str
|
||
|
|
tags: tuple[str, ...]
|
||
|
|
summary: str
|
||
|
|
content: str
|
||
|
|
source_path: str
|
||
|
|
source_anchor: str | None
|
||
|
|
content_hash: str
|
||
|
|
|
||
|
|
def as_dict(self, *, include_content: bool = True) -> dict[str, object]:
|
||
|
|
result = asdict(self)
|
||
|
|
if not include_content:
|
||
|
|
result.pop("content")
|
||
|
|
return result
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass(frozen=True)
|
||
|
|
class Edge:
|
||
|
|
source_id: str
|
||
|
|
relation: str
|
||
|
|
target_id: str
|
||
|
|
|
||
|
|
def as_dict(self) -> dict[str, str]:
|
||
|
|
return asdict(self)
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass(frozen=True)
|
||
|
|
class ProjectSnapshot:
|
||
|
|
descriptor: ProjectDescriptor
|
||
|
|
nodes: tuple[Node, ...]
|
||
|
|
edges: tuple[Edge, ...]
|
||
|
|
source_hash: str
|
||
|
|
revision: str
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass(frozen=True)
|
||
|
|
class ContextEntry:
|
||
|
|
node_id: str
|
||
|
|
reason: str
|
||
|
|
estimated_tokens: int
|
||
|
|
source_path: str
|
||
|
|
content_hash: str
|
||
|
|
text: str
|
||
|
|
|
||
|
|
def as_dict(self) -> dict[str, object]:
|
||
|
|
return asdict(self)
|