项目文件夹

文件
wehub-resource-sync c889a57b6b
Test Suites / Build CI Environment (push) Has been cancelled
Test Suites / Basic Tests (push) Has been cancelled
Test Suites / End-to-End Tests (push) Has been cancelled
Test Suites / CLI Tests (push) Has been cancelled
Test Suites / Slow End-to-End Tests (push) Has been cancelled
Test Suites / Graph Database Tests (push) Has been cancelled
Test Suites / Vector DB Tests (push) Has been cancelled
Test Suites / Temporal Graph Test (push) Has been cancelled
Test Suites / Search Test on Different DBs (push) Has been cancelled
Test Suites / Example Tests (push) Has been cancelled
Test Suites / Notebook Tests (push) Has been cancelled
Test Suites / OS and Python Tests Ubuntu (push) Has been cancelled
Test Suites / OS and Python Tests Extended (push) Has been cancelled
Test Suites / LLM Test Suite (push) Has been cancelled
Test Suites / S3 File Storage Test (push) Has been cancelled
Test Suites / Run Integration Tests (push) Has been cancelled
Test Suites / MCP Tests (push) Has been cancelled
Test Suites / Docker Compose Test (push) Has been cancelled
Test Suites / Docker CI test (push) Has been cancelled
Test Suites / Relational DB Migration Tests (push) Has been cancelled
Test Suites / Distributed Cognee Test (push) Has been cancelled
Test Suites / DB Examples Tests (push) Has been cancelled
Test Suites / Test Completion Status (push) Has been cancelled
Test Suites / Claude Code Review (push) Has been cancelled
Test Suites / basic checks (push) Has been cancelled
build | Build and Push Cognee MCP Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
build | Build and Push Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.11) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.12) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (kuzu, kuzu) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (neo4j, neo4j) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Examples (push) Has been cancelled
Weighted Edges Tests / Code Quality for Weighted Edges (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:02:24 +08:00

199 行
6.5 KiB
Python

"""Centroid-slot helpers for truth-subspace reranking.
The core invariant is simple: slot ``i`` always means the centroid stored in
slot ``i`` for the current truth epoch. Everything here is deterministic so a
rebuild from the same learning statements produces the same slots.
"""
from __future__ import annotations
import math
from datetime import datetime, timezone
from typing import Sequence
from uuid import NAMESPACE_OID, UUID, uuid5
from .align import cosine
from .constants import DEFAULT_K, TRUTH_CENTROID_COLLECTION
from .models import TruthCentroidPayload
def centroid_id(dataset_id: str, slot: int) -> UUID:
return uuid5(NAMESPACE_OID, f"TruthCentroid:{dataset_id}:{slot}")
def learning_id(statement: str) -> str:
normalized = _normalize_statement(statement)
return str(uuid5(NAMESPACE_OID, f"TruthLearning:{normalized}"))
def normalize(vector: Sequence[float]) -> list[float]:
values = [float(value) for value in vector]
norm = math.sqrt(sum(value * value for value in values))
if norm == 0.0:
return [0.0 for _ in values]
return [value / norm for value in values]
def pad_coords(coords: Sequence[float], k: int = DEFAULT_K) -> list[float]:
values = [float(value) for value in list(coords)[:k]]
return values + [0.0 for _ in range(max(0, k - len(values)))]
def weighted_centroid(old: Sequence[float], count: int, new: Sequence[float]) -> list[float]:
old_values = list(old)
new_values = list(new)
if not old_values:
return normalize(new_values)
safe_count = max(0, int(count))
merged = [
(safe_count * old_value + new_value) / (safe_count + 1)
for old_value, new_value in zip(old_values, new_values)
]
return normalize(merged)
def unique_learning_vectors(
statements: Sequence[str],
vectors: Sequence[Sequence[float]],
) -> list[tuple[str, list[float]]]:
unique: dict[str, list[float]] = {}
for statement, vector in zip(statements, vectors):
if not str(statement).strip():
continue
unique.setdefault(learning_id(statement), list(vector))
return sorted(unique.items(), key=lambda item: item[0])
def build_centroids_from_learning_vectors(
dataset_id: str,
learning_vectors: Sequence[tuple[str, Sequence[float]]],
truth_epoch: int,
updated_at: int | None = None,
k: int = DEFAULT_K,
) -> list[TruthCentroidPayload]:
return extend_centroids_with_learning_vectors(
dataset_id,
[],
learning_vectors,
truth_epoch=truth_epoch,
updated_at=updated_at,
k=k,
)
def extend_centroids_with_learning_vectors(
dataset_id: str,
existing_centroids: Sequence[TruthCentroidPayload],
learning_vectors: Sequence[tuple[str, Sequence[float]]],
truth_epoch: int,
updated_at: int | None = None,
k: int = DEFAULT_K,
) -> list[TruthCentroidPayload]:
if updated_at is None:
updated_at = int(datetime.now(timezone.utc).timestamp() * 1000)
slots = [
{
"centroid": list(centroid.centroid),
"count": int(centroid.count),
"learning_ids": list(centroid.learning_ids),
}
for centroid in sorted(existing_centroids, key=lambda centroid: centroid.slot)[:k]
]
seen_learning_ids = {learning_id for slot in slots for learning_id in slot["learning_ids"]}
for new_learning_id, vector in learning_vectors:
if new_learning_id in seen_learning_ids:
continue
normalized_vector = normalize(vector)
if len(slots) < k:
slots.append(
{"centroid": normalized_vector, "count": 1, "learning_ids": [new_learning_id]}
)
seen_learning_ids.add(new_learning_id)
continue
nearest_slot = max(
range(len(slots)),
key=lambda index: cosine(normalized_vector, slots[index]["centroid"]),
)
slot = slots[nearest_slot]
slot["centroid"] = weighted_centroid(slot["centroid"], slot["count"], normalized_vector)
slot["count"] += 1
slot["learning_ids"].append(new_learning_id)
seen_learning_ids.add(new_learning_id)
return [
TruthCentroidPayload(
dataset_id=str(dataset_id),
slot=slot_index,
count=int(slot["count"]),
truth_epoch=int(truth_epoch),
updated_at=updated_at,
centroid=list(slot["centroid"]),
learning_ids=list(slot["learning_ids"]),
)
for slot_index, slot in enumerate(slots)
]
def centroids_changed(
old: Sequence[TruthCentroidPayload],
new: Sequence[TruthCentroidPayload],
tolerance: float = 1e-6,
) -> bool:
if len(old) != len(new):
return True
old_by_slot = {centroid.slot: centroid for centroid in old}
for new_centroid in new:
old_centroid = old_by_slot.get(new_centroid.slot)
if old_centroid is None:
return True
if old_centroid.count != new_centroid.count:
return True
if len(old_centroid.centroid) != len(new_centroid.centroid):
return True
if old_centroid.learning_ids != new_centroid.learning_ids:
return True
for old_value, new_value in zip(old_centroid.centroid, new_centroid.centroid):
if abs(float(old_value) - float(new_value)) > tolerance:
return True
return False
async def load_centroids(vector_engine, dataset_id: str, k: int = DEFAULT_K):
centroid_ids = [str(centroid_id(str(dataset_id), slot)) for slot in range(k)]
rows = await vector_engine.retrieve(TRUTH_CENTROID_COLLECTION, centroid_ids)
centroids = []
for row in rows:
payload = getattr(row, "payload", None)
if not isinstance(payload, dict):
continue
centroid = TruthCentroidPayload.model_validate(payload)
if centroid.dataset_id == str(dataset_id):
centroids.append(centroid)
return sorted(centroids, key=lambda centroid: centroid.slot)
async def upsert_centroids(vector_engine, centroids: Sequence[TruthCentroidPayload]) -> None:
if not centroids:
return
points = [
{
"id": centroid_id(centroid.dataset_id, centroid.slot),
"vector": centroid.centroid,
"payload": centroid.model_dump(),
}
for centroid in centroids
]
await vector_engine.upsert_raw_vectors(
TRUTH_CENTROID_COLLECTION,
points,
payload_schema=TruthCentroidPayload,
)
def _normalize_statement(statement: str) -> str:
return " ".join(str(statement).casefold().split())