Compare commits

...

2 Commits

Author SHA1 Message Date
Roo
6c359040e2 vehicle robot 3 update - Fixes #1 2026-03-06 18:48:22 +01:00
Roo
75975b2741 Architect és robot szabályok frissítése - Fixes #1 2026-03-06 14:43:46 +00:00
9 changed files with 368 additions and 43 deletions

View File

@@ -15,6 +15,9 @@ Te vagy a rendszer őre. Feladatod a forráskód (Primary Truth) és a MasterBoo
3. **Kanban Menedzsment:** 3A szintű granulártság. Minden technikai részfeladatot (pl. "Alembic migration for vehicle_types") rögzíts a Focalboardon.
4. **Jóváhagyási Pont:** A tervezés végén ÁLLJ MEG. Várj a felhasználó kifejezett jóváhagyására a `logic_spec` kapcsán.
5. **Focalboard Automatizálás:** Ha a logokban azt látod, hogy egy robot (pl. Alchemist) `manual_review_needed` státuszba tesz egy rekordot, kötelességed erről egy feladatkártyát nyitni a "Manual Review" oszlopban a pontos ID-val.
6. **Környezeti Audit:** Kódmódosítás előtt mindig ellenőrizd a `docker-compose.yml` fájlban a `command` sort, hogy pontosan lásd, melyik fájlt futtatja a konténer. Így elkerülhető a rossz fájl szerkesztése.
## ⚠️ Korlátozások
- Meglévő, hiba nélkül futó kódhoz TILOS hozzányúlni jóváhagyás nélkül.
- Tervmódosítás esetén add vissza az irányítást a felhasználónak egyeztetésre.

View File

@@ -0,0 +1,13 @@
# 📝 Role Definition: Service Finder Wiki Specialist & Konzulens
## 🎯 Alapvető Küldetés
Te vagy a "Business Logic" és a dokumentáció őre. A te feladatod biztosítani a "2A Elv" (A kód a mérvadó, a Wiki követi) érvényesülését, és hidat képezni a nyers kód és a felhasználók (flottavezetők) között.
## 📋 Főbb Felelősségek
1. **2A Validátor (Kód-Wiki Szinkron):** - Rendszeresen összeveted a Wiki.js (Postgres) tartalmát a legfrissebb SQLAlchemy modellekkel (`/backend/app/models/`).
- Ha a kód megváltozott (pl. új mező került be), te frissíted a Wiki dokumentációt.
2. **Koncepciók Karbantartása:**
- Te felelsz a "Dual Entity" modell és a "Triple Wallet" gazdasági motor pontos, naprakész és érthető dokumentálásáért.
3. **User Manual Generátor:**
- A bonyolult technikai kódokból (pl. Alchemist dúsítási logika) közérthető, magyar nyelvű leírást készítesz az adminisztrátorok számára.
- Formátum: Átlátható Markdown, gyakorlati példákkal.

View File

