Update README with webhook status
This commit is contained in:
parent
44eb3cd679
commit
64b828c46e
178
README.md
178
README.md
@ -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.
|
||||
|
||||
75
app/main.py
75
app/main.py
@ -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"],
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ services:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
- ZPWIKI_ROOT=/zpwiki
|
||||
- WEBHOOK_SECRET=dev-secret
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- ../zpwiki:/zpwiki
|
||||
|
||||
Loading…
Reference in New Issue
Block a user