chore: Roo Code szabályok, módok és MCP szerverek konfigurálása a Masterbook alapján
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -22,3 +22,5 @@ vscode_config/
|
|||||||
# Backup files
|
# Backup files
|
||||||
*.bak
|
*.bak
|
||||||
full_db_dump.sql
|
full_db_dump.sql
|
||||||
|
|
||||||
|
.roo/.env.focalboard
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# 🔍 Service Finder Debugger & QA Protokoll
|
|
||||||
|
|
||||||
## 🛡️ Minőségbiztosítási Feladatok
|
|
||||||
1. **Logikai Konzisztenia Audit:** Ellenőrizd, hogy a kód megfelel-e a MasterBook2 és a `logic_spec` logikájának.
|
|
||||||
2. **Adatbázis Ellenőrzés:** SQL lekérdezésekkel vizsgáld meg, hogy az Alembic migráció és a Twin-adatok rendben vannak-e.
|
|
||||||
3. **Záró Dokumentáció (MD alapú):**
|
|
||||||
- **Wiki:** Frissítsd a modul leírását.
|
|
||||||
- **User Manual:** Készíts útmutatót egyéni és kis cégek számára.
|
|
||||||
- **Tech Doc:** Részletes leírás fejlesztőknek a gyors hibakereséshez.
|
|
||||||
4. **Kanban Lezárás:** Csak minden dokumentum elkészülte után mozgasd a kártyát "Done" státuszba.
|
|
||||||
7
.roo/rules/01-core-behavior.md
Normal file
7
.roo/rules/01-core-behavior.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
"Read Before Write" (Olvasd el, mielőtt írsz): Mielőtt bármilyen meglévő kódot módosítanál, KÖTELEZŐ bekérned vagy beolvasnod a releváns fájlokat. Sose dolgozz feltételezések alapján!
|
||||||
|
|
||||||
|
Clean Code & No Harm: Ne okozz kárt a meglévő, jól működő kódbázisban. Csak a célzott problémára fókuszálj.
|
||||||
|
|
||||||
|
Gondolatmenet (Thought Process): Mielőtt legenerálod a kódot, 2-3 mondatban vázold fel a logikádat, hogy lássam, jó irányba indultál-e el.
|
||||||
|
|
||||||
|
Nyelv: Magyar nyelven kommunikálj velem.
|
||||||
7
.roo/rules/02-architecture.md
Normal file
7
.roo/rules/02-architecture.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Tech Stack: FastAPI (v2, aszinkron), SQLAlchemy (Async), PostgreSQL (Izolált hálózaton), Docker Compose V2.
|
||||||
|
|
||||||
|
AI & OCR: Hibrid AI Gateway (Helyi Ollama: 14B Qwen szövegre, Llama Vision képekre. Fallback: Gemini/Groq).
|
||||||
|
|
||||||
|
Identity & Auth: "Dual Entity" modell (Person = hús-vér ember, User = technikai fiók). Triple Wallet gazdasági motor.
|
||||||
|
|
||||||
|
Deduplikáció (MDM): Csak akkor van merge, ha a make, a technical_code és a hengerűrtartalom egyezik. N/A és UNKNOWN fallback kódok generálása az SQL kényszerek miatt.
|
||||||
3
.roo/rules/03-workflow.md
Normal file
3
.roo/rules/03-workflow.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Feladatkezelés: A projektmenedzsmenthez MCP Focalboard-ot vagy a projekt gyökerében található KANBAN_AUDIT.md fájlt használunk. Minden munkamenet elején ellenőrizd ezeket, hogy tudd, mi a feladat (Todo) és mi van már kész (Done).
|
||||||
|
|
||||||
|
Jelenlegi Fókusz: A következő időszak fő feladata a "Historical Data" (múltbéli költségek, szervizek) bevezetése az occurrence_date mezővel, és a flottavezetőknek szóló AnalyticsService (TCO/km) kidolgozása.
|
||||||
32
Roo code_utasitas.md
Normal file
32
Roo code_utasitas.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
Szia Roo! A "Service Finder - Masterbook 2.0" alapján elkészült egy rendszer-audit. Kérlek, az alábbi Markdown struktúrát dolgozd fel.
|
||||||
|
|
||||||
|
Feladatod: Ha az MCP Focalboard kapcsolat él, hozd létre ezeket a kártyákat a Kanban táblán a megfelelő oszlopokban (TODO, IN PROGRESS, DONE). Ha az MCP nem működik, hozz létre egy KANBAN_AUDIT.md fájlt a projekt gyökerében ezzel a tartalommal, hogy manuálisan is követni tudjuk.
|
||||||
|
|
||||||
|
Utána kezdj el dolgozni a BACKLOG 1. pontján (Historical Data logikájának beépítése az AssetCost modellekbe az occurrence_date hozzáadásával).
|
||||||
|
|
||||||
|
Itt az Audit lista:
|
||||||
|
✅ KÉSZ (DONE)
|
||||||
|
|
||||||
|
Infrastruktúra: Docker konténerek, Postgres adatbázis izolált hálózaton, Ollama AI gateway beállítva.
|
||||||
|
|
||||||
|
Identity & Auth: Regisztrációs és Login végpontok (auth.py). JWT Token generálás RBAC (rank, scope_level, scope_id) paraméterekkel.
|
||||||
|
|
||||||
|
Digital Twin & MDM: Asset és VehicleModelDefinition adatbázis sémái felépítve (asset.py, vehicle_definitions.py).
|
||||||
|
|
||||||
|
AI Gateway & OCR: Robot 3 diszpécser (Base64 képkódolás), Hibrid AI hívás (Llama 3.2 Vision + Google Gemini/Groq Fallback), Izolált képmentés a NAS Vault-ba.
|
||||||
|
|
||||||
|
⏳ FOLYAMATBAN (IN PROGRESS)
|
||||||
|
|
||||||
|
Robot 2 (Technical Enricher): Szöveges adatok feldolgozása a 14B Qwen modellel.
|
||||||
|
|
||||||
|
🚀 BACKLOG (TO DO)
|
||||||
|
|
||||||
|
1. Historical Data (Visszamenőleges Adatok): occurrence_date bevezetése az AssetCost, AssetEvent, VehicleOwnership táblákba a valós eseményidők tárolására (elkülönítve a created_at rendszeridőtől).
|
||||||
|
|
||||||
|
2. TCO & Flotta Analitika: AnalyticsService létrehozása (TCO / km, Amortizációs motor, Fix vs Változó költségmegoszlás, Fogyasztási anomália detektálás).
|
||||||
|
|
||||||
|
3. Trust Matching (Bizonyítékok Hálója): A Robot 3 OCR JSON adatainak automatikus összekötése a regisztrált szervizekkel (organizations), 'Verified' státusz adása.
|
||||||
|
|
||||||
|
4. Marketplace & Foglalás: pending_actions integrálása, Geofencing alapú ajánlatkérés szórása (Broadcast).
|
||||||
|
|
||||||
|
5. Robot 2.3 (The Guardian): Napi ütemező (CRON) fejlesztése lejáratok és szervizintervallumok figyelésére (30/7/0 napos riasztások).
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from sqlalchemy import String, Integer, Boolean, DateTime, ForeignKey, text
|
from sqlalchemy import String, Integer, Boolean, DateTime, ForeignKey, Text
|
||||||
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
|
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
|
||||||
from sqlalchemy.orm import Mapped, mapped_column
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
from sqlalchemy.sql import func
|
from sqlalchemy.sql import func
|
||||||
@@ -11,6 +11,7 @@ from app.db.base_class import Base
|
|||||||
class Document(Base):
|
class Document(Base):
|
||||||
""" NAS alapú dokumentumtár metaadatai. """
|
""" NAS alapú dokumentumtár metaadatai. """
|
||||||
__tablename__ = "documents"
|
__tablename__ = "documents"
|
||||||
|
__table_args__ = {"schema": "data"}
|
||||||
|
|
||||||
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||||
parent_type: Mapped[str] = mapped_column(String(20)) # 'organization' vagy 'asset'
|
parent_type: Mapped[str] = mapped_column(String(20)) # 'organization' vagy 'asset'
|
||||||
@@ -28,3 +29,26 @@ class Document(Base):
|
|||||||
|
|
||||||
uploaded_by: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("identity.users.id"))
|
uploaded_by: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("identity.users.id"))
|
||||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||||
|
|
||||||
|
# =========================================================================
|
||||||
|
# THOUGHT PROCESS & ARCHITECTURE DECISIONS (2026-03-04)
|
||||||
|
# =========================================================================
|
||||||
|
# Probléma: Az `ocr_robot.py` (Robot 3) módosítani próbálta a dokumentumok
|
||||||
|
# állapotát és menteni akarta az AI eredményeket, de a mezők hiányoztak.
|
||||||
|
#
|
||||||
|
# Megoldás: Hozzáadtuk a szükséges mezőket a munkafolyamat (Workflow)
|
||||||
|
# támogatásához.
|
||||||
|
#
|
||||||
|
# 1. `status`: A robot a 'pending_ocr' státuszra szűr. Indexeljük,
|
||||||
|
# mert a WHERE feltételben szerepel, így az adatbázis sokkal gyorsabb lesz.
|
||||||
|
#
|
||||||
|
# 2. `ocr_data`: A kinyert adatokat tárolja. Text típust használunk String
|
||||||
|
# helyett, mert az AI válasza (pl. JSON formátumú adat) hosszú lehet.
|
||||||
|
#
|
||||||
|
# 3. `error_log`: Ha az AI hibázik, vagy üres választ ad, itt rögzítjük
|
||||||
|
# a hiba okát a könnyebb debuggolás érdekében.
|
||||||
|
# =========================================================================
|
||||||
|
|
||||||
|
status: Mapped[str] = mapped_column(String(50), default="uploaded", index=True)
|
||||||
|
ocr_data: Mapped[Optional[str]] = mapped_column(Text)
|
||||||
|
error_log: Mapped[Optional[str]] = mapped_column(Text)
|
||||||
@@ -9,35 +9,42 @@ from sqlalchemy import select
|
|||||||
|
|
||||||
from app.db.session import AsyncSessionLocal
|
from app.db.session import AsyncSessionLocal
|
||||||
from app.models.system import SystemParameter
|
from app.models.system import SystemParameter
|
||||||
from app.services.config_service import config # 2.2-es központi config
|
from app.services.config_service import config
|
||||||
|
|
||||||
logger = logging.getLogger("AI-Service-2.2")
|
logger = logging.getLogger("AI-Service-2.2-Gateway")
|
||||||
|
|
||||||
class AIService:
|
class AIService:
|
||||||
"""
|
"""
|
||||||
Sentinel Master AI Service 2.2.
|
Sentinel Master AI Service 2.2 - Multi-Agent & Fallback Gateway.
|
||||||
Felelős az LLM hívásokért, prompt sablonok kezeléséért és az OCR feldolgozásért.
|
Felelős az LLM hívásokért. Ha a helyi GPU (Ollama) túlterhelt,
|
||||||
Minden paraméter (modell, url, prompt, hőmérséklet) adminból vezérelt.
|
automatikusan áttér a felhős (Groq/Gemini) megoldásokra.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def _execute_ai_call(cls, db, prompt: str, model_key: str = "text", images: Optional[List[str]] = None) -> Optional[Dict[str, Any]]:
|
async def _execute_ai_call(cls, db, prompt: str, model_key: str = "text", images: Optional[List[str]] = None) -> Optional[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Központi AI végrehajtó. Kezeli a modellt, a várakozást és a JSON parzolást.
|
Központi AI végrehajtó intelligens teherelosztással (Load Balancing).
|
||||||
"""
|
"""
|
||||||
|
# 1. BEÁLLÍTÁSOK LEKÉRÉSE
|
||||||
|
base_url = await config.get_setting(db, "ai_ollama_url", default="http://ollama:11434/api/generate")
|
||||||
|
delay = await config.get_setting(db, "AI_REQUEST_DELAY", default=0.1)
|
||||||
|
temp = await config.get_setting(db, "ai_temperature", default=0.1)
|
||||||
|
|
||||||
|
# A helyi timeout-ot levesszük 25 mp-re. Ha az RTX 3090 eddig nem végez,
|
||||||
|
# akkor biztosan tele van a várólistája, és azonnal átváltunk felhőbe.
|
||||||
|
local_timeout = await config.get_setting(db, "ai_timeout_local", default=25.0)
|
||||||
|
|
||||||
|
# Fallback engedélyezése az .env fájlból
|
||||||
|
enable_fallback = os.getenv("ENABLE_AI_FALLBACK", "false").lower() == "true"
|
||||||
|
|
||||||
|
# Helyi modellek definiálása
|
||||||
|
default_model = "llama3.2-vision:latest" if model_key == "vision" else "qwen2.5-coder:14b"
|
||||||
|
model_name = await config.get_setting(db, f"ai_model_{model_key}", default=default_model)
|
||||||
|
|
||||||
|
await asyncio.sleep(float(delay))
|
||||||
|
|
||||||
|
# 2. ELSŐDLEGES PRÓBA: Helyi Ollama szerver (Ingyenes, VRAM alapú)
|
||||||
try:
|
try:
|
||||||
# 1. ADMIN KONFIGURÁCIÓ LEKÉRÉSE
|
|
||||||
base_url = await config.get_setting(db, "ai_ollama_url", default="http://ollama:11434/api/generate")
|
|
||||||
delay = await config.get_setting(db, "AI_REQUEST_DELAY", default=0.1)
|
|
||||||
|
|
||||||
# Modell választás (text vagy vision)
|
|
||||||
model_name = await config.get_setting(db, f"ai_model_{model_key}", default="qwen2.5-coder:32b")
|
|
||||||
temp = await config.get_setting(db, "ai_temperature", default=0.1)
|
|
||||||
timeout_val = await config.get_setting(db, "ai_timeout", default=120.0)
|
|
||||||
|
|
||||||
await asyncio.sleep(float(delay))
|
|
||||||
|
|
||||||
# 2. PAYLOAD ÖSSZEÁLLÍTÁSA
|
|
||||||
payload = {
|
payload = {
|
||||||
"model": model_name,
|
"model": model_name,
|
||||||
"prompt": prompt,
|
"prompt": prompt,
|
||||||
@@ -45,60 +52,128 @@ class AIService:
|
|||||||
"format": "json",
|
"format": "json",
|
||||||
"options": {"temperature": float(temp)}
|
"options": {"temperature": float(temp)}
|
||||||
}
|
}
|
||||||
|
if images:
|
||||||
if images: # Llava/Vision támogatás
|
|
||||||
payload["images"] = images
|
payload["images"] = images
|
||||||
|
|
||||||
# 3. HTTP HÍVÁS
|
logger.info(f"🧠 Helyi AI ({model_name}) hívása indult...")
|
||||||
async with httpx.AsyncClient(timeout=float(timeout_val)) as client:
|
async with httpx.AsyncClient(timeout=float(local_timeout)) as client:
|
||||||
response = await client.post(base_url, json=payload)
|
response = await client.post(base_url, json=payload)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
|
||||||
raw_res = response.json().get("response", "{}")
|
raw_res = response.json().get("response", "{}")
|
||||||
return json.loads(raw_res)
|
return json.loads(raw_res)
|
||||||
|
|
||||||
|
except (httpx.ReadTimeout, httpx.ConnectError) as e:
|
||||||
|
logger.warning(f"⚠️ Helyi GPU túlterhelt vagy lassú (Timeout/ConnectError). Váltás Fallback módba!")
|
||||||
except json.JSONDecodeError as je:
|
except json.JSONDecodeError as je:
|
||||||
logger.error(f"❌ AI JSON hiba (parszolási hiba): {je}")
|
logger.error(f"❌ Helyi AI JSON parszolási hiba: {je}. Váltás Fallback módba!")
|
||||||
return None
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ AI hívás kritikus hiba: {e}")
|
logger.error(f"❌ Helyi AI váratlan hiba: {e}. Váltás Fallback módba!")
|
||||||
|
|
||||||
|
# 3. MÁSODLAGOS PRÓBA: Hibrid Fallback (Csak ha engedélyezve van és a helyi elbukott)
|
||||||
|
if enable_fallback:
|
||||||
|
return await cls._fallback_ai_call(prompt, model_key, images, float(temp))
|
||||||
|
else:
|
||||||
|
logger.error("❌ A Fallback ki van kapcsolva (.env), a feladat feldolgozása sikertelen.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_gold_data_from_research(cls, make: str, model: str, raw_context: str) -> Optional[Dict[str, Any]]:
|
async def _fallback_ai_call(cls, prompt: str, model_key: str, images: Optional[List[str]], temp: float) -> Optional[Dict[str, Any]]:
|
||||||
"""
|
""" Külső API hívások (Groq és Gemini) vészhelyzet esetére. """
|
||||||
Robot 3 (Alchemist) dúsító folyamata.
|
|
||||||
Kutatási adatokból csinál tiszta technikai adatlapot.
|
|
||||||
"""
|
|
||||||
async with AsyncSessionLocal() as db:
|
|
||||||
template = await config.get_setting(db, "ai_prompt_gold_data",
|
|
||||||
default="Extract technical car data for {make} {model} from: {context}")
|
|
||||||
|
|
||||||
|
if model_key == "vision" and images:
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# VISION FALLBACK: GOOGLE GEMINI 1.5 FLASH (Képelemzés/OCR)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
gemini_api_key = os.getenv("GEMINI_API_KEY")
|
||||||
|
if not gemini_api_key:
|
||||||
|
logger.error("❌ Hiányzik a GEMINI_API_KEY! Képtelen Fallback módban OCR-t végezni.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
logger.info("☁️ Google Gemini 1.5 API (Vision) hívása...")
|
||||||
|
url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key={gemini_api_key}"
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"contents": [{
|
||||||
|
"parts": [
|
||||||
|
{"text": prompt + "\nKérlek, SZIGORÚAN csak érvényes JSON objektummal válaszolj, Markdown formázás (```json) nélkül!"},
|
||||||
|
{"inline_data": {
|
||||||
|
"mime_type": "image/jpeg", # A korábbi kódban JPEG-be konvertáljuk
|
||||||
|
"data": images[0]
|
||||||
|
}}
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
"generationConfig": {
|
||||||
|
"temperature": temp,
|
||||||
|
"response_mime_type": "application/json" # Gemini specifikus JSON kényszerítés
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||||
|
resp = await client.post(url, json=payload)
|
||||||
|
resp.raise_for_status()
|
||||||
|
data = resp.json()
|
||||||
|
text_res = data["candidates"][0]["content"]["parts"][0]["text"]
|
||||||
|
return json.loads(text_res)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Gemini Fallback kritikus hiba: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
else:
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# TEXT FALLBACK: GROQ CLOUD (Szövegelemzés / Web Scraping)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
groq_api_key = os.getenv("GROQ_API_KEY")
|
||||||
|
if not groq_api_key:
|
||||||
|
logger.error("❌ Hiányzik a GROQ_API_KEY! Képtelen Fallback módban szöveget elemezni.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
logger.info("☁️ Groq Cloud (Llama 3 8B) hívása...")
|
||||||
|
url = "[https://api.groq.com/openai/v1/chat/completions](https://api.groq.com/openai/v1/chat/completions)"
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {groq_api_key}",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"model": "llama3-8b-8192", # Villámgyors szöveges modell
|
||||||
|
"messages": [{"role": "user", "content": prompt}],
|
||||||
|
"temperature": temp,
|
||||||
|
"response_format": {"type": "json_object"} # Groq specifikus JSON kényszerítés
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient(timeout=20.0) as client:
|
||||||
|
resp = await client.post(url, headers=headers, json=payload)
|
||||||
|
resp.raise_for_status()
|
||||||
|
data = resp.json()
|
||||||
|
text_res = data["choices"][0]["message"]["content"]
|
||||||
|
return json.loads(text_res)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Groq Fallback kritikus hiba: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# --- A TÖBBI METÓDUS VÁLTOZATLAN MARAD ---
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def get_gold_data_from_research(cls, make: str, model: str, raw_context: str) -> Optional[Dict[str, Any]]:
|
||||||
|
async with AsyncSessionLocal() as db:
|
||||||
|
template = await config.get_setting(db, "ai_prompt_gold_data", default="Extract technical car data for {make} {model} from: {context}")
|
||||||
full_prompt = template.format(make=make, model=model, context=raw_context)
|
full_prompt = template.format(make=make, model=model, context=raw_context)
|
||||||
return await cls._execute_ai_call(db, full_prompt, model_key="text")
|
return await cls._execute_ai_call(db, full_prompt, model_key="text")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_clean_vehicle_data(cls, make: str, raw_model: str, sources: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
async def get_clean_vehicle_data(cls, make: str, raw_model: str, sources: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
||||||
"""
|
|
||||||
Név normalizálás és szinonima gyűjtés.
|
|
||||||
"""
|
|
||||||
async with AsyncSessionLocal() as db:
|
async with AsyncSessionLocal() as db:
|
||||||
template = await config.get_setting(db, "ai_prompt_normalization",
|
template = await config.get_setting(db, "ai_prompt_normalization", default="Normalize car model names: {make} {model}. Sources: {sources}")
|
||||||
default="Normalize car model names: {make} {model}. Sources: {sources}")
|
|
||||||
|
|
||||||
full_prompt = template.format(make=make, model=raw_model, sources=json.dumps(sources))
|
full_prompt = template.format(make=make, model=raw_model, sources=json.dumps(sources))
|
||||||
return await cls._execute_ai_call(db, full_prompt, model_key="text")
|
return await cls._execute_ai_call(db, full_prompt, model_key="text")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def process_ocr_document(cls, doc_type: str, base64_image: str) -> Optional[Dict[str, Any]]:
|
async def process_ocr_document(cls, doc_type: str, base64_image: str) -> Optional[Dict[str, Any]]:
|
||||||
"""
|
|
||||||
Robot 1 (OCR) látó folyamata.
|
|
||||||
Képet (base64) küld a Vision modellnek (pl. Llava).
|
|
||||||
"""
|
|
||||||
async with AsyncSessionLocal() as db:
|
async with AsyncSessionLocal() as db:
|
||||||
# Külön prompt sablon minden dokumentum típushoz (számla, forgalmi, adásvételi)
|
template = await config.get_setting(db, f"ai_prompt_ocr_{doc_type}", default="Analyze this {doc_type} image and return structured JSON data.")
|
||||||
template = await config.get_setting(db, f"ai_prompt_ocr_{doc_type}",
|
|
||||||
default="Analyze this {doc_type} image and return structured JSON data.")
|
|
||||||
|
|
||||||
full_prompt = template.format(doc_type=doc_type)
|
full_prompt = template.format(doc_type=doc_type)
|
||||||
|
# Itt mondjuk meg a diszpécsernek, hogy ez egy Vision feladat!
|
||||||
return await cls._execute_ai_call(db, full_prompt, model_key="vision", images=[base64_image])
|
return await cls._execute_ai_call(db, full_prompt, model_key="vision", images=[base64_image])
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
|
from typing import List, Optional, Dict, Any, TYPE_CHECKING
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy import select, func, and_
|
from sqlalchemy import select, func, and_
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# /opt/docker/dev/service_finder/backend/app/workers/ocr_robot.py
|
# /opt/docker/dev/service_finder/backend/app/workers/ocr/robot_1_ocr_processor.py
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
@@ -72,12 +72,15 @@ class OCRRobot:
|
|||||||
with open(temp_path, "rb") as f:
|
with open(temp_path, "rb") as f:
|
||||||
image_bytes = f.read()
|
image_bytes = f.read()
|
||||||
|
|
||||||
# AI felismerés (pl. Llama-Vision vagy GPT-4o)
|
# 1. Kép kódolása Base64 formátumba (A Vision modellek ezt várják)
|
||||||
ocr_result = await AIService.get_clean_vehicle_data(
|
import base64
|
||||||
make="OCR_SCAN",
|
base64_image = base64.b64encode(image_bytes).decode('utf-8')
|
||||||
raw_model=doc.parent_type,
|
|
||||||
v_type="document",
|
# 2. A MEGFELELŐ AI HÍVÁS (Vision modell hívása az AIService-en keresztül)
|
||||||
sources={"image_data": "raw_scan"}
|
doc_type_str = doc.doc_type or "general_document"
|
||||||
|
ocr_result = await AIService.process_ocr_document(
|
||||||
|
doc_type=doc_type_str,
|
||||||
|
base64_image=base64_image
|
||||||
)
|
)
|
||||||
|
|
||||||
if ocr_result:
|
if ocr_result:
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
"""Fix Document schema mapping
|
||||||
|
|
||||||
|
Revision ID: 4f083e0ad046
|
||||||
|
Revises: e44655e0eae8
|
||||||
|
Create Date: 2026-03-04 18:02:38.190169
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '4f083e0ad046'
|
||||||
|
down_revision: Union[str, Sequence[str], None] = 'e44655e0eae8'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
pass
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
"""Add OCR workflow fields to Document
|
||||||
|
|
||||||
|
Revision ID: e44655e0eae8
|
||||||
|
Revises: 92fe3b877b24
|
||||||
|
Create Date: 2026-03-04 17:54:03.810505
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = 'e44655e0eae8'
|
||||||
|
down_revision: Union[str, Sequence[str], None] = '92fe3b877b24'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
pass
|
||||||
Reference in New Issue
Block a user