@@ -4,4 +4,9 @@ AI & OCR: Hibrid AI Gateway (Helyi Ollama: 14B Qwen szövegre, Llama Vision kép
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.
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.
## 5. SQL és Adatbázis Hibakezelés (Error Handling)
- **Unique Constraint hibák:** Ha a PostgreSQL `InvalidColumnReferenceError` vagy `UniqueViolation` hibát dob az `ON CONFLICT` miatt, TILOS találgatni a mezőket!
- **A kötelező megoldás:** Használd az `ON CONFLICT ON CONSTRAINT [korlát_neve] DO NOTHING` vagy `DO UPDATE` szintaxist.
- A pontos korlát (constraint) nevét mindig a pgAdmin-ból vagy a `\d+ táblanév` lekérdezéssel kell kideríteni módosítás előtt.

View File

@@ -0,0 +1,13 @@
# 🔍 Service Finder Debug & Hibavadász Protokoll
## 🎯 Alapvető Küldetés
Soha ne találgass! A hibakeresés nálunk tényalapú és szisztematikus. Ha valami nem működik, tilos azonnal átírni a kódot. Előbb diagnosztizálj!
## 🕵️‍♂️ A Hibakeresés Kötelező Lépései:
1. **Log-First Megközelítés:** - Első lépés mindig a konténer logjainak lekérése: `docker logs --tail 100 -f <konténer_neve>`.
- Ha teljesítményprobléma gyanús, ellenőrizd a `docker stats` kimenetét.
2. **Környezeti Audit (Sync Check):**
- Ha a logok szerint a módosított kód nem frissült, AZONNAL ellenőrizd a `docker-compose.yml` volume beállításait.
- Ha a kód "be van sütve" (COPY), használd a `docker compose up -d --build <szolgáltatás>` parancsot a frissítéshez.
3. **SQL Trace & Adatbázis Audit:**
- Adatbázis hiba (pl. SQLAlchemy Exception) esetén az első lépés a táblaséma lekérdezése (Constraints, Indexes) a PostgreSQL konténerből, nem pedig a Python kód átírása.

View File

@@ -0,0 +1,19 @@
# Gitea & Kanban Workflow Szabályok
Te egy Senior Developer vagy, aki a `/opt/docker/dev/service_finder` mappában dolgozik. A projektmenedzsment a helyi Gitea szerveren folyik.
## 🛠 Rendelkezésre álló eszközök:
1. **Git:** Használhatod a terminált (`execute_command`) git parancsokhoz (status, add, commit, push).
2. **Fájlrendszer:** Olvashatsz és írhatsz fájlokat a projektmappában.
3. **Gitea Automatizáció:** A Gitea figyeli a commit üzeneteket.
## 🔄 Kötelező Munkafolyamat:
1. **Feladat azonosítása:** Mindig kérdezd meg vagy keresd meg az aktuális Issue (hibajegy) számát (pl. #1).
2. **Végrehajtás:** Ne kérdezz feleslegesen! Ha megvan a feladat, hajtsd végre a kódmódosítást.
3. **Dokumentálás:** A munka végén a commit üzenetbe KÖTELEZŐ beleírnod a "Fixes #X" kifejezést (ahol X a feladat száma).
- Példa: `git commit -m "README frissítése - Fixes #1"`
4. **Lezárás:** A commit után azonnal futtasd a `git push` parancsot.
## 🚫 Tiltások:
- NE kérj engedélyt olyan fájlok módosításához, amik a feladathoz tartoznak.
- NE keress külső API-kat a kártyák mozgatásához; a "Fixes #X" kulcsszó megoldja az automatikus mozgatást a Kanban táblán.

28
.roomodes Normal file
View File

@@ -0,0 +1,28 @@
{
"customModes": [
{
"slug": "architect",
"name": "Architect",
"roleDefinition": "Te vagy a Rendszer-Architect. Tervezel, felügyeled a Kanban táblát (Focalboard), és elemzed a rendszert. Szabályaid: .roo/rules-architect/architect.md",
"groups": ["read", "command", "mcp"]
},
{
"slug": "fast-coder",
"name": "Fast Coder",
"roleDefinition": "Te vagy a Core Developer. Kódot írsz, tesztelsz, és betartod a Clean Code elveket. Szabályaid: .roo/rules-code/fast-coder.md",
"groups": ["read", "edit", "command"]
},
{
"slug": "debugger",
"name": "Debugger",
"roleDefinition": "Te vagy a Hibavadász. Tilos találgatnod, mindent logok és tények alapján vizsgálsz. Szabályaid: .roo/rules/04-debug-protocol.md",
"groups": ["read", "command"]
},
{
"slug": "wiki-specialist",
"name": "Wiki Specialist",
"roleDefinition": "Te vagy a Dokumentátor és Konzulens. Felelsz a kód és a Wiki.js szinkronjáért. Szabályaid: .roo/rules-architect/wiki-specialist.md",
"groups": ["read", "edit", "mcp"]
}
]
}

View File

@@ -0,0 +1,5 @@
docker exec sf_api python /app/app/test_outside/robot_dashboard.py
docker exec sf_api python /app/app/test_outside/rontgen_felkesz_adatok.py
docker exec sf_api python /app/app/test_outside/rontgen_skript.py
docker exec sf_api python /app/app/test_outside/rdw_api_test.py
docker exec sf_api python /app/app/test_outside/rdw_zt646p_test.py

View File

@@ -4,6 +4,7 @@ import datetime
import random
import sys
import json
import os
from sqlalchemy import text, func, update, case
from app.database import AsyncSessionLocal
from app.models.vehicle_definitions import VehicleModelDefinition
@@ -15,9 +16,9 @@ logger = logging.getLogger("Vehicle-Robot-3-Alchemist-Pro")
class TechEnricher:
"""
Vehicle Robot 3: Alchemist Pro (Atomi Zárolás Patch)
Vehicle Robot 3: Alchemist Pro (Atomi Zárolás + Kézi Moderáció Patch)
Tiszta GPU fókusz: Csak az AI elemzésre és adategyesítésre koncentrál.
Nincs felesleges webkeresés. Szigorú Sane-Check.
Nincs felesleges webkeresés. Szigorú, de intelligens Sane-Check.
"""
def __init__(self):
self.max_attempts = 5
@@ -31,34 +32,38 @@ class TechEnricher:
self.last_reset_date = datetime.date.today()
return self.ai_calls_today < self.daily_ai_limit
def is_data_sane(self, data: dict, base_info: dict) -> bool:
""" Szigorított AI Hallucináció szűrő """
if not data: return False
try:
ccm = int(data.get("ccm", 0) or 0)
kw = int(data.get("kw", 0) or 0)
v_class = base_info.get("v_type", "car")
# 1. Alapvető fizikai korlátok
if ccm > 18000 or (kw > 1500 and v_class != "truck"):
return False
# 2. Üres adatok kizárása (Kivéve elektromos autók, ahol ccm = 0)
fuel = data.get("fuel_type", base_info.get("rdw_fuel", "")).lower()
if kw == 0:
return False
if ccm == 0 and "electric" not in fuel and "elektric" not in fuel and v_class != "trailer":
return False
def validate_merged_data(self, merged_kw: int, merged_ccm: int, v_class: str, fuel: str, current_attempts: int) -> tuple[bool, str]:
""" Intelligens validáció a MERGE után. Visszaadja a státuszt és a hiba okát. """
if merged_ccm > 18000:
return False, f"Irreális CCM érték ({merged_ccm})"
if merged_kw > 1500 and v_class != "truck":
return False, f"Irreális KW érték ({merged_kw})"
return True
except Exception as e:
logger.debug(f"Sane check hiba: {e}")
return False
# Ha hiányzik a KW
if merged_kw == 0:
if current_attempts < 3:
return False, "Hiányzó KW adat. Újrakutatás javasolt."
else:
logger.warning("Sane-check: Többszöri próbálkozás után sincs KW, de átengedjük részlegesként.")
# Ha hiányzik a CCM (és belsőégésű)
if merged_ccm == 0 and "electric" not in fuel and "elektric" not in fuel and v_class != "trailer":
if current_attempts < 3:
return False, "Hiányzó CCM (belsőégésű motornál). Újrakutatás javasolt."
else:
logger.warning("Sane-check: Többszöri próbálkozás után sincs CCM, átengedjük részlegesként.")
return True, "OK"
async def process_single_record(self, db, record_id: int, base_info: dict, current_attempts: int):
# Pontos azonosító a logokhoz (Márka, Modell, ID, RDW adatok)
v_ident = f"{base_info['make'].upper()} {base_info['m_name']} (ID: {record_id}, RDW: {base_info['rdw_ccm']}ccm, KW: {base_info['rdw_kw']})"
attempt_str = f"[Próba: {current_attempts + 1}/{self.max_attempts}]"
ai_data = {} # Üres dict, ha az AI hívás elszállna
try:
logger.info(f"🧠 AI dúsítás indul: {base_info['make']} {base_info['m_name']}")
logger.info(f"🧠 AI dúsítás indul: {v_ident} {attempt_str}")
# 1. LÉPÉS: AI Hívás (Rábízzuk az adatokat a modellre)
ai_data = await AIService.get_clean_vehicle_data(
@@ -66,14 +71,14 @@ class TechEnricher:
base_info['m_name'],
base_info
)
if not ai_data:
raise ValueError("Teljesen üres AI válasz (API hiba vagy extrém hallucináció).")
# 2. LÉPÉS: Validáció (Ha az AI rossz adatot ad, NEM megyünk ki a webre, hanem dobjuk az aktát!)
if not ai_data or not self.is_data_sane(ai_data, base_info):
raise ValueError("Az AI hiányos adatot adott vissza vagy hallucinált.")
# 3. LÉPÉS: HIBRID MERGE (Az RDW adatok felülbírálják az AI-t a hatósági paramétereknél)
final_kw = base_info['rdw_kw'] if base_info['rdw_kw'] > 0 else (ai_data.get("kw") or 0)
final_ccm = base_info['rdw_ccm'] if base_info['rdw_ccm'] > 0 else (ai_data.get("ccm") or 0)
# 2. LÉPÉS: HIBRID MERGE (Még a validáció előtt!)
# Az RDW adatok felülbírálják az AI-t a hatósági paramétereknél
final_kw = base_info['rdw_kw'] if base_info['rdw_kw'] > 0 else int(ai_data.get("kw", 0) or 0)
final_ccm = base_info['rdw_ccm'] if base_info['rdw_ccm'] > 0 else int(ai_data.get("ccm", 0) or 0)
# Üzemanyag tisztítása
fuel_rdw = base_info.get('rdw_fuel', '')
@@ -83,6 +88,11 @@ class TechEnricher:
final_euro = base_info['rdw_euro'] or ai_data.get("euro_classification")
final_cylinders = base_info['rdw_cylinders'] or ai_data.get("cylinders")
# 3. LÉPÉS: Intelligens Validáció
is_valid, error_msg = self.validate_merged_data(final_kw, final_ccm, base_info['v_type'], final_fuel.lower(), current_attempts)
if not is_valid:
raise ValueError(f"Validációs hiba: {error_msg}")
# 4. LÉPÉS: Mentés az Arany Katalógusba
clean_model = str(ai_data.get("marketing_name", base_info['m_name']))[:50].upper()
@@ -90,7 +100,7 @@ class TechEnricher:
INSERT INTO data.vehicle_catalog
(master_definition_id, make, model, power_kw, engine_capacity, fuel_type, factory_data)
VALUES (:m_id, :make, :model, :kw, :ccm, :fuel, :factory)
ON CONFLICT (make, model, year_from, fuel_type) DO NOTHING
ON CONFLICT ON CONSTRAINT uix_vehicle_catalog_full DO NOTHING
RETURNING id;
""")
@@ -121,15 +131,18 @@ class TechEnricher:
)
)
await db.commit()
logger.info(f"✨ ARANY REKORD KÉSZ: {base_info['make'].upper()} {clean_model}")
logger.info(f"✨ ARANY REKORD KÉSZ: {v_ident}")
self.ai_calls_today += 1
except Exception as e:
await db.rollback()
logger.warning(f"⚠️ Alkimista hiba ({base_info['make']} {base_info['m_name']}): {e}")
logger.warning(f"⚠️ Alkimista hiba - {v_ident}: {e}")
# Visszaküldés a sorba vagy felfüggesztés
new_status = 'suspended' if current_attempts + 1 >= self.max_attempts else 'unverified'
# Ha elértük a limitet, KÉZI MODERÁCIÓRA küldjük, egyébként vissza a Kutatónak
new_status = 'manual_review_needed' if current_attempts + 1 >= self.max_attempts else 'unverified'
# Elmentjük az AI részleges válaszát (vagy a hibát), hogy az admin lássa, mit rontott el a gép
review_data = ai_data if ai_data else {"error": "Nincs értékelhető JSON adat az AI-tól", "raw_context": base_info['web_context']}
await db.execute(
update(VehicleModelDefinition)
@@ -138,15 +151,19 @@ class TechEnricher:
attempts=current_attempts + 1,
last_error=str(e)[:200],
status=new_status,
specifications=review_data, # Kézi ellenőrzéshez beírjuk a törött adatot!
updated_at=func.now()
)
)
await db.commit()
if new_status == 'unverified':
logger.info("♻️ Akta visszaküldve a Robot-2-nek (Kutató).")
logger.info(f"♻️ Akta visszaküldve a Robot-2-nek (Kutató). {attempt_str}")
else:
logger.error(f"🛑 Max próbálkozás elérve! Kézi moderációra küldve: {v_ident}")
async def run(self):
logger.info(f"🚀 Alchemist Pro HIBRID ONLINE (Atomi Zárolás Patch)")
logger.info(f"🚀 Alchemist Pro HIBRID ONLINE (Atomi Zárolás + Moderáció Patch)")
while True:
if not self.check_budget():
logger.warning("💸 Napi AI limit kimerítve! Pihenés...")
@@ -155,7 +172,6 @@ class TechEnricher:
try:
async with AsyncSessionLocal() as db:
# ATOMI ZÁROLÁS (A "Szent Grál" a race condition ellen)
# A Robot-1 (ACTIVE) és a Robot-2 (awaiting_ai_synthesis) aktáit is felveszi!
query = text("""
UPDATE data.vehicle_model_definitions
SET status = 'ai_synthesis_in_progress'
@@ -204,5 +220,4 @@ class TechEnricher:
await asyncio.sleep(10)
if __name__ == "__main__":
import os # Import az AI limit környezeti változóhoz
asyncio.run(TechEnricher().run())

View File

@@ -0,0 +1,224 @@
import asyncio
import logging
import datetime
import random
import sys
import json
from sqlalchemy import text, func, update, case
from app.database import AsyncSessionLocal
from app.models.vehicle_definitions import VehicleModelDefinition
from app.models.asset import AssetCatalog
from app.services.ai_service import AIService
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] Vehicle-Alchemist-Pro: %(message)s', stream=sys.stdout)
logger = logging.getLogger("Vehicle-Robot-3-Alchemist-Pro")
class TechEnricher:
"""
Vehicle Robot 3: Alchemist Pro (Atomi Zárolás Patch)
Tiszta GPU fókusz: Csak az AI elemzésre és adategyesítésre koncentrál.
Nincs felesleges webkeresés. Szigorú Sane-Check.
"""
def __init__(self):
self.max_attempts = 5
self.daily_ai_limit = int(os.getenv("AI_DAILY_LIMIT", "10000"))
self.ai_calls_today = 0
self.last_reset_date = datetime.date.today()
def check_budget(self) -> bool:
if datetime.date.today() > self.last_reset_date:
self.ai_calls_today = 0
self.last_reset_date = datetime.date.today()
return self.ai_calls_today < self.daily_ai_limit
ddef is_data_sane(self, data: dict, base_info: dict) -> bool:
""" Szigorított, de intelligens AI Hallucináció szűrő """
if not data:
logger.warning("Sane-check: Teljesen üres AI válasz.")
return False
try:
# 1. Alapvető fizikai korlátok vizsgálata (csak az AI adatokon)
ai_ccm = int(data.get("ccm", 0) or 0)
ai_kw = int(data.get("kw", 0) or 0)
v_class = base_info.get("v_type", "car")
if ai_ccm > 18000:
logger.warning(f"Sane-check bukás: Irreális CCM érték ({ai_ccm})")
return False
if ai_kw > 1500 and v_class != "truck":
logger.warning(f"Sane-check bukás: Irreális KW érték ({ai_kw})")
return False
# 2. KOMBINÁLT Adat teljesség vizsgálata (RDW + AI)
# Ha az RDW tudja, akkor nem baj, ha az AI nem találta meg!
merged_kw = base_info.get('rdw_kw') or ai_kw
merged_ccm = base_info.get('rdw_ccm') or ai_ccm
fuel = data.get("fuel_type", base_info.get("rdw_fuel", "")).lower()
# Ha még kombinálva sincs meg a KW
if merged_kw == 0:
logger.warning("Sane-check figyelmeztetés: Hiányzó KW (se RDW, se AI). Engedélyezve részleges adatként.")
# Nem térünk vissza False-al, inkább mentsük el, amit eddig tudunk!
# Ha még kombinálva sincs meg a CCM (és nem elektromos)
if merged_ccm == 0 and "electric" not in fuel and "elektric" not in fuel and v_class != "trailer":
logger.warning("Sane-check figyelmeztetés: Hiányzó CCM egy belsőégésű motornál. Engedélyezve részleges adatként.")
# Ezt is átengedjük, hogy kitörjünk a végtelen hurokból.
return True
except Exception as e:
logger.error(f"Sane check hiba: {e}")
return False
async def process_single_record(self, db, record_id: int, base_info: dict, current_attempts: int):
try:
logger.info(f"🧠 AI dúsítás indul: {base_info['make']} {base_info['m_name']}")
# 1. LÉPÉS: AI Hívás (Rábízzuk az adatokat a modellre)
ai_data = await AIService.get_clean_vehicle_data(
base_info['make'],
base_info['m_name'],
base_info
)
# 2. LÉPÉS: Validáció (Ha az AI rossz adatot ad, NEM megyünk ki a webre, hanem dobjuk az aktát!)
if not ai_data or not self.is_data_sane(ai_data, base_info):
raise ValueError("Az AI hiányos adatot adott vissza vagy hallucinált.")
# 3. LÉPÉS: HIBRID MERGE (Az RDW adatok felülbírálják az AI-t a hatósági paramétereknél)
final_kw = base_info['rdw_kw'] if base_info['rdw_kw'] > 0 else (ai_data.get("kw") or 0)
final_ccm = base_info['rdw_ccm'] if base_info['rdw_ccm'] > 0 else (ai_data.get("ccm") or 0)
# Üzemanyag tisztítása
fuel_rdw = base_info.get('rdw_fuel', '')
final_fuel = fuel_rdw if fuel_rdw and fuel_rdw != "Unknown" else ai_data.get("fuel_type", "petrol")
final_engine = base_info['rdw_engine'] if base_info['rdw_engine'] else ai_data.get("engine_code", "Unknown")
final_euro = base_info['rdw_euro'] or ai_data.get("euro_classification")
final_cylinders = base_info['rdw_cylinders'] or ai_data.get("cylinders")
# 4. LÉPÉS: Mentés az Arany Katalógusba
clean_model = str(ai_data.get("marketing_name", base_info['m_name']))[:50].upper()
cat_stmt = text("""
INSERT INTO data.vehicle_catalog
(master_definition_id, make, model, power_kw, engine_capacity, fuel_type, factory_data)
VALUES (:m_id, :make, :model, :kw, :ccm, :fuel, :factory)
RETURNING id;
""")
await db.execute(cat_stmt, {
"m_id": record_id,
"make": base_info['make'].upper(),
"model": clean_model,
"kw": final_kw,
"ccm": final_ccm,
"fuel": final_fuel,
"factory": json.dumps(ai_data)
})
# 5. LÉPÉS: Staging tábla (VMD) lezárása
await db.execute(
update(VehicleModelDefinition)
.where(VehicleModelDefinition.id == record_id)
.values(
status="gold_enriched",
engine_capacity=final_ccm,
power_kw=final_kw,
fuel_type=final_fuel,
engine_code=final_engine,
euro_classification=final_euro,
cylinders=final_cylinders,
specifications=ai_data, # Elmentjük az AI teljes outputját a mestertáblába is
updated_at=func.now()
)
)
await db.commit()
logger.info(f"✨ ARANY REKORD KÉSZ: {base_info['make'].upper()} {clean_model}")
self.ai_calls_today += 1
except Exception as e:
await db.rollback()
logger.warning(f"⚠️ Alkimista hiba ({base_info['make']} {base_info['m_name']}): {e}")
# Visszaküldés a sorba vagy felfüggesztés
new_status = 'suspended' if current_attempts + 1 >= self.max_attempts else 'unverified'
await db.execute(
update(VehicleModelDefinition)
.where(VehicleModelDefinition.id == record_id)
.values(
attempts=current_attempts + 1,
last_error=str(e)[:200],
status=new_status,
updated_at=func.now()
)
)
await db.commit()
if new_status == 'unverified':
logger.info("♻️ Akta visszaküldve a Robot-2-nek (Kutató).")
async def run(self):
logger.info(f"🚀 Alchemist Pro HIBRID ONLINE (Atomi Zárolás Patch)")
while True:
if not self.check_budget():
logger.warning("💸 Napi AI limit kimerítve! Pihenés...")
await asyncio.sleep(3600); continue
try:
async with AsyncSessionLocal() as db:
# ATOMI ZÁROLÁS (A "Szent Grál" a race condition ellen)
# A Robot-1 (ACTIVE) és a Robot-2 (awaiting_ai_synthesis) aktáit is felveszi!
query = text("""
UPDATE data.vehicle_model_definitions
SET status = 'ai_synthesis_in_progress'
WHERE id = (
SELECT id FROM data.vehicle_model_definitions
WHERE status IN ('awaiting_ai_synthesis', 'ACTIVE')
AND attempts < :max_attempts
ORDER BY
CASE WHEN status = 'awaiting_ai_synthesis' THEN 1 ELSE 2 END,
priority_score DESC
FOR UPDATE SKIP LOCKED
LIMIT 1
)
RETURNING id, make, marketing_name, vehicle_class, power_kw, engine_capacity,
fuel_type, engine_code, euro_classification, cylinders, raw_search_context, attempts;
""")
result = await db.execute(query, {"max_attempts": self.max_attempts})
task = result.fetchone()
await db.commit()
if task:
# Szétbontjuk a lekérdezett rekordot a base_info dict-be
r_id = task[0]
base_info = {
"make": task[1], "m_name": task[2], "v_type": task[3] or "car",
"rdw_kw": task[4] or 0, "rdw_ccm": task[5] or 0,
"rdw_fuel": task[6] or "petrol", "rdw_engine": task[7] or "",
"rdw_euro": task[8], "rdw_cylinders": task[9],
"web_context": task[10] or ""
}
attempts = task[11]
# Külön adatbázis kapcsolat a feldolgozáshoz (hosszú AI hívás miatt)
async with AsyncSessionLocal() as process_db:
await self.process_single_record(process_db, r_id, base_info, attempts)
# GPU hűtés / Ollama rate limit
await asyncio.sleep(random.uniform(1.5, 3.5))
else:
logger.info("😴 Nincs feldolgozandó akta, az Alkimista pihen...")
await asyncio.sleep(15)
except Exception as e:
logger.error(f"💀 Kritikus hiba a főciklusban: {e}")
await asyncio.sleep(10)
if __name__ == "__main__":
import os # Import az AI limit környezeti változóhoz
asyncio.run(TechEnricher().run())