style: fix ruff lint violations across service and tests
CI / quality (push) Canceled after 0s

Wraps over-long lines, sorts imports, drops an unused local, and bumps the
agent submodule for its own import-sort fix so make check passes.
This commit is contained in:
vasily committed 2026-10-16 20:13:59 +08:00
1 parent 78033bb830
commit c492124fe1
12 files changed
+272 -68

No files matched your search

+59 -20
View File
@@ -19,12 +19,12 @@ from gewu.application.search import (
BrowseIndexResult,
GraphIndexResult,
IndexedNode,
SourceRelatedNodeHit,
SourceNodesIndexResult,
SearchEdgeHit,
SearchEdgeResult,
SearchIndexResult,
SourceIndexResult,
SourceNodesIndexResult,
SourceRelatedNodeHit,
)
from gewu.application.serialization import safe_yaml_record
from gewu.domain.contracts import Confidence, KnowledgeNode, SourceRevisionRecord, TypedEdge
@@ -113,7 +113,8 @@ class SQLiteSearchIndex:
)
connection.execute(
"CREATE TABLE IF NOT EXISTS indexed_citations ("
"node_id TEXT NOT NULL, project_id TEXT NOT NULL, source_revision_id TEXT NOT NULL, "
"node_id TEXT NOT NULL, project_id TEXT NOT NULL, "
"source_revision_id TEXT NOT NULL, "
"PRIMARY KEY(node_id, source_revision_id))"
)
connection.execute(
@@ -700,7 +701,13 @@ class SQLiteSearchIndex:
project_scope = tuple(sorted(set(project_ids) | ({"shared"} if include_shared else set())))
if not project_scope:
return BrowseIndexResult(
revision, (), 0, 0, 0, {k: {} for k in ("tags", "audience", "use_for", "domain")}, False
revision,
(),
0,
0,
0,
{k: {} for k in ("tags", "audience", "use_for", "domain")},
False,
)
removal_filter = (
@@ -766,17 +773,37 @@ class SQLiteSearchIndex:
match = self._match_expression(query)
if order == "title_asc":
order_by = "LOWER(json_extract(indexed_nodes.node_json, '$.title')) ASC, json_extract(indexed_nodes.node_json, '$.title') ASC, indexed_nodes.node_id ASC"
order_by = (
"LOWER(json_extract(indexed_nodes.node_json, '$.title')) ASC, "
"json_extract(indexed_nodes.node_json, '$.title') ASC, "
"indexed_nodes.node_id ASC"
)
elif order == "title_desc":
order_by = "LOWER(json_extract(indexed_nodes.node_json, '$.title')) DESC, json_extract(indexed_nodes.node_json, '$.title') DESC, indexed_nodes.node_id DESC"
order_by = (
"LOWER(json_extract(indexed_nodes.node_json, '$.title')) DESC, "
"json_extract(indexed_nodes.node_json, '$.title') DESC, "
"indexed_nodes.node_id DESC"
)
elif order == "date_newest":
order_by = "(json_extract(indexed_nodes.node_json, '$.as_of') IS NULL) ASC, json_extract(indexed_nodes.node_json, '$.as_of') DESC, indexed_nodes.node_id ASC"
order_by = (
"(json_extract(indexed_nodes.node_json, '$.as_of') IS NULL) ASC, "
"json_extract(indexed_nodes.node_json, '$.as_of') DESC, "
"indexed_nodes.node_id ASC"
)
elif order == "date_oldest":
order_by = "(json_extract(indexed_nodes.node_json, '$.as_of') IS NULL) ASC, json_extract(indexed_nodes.node_json, '$.as_of') ASC, indexed_nodes.node_id ASC"
order_by = (
"(json_extract(indexed_nodes.node_json, '$.as_of') IS NULL) ASC, "
"json_extract(indexed_nodes.node_json, '$.as_of') ASC, "
"indexed_nodes.node_id ASC"
)
elif order == "relevance" and match:
order_by = "score ASC, indexed_nodes.node_id ASC"
else:
order_by = "LOWER(json_extract(indexed_nodes.node_json, '$.title')) ASC, json_extract(indexed_nodes.node_json, '$.title') ASC, indexed_nodes.node_id ASC"
order_by = (
"LOWER(json_extract(indexed_nodes.node_json, '$.title')) ASC, "
"json_extract(indexed_nodes.node_json, '$.title') ASC, "
"indexed_nodes.node_id ASC"
)
try:
with closing(self._connect()) as connection:
@@ -794,7 +821,8 @@ class SQLiteSearchIndex:
if project_ids:
p_holders = ", ".join("?" for _ in project_ids)
p_row = connection.execute(
f"SELECT COUNT(*) FROM indexed_nodes WHERE project_id IN ({p_holders}){removal_filter}",
f"SELECT COUNT(*) FROM indexed_nodes "
f"WHERE project_id IN ({p_holders}){removal_filter}",
project_ids,
).fetchone()
project_node_count = int(p_row[0]) if p_row else 0
@@ -803,7 +831,8 @@ class SQLiteSearchIndex:
shared_node_count = 0
if include_shared:
s_row = connection.execute(
f"SELECT COUNT(*) FROM indexed_nodes WHERE project_id='shared'{removal_filter}"
f"SELECT COUNT(*) FROM indexed_nodes "
f"WHERE project_id='shared'{removal_filter}"
).fetchone()
shared_node_count = int(s_row[0]) if s_row else 0
@@ -825,7 +854,8 @@ class SQLiteSearchIndex:
if match:
query_stmt = (
"SELECT indexed_nodes.node_json, bm25(node_fts) AS score "
f"FROM node_fts JOIN indexed_nodes USING(node_id) {where_clause} AND node_fts MATCH ? "
f"FROM node_fts JOIN indexed_nodes USING(node_id) "
f"{where_clause} AND node_fts MATCH ? "
f"ORDER BY {order_by} LIMIT ? OFFSET ?"
)
node_rows = connection.execute(
@@ -852,7 +882,8 @@ class SQLiteSearchIndex:
for field in ("tags", "audience", "use_for", "domain"):
facet_stmt = (
"SELECT facet.value, COUNT(DISTINCT indexed_nodes.node_id) "
f"FROM indexed_nodes, json_each(indexed_nodes.node_json, '$.' || ?) AS facet "
f"FROM indexed_nodes, "
f"json_each(indexed_nodes.node_json, '$.' || ?) AS facet "
f"WHERE indexed_nodes.project_id IN ({scope_holders}){removal_filter} "
"GROUP BY facet.value"
)
@@ -884,7 +915,7 @@ class SQLiteSearchIndex:
limit: int = 20,
offset: int = 0,
) -> SourceNodesIndexResult | None:
revision = self._rebuild_if_needed()
self._rebuild_if_needed()
project_scope = tuple(sorted(set(project_ids) | ({"shared"} if include_shared else set())))
if not project_scope:
return None
@@ -904,7 +935,8 @@ class SQLiteSearchIndex:
# Check source revision grant
grant_row = connection.execute(
f"SELECT 1 FROM indexed_source_grants WHERE source_revision_id=? AND project_id IN ({scope_holders}) LIMIT 1",
f"SELECT 1 FROM indexed_source_grants WHERE source_revision_id=? "
f"AND project_id IN ({scope_holders}) LIMIT 1",
(source_revision_id, *project_scope),
).fetchone()
if grant_row is None:
@@ -969,16 +1001,21 @@ class SQLiteSearchIndex:
count_row = connection.execute(count_stmt, (*filter_params, match)).fetchone()
else:
count_stmt = (
f"{cte} SELECT COUNT(*) FROM combined JOIN indexed_nodes USING(node_id) {where_clause}"
f"{cte} SELECT COUNT(*) FROM combined "
f"JOIN indexed_nodes USING(node_id) {where_clause}"
)
count_row = connection.execute(count_stmt, filter_params).fetchone()
total = int(count_row[0]) if count_row else 0
# Rows query
order_by = "LOWER(json_extract(indexed_nodes.node_json, '$.title')) ASC, indexed_nodes.node_id ASC"
order_by = (
"LOWER(json_extract(indexed_nodes.node_json, '$.title')) ASC, "
"indexed_nodes.node_id ASC"
)
if match:
row_stmt = (
f"{cte} SELECT indexed_nodes.node_json, combined.relation_basis FROM combined "
f"{cte} SELECT indexed_nodes.node_json, "
f"combined.relation_basis FROM combined "
f"JOIN indexed_nodes USING(node_id) JOIN node_fts USING(node_id) "
f"{where_clause} AND node_fts MATCH ? ORDER BY {order_by} LIMIT ? OFFSET ?"
)
@@ -987,8 +1024,10 @@ class SQLiteSearchIndex:
).fetchall()
else:
row_stmt = (
f"{cte} SELECT indexed_nodes.node_json, combined.relation_basis FROM combined "
f"JOIN indexed_nodes USING(node_id) {where_clause} ORDER BY {order_by} LIMIT ? OFFSET ?"
f"{cte} SELECT indexed_nodes.node_json, "
f"combined.relation_basis FROM combined "
f"JOIN indexed_nodes USING(node_id) "
f"{where_clause} ORDER BY {order_by} LIMIT ? OFFSET ?"
)
rows = connection.execute(
row_stmt, (*filter_params, limit + 1, offset)
+49 -15
View File
@@ -21,18 +21,18 @@ from gewu.domain.contracts import (
EdgeEndpoint,
EvidenceReference,
Finding,
KnowledgeNode,
KnowledgeBrowseRequest,
KnowledgeBrowseEnvelope,
KnowledgeBrowseRequest,
KnowledgeGraphEnvelope,
KnowledgeGraphRequest,
SourceRelatedNode,
SourceRelatedNodesEnvelope,
KnowledgeNode,
NeighborRequest,
ReadEnvelope,
Scope,
SearchRequest,
SourceListRequest,
SourceRelatedNode,
SourceRelatedNodesEnvelope,
SourceRevisionRecord,
TypedEdge,
)
@@ -165,7 +165,9 @@ class SearchIndex(Protocol):
def count_sources(self, *, project_ids: tuple[str, ...], include_shared: bool) -> int: ...
def graph(self, *, project_ids: tuple[str, ...], include_shared: bool, max_nodes: int, max_edges: int) -> GraphIndexResult: ...
def graph(
self, *, project_ids: tuple[str, ...], include_shared: bool, max_nodes: int, max_edges: int
) -> GraphIndexResult: ...
def browse(
self,
@@ -616,8 +618,15 @@ class HybridSearchIndex:
def count_sources(self, *, project_ids: tuple[str, ...], include_shared: bool) -> int:
return self.lexical.count_sources(project_ids=project_ids, include_shared=include_shared)
def graph(self, *, project_ids: tuple[str, ...], include_shared: bool, max_nodes: int, max_edges: int) -> GraphIndexResult:
return self.lexical.graph(project_ids=project_ids, include_shared=include_shared, max_nodes=max_nodes, max_edges=max_edges)
def graph(
self, *, project_ids: tuple[str, ...], include_shared: bool, max_nodes: int, max_edges: int
) -> GraphIndexResult:
return self.lexical.graph(
project_ids=project_ids,
include_shared=include_shared,
max_nodes=max_nodes,
max_edges=max_edges,
)
def browse(self, **kwargs: Any) -> BrowseIndexResult:
return self.lexical.browse(**kwargs)
@@ -709,7 +718,9 @@ class CanonicalSearchService:
state = "current" if index_revision == canonical_revision else "lagging"
return canonical_revision, index_revision, state, schema_version
def browse(self, request: KnowledgeBrowseRequest, *, principal: Principal) -> KnowledgeBrowseEnvelope:
def browse(
self, request: KnowledgeBrowseRequest, *, principal: Principal
) -> KnowledgeBrowseEnvelope:
project_ids = self._resolve_scope(request.scope, principal)
fingerprint = hashlib.sha256(canonical_json({
"projects": project_ids,
@@ -723,7 +734,9 @@ class CanonicalSearchService:
"to": request.as_of_to.isoformat() if request.as_of_to else None,
"stale": request.stale_only, "order": request.order, "limit": request.limit,
})).hexdigest()
offset, expected_revision = self._cursor_offset(request.cursor, fingerprint, max_offset=100_000)
offset, expected_revision = self._cursor_offset(
request.cursor, fingerprint, max_offset=100_000
)
lexical = self.index.lexical if isinstance(self.index, HybridSearchIndex) else self.index
result = lexical.browse(
query=request.query,
@@ -761,7 +774,12 @@ class CanonicalSearchService:
canonical_revision = self.canonical_head()
warnings: tuple[Finding, ...] = ()
if result.index_revision != canonical_revision:
warnings = (Finding(code="INDEX_LAG", message="Browse data reflects an older accepted revision."),)
warnings = (
Finding(
code="INDEX_LAG",
message="Browse data reflects an older accepted revision.",
),
)
return KnowledgeBrowseEnvelope(
request_id="browse-" + secrets.token_hex(12),
resolved_scope=Scope(projects=project_ids, include_shared=request.scope.include_shared),
@@ -824,7 +842,12 @@ class CanonicalSearchService:
canonical_revision = self.canonical_head()
warnings: tuple[Finding, ...] = ()
if result.index_revision != canonical_revision:
warnings = (Finding(code="INDEX_LAG", message="Source nodes reflect an older accepted revision."),)
warnings = (
Finding(
code="INDEX_LAG",
message="Source nodes reflect an older accepted revision.",
),
)
return SourceRelatedNodesEnvelope(
request_id="srcnodes-" + secrets.token_hex(12),
resolved_scope=Scope(projects=(project_id,), include_shared=include_shared),
@@ -972,7 +995,9 @@ class CanonicalSearchService:
),
)
def graph(self, request: KnowledgeGraphRequest, *, principal: Principal) -> KnowledgeGraphEnvelope:
def graph(
self, request: KnowledgeGraphRequest, *, principal: Principal
) -> KnowledgeGraphEnvelope:
project_ids = self._resolve_scope(request.scope, principal)
if request.center_node_id is None:
result = self.index.graph(
@@ -995,7 +1020,8 @@ class CanonicalSearchService:
if request.max_nodes > 1:
neighbors = self.index.neighbors(
node_id=origin.node_id, edge_types=(), project_ids=project_ids,
include_shared=request.scope.include_shared, limit=min(50, request.max_nodes - 1),
include_shared=request.scope.include_shared,
limit=min(50, request.max_nodes - 1),
)
for hit in neighbors.edges:
endpoint = hit.neighbor_endpoint
@@ -1007,12 +1033,20 @@ class CanonicalSearchService:
index_revision=neighbors.index_revision if neighbors else scoped.index_revision,
nodes=tuple(nodes_by_id.values()),
edges=tuple(edges[:request.max_edges]),
truncated=(neighbors.has_more if neighbors else False) or len(edges) > request.max_edges,
truncated=(
(neighbors.has_more if neighbors else False)
or len(edges) > request.max_edges
),
)
canonical_revision = self.canonical_head()
warnings: tuple[Finding, ...] = ()
if result.index_revision != canonical_revision:
warnings = (Finding(code="INDEX_LAG", message="Graph data reflects an older accepted revision."),)
warnings = (
Finding(
code="INDEX_LAG",
message="Graph data reflects an older accepted revision.",
),
)
return KnowledgeGraphEnvelope(
request_id="graph-" + secrets.token_hex(12),
resolved_scope=Scope(projects=project_ids, include_shared=request.scope.include_shared),
+1 -1
View File
@@ -50,11 +50,11 @@ from gewu.domain.contracts import (
GitCommitId,
KnowledgeBrowseEnvelope,
KnowledgeGraphEnvelope,
SourceRelatedNodesEnvelope,
ProjectRecord,
ReadEnvelope,
RecordId,
ServiceDiagnostics,
SourceRelatedNodesEnvelope,
)
from gewu.transports.accounts_http import register_account_routes
from gewu.transports.admin_http import register_admin_routes
+14 -3
View File
@@ -490,7 +490,12 @@ class OnlineEvidence(StrictRecord):
def public_http_url(cls, value: str) -> str:
parsed = urlsplit(value)
hostname = parsed.hostname
if parsed.scheme not in {"http", "https"} or not hostname or parsed.username or parsed.password:
if (
parsed.scheme not in {"http", "https"}
or not hostname
or parsed.username
or parsed.password
):
raise ValueError("online evidence requires an HTTP(S) URL without embedded credentials")
if hostname.lower() == "localhost" or hostname.lower().endswith((".localhost", ".local")):
raise ValueError("web references must use a public hostname")
@@ -830,14 +835,20 @@ class KnowledgeBrowseRequest(StrictRecord):
as_of_from: date | None = None
as_of_to: date | None = None
stale_only: bool = False
order: Literal["title_asc", "title_desc", "date_newest", "date_oldest", "relevance"] = "title_asc"
order: Literal[
"title_asc", "title_desc", "date_newest", "date_oldest", "relevance"
] = "title_asc"
limit: int = Field(default=20, ge=1, le=50)
cursor: str | None = Field(default=None, max_length=512)
budget_tokens: int = Field(ge=256, le=50_000)
@model_validator(mode="after")
def validate_browse(self) -> KnowledgeBrowseRequest:
if self.as_of_from is not None and self.as_of_to is not None and self.as_of_from > self.as_of_to:
if (
self.as_of_from is not None
and self.as_of_to is not None
and self.as_of_from > self.as_of_to
):
raise ValueError("as_of_from must not be later than as_of_to")
if self.order == "relevance" and not self.query:
raise ValueError("relevance ordering requires a search query")
+60 -13
View File
@@ -211,7 +211,11 @@ def _validate_node_enrichment(
for field in ("node_id", "project_id", "node_type", "created", "lifecycle"):
if getattr(previous, field) != getattr(updated, field):
findings.append(
_finding("NODE_ENRICHMENT_IDENTITY_CHANGED", f"Enrichment cannot change {field}", path)
_finding(
"NODE_ENRICHMENT_IDENTITY_CHANGED",
f"Enrichment cannot change {field}",
path,
)
)
if updated.updated <= previous.updated:
findings.append(
@@ -230,7 +234,11 @@ def _validate_node_enrichment(
new_items = {claim.claim_id: claim for claim in getattr(updated, field)}
if not old_items.keys() <= new_items.keys():
findings.append(
_finding("NODE_ENRICHMENT_REMOVED_CLAIM", "Enrichment cannot remove accepted claims", f"{path}.{field}")
_finding(
"NODE_ENRICHMENT_REMOVED_CLAIM",
"Enrichment cannot remove accepted claims",
f"{path}.{field}",
)
)
continue
for claim_id, old_claim in old_items.items():
@@ -241,27 +249,58 @@ def _validate_node_enrichment(
or old_claim.evidence_type != new_claim.evidence_type
):
findings.append(
_finding("NODE_ENRICHMENT_CHANGED_CLAIM", "Enrichment cannot alter an accepted assertion", claim_path)
_finding(
"NODE_ENRICHMENT_CHANGED_CLAIM",
"Enrichment cannot alter an accepted assertion",
claim_path,
)
)
continue
if not set(old_claim.online_evidence) <= set(new_claim.online_evidence):
findings.append(
_finding("NODE_ENRICHMENT_REMOVED_ONLINE_EVIDENCE", "Enrichment cannot remove prior online evidence", claim_path)
_finding(
"NODE_ENRICHMENT_REMOVED_ONLINE_EVIDENCE",
"Enrichment cannot remove prior online evidence",
claim_path,
)
)
old_evidence = set(old_claim.evidence)
if not old_evidence <= set(new_claim.evidence):
findings.append(
_finding("NODE_ENRICHMENT_REMOVED_EVIDENCE", "Enrichment cannot remove accepted evidence", claim_path)
_finding(
"NODE_ENRICHMENT_REMOVED_EVIDENCE",
"Enrichment cannot remove accepted evidence",
claim_path,
)
)
if new_claim.evidence != old_claim.evidence and new_claim.assessment.assessor_id != proposer_id:
if (
new_claim.evidence != old_claim.evidence
and new_claim.assessment.assessor_id != proposer_id
):
findings.append(
_finding("ASSESSOR_ATTRIBUTION_MISMATCH", "New evidence requires an assessment by its proposer", claim_path)
_finding(
"ASSESSOR_ATTRIBUTION_MISMATCH",
"New evidence requires an assessment by its proposer",
claim_path,
)
)
if not set(previous.tags) <= set(updated.tags):
findings.append(_finding("NODE_ENRICHMENT_REMOVED_FACET", "Enrichment cannot remove existing tags", f"{path}.tags"))
findings.append(
_finding(
"NODE_ENRICHMENT_REMOVED_FACET",
"Enrichment cannot remove existing tags",
f"{path}.tags",
)
)
for field in ("audience", "domain", "use_for"):
if not set(getattr(previous, field)) <= set(getattr(updated, field)):
findings.append(_finding("NODE_ENRICHMENT_REMOVED_FACET", "Enrichment cannot remove existing facets", f"{path}.{field}"))
findings.append(
_finding(
"NODE_ENRICHMENT_REMOVED_FACET",
"Enrichment cannot remove existing facets",
f"{path}.{field}",
)
)
return tuple(findings)
@@ -378,11 +417,17 @@ def validate_proposal(
elif operation.operation == "update_node":
if previous_node is None:
findings.append(
_finding("NODE_NOT_FOUND", "Update target is not an accepted node", f"{path}.target_id")
_finding(
"NODE_NOT_FOUND",
"Update target is not an accepted node",
f"{path}.target_id",
)
)
else:
findings.extend(
_validate_node_enrichment(previous_node, node, proposal.proposer_id, f"{path}.node")
_validate_node_enrichment(
previous_node, node, proposal.proposer_id, f"{path}.node"
)
)
if node.node_type.value == "entity":
possible_duplicates = [
@@ -444,7 +489,10 @@ def validate_proposal(
and old_claim.evidence == claim.evidence
and old_claim.assessment == claim.assessment
)
if claim.assessment.assessor_id != proposal.proposer_id and not unchanged_prior_assessment:
if (
claim.assessment.assessor_id != proposal.proposer_id
and not unchanged_prior_assessment
):
findings.append(
_finding(
"ASSESSOR_ATTRIBUTION_MISMATCH",
@@ -873,7 +921,6 @@ def validate_proposal(
"EDGE_SOURCE_MISSING",
"EDGE_PROJECT_MISSING",
"EDGE_TYPE_MISMATCH",
"NODE_NOT_FOUND",
"POLICY_WEAKENING",
"OUT_OF_SCOPE",
}
+1 -1
View File
@@ -10,9 +10,9 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_valida
from gewu.domain.contracts import (
AutoAcceptPolicy,
Confidence,
NeighborRequest,
KnowledgeBrowseRequest,
KnowledgeGraphRequest,
NeighborRequest,
NodeType,
ProjectRecord,
RecordId,
+4 -1
View File
@@ -528,7 +528,10 @@ def create_mcp_server(
stale_only: bool = False,
include_evidence: bool = True,
) -> str:
"""Search accepted knowledge in scope. Set include_evidence=false for compact claim summaries; the default includes full citations."""
(
"Search accepted knowledge in scope. Set include_evidence=false "
"for compact claim summaries; the default includes full citations."
)
principal = _principal(ctx, identity_provider)
try: