ciyidogan commited on
Commit
25df053
Β·
verified Β·
1 Parent(s): 7f68c27

Update chat_handler.py

Browse files
Files changed (1) hide show
  1. chat_handler.py +91 -39
chat_handler.py CHANGED
@@ -1,6 +1,6 @@
1
  """
2
- Flare – Chat Handler (v1.3 Β· robust trimming + param validation)
3
- =================================================================
4
  """
5
 
6
  import re, json, uuid, sys, httpx, commentjson
@@ -10,6 +10,9 @@ from fastapi import APIRouter, HTTPException, Header
10
  from pydantic import BaseModel
11
  from commentjson import JSONLibraryException
12
  from prompt_builder import build_intent_prompt, build_parameter_prompt, log
 
 
 
13
 
14
  # ───────────────────────── HELPERS ───────────────────────── #
15
  def _trim_response(raw: str) -> str:
@@ -36,17 +39,16 @@ def _safe_intent_parse(raw: str) -> (str, str):
36
  return name, tail
37
 
38
  # ───────────────────────── CONFIG ───────────────────────── #
39
- CFG = commentjson.load(open("service_config.jsonc", encoding="utf-8"))
40
- PROJECTS = {p["name"]: p for p in CFG["projects"]}
41
- APIS = {a["name"]: a for a in CFG["apis"]}
42
- SPARK_URL = CFG["config"]["spark_endpoint"].rstrip("/") + "/generate"
43
  ALLOWED_INTENTS = {"flight-booking", "flight-info", "booking-cancel"}
44
 
45
  # ───────────────────────── SESSION ───────────────────────── #
46
  class Session:
47
- def __init__(self, project: str):
48
  self.id = str(uuid.uuid4())
49
- self.project = PROJECTS[project]
50
  self.history: List[Dict[str, str]] = []
51
  self.vars: Dict[str, str] = {}
52
  self.awaiting: Optional[Dict] = None
@@ -56,8 +58,13 @@ SESSIONS: Dict[str, Session] = {}
56
 
57
  # ───────────────────────── SPARK ───────────────────────── #
58
  async def spark_generate(s: Session, prompt: str, user_msg: str) -> str:
 
 
 
 
 
