Skip to content

Commit e764954

Browse files
committed
feat(ai_client): integrate skills registry and add OLLAMA_TIMEOUT
1 parent e1977b0 commit e764954

1 file changed

Lines changed: 74 additions & 6 deletions

File tree

backend/app/ai_client.py

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,28 @@
22
import requests
33
from typing import Dict
44

5-
OLLAMA_URL = os.getenv('OLLAMA_URL') # e.g. http://localhost:11434/v1/generate
5+
OLLAMA_URL = os.getenv("OLLAMA_URL") # e.g. http://localhost:11434/v1/generate
6+
OLLAMA_TIMEOUT = int(os.getenv("OLLAMA_TIMEOUT", "10"))
7+
8+
# Intentar importar el registry de skills; la app ya contiene `backend/app/skills_registry.py`
9+
try:
10+
from .skills_registry import get_skill, all_skills, load_all_skills
11+
try:
12+
load_all_skills()
13+
except Exception:
14+
pass
15+
except Exception:
16+
# Proporcionar stubs si el módulo no está disponible
17+
def get_skill(name):
18+
return None
19+
20+
def all_skills():
21+
return []
22+
623

724
def _build_prompt(user_input: Dict, top_stack: Dict) -> str:
8-
proyecto = user_input.get('proyecto', 'un proyecto')
9-
prioridades = user_input.get('weights', {})
25+
proyecto = user_input.get("proyecto", "un proyecto")
26+
prioridades = user_input.get("weights", {})
1027
prompt = (
1128
f"Actúa como un Arquitecto Senior. El usuario quiere construir: {proyecto}. "
1229
f"Prioridades: {prioridades}.\n\n"
@@ -16,21 +33,72 @@ def _build_prompt(user_input: Dict, top_stack: Dict) -> str:
1633
)
1734
return prompt
1835

36+
37+
def _run_skill_if_available(user_input: Dict, top_stack: Dict) -> str:
38+
"""Intenta ejecutar una skill registrada para generar la justificación.
39+
40+
Prioridad de selección de skill:
41+
1) `JUSTIFICATION_SKILL` env var
42+
2) primera skill registrada (si existe)
43+
Si falla o no existe, devuelve None para indicar que hay que usar LLM/fallback.
44+
"""
45+
skill_name = os.getenv("JUSTIFICATION_SKILL")
46+
try:
47+
if not skill_name:
48+
skills = all_skills()
49+
if skills:
50+
skill_name = skills[0]
51+
52+
if not skill_name:
53+
return None
54+
55+
module = get_skill(skill_name)
56+
if not module:
57+
return None
58+
59+
payload = {
60+
"user_weights": user_input.get("weights", {}),
61+
"tech": top_stack,
62+
"context": {"request_id": user_input.get("request_id")}
63+
}
64+
result = module.run_skill(payload)
65+
if isinstance(result, dict) and result.get("status") == "ok":
66+
res = result.get("result") or {}
67+
if isinstance(res, dict):
68+
text = res.get("text")
69+
if text:
70+
return text
71+
# if result.result is a string or fallback
72+
if isinstance(res, str) and res:
73+
return res
74+
except Exception:
75+
# No propagamos errores en la generación por skill; seguiremos al LLM/fallback.
76+
return None
77+
78+
return None
79+
80+
1981
def generate_justification(user_input: Dict, top_stack: Dict) -> str:
82+
# 1) Intentar skill registrado
83+
text = _run_skill_if_available(user_input, top_stack)
84+
if text:
85+
return text
86+
87+
# 2) Intentar LLM externo
2088
prompt = _build_prompt(user_input, top_stack)
2189

2290
if OLLAMA_URL:
2391
try:
24-
r = requests.post(OLLAMA_URL, json={"prompt": prompt}, timeout=10)
92+
r = requests.post(OLLAMA_URL, json={"prompt": prompt}, timeout=OLLAMA_TIMEOUT)
2593
r.raise_for_status()
2694
data = r.json()
2795
# Expect provider to return {'text': '...'} or similar
28-
return data.get('text') or data.get('output') or str(data)
96+
return data.get("text") or data.get("output") or str(data)
2997
except Exception:
3098
# fallback to local template
3199
pass
32100

33-
# Fallback (deterministic, safe)
101+
# 3) Fallback determinista y seguro
34102
justification = (
35103
f"1) Rapidez de entrega: {top_stack['name']} permite iterar rápidamente y aprovechar ecosistemas maduros.\n"
36104
f"2) Ecosistema y librerías: ofrece amplia disponibilidad de paquetes y soporte.\n"

0 commit comments

Comments
 (0)