Compare commits
3 commits
84f6d3682c
...
cfcb06a6dc
| Author | SHA1 | Date | |
|---|---|---|---|
| cfcb06a6dc | |||
| 2f6424617b | |||
| 9a943e8460 |
4 changed files with 179 additions and 2 deletions
|
|
@ -143,7 +143,7 @@ async def handle_activity(request):
|
|||
# Each PR generates events at created_at and merged_at timestamps
|
||||
pr_query = """
|
||||
SELECT number, status, domain, agent, branch, source_path,
|
||||
created_at, merged_at
|
||||
created_at, merged_at, source_channel
|
||||
FROM prs
|
||||
WHERE {where_clause}
|
||||
ORDER BY COALESCE(merged_at, created_at) DESC
|
||||
|
|
@ -189,6 +189,7 @@ async def handle_activity(request):
|
|||
'description': description,
|
||||
'status': row_dict['status'],
|
||||
'pr_number': row_dict['number'],
|
||||
'source_channel': row_dict.get('source_channel') or 'unknown',
|
||||
})
|
||||
|
||||
# Source 2: Audit log events (secondary — pipeline-level)
|
||||
|
|
@ -228,6 +229,7 @@ async def handle_activity(request):
|
|||
'description': description,
|
||||
'status': None,
|
||||
'pr_number': None,
|
||||
'source_channel': None, # audit events not tied to a PR
|
||||
})
|
||||
|
||||
conn.close()
|
||||
|
|
|
|||
|
|
@ -83,7 +83,8 @@ def _build_events():
|
|||
try:
|
||||
rows = conn.execute("""
|
||||
SELECT p.number, p.branch, p.domain, p.agent, p.submitted_by,
|
||||
p.merged_at, p.description, p.commit_type, p.cost_usd
|
||||
p.merged_at, p.description, p.commit_type, p.cost_usd,
|
||||
p.source_channel
|
||||
FROM prs p
|
||||
WHERE p.status = 'merged'
|
||||
AND p.commit_type = 'knowledge'
|
||||
|
|
@ -139,6 +140,7 @@ def _build_events():
|
|||
"ci_earned": round(ci_earned, 2),
|
||||
"summary": summary_text,
|
||||
"pr_number": row["number"],
|
||||
"source_channel": row["source_channel"] or "unknown",
|
||||
})
|
||||
|
||||
return events, claim_activity
|
||||
|
|
|
|||
|
|
@ -2283,6 +2283,18 @@ def create_app() -> web.Application:
|
|||
# Response audit - cost tracking + reasoning traces
|
||||
app["db_path"] = str(DB_PATH)
|
||||
register_response_audit_routes(app)
|
||||
# Timeline activity feed (per-PR + audit_log events for dashboard v2)
|
||||
from activity_endpoint import handle_activity
|
||||
app.router.add_get("/api/activity", handle_activity)
|
||||
# Gamification activity feed (hot/recent/important sort)
|
||||
from activity_feed_api import register as register_activity_feed
|
||||
register_activity_feed(app)
|
||||
# Claims browser + detail
|
||||
from claims_api import register_claims_routes
|
||||
register_claims_routes(app)
|
||||
# Contributor profile (handle lookup, leaderboard with action CI)
|
||||
from contributor_profile_api import register_contributor_routes
|
||||
register_contributor_routes(app)
|
||||
app.on_cleanup.append(_cleanup)
|
||||
return app
|
||||
|
||||
|
|
|
|||
161
diagnostics/claims_api.py
Normal file
161
diagnostics/claims_api.py
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
"""Claims API endpoint — serves claim data from the codex filesystem."""
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
from aiohttp import web
|
||||
|
||||
CODEX_ROOT = Path("/opt/teleo-eval/workspaces/main/domains")
|
||||
_cache = {"data": None, "ts": 0}
|
||||
CACHE_TTL = 300 # 5 minutes
|
||||
|
||||
def _parse_frontmatter(filepath):
|
||||
try:
|
||||
text = filepath.read_text(encoding="utf-8")
|
||||
if not text.startswith("---"):
|
||||
return None
|
||||
end = text.index("---", 3)
|
||||
fm = yaml.safe_load(text[3:end])
|
||||
if not fm or fm.get("type") != "claim":
|
||||
return None
|
||||
body = text[end+3:].strip()
|
||||
# Count wiki-links
|
||||
links = re.findall(r"\[\[([^\]]+)\]\]", body)
|
||||
# Extract first paragraph as summary
|
||||
paragraphs = [p.strip() for p in body.split("\n\n") if p.strip() and not p.strip().startswith("#")]
|
||||
summary = paragraphs[0][:300] if paragraphs else ""
|
||||
return {
|
||||
"slug": filepath.stem,
|
||||
"title": fm.get("title", filepath.stem.replace("-", " ")),
|
||||
"domain": fm.get("domain", "unknown"),
|
||||
"confidence": fm.get("confidence", "unknown"),
|
||||
"agent": fm.get("agent"),
|
||||
"scope": fm.get("scope"),
|
||||
"created": str(fm.get("created", "")),
|
||||
"source": fm.get("source", "") if isinstance(fm.get("source"), str) else "",
|
||||
"sourcer": fm.get("sourcer", ""),
|
||||
"wiki_link_count": len(links),
|
||||
"summary": summary,
|
||||
"challenged_by": fm.get("challenged_by"),
|
||||
"related_claims": fm.get("related_claims", []),
|
||||
}
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _load_all_claims():
|
||||
now = time.time()
|
||||
if _cache["data"] and now - _cache["ts"] < CACHE_TTL:
|
||||
return _cache["data"]
|
||||
|
||||
claims = []
|
||||
for domain_dir in sorted(CODEX_ROOT.iterdir()):
|
||||
if not domain_dir.is_dir():
|
||||
continue
|
||||
for f in sorted(domain_dir.glob("*.md")):
|
||||
if f.name == "_map.md":
|
||||
continue
|
||||
c = _parse_frontmatter(f)
|
||||
if c:
|
||||
claims.append(c)
|
||||
|
||||
_cache["data"] = claims
|
||||
_cache["ts"] = now
|
||||
return claims
|
||||
|
||||
|
||||
async def handle_claims(request):
|
||||
claims = _load_all_claims()
|
||||
|
||||
# Filters
|
||||
domain = request.query.get("domain")
|
||||
search = request.query.get("q", "").lower()
|
||||
confidence = request.query.get("confidence")
|
||||
agent = request.query.get("agent")
|
||||
sort = request.query.get("sort", "recent") # recent, alpha, domain
|
||||
|
||||
filtered = claims
|
||||
if domain:
|
||||
filtered = [c for c in filtered if c["domain"] == domain]
|
||||
if confidence:
|
||||
filtered = [c for c in filtered if c["confidence"] == confidence]
|
||||
if agent:
|
||||
filtered = [c for c in filtered if c["agent"] == agent]
|
||||
if search:
|
||||
filtered = [c for c in filtered if search in c["title"].lower() or search in c["summary"].lower()]
|
||||
|
||||
# Sort
|
||||
if sort == "recent":
|
||||
filtered.sort(key=lambda c: c["created"], reverse=True)
|
||||
elif sort == "alpha":
|
||||
filtered.sort(key=lambda c: c["title"].lower())
|
||||
elif sort == "domain":
|
||||
filtered.sort(key=lambda c: (c["domain"], c["title"].lower()))
|
||||
|
||||
# Pagination
|
||||
limit = min(int(request.query.get("limit", "50")), 200)
|
||||
offset = int(request.query.get("offset", "0"))
|
||||
page = filtered[offset:offset+limit]
|
||||
|
||||
# Domain counts for sidebar
|
||||
domain_counts = {}
|
||||
for c in claims:
|
||||
domain_counts[c["domain"]] = domain_counts.get(c["domain"], 0) + 1
|
||||
|
||||
return web.json_response({
|
||||
"claims": page,
|
||||
"total": len(filtered),
|
||||
"offset": offset,
|
||||
"limit": limit,
|
||||
"domains": dict(sorted(domain_counts.items(), key=lambda x: -x[1])),
|
||||
"confidence_levels": sorted(set(c["confidence"] for c in claims)),
|
||||
"agents": sorted(set(c["agent"] for c in claims if c["agent"])),
|
||||
}, headers={"Access-Control-Allow-Origin": "*"})
|
||||
|
||||
|
||||
async def handle_claim_detail(request):
|
||||
slug = request.match_info["slug"]
|
||||
claims = _load_all_claims()
|
||||
for c in claims:
|
||||
if c["slug"] == slug:
|
||||
# Read full body for detail view
|
||||
for domain_dir in CODEX_ROOT.iterdir():
|
||||
if not domain_dir.is_dir():
|
||||
continue
|
||||
f = domain_dir / f"{slug}.md"
|
||||
if f.exists():
|
||||
text = f.read_text(encoding="utf-8")
|
||||
end = text.index("---", 3)
|
||||
body = text[end+3:].strip()
|
||||
c["body"] = body
|
||||
break
|
||||
return web.json_response(c, headers={"Access-Control-Allow-Origin": "*"})
|
||||
return web.json_response({"error": "claim not found"}, status=404)
|
||||
|
||||
|
||||
async def handle_domains(request):
|
||||
claims = _load_all_claims()
|
||||
domains = {}
|
||||
for c in claims:
|
||||
d = c["domain"]
|
||||
if d not in domains:
|
||||
domains[d] = {"name": d, "count": 0, "agents": set(), "confidence_dist": {}}
|
||||
domains[d]["count"] += 1
|
||||
if c["agent"]:
|
||||
domains[d]["agents"].add(c["agent"])
|
||||
conf = c["confidence"]
|
||||
domains[d]["confidence_dist"][conf] = domains[d]["confidence_dist"].get(conf, 0) + 1
|
||||
|
||||
result = []
|
||||
for d in sorted(domains.values(), key=lambda x: -x["count"]):
|
||||
d["agents"] = sorted(d["agents"])
|
||||
result.append(d)
|
||||
|
||||
return web.json_response(result, headers={"Access-Control-Allow-Origin": "*"})
|
||||
|
||||
|
||||
def register_claims_routes(app):
|
||||
app.router.add_get("/api/claims", handle_claims)
|
||||
app.router.add_get("/api/claims/{slug}", handle_claim_detail)
|
||||
app.router.add_get("/api/domains", handle_domains)
|
||||
Loading…
Reference in a new issue