59
  payload = {
60
- "project_name": s.project["name"],
61
  "user_input": user_msg,
62
  "context": s.history[-10:],
63
  "system_prompt": prompt
@@ -96,6 +103,11 @@ async def chat(body: Body, x_session_id: str = Header(...)):
96
  user = body.user_input.strip()
97
  s.history.append({"role": "user", "content": user})
98
 
 
 
 
 
 
99
  # ---------- follow-up ----------
100
  if s.awaiting:
101
  ans = await _followup(s, user)
@@ -104,8 +116,8 @@ async def chat(body: Body, x_session_id: str = Header(...)):
104
 
105
  # ---------- intent detect ----------
106
  p = build_intent_prompt(
107
- s.project["versions"][0]["general_prompt"],
108
- s.history, user, s.project["versions"][0]["intents"])
109
  raw = await spark_generate(s, p, user)
110
 
111
  if raw == "":
@@ -125,55 +137,95 @@ async def chat(body: Body, x_session_id: str = Header(...)):
125
  s.history.append({"role": "assistant", "content": clean})
126
  return Resp(session_id=s.id, answer=clean)
127
 
128
- cfg = _find_intent(s, intent)
129
- if not cfg:
130
  err = "ÜzgΓΌnΓΌm, anlayamadΔ±m."
131
  s.history.append({"role": "assistant", "content": err})
132
  return Resp(session_id=s.id, answer=err)
133
 
134
- answer = await _handle_intent(s, cfg, user)
135
  s.history.append({"role": "assistant", "content": answer})
136
  return Resp(session_id=s.id, answer=answer)
137
 
138
  # ────────────────── INTENT / PARAM / API HELPERS ────────────────── #
139
- def _find_intent(s, name): return next((i for i in s.project["versions"][0]["intents"] if i["name"] == name), None)
140
- def _missing(s, cfg): return [p["name"] for p in cfg["parameters"] if p["variable_name"] not in s.vars]
 
 
 
 
141
 
142
- async def _handle_intent(s, cfg, user):
143
- missing = _missing(s, cfg)
144
  if missing:
145
- prmpt = build_parameter_prompt(cfg, missing, user, s.history)
146
  raw = await spark_generate(s, prmpt, user)
147
- if raw.startswith("#PARAMETERS:") and not _proc_params(s, cfg, raw):
148
- missing = _missing(s, cfg)
 
149
 
150
  if missing:
151
- s.awaiting = {"intent": cfg, "missing": missing}
152
- cap = next(p for p in cfg["parameters"] if p["name"] == missing[0])["caption"]
153
  return f"{cap} nedir?"
154
  s.awaiting = None
155
- return await _call_api(s, cfg)
156
 
157
  async def _followup(s, user):
158
- cfg = s.awaiting["intent"]; miss = s.awaiting["missing"]
159
- prmpt = build_parameter_prompt(cfg, miss, user, s.history)
 
160
  raw = await spark_generate(s, prmpt, user)
161
- if not raw.startswith("#PARAMETERS:") or _proc_params(s, cfg, raw):
162
  return "ÜzgΓΌnΓΌm, anlayamadΔ±m."
163
- miss = _missing(s, cfg)
 
 
164
  if miss:
165
  s.awaiting["missing"] = miss
166
- cap = next(p for p in cfg["parameters"] if p["name"] == miss[0])["caption"]
167
  return f"{cap} nedir?"
168
  s.awaiting = None
169
- return await _call_api(s, cfg)
170
-
171
- def _proc_params(s, cfg, raw):
172
- try: d = json.loads(raw[len("#PARAMETERS:"):])
173
- except: return True
 
 
 
174
  for pr in d.get("extracted", []):
175
- pcfg = next(p for p in cfg["parameters"] if p["name"] == pr["name"])
176
- if not re.fullmatch(pcfg.get("validation_regex", ".*"), pr["value"]):
177
- return True
178
- s.vars[pcfg["variable_name"]] = pr["value"]
179
- return Fals
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
+ Flare – Chat Handler (v1.4 Β· tutarsΔ±zlΔ±klar giderildi)
3
+ =======================================================
4
  """
5
 
6
  import re, json, uuid, sys, httpx, commentjson
 
10
  from pydantic import BaseModel
11
  from commentjson import JSONLibraryException
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:
 
39
  return name, tail
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
 
58
 
59
  # ───────────────────────── SPARK ───────────────────────── #
60
  async def spark_generate(s: Session, prompt: str, user_msg: str) -> str:
61
+ # Get the first published version
62
+ version = next((v for v in s.project.versions if v.published), None)
63
+ if not version:
64
+ raise HTTPException(500, "No published version found")
65
+
66
  payload = {
67
+ "project_name": s.project.name,
68
  "user_input": user_msg,
69
  "context": s.history[-10:],
70
  "system_prompt": prompt
 
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)
 
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
  if raw == "":
 
137
  s.history.append({"role": "assistant", "content": clean})
138
  return Resp(session_id=s.id, answer=clean)
139
 
140
+ intent_cfg = _find_intent(s, intent)
141
+ if not intent_cfg:
142
  err = "ÜzgΓΌnΓΌm, anlayamadΔ±m."
143
  s.history.append({"role": "assistant", "content": err})
144
  return Resp(session_id=s.id, answer=err)
145
 
146
+ answer = await _handle_intent(s, intent_cfg, user)
147
  s.history.append({"role": "assistant", "content": answer})
148
  return Resp(session_id=s.id, answer=answer)
149
 
150
  # ────────────────── INTENT / PARAM / API HELPERS ────────────────── #
151
+ def _find_intent(s, name):
152
+ version = next((v for v in s.project.versions if v.published), None)
153
+ return next((i for i in version.intents if i.name == name), None) if version else None
154
+
155
+ def _missing(s, intent_cfg):
156
+ return [p.name for p in intent_cfg.parameters if p.required and p.variable_name not in s.vars]
157
 
158
+ async def _handle_intent(s, intent_cfg, user):
159
+ missing = _missing(s, intent_cfg)
160
  if missing:
161
+ prmpt = build_parameter_prompt(intent_cfg, missing, user, s.history)
162
  raw = await spark_generate(s, prmpt, user)
163
+ if raw.startswith("#PARAMETERS:"):
164
+ _proc_params(s, intent_cfg, raw)
165
+ missing = _missing(s, intent_cfg)
166
 
167
  if missing:
168
+ s.awaiting = {"intent": intent_cfg, "missing": missing}
169
+ cap = next(p for p in intent_cfg.parameters if p.name == missing[0]).caption
170
  return f"{cap} nedir?"
171
  s.awaiting = None
172
+ return await _call_api(s, intent_cfg)
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
  for pr in d.get("extracted", []):
198
+ pcfg = next((p for p in intent_cfg.parameters if p.name == pr["name"]), None)
199
+ if not pcfg:
200
+ continue
201
+
202
+ # Use validation engine
203
+ if not validate(pr["value"], pcfg):
204
+ continue
205
+
206
+ s.vars[pcfg.variable_name] = pr["value"]
207
+ return True
208
+
209
+ async def _call_api(s, intent_cfg):
210
+ """Execute API call and humanize response"""
211
+ api_name = intent_cfg.action
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
+ # Call API using api_executor
218
+ response = execute_api(api_cfg, s.vars)
219
+ api_json = response.json()
220
+
221
+ # Humanize response
222
+ if api_cfg.response_prompt:
223
+ prompt = api_cfg.response_prompt.replace("{{api_response}}", json.dumps(api_json, ensure_ascii=False))
224
+ human_response = await spark_generate(s, prompt, json.dumps(api_json))
225
+ return human_response if human_response else f"İşlem sonucu: {api_json}"
226
+ else:
227
+ return f"İşlem başarılı: {api_json}"
228
+
229
+ except Exception as e:
230
+ log(f"❌ API call error: {e}")
231
+ return intent_cfg.get("fallback_error_prompt", "İşlem sırasında bir hata oluştu.")