Update README with webhook status

This commit is contained in:
Ján Pták 2026-06-04 19:15:20 +02:00
parent 44eb3cd679
commit 64b828c46e
3 changed files with 115 additions and 139 deletions

178
README.md
View File

@ -2,7 +2,7 @@
Agent pre manažment záverečných prác nad repozitárom `zpwiki`.
Projekt rieši základnú službu pre indexovanie a vyhľadávanie v Markdown súboroch zo školského repozitára záverečných prác. Cieľom je vytvoriť samostatné API, ktoré vie načítať obsah `zpwiki`, spracovať metadata, rozdeliť dokumenty na chunky, vyhľadávať v nich a neskôr sa napojiť na OpenWebUI, RAG, znalostný graf a Gitea webhook.
Projekt zatiaľ rieši základnú časť systému pre vyhľadávanie v Markdown súboroch zo školského repozitára záverečných prác. Cieľom je vytvoriť samostatnú službu, ktorá vie indexovať obsah `zpwiki`, vyhľadávať v ňom a neskôr sa napojí na OpenWebUI, RAG, znalostný graf a webhook synchronizáciu.
## Aktuálny stav
@ -17,16 +17,8 @@ Zatiaľ je implementované:
7. rozlíšenie režimu vyhľadávania:
1. `person` pre mená osôb, napríklad `jan ptak`,
2. `topic` pre tematické dopyty, napríklad `rag agent` alebo `knowledge graph`,
8. FastAPI backend s endpointmi:
1. `/health`,
2. `/search`,
3. `/sync`,
9. automatická Swagger dokumentácia API,
10. Dockerfile pre zostavenie API kontajnera,
11. `docker-compose.yml` pre spustenie služby,
12. mount repozitára `zpwiki` do kontajnera,
13. environment premenná `ZPWIKI_ROOT`,
14. reindexovanie dát cez endpoint `/sync`.
8. FastAPI backend s endpointmi `/health` a `/search`,
9. automatická Swagger dokumentácia API.
## Štruktúra projektu
@ -39,14 +31,10 @@ dp-zp-agent/
│ ├── scan_zpwiki.py
│ ├── build_chunks.py
│ ├── build_sqlite_index.py
│ ├── rebuild_index.py
│ ├── search_chunks.py
│ └── search_db.py
├── data/
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
├── .dockerignore
├── .gitignore
└── README.md
```
@ -91,18 +79,6 @@ Nakoniec sa vytvorí SQLite index:
python scripts/build_sqlite_index.py
```
Alebo sa dá celý index obnoviť jedným príkazom:
```bash
python scripts/rebuild_index.py
```
S voliteľným `git pull` pred reindexovaním:
```bash
python scripts/rebuild_index.py --pull
```
## Testovanie vyhľadávania v termináli
Vyhľadávanie podľa osoby:
@ -123,7 +99,7 @@ Vyhľadávanie podľa znalostného grafu:
python scripts/search_db.py "knowledge graph"
```
## Spustenie API lokálne
## Spustenie API
FastAPI server sa spustí príkazom:
@ -145,24 +121,6 @@ curl -X POST http://127.0.0.1:8000/search \
-d '{"query":"jan ptak","limit":5}'
```
Reindexovanie cez API:
```bash
curl -X POST http://127.0.0.1:8000/sync \
-H "Content-Type: application/json" \
-d '{"pull_git":false}'
```
Reindexovanie aj s `git pull`:
```bash
curl -X POST http://127.0.0.1:8000/sync \
-H "Content-Type: application/json" \
-d '{"pull_git":true}'
```
Poznámka: pri Docker verzii môže `pull_git:true` vyžadovať vyriešenie SSH prístupu ku Gitu. Zatiaľ je bezpečné používať hlavne `pull_git:false`.
## Swagger UI
FastAPI automaticky generuje Swagger dokumentáciu API.
@ -173,68 +131,26 @@ Po spustení servera je dostupná na adrese:
http://127.0.0.1:8000/docs
```
V Swagger UI je možné testovať endpointy `/health`, `/search` a `/sync` priamo z prehliadača.
V Swagger UI je možné testovať endpointy `/health` a `/search` priamo z prehliadača.
## Spustenie cez Docker
## Čo ešte treba dorobiť
Služba sa dá spustiť cez Docker Compose:
### 1. Dockerizácia aplikácie
Treba vytvoriť:
1. `Dockerfile`,
2. `docker-compose.yml`,
3. jednoduchý návod na spustenie cez Docker,
4. volume alebo mount pre dáta a SQLite databázu.
Cieľ je, aby sa služba dala spustiť jedným príkazom:
```bash
docker compose up --build
```
Po spustení je API dostupné na:
```text
http://127.0.0.1:8000
```
Swagger UI:
```text
http://127.0.0.1:8000/docs
```
Health check:
```bash
curl http://127.0.0.1:8000/health
```
Vyhľadávanie:
```bash
curl -X POST http://127.0.0.1:8000/search \
-H "Content-Type: application/json" \
-d '{"query":"knowledge graph","limit":5}'
```
Reindexovanie:
```bash
curl -X POST http://127.0.0.1:8000/sync \
-H "Content-Type: application/json" \
-d '{"pull_git":false}'
```
V `docker-compose.yml` je repozitár `zpwiki` pripojený ako volume:
```yaml
volumes:
- ./data:/app/data
- ../zpwiki:/zpwiki
```
A cesta k nemu je nastavená cez environment premennú:
```yaml
environment:
- ZPWIKI_ROOT=/zpwiki
```
## Čo ešte treba dorobiť
### 1. Upratanie kódu do modulov
### 2. Upratanie kódu do modulov
Aktuálne je veľká časť logiky priamo v `app/main.py`. Neskôr treba kód rozdeliť napríklad takto:
@ -250,20 +166,19 @@ app/
Cieľ je, aby API, vyhľadávanie, databáza a synchronizácia neboli v jednom veľkom súbore.
### 2. Vylepšenie synchronizácie so `zpwiki`
### 3. Synchronizácia so `zpwiki`
Aktuálne endpoint `/sync` vie obnoviť celý index. Neskôr treba pridať efektívnejšiu synchronizáciu.
Treba pridať mechanizmus, ktorý bude vedieť aktualizovať dáta zo školského repozitára.
Treba dorobiť:
Plánované časti:
1. zistenie aktuálneho commitu,
2. uloženie posledného spracovaného commitu,
3. detekciu zmenených Markdown súborov,
4. reindexovanie iba zmenených dokumentov,
5. uloženie stavu synchronizácie do databázy,
6. logovanie výsledkov synchronizácie.
1. skript pre `git pull`,
2. zistenie aktuálneho commitu,
3. detekcia zmenených Markdown súborov,
4. reindexovanie zmenených dokumentov,
5. uloženie stavu synchronizácie do databázy.
### 3. Webhook endpoint pre Gitea
### 4. Webhook endpoint pre Gitea
Treba vytvoriť endpoint napríklad:
@ -275,12 +190,11 @@ Tento endpoint má:
1. prijať webhook z Gitea,
2. overiť secret alebo podpis webhooku,
3. spracovať push event,
4. spustiť synchronizáciu repozitára,
5. spustiť reindexovanie,
6. vrátiť výsledok synchronizácie.
3. spustiť synchronizáciu repozitára,
4. spustiť reindexovanie zmenených súborov,
5. zapísať výsledok do logu alebo tabuľky synchronizácie.
### 4. OpenWebUI integrácia
### 5. OpenWebUI integrácia
Treba napojiť API na OpenWebUI.
@ -293,7 +207,7 @@ Možné riešenia:
Cieľ je, aby používateľ mohol v OpenWebUI položiť otázku a agent použil vyhľadávanie nad `zpwiki`.
### 5. Embeddingy a vektorové vyhľadávanie
### 6. Embeddingy a vektorové vyhľadávanie
Aktuálne vyhľadávanie je fulltextové a skórovacie. Ďalší krok je pridať embeddingy.
@ -305,14 +219,14 @@ Treba dorobiť:
4. vektorové vyhľadávanie,
5. porovnanie fulltextového a vektorového vyhľadávania.
Možné databázy alebo nástroje:
Možné databázy:
1. PostgreSQL plus pgvector,
2. Qdrant,
3. ChromaDB,
4. FAISS ako jednoduchý lokálny prototyp.
### 6. RAG odpovede s citáciami
### 7. RAG odpovede s citáciami
Treba doplniť generovanie odpovede pomocou jazykového modelu.
@ -326,7 +240,7 @@ Postup:
Cieľ je, aby agent nehalucinoval a vedel ukázať, z ktorých dokumentov odpovedal.
### 7. Znalostný graf
### 8. Znalostný graf
Treba vytvoriť štruktúrovaný graf nad dátami zo `zpwiki`.
@ -348,7 +262,7 @@ Základné vzťahy:
5. práca je podobná inej práci,
6. práca patrí do roka alebo obdobia.
### 8. GraphRAG
### 9. GraphRAG
Treba prepojiť RAG a znalostný graf.
@ -360,7 +274,7 @@ GraphRAG časť má umožniť:
4. analýzu tém podľa tagov, rokov a kategórií,
5. kombináciu textového, vektorového a grafového vyhľadávania.
### 9. Vyhodnotenie systému
### 10. Vyhodnotenie systému
Treba pripraviť testovaciu sadu otázok a porovnať viacero prístupov.
@ -389,7 +303,7 @@ Sledované vlastnosti:
5. čas odpovede,
6. čas reindexovania po zmene v Gite.
### 10. Dokumentácia do diplomovej práce
### 11. Dokumentácia do diplomovej práce
Treba priebežne písať:
@ -400,20 +314,10 @@ Treba priebežne písať:
5. ako funguje `zpwiki`,
6. návrh architektúry systému,
7. návrh databázy a indexu,
8. návrh synchronizácie,
9. návrh webhook integrácie,
10. návrh integrácie s OpenWebUI,
11. popis experimentov a vyhodnotenia.
8. návrh webhook synchronizácie,
9. návrh integrácie s OpenWebUI,
10. popis experimentov a vyhodnotenia.
## Najbližší praktický krok
Najbližšie treba spraviť Gitea webhook endpoint.
Poradie najbližších krokov:
1. commitnúť aktuálne zmeny,
2. pushnúť projekt na KEMT Git,
3. vytvoriť endpoint `POST /webhook/gitea`,
4. pridať overenie secretu alebo podpisu webhooku,
5. napojiť webhook na `/sync`,
6. otestovať webhook lokálne alebo cez verejne dostupný tunel.
Najbližšie treba spraviť Docker nasadenie aktuálneho FastAPI prototypu.

View File

@ -1,4 +1,7 @@
from pathlib import Path
import hashlib
import hmac
import json
import os
import re
import sqlite3
@ -8,12 +11,13 @@ import time
import unicodedata
from collections import Counter
from fastapi import FastAPI, HTTPException
from fastapi import FastAPI, Header, HTTPException, Request
from pydantic import BaseModel, Field
DB_FILE = Path("data/zp_index.sqlite")
ZPWIKI_ROOT = Path(os.getenv("ZPWIKI_ROOT", "../zpwiki"))
WEBHOOK_SECRET = os.getenv("WEBHOOK_SECRET", "dev-secret")
TECHNICAL_TERMS = {
@ -51,7 +55,7 @@ TECHNICAL_TERMS = {
app = FastAPI(
title="ZP Agent API",
description="API pre vyhľadávanie v repozitári záverečných prác zpwiki.",
version="0.2.0",
version="0.3.0",
)
@ -338,6 +342,31 @@ def rebuild_index(pull_git: bool = False) -> dict:
}
def verify_gitea_signature(raw_body: bytes, signature: str | None) -> bool:
if not signature:
return False
expected = hmac.new(
WEBHOOK_SECRET.encode("utf-8"),
raw_body,
hashlib.sha256,
).hexdigest()
signature = signature.strip()
if signature.startswith("sha256="):
signature = signature.replace("sha256=", "", 1)
return hmac.compare_digest(expected, signature)
def verify_simple_token(token: str | None) -> bool:
if not token:
return False
return hmac.compare_digest(token, WEBHOOK_SECRET)
@app.get("/health")
def health():
return {
@ -346,6 +375,7 @@ def health():
"database_path": str(DB_FILE),
"zpwiki_root": str(ZPWIKI_ROOT),
"zpwiki_exists": ZPWIKI_ROOT.exists(),
"webhook_secret_configured": bool(WEBHOOK_SECRET),
}
@ -377,3 +407,44 @@ def sync(request: SyncRequest):
"duration_seconds": result["duration_seconds"],
"counts": result["counts"],
}
@app.post("/webhook/gitea")
async def gitea_webhook(
request: Request,
x_gitea_event: str | None = Header(default=None, alias="X-Gitea-Event"),
x_gitea_signature: str | None = Header(default=None, alias="X-Gitea-Signature"),
x_gitea_token: str | None = Header(default=None, alias="X-Gitea-Token"),
):
raw_body = await request.body()
signature_ok = verify_gitea_signature(raw_body, x_gitea_signature)
token_ok = verify_simple_token(x_gitea_token)
if not signature_ok and not token_ok:
raise HTTPException(
status_code=401,
detail="Invalid webhook signature or token",
)
try:
payload = json.loads(raw_body.decode("utf-8")) if raw_body else {}
except json.JSONDecodeError:
payload = {}
repository = payload.get("repository", {})
repository_name = repository.get("full_name") or repository.get("name")
try:
result = rebuild_index(pull_git=False)
except RuntimeError as error:
raise HTTPException(status_code=500, detail=str(error)) from error
return {
"status": "ok",
"event": x_gitea_event or "unknown",
"repository": repository_name,
"verified_by": "signature" if signature_ok else "token",
"duration_seconds": result["duration_seconds"],
"counts": result["counts"],
}

View File

@ -6,6 +6,7 @@ services:
- "8000:8000"
environment:
- ZPWIKI_ROOT=/zpwiki
- WEBHOOK_SECRET=dev-secret
volumes:
- ./data:/app/data
- ../zpwiki:/zpwiki