SPB Git forge

spb/ai-atlas

Public
41commits 1branches 0releases
4.6 MBsize
maindefault branch
12 days agolast push
HTML 77.2% TypeScript 10.5% Python 9.6% JavaScript 2.5%
5.1 KB · 79 lines python
Raw Blame History
1"""/graph/explore — neighbourhood exploration filtered by predicate sets per mode (API 1.1). `/entities/{slug}/graph` (v1) is unchanged."""2from __future__ import annotations34from typing import Any56from fastapi import APIRouter, Query, Request78from aiatlas.api.common import ApiError, cached, resolve_entity9from aiatlas.db import connection, fetch_all1011router = APIRouter(prefix="/api/v1/graph", tags=["graph"])1213MODES: dict[str, tuple[str, ...]] = {14    "lineage": ("artifact_of", "quantized_from", "fine_tuned_from", "distilled_from", "merged_from", "derived_from", "superseded_by", "member_of_family", "develops"),15    "research": ("authored", "described_by", "published_by", "uses_dataset", "evaluates_on", "evaluated_on"),16    "company": ("develops", "owns", "operates", "manufactures", "member_of_family"),17    "benchmark": ("evaluated_on", "evaluates_on", "variant_of"),18    "dataset": ("uses_dataset", "derived_from", "subset_of"),19    "provider": ("available_through",),20    "hardware": ("runs_on", "uses", "manufactures"),21}22KEY_ATTRS = ("parameter_count", "context_length", "release_date", "openness", "published_at", "category", "memory_gb", "kind")232425@router.get("/explore")26@cached(300)27async def explore(request: Request, node: str = Query(...), mode: str = Query("lineage"), depth: int = Query(1, ge=1, le=2), limit: int = Query(150, ge=2, le=150)) -> dict[str, Any]:28    if mode not in MODES:29        raise ApiError(400, f"mode must be one of {', '.join(MODES)}")30    preds = list(MODES[mode])31    async with connection() as conn:32        root_row = await resolve_entity(conn, node)33        root = root_row["id"]34        seen: dict[str, int] = {root: 0}35        frontier = [root]36        edges: dict[tuple[str, str, str], dict[str, Any]] = {}37        truncated = False38        for level in range(1, depth + 1):39            if not frontier:40                break41            rows = await fetch_all(conn, """select r.subject_id, r.predicate, r.object_id, r.attributes, r.tier from relations r42                                            where r.valid_to is null and r.predicate = any(cast(:preds as text[]))43                                              and (r.subject_id = any(cast(:ids as text[])) or r.object_id = any(cast(:ids as text[])))44                                            order by r.tier, r.observed_at desc limit :lim""", preds=preds, ids=frontier, lim=limit * 4)45            nxt: list[str] = []46            for r in rows:47                other = r["object_id"] if r["subject_id"] in seen else r["subject_id"]48                if other not in seen:49                    if len(seen) >= limit:50                        truncated = True51                        continue52                    seen[other] = level53                    nxt.append(other)54                edges[(r["subject_id"], r["object_id"], r["predicate"])] = {"source": r["subject_id"], "target": r["object_id"], "predicate": r["predicate"], "attributes": r["attributes"] or {}, "tier": r["tier"]}55            frontier = nxt56        # organisation edges for the company mode come from entities.organization_id as well57        if mode == "company":58            org_rows = await fetch_all(conn, "select id, organization_id from entities where organization_id is not null and merged_into is null and (id = any(cast(:ids as text[])) or organization_id = any(cast(:ids as text[]))) limit :lim",59                                       ids=list(seen), lim=limit * 4)60            for r in org_rows:61                for x in (r["id"], r["organization_id"]):62                    if x not in seen:63                        if len(seen) >= limit:64                            truncated = True65                            break66                        seen[x] = depth67                edges.setdefault((r["organization_id"], r["id"], "develops"), {"source": r["organization_id"], "target": r["id"], "predicate": "develops", "attributes": {"from": "organization_id"}, "tier": None})68        nodes = await fetch_all(conn, """select e.id, e.slug, e.canonical_name, e.entity_type, e.attributes, e.merged_into, e.artifact_kind, o.canonical_name as org_name, o.slug as org_slug69                                         from entities e left join entities o on o.id = e.organization_id where e.id = any(cast(:ids as text[]))""", ids=list(seen))70    node_ids = {n["id"] for n in nodes}71    out_edges = [e for e in edges.values() if e["source"] in node_ids and e["target"] in node_ids]72    counts: dict[str, int] = {}73    for n in nodes:74        counts[n["entity_type"]] = counts.get(n["entity_type"], 0) + 175    return {"root": root, "mode": mode, "depth": depth, "predicates": preds,76            "nodes": [{"id": n["id"], "slug": n["slug"], "name": n["canonical_name"], "entity_type": n["entity_type"], "org": n["org_name"], "org_slug": n["org_slug"], "level": seen.get(n["id"], 0),77                       "artifact_kind": n["artifact_kind"], "attributes": {k: (n["attributes"] or {}).get(k) for k in KEY_ATTRS if (n["attributes"] or {}).get(k) not in (None, "", [])}} for n in nodes],78            "edges": out_edges, "truncated": truncated, "counts": {"nodes": len(nodes), "edges": len(out_edges), "by_type": counts}}79