Spaces:
Building
Building
Update chat_handler.py
Browse files- chat_handler.py +294 -167
chat_handler.py
CHANGED
@@ -1,18 +1,19 @@
|
|
1 |
"""
|
2 |
-
Flare β Chat Handler (v1.
|
3 |
-
|
4 |
"""
|
5 |
|
6 |
-
import re, json,
|
7 |
from datetime import datetime
|
8 |
from typing import Dict, List, Optional
|
9 |
from fastapi import APIRouter, HTTPException, Header
|
10 |
from pydantic import BaseModel
|
11 |
-
|
12 |
from prompt_builder import build_intent_prompt, build_parameter_prompt, log
|
13 |
from api_executor import call_api as execute_api
|
14 |
from config_provider import ConfigProvider
|
15 |
from validation_engine import validate
|
|
|
16 |
|
17 |
# βββββββββββββββββββββββββ HELPERS βββββββββββββββββββββββββ #
|
18 |
def _trim_response(raw: str) -> str:
|
@@ -29,7 +30,7 @@ def _trim_response(raw: str) -> str:
|
|
29 |
raw = re.sub(r"HoΕ[\s-]?geldin(iz)?", "HoΕ geldiniz", raw, flags=re.IGNORECASE)
|
30 |
return raw.strip()
|
31 |
|
32 |
-
def _safe_intent_parse(raw: str) ->
|
33 |
"""Extract intent name and extra tail."""
|
34 |
m = re.search(r"#DETECTED_INTENT:\s*([A-Za-z0-9_-]+)", raw)
|
35 |
if not m:
|
@@ -40,192 +41,318 @@ def _safe_intent_parse(raw: str) -> (str, str):
|
|
40 |
|
41 |
# βββββββββββββββββββββββββ CONFIG βββββββββββββββββββββββββ #
|
42 |
cfg = ConfigProvider.get()
|
43 |
-
PROJECTS = {p.name: p for p in cfg.projects}
|
44 |
SPARK_URL = str(cfg.global_config.spark_endpoint).rstrip("/")
|
45 |
ALLOWED_INTENTS = {"flight-booking", "flight-info", "booking-cancel"}
|
46 |
|
47 |
-
# βββββββββββββββββββββββββ SESSION βββββββββββββββββββββββββ #
|
48 |
-
class Session:
|
49 |
-
def __init__(self, project_name: str):
|
50 |
-
self.id = str(uuid.uuid4())
|
51 |
-
self.project = PROJECTS[project_name]
|
52 |
-
self.history: List[Dict[str, str]] = []
|
53 |
-
self.vars: Dict[str, str] = {}
|
54 |
-
self.awaiting: Optional[Dict] = None
|
55 |
-
log(f"π Session {self.id}")
|
56 |
-
|
57 |
-
SESSIONS: Dict[str, Session] = {}
|
58 |
-
|
59 |
# βββββββββββββββββββββββββ SPARK βββββββββββββββββββββββββ #
|
60 |
async def spark_generate(s: Session, prompt: str, user_msg: str) -> str:
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
|
80 |
# βββββββββββββββββββββββββ FASTAPI βββββββββββββββββββββββββ #
|
81 |
router = APIRouter()
|
82 |
-
@router.get("/") # health
|
83 |
-
def health(): return {"ok": True}
|
84 |
-
|
85 |
-
class Start(BaseModel): project_name: str
|
86 |
-
class Body(BaseModel): user_input: str
|
87 |
-
class Resp(BaseModel): session_id: str; answer: str
|
88 |
-
|
89 |
-
@router.post("/start_session", response_model=Resp)
|
90 |
-
async def start_session(req: Start):
|
91 |
-
if req.project_name not in PROJECTS:
|
92 |
-
raise HTTPException(404, "project")
|
93 |
-
s = Session(req.project_name)
|
94 |
-
SESSIONS[s.id] = s
|
95 |
-
greet = "HoΕ geldiniz! Size nasΔ±l yardΔ±mcΔ± olabilirim?"
|
96 |
-
return Resp(session_id=s.id, answer=greet)
|
97 |
-
|
98 |
-
@router.post("/chat", response_model=Resp)
|
99 |
-
async def chat(body: Body, x_session_id: str = Header(...)):
|
100 |
-
if x_session_id not in SESSIONS:
|
101 |
-
raise HTTPException(404, "session")
|
102 |
-
s = SESSIONS[x_session_id]
|
103 |
-
user = body.user_input.strip()
|
104 |
-
s.history.append({"role": "user", "content": user})
|
105 |
-
|
106 |
-
# Get the first published version
|
107 |
-
version = next((v for v in s.project.versions if v.published), None)
|
108 |
-
if not version:
|
109 |
-
raise HTTPException(500, "No published version found")
|
110 |
-
|
111 |
-
# ---------- follow-up ----------
|
112 |
-
if s.awaiting:
|
113 |
-
ans = await _followup(s, user)
|
114 |
-
s.history.append({"role": "assistant", "content": ans})
|
115 |
-
return Resp(session_id=s.id, answer=ans)
|
116 |
-
|
117 |
-
# ---------- intent detect ----------
|
118 |
-
p = build_intent_prompt(
|
119 |
-
version.general_prompt,
|
120 |
-
s.history, user, version.intents)
|
121 |
-
raw = await spark_generate(s, p, user)
|
122 |
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
|
128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
if not raw.startswith("#DETECTED_INTENT"):
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
return
|
139 |
-
|
140 |
-
|
141 |
-
if
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
return
|
154 |
-
|
155 |
-
def
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
if missing:
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
|
|
|
|
166 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
167 |
if missing:
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
async def _followup(s, user):
|
175 |
-
intent_cfg = s.awaiting["intent"]
|
176 |
-
miss = s.awaiting["missing"]
|
177 |
-
prmpt = build_parameter_prompt(intent_cfg, miss, user, s.history)
|
178 |
-
raw = await spark_generate(s, prmpt, user)
|
179 |
-
if not raw.startswith("#PARAMETERS:"):
|
180 |
-
return "ΓzgΓΌnΓΌm, anlayamadΔ±m."
|
181 |
-
|
182 |
-
_proc_params(s, intent_cfg, raw)
|
183 |
-
miss = _missing(s, intent_cfg)
|
184 |
-
if miss:
|
185 |
-
s.awaiting["missing"] = miss
|
186 |
-
cap = next(p for p in intent_cfg.parameters if p.name == miss[0]).caption
|
187 |
-
return f"{cap} nedir?"
|
188 |
-
s.awaiting = None
|
189 |
-
return await _call_api(s, intent_cfg)
|
190 |
-
|
191 |
-
def _proc_params(s, intent_cfg, raw):
|
192 |
-
try:
|
193 |
-
d = json.loads(raw[len("#PARAMETERS:"):])
|
194 |
-
except:
|
195 |
-
return False
|
196 |
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
|
202 |
-
|
203 |
-
|
204 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
205 |
|
206 |
-
|
207 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
208 |
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
api_cfg = cfg.get_api(api_name)
|
213 |
-
if not api_cfg:
|
214 |
-
return intent_cfg.get("fallback_error_prompt", "Δ°Εlem baΕarΔ±sΔ±z oldu.")
|
215 |
-
|
216 |
try:
|
217 |
-
|
218 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
219 |
api_json = response.json()
|
220 |
|
221 |
# Humanize response
|
222 |
-
|
223 |
-
|
224 |
-
|
|
|
|
|
|
|
|
|
|
|
225 |
return human_response if human_response else f"Δ°Εlem sonucu: {api_json}"
|
226 |
else:
|
227 |
-
|
|
|
228 |
|
|
|
|
|
|
|
229 |
except Exception as e:
|
230 |
log(f"β API call error: {e}")
|
231 |
-
|
|
|
|
1 |
"""
|
2 |
+
Flare β Chat Handler (v1.5 Β· modΓΌler yapΔ±)
|
3 |
+
==========================================
|
4 |
"""
|
5 |
|
6 |
+
import re, json, sys, httpx
|
7 |
from datetime import datetime
|
8 |
from typing import Dict, List, Optional
|
9 |
from fastapi import APIRouter, HTTPException, Header
|
10 |
from pydantic import BaseModel
|
11 |
+
|
12 |
from prompt_builder import build_intent_prompt, build_parameter_prompt, log
|
13 |
from api_executor import call_api as execute_api
|
14 |
from config_provider import ConfigProvider
|
15 |
from validation_engine import validate
|
16 |
+
from session import session_store, Session
|
17 |
|
18 |
# βββββββββββββββββββββββββ HELPERS βββββββββββββββββββββββββ #
|
19 |
def _trim_response(raw: str) -> str:
|
|
|
30 |
raw = re.sub(r"HoΕ[\s-]?geldin(iz)?", "HoΕ geldiniz", raw, flags=re.IGNORECASE)
|
31 |
return raw.strip()
|
32 |
|
33 |
+
def _safe_intent_parse(raw: str) -> tuple[str, str]:
|
34 |
"""Extract intent name and extra tail."""
|
35 |
m = re.search(r"#DETECTED_INTENT:\s*([A-Za-z0-9_-]+)", raw)
|
36 |
if not m:
|
|
|
41 |
|
42 |
# βββββββββββββββββββββββββ CONFIG βββββββββββββββββββββββββ #
|
43 |
cfg = ConfigProvider.get()
|
|
|
44 |
SPARK_URL = str(cfg.global_config.spark_endpoint).rstrip("/")
|
45 |
ALLOWED_INTENTS = {"flight-booking", "flight-info", "booking-cancel"}
|
46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
# βββββββββββββββββββββββββ SPARK βββββββββββββββββββββββββ #
|
48 |
async def spark_generate(s: Session, prompt: str, user_msg: str) -> str:
|
49 |
+
"""Call Spark with proper error handling"""
|
50 |
+
try:
|
51 |
+
project = next((p for p in cfg.projects if p.name == s.project_name), None)
|
52 |
+
if not project:
|
53 |
+
raise ValueError(f"Project not found: {s.project_name}")
|
54 |
+
|
55 |
+
version = next((v for v in project.versions if v.published), None)
|
56 |
+
if not version:
|
57 |
+
raise ValueError("No published version found")
|
58 |
+
|
59 |
+
payload = {
|
60 |
+
"project_name": s.project_name,
|
61 |
+
"user_input": user_msg,
|
62 |
+
"context": s.chat_history[-10:],
|
63 |
+
"system_prompt": prompt
|
64 |
+
}
|
65 |
+
|
66 |
+
log(f"π Calling Spark for session {s.session_id[:8]}...")
|
67 |
+
async with httpx.AsyncClient(timeout=60) as client:
|
68 |
+
response = await client.post(SPARK_URL + "/generate", json=payload)
|
69 |
+
response.raise_for_status()
|
70 |
+
data = response.json()
|
71 |
+
raw = (data.get("assistant") or data.get("model_answer") or data.get("text", "")).strip()
|
72 |
+
log(f"πͺ Spark raw: {raw[:120]!r}")
|
73 |
+
return raw
|
74 |
+
|
75 |
+
except httpx.TimeoutException:
|
76 |
+
log(f"β±οΈ Spark timeout for session {s.session_id[:8]}")
|
77 |
+
raise
|
78 |
+
except Exception as e:
|
79 |
+
log(f"β Spark error: {e}")
|
80 |
+
raise
|
81 |
|
82 |
# βββββββββββββββββββββββββ FASTAPI βββββββββββββββββββββββββ #
|
83 |
router = APIRouter()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
|
85 |
+
@router.get("/")
|
86 |
+
def health():
|
87 |
+
return {"status": "ok", "sessions": len(session_store._sessions)}
|
88 |
+
|
89 |
+
class StartRequest(BaseModel):
|
90 |
+
project_name: str
|
91 |
+
|
92 |
+
class ChatRequest(BaseModel):
|
93 |
+
user_input: str
|
94 |
+
|
95 |
+
class ChatResponse(BaseModel):
|
96 |
+
session_id: str
|
97 |
+
answer: str
|
98 |
+
|
99 |
+
@router.post("/start_session", response_model=ChatResponse)
|
100 |
+
async def start_session(req: StartRequest):
|
101 |
+
"""Create new session"""
|
102 |
+
try:
|
103 |
+
# Validate project exists
|
104 |
+
project = next((p for p in cfg.projects if p.name == req.project_name and p.enabled), None)
|
105 |
+
if not project:
|
106 |
+
raise HTTPException(404, f"Project '{req.project_name}' not found or disabled")
|
107 |
+
|
108 |
+
# Create session
|
109 |
+
session = session_store.create_session(req.project_name)
|
110 |
+
greeting = "HoΕ geldiniz! Size nasΔ±l yardΔ±mcΔ± olabilirim?"
|
111 |
+
session.add_turn("assistant", greeting)
|
112 |
+
|
113 |
+
return ChatResponse(session_id=session.session_id, answer=greeting)
|
114 |
+
|
115 |
+
except Exception as e:
|
116 |
+
log(f"β Error creating session: {e}")
|
117 |
+
raise HTTPException(500, str(e))
|
118 |
+
|
119 |
+
@router.post("/chat", response_model=ChatResponse)
|
120 |
+
async def chat(body: ChatRequest, x_session_id: str = Header(...)):
|
121 |
+
"""Process chat message"""
|
122 |
+
try:
|
123 |
+
# Get session
|
124 |
+
session = session_store.get_session(x_session_id)
|
125 |
+
if not session:
|
126 |
+
raise HTTPException(404, "Session not found")
|
127 |
+
|
128 |
+
user_input = body.user_input.strip()
|
129 |
+
if not user_input:
|
130 |
+
raise HTTPException(400, "Empty message")
|
131 |
+
|
132 |
+
session.add_turn("user", user_input)
|
133 |
+
|
134 |
+
# Get project config
|
135 |
+
project = next((p for p in cfg.projects if p.name == session.project_name), None)
|
136 |
+
if not project:
|
137 |
+
raise HTTPException(500, "Project configuration lost")
|
138 |
+
|
139 |
+
version = next((v for v in project.versions if v.published), None)
|
140 |
+
if not version:
|
141 |
+
raise HTTPException(500, "No published version")
|
142 |
|
143 |
+
# Handle based on state
|
144 |
+
if session.state == "await_param":
|
145 |
+
answer = await _handle_parameter_followup(session, user_input, version)
|
146 |
+
else:
|
147 |
+
answer = await _handle_new_message(session, user_input, version)
|
148 |
+
|
149 |
+
session.add_turn("assistant", answer)
|
150 |
+
return ChatResponse(session_id=session.session_id, answer=answer)
|
151 |
+
|
152 |
+
except HTTPException:
|
153 |
+
raise
|
154 |
+
except Exception as e:
|
155 |
+
log(f"β Chat error: {e}")
|
156 |
+
session.reset_flow()
|
157 |
+
error_msg = "Bir hata oluΕtu. LΓΌtfen tekrar deneyin."
|
158 |
+
session.add_turn("assistant", error_msg)
|
159 |
+
return ChatResponse(session_id=x_session_id, answer=error_msg)
|
160 |
+
|
161 |
+
# βββββββββββββββββββββββββ MESSAGE HANDLERS βββββββββββββββββββββββββ #
|
162 |
+
async def _handle_new_message(session: Session, user_input: str, version) -> str:
|
163 |
+
"""Handle new message (not parameter followup)"""
|
164 |
+
# Build intent detection prompt
|
165 |
+
prompt = build_intent_prompt(
|
166 |
+
version.general_prompt,
|
167 |
+
session.chat_history,
|
168 |
+
user_input,
|
169 |
+
version.intents
|
170 |
+
)
|
171 |
+
|
172 |
+
# Get Spark response
|
173 |
+
raw = await spark_generate(session, prompt, user_input)
|
174 |
+
|
175 |
+
# Empty response fallback
|
176 |
+
if not raw:
|
177 |
+
return "ΓzgΓΌnΓΌm, mesajΔ±nΔ±zΔ± anlayamadΔ±m. LΓΌtfen tekrar dener misiniz?"
|
178 |
+
|
179 |
+
# Check for intent
|
180 |
if not raw.startswith("#DETECTED_INTENT"):
|
181 |
+
# Small talk response
|
182 |
+
return _trim_response(raw)
|
183 |
+
|
184 |
+
# Parse intent
|
185 |
+
intent_name, tail = _safe_intent_parse(raw)
|
186 |
+
|
187 |
+
# Validate intent
|
188 |
+
if intent_name not in ALLOWED_INTENTS:
|
189 |
+
return _trim_response(tail) if tail else "Size nasΔ±l yardΔ±mcΔ± olabilirim?"
|
190 |
+
|
191 |
+
# Short message guard (less than 3 words usually means incomplete request)
|
192 |
+
if len(user_input.split()) < 3 and intent_name != "flight-info":
|
193 |
+
return _trim_response(tail) if tail else "LΓΌtfen talebinizi biraz daha detaylandΔ±rΔ±r mΔ±sΔ±nΔ±z?"
|
194 |
+
|
195 |
+
# Find intent config
|
196 |
+
intent_config = next((i for i in version.intents if i.name == intent_name), None)
|
197 |
+
if not intent_config:
|
198 |
+
return "ΓzgΓΌnΓΌm, bu iΕlemi gerΓ§ekleΕtiremiyorum."
|
199 |
+
|
200 |
+
# Set intent in session
|
201 |
+
session.last_intent = intent_name
|
202 |
+
|
203 |
+
# Extract parameters
|
204 |
+
return await _extract_parameters(session, intent_config, user_input)
|
205 |
+
|
206 |
+
async def _handle_parameter_followup(session: Session, user_input: str, version) -> str:
|
207 |
+
"""Handle parameter collection followup"""
|
208 |
+
if not session.last_intent:
|
209 |
+
session.reset_flow()
|
210 |
+
return "ΓzgΓΌnΓΌm, hangi iΕlem iΓ§in bilgi istediΔimi unuttum. BaΕtan baΕlayalΔ±m."
|
211 |
+
|
212 |
+
# Get intent config
|
213 |
+
intent_config = next((i for i in version.intents if i.name == session.last_intent), None)
|
214 |
+
if not intent_config:
|
215 |
+
session.reset_flow()
|
216 |
+
return "Bir hata oluΕtu. LΓΌtfen tekrar deneyin."
|
217 |
+
|
218 |
+
# Try to extract missing parameters
|
219 |
+
missing = session.awaiting_parameters
|
220 |
+
prompt = build_parameter_prompt(intent_config, missing, user_input, session.chat_history)
|
221 |
+
raw = await spark_generate(session, prompt, user_input)
|
222 |
+
|
223 |
+
if not raw.startswith("#PARAMETERS:"):
|
224 |
+
# Increment miss count
|
225 |
+
session.missing_ask_count += 1
|
226 |
+
if session.missing_ask_count >= 3:
|
227 |
+
session.reset_flow()
|
228 |
+
return "ΓzgΓΌnΓΌm, istediΔiniz bilgileri anlayamadΔ±m. BaΕka bir konuda yardΔ±mcΔ± olabilir miyim?"
|
229 |
+
return "ΓzgΓΌnΓΌm, anlayamadΔ±m. LΓΌtfen tekrar sΓΆyler misiniz?"
|
230 |
+
|
231 |
+
# Process parameters
|
232 |
+
success = _process_parameters(session, intent_config, raw)
|
233 |
+
if not success:
|
234 |
+
return "GirdiΔiniz bilgilerde bir hata var. LΓΌtfen kontrol edip tekrar deneyin."
|
235 |
+
|
236 |
+
# Check if we have all required parameters
|
237 |
+
missing = _get_missing_parameters(session, intent_config)
|
238 |
if missing:
|
239 |
+
session.awaiting_parameters = missing
|
240 |
+
param = next(p for p in intent_config.parameters if p.name == missing[0])
|
241 |
+
return f"{param.caption} bilgisini alabilir miyim?"
|
242 |
+
|
243 |
+
# All parameters collected, call API
|
244 |
+
session.state = "call_api"
|
245 |
+
return await _execute_api_call(session, intent_config)
|
246 |
|
247 |
+
# βββββββββββββββββββββββββ PARAMETER HANDLING βββββββββββββββββββββββββ #
|
248 |
+
async def _extract_parameters(session: Session, intent_config, user_input: str) -> str:
|
249 |
+
"""Extract parameters from user input"""
|
250 |
+
missing = _get_missing_parameters(session, intent_config)
|
251 |
+
if not missing:
|
252 |
+
# All parameters already available
|
253 |
+
return await _execute_api_call(session, intent_config)
|
254 |
+
|
255 |
+
# Build parameter extraction prompt
|
256 |
+
prompt = build_parameter_prompt(intent_config, missing, user_input, session.chat_history)
|
257 |
+
raw = await spark_generate(session, prompt, user_input)
|
258 |
+
|
259 |
+
if raw.startswith("#PARAMETERS:"):
|
260 |
+
success = _process_parameters(session, intent_config, raw)
|
261 |
+
if success:
|
262 |
+
missing = _get_missing_parameters(session, intent_config)
|
263 |
+
|
264 |
if missing:
|
265 |
+
# Still missing parameters
|
266 |
+
session.state = "await_param"
|
267 |
+
session.awaiting_parameters = missing
|
268 |
+
session.missing_ask_count = 0
|
269 |
+
param = next(p for p in intent_config.parameters if p.name == missing[0])
|
270 |
+
return f"{param.caption} bilgisini alabilir miyim?"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
271 |
|
272 |
+
# All parameters collected
|
273 |
+
return await _execute_api_call(session, intent_config)
|
274 |
+
|
275 |
+
def _get_missing_parameters(session: Session, intent_config) -> List[str]:
|
276 |
+
"""Get list of missing required parameters"""
|
277 |
+
return [
|
278 |
+
p.name for p in intent_config.parameters
|
279 |
+
if p.required and p.variable_name not in session.variables
|
280 |
+
]
|
281 |
+
|
282 |
+
def _process_parameters(session: Session, intent_config, raw: str) -> bool:
|
283 |
+
"""Process parameter extraction response"""
|
284 |
+
try:
|
285 |
+
json_str = raw[len("#PARAMETERS:"):]
|
286 |
+
data = json.loads(json_str)
|
287 |
+
|
288 |
+
extracted = data.get("extracted", [])
|
289 |
+
any_valid = False
|
290 |
|
291 |
+
for param_data in extracted:
|
292 |
+
param_name = param_data.get("name")
|
293 |
+
param_value = param_data.get("value")
|
294 |
+
|
295 |
+
if not param_name or not param_value:
|
296 |
+
continue
|
297 |
+
|
298 |
+
# Find parameter config
|
299 |
+
param_config = next(
|
300 |
+
(p for p in intent_config.parameters if p.name == param_name),
|
301 |
+
None
|
302 |
+
)
|
303 |
+
if not param_config:
|
304 |
+
continue
|
305 |
|
306 |
+
# Validate parameter
|
307 |
+
if validate(str(param_value), param_config):
|
308 |
+
session.variables[param_config.variable_name] = str(param_value)
|
309 |
+
any_valid = True
|
310 |
+
log(f"β
Extracted {param_name}={param_value}")
|
311 |
+
else:
|
312 |
+
log(f"β Invalid {param_name}={param_value}")
|
313 |
+
|
314 |
+
return any_valid
|
315 |
+
|
316 |
+
except Exception as e:
|
317 |
+
log(f"β Parameter processing error: {e}")
|
318 |
+
return False
|
319 |
|
320 |
+
# βββββββββββββββββββββββββ API EXECUTION βββββββββββββββββββββββββ #
|
321 |
+
async def _execute_api_call(session: Session, intent_config) -> str:
|
322 |
+
"""Execute API call and return humanized response"""
|
|
|
|
|
|
|
|
|
323 |
try:
|
324 |
+
session.state = "call_api"
|
325 |
+
api_name = intent_config.action
|
326 |
+
api_config = cfg.get_api(api_name)
|
327 |
+
|
328 |
+
if not api_config:
|
329 |
+
session.reset_flow()
|
330 |
+
return intent_config.get("fallback_error_prompt", "Δ°Εlem baΕarΔ±sΔ±z oldu.")
|
331 |
+
|
332 |
+
log(f"π‘ Calling API: {api_name}")
|
333 |
+
|
334 |
+
# Execute API call
|
335 |
+
response = execute_api(api_config, session.variables)
|
336 |
api_json = response.json()
|
337 |
|
338 |
# Humanize response
|
339 |
+
session.state = "humanize"
|
340 |
+
if api_config.response_prompt:
|
341 |
+
prompt = api_config.response_prompt.replace(
|
342 |
+
"{{api_response}}",
|
343 |
+
json.dumps(api_json, ensure_ascii=False)
|
344 |
+
)
|
345 |
+
human_response = await spark_generate(session, prompt, json.dumps(api_json))
|
346 |
+
session.reset_flow()
|
347 |
return human_response if human_response else f"Δ°Εlem sonucu: {api_json}"
|
348 |
else:
|
349 |
+
session.reset_flow()
|
350 |
+
return f"Δ°Εlem tamamlandΔ±: {api_json}"
|
351 |
|
352 |
+
except httpx.TimeoutException:
|
353 |
+
session.reset_flow()
|
354 |
+
return intent_config.get("fallback_timeout_prompt", "Δ°Εlem zaman aΕΔ±mΔ±na uΔradΔ±.")
|
355 |
except Exception as e:
|
356 |
log(f"β API call error: {e}")
|
357 |
+
session.reset_flow()
|
358 |
+
return intent_config.get("fallback_error_prompt", "Δ°Εlem sΔ±rasΔ±nda bir hata oluΕtu.")
|