You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

130 lines
3.7 KiB

"""
routers/meta.py — /health, /stats, /tags, /graph endpoints.
"""
from __future__ import annotations
from fastapi import APIRouter
import httpx
from core.database import get_pool
from core.settings import Settings
from models.responses import HealthResponse, StatsResponse, TagCount, GraphResponse, GraphNode, GraphEdge
router = APIRouter(tags=['meta'])
def _get_settings() -> Settings:
from main import app_settings
return app_settings
@router.get('/health', response_model=HealthResponse)
async def health():
settings = _get_settings()
db_status = 'ok'
ollama_status = 'ok'
try:
pool = await get_pool()
async with pool.acquire() as conn:
await conn.fetchval('SELECT 1')
except Exception:
db_status = 'error'
try:
async with httpx.AsyncClient(timeout=5.0) as client:
resp = await client.get(f'{settings.ollama_url}/api/tags')
if resp.status_code != 200:
ollama_status = 'error'
except Exception:
ollama_status = 'unavailable'
overall = 'ok' if db_status == 'ok' else 'degraded'
return HealthResponse(
status=overall,
database=db_status,
ollama=ollama_status,
version=settings.app_version,
)
@router.get('/stats', response_model=StatsResponse)
async def stats():
settings = _get_settings()
pool = await get_pool()
async with pool.acquire() as conn:
docs = await conn.fetchval('SELECT COUNT(*) FROM documents')
chunks = await conn.fetchval('SELECT COUNT(*) FROM chunks')
relations = await conn.fetchval('SELECT COUNT(*) FROM relations')
tags_count = await conn.fetchval(
"SELECT COUNT(DISTINCT tag) FROM documents, unnest(tags) AS tag"
)
last_indexed = await conn.fetchval(
'SELECT MAX(indexed_at) FROM documents'
)
return StatsResponse(
total_documents=docs or 0,
total_chunks=chunks or 0,
total_relations=relations or 0,
total_tags=tags_count or 0,
last_indexed=last_indexed,
embedding_model=settings.embedding_model,
chat_model=settings.chat_model,
)
@router.get('/tags', response_model=list[TagCount])
async def list_tags():
pool = await get_pool()
async with pool.acquire() as conn:
rows = await conn.fetch(
"""
SELECT tag, COUNT(*) AS count
FROM documents, unnest(tags) AS tag
GROUP BY tag
ORDER BY count DESC, tag
"""
)
return [TagCount(tag=row['tag'], count=row['count']) for row in rows]
@router.get('/graph', response_model=GraphResponse)
async def knowledge_graph(limit: int = 200):
pool = await get_pool()
async with pool.acquire() as conn:
doc_rows = await conn.fetch(
'SELECT id, title, path, tags, word_count FROM documents LIMIT $1',
limit,
)
rel_rows = await conn.fetch(
"""
SELECT r.source_doc_id::text, r.target_doc_id::text, r.relation_type, r.label
FROM relations r
WHERE r.target_doc_id IS NOT NULL
LIMIT $1
""",
limit * 3,
)
nodes = [
GraphNode(
id=str(row['id']),
title=row['title'] or '',
path=row['path'],
tags=list(row['tags'] or []),
word_count=row['word_count'],
)
for row in doc_rows
]
edges = [
GraphEdge(
source=row['source_doc_id'],
target=row['target_doc_id'],
relation_type=row['relation_type'],
label=row['label'],
)
for row in rel_rows
]
return GraphResponse(nodes=nodes, edges=edges)

Powered by TurnKey Linux.