ciyidogan commited on
Commit
f24aded
·
verified ·
1 Parent(s): bf5274e

Update chat_handler.py

Browse files
Files changed (1) hide show
  1. chat_handler.py +136 -118
chat_handler.py CHANGED
@@ -1,136 +1,154 @@
1
  """
2
- Flare – Chat Handler (start_session & chat)
3
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
- Intent → Parametre → API → Humanize akışı
5
  """
6
 
7
  from __future__ import annotations
8
-
9
- import json
10
- from typing import Dict, Optional
11
-
12
- from fastapi import APIRouter, HTTPException
13
  from pydantic import BaseModel
14
-
15
- from config_provider import ConfigProvider, VersionConfig
16
- from prompt_builder import build_intent_prompt, build_param_prompt, build_api_humanize_prompt
 
 
17
  from api_executor import call_api
18
  from session import session_store, Session
19
  from utils import log
20
 
21
- cfg = ConfigProvider.get()
 
22
 
23
- router = APIRouter()
24
 
 
 
 
 
 
 
 
25
 
26
- # --------- Body models ----------
27
  class SessionStartRequest(BaseModel):
28
- project_name: str
29
-
30
-
31
  class SessionStartResponse(BaseModel):
32
- session_id: str
33
 
 
 
 
 
34
 
35
  class ChatRequest(BaseModel):
36
- session_id: str
37
- user_input: str
38
-
39
-
40
  class ChatResponse(BaseModel):
41
- reply: str
42
- session: Dict
43
-
44
-
45
- # --------- LLM stub -------------
46
- def ask_llm(prompt: str, user_input: str = "", mode: str = "text"):
47
- log(f"🤖 [LLM-stub] mode={mode} prompt_len={len(prompt)}")
48
- if mode == "classification":
49
- return ""
50
- if mode == "json":
51
- return {}
52
- return "LLM yanıtı (stub)."
53
-
54
-
55
- # --------- Helpers --------------
56
- def _get_live_version(project_name: str) -> VersionConfig:
57
- proj = next((p for p in cfg.projects if p.name == project_name and p.enabled), None)
58
- if proj is None:
59
- raise HTTPException(status_code=400, detail="Project not found or disabled")
60
- v = max((v for v in proj.versions if v.published), key=lambda x: x.id, default=None)
61
- if v is None:
62
- raise HTTPException(status_code=400, detail="No published version")
63
- return v
64
-
65
-
66
- # --------- Endpoints ------------
67
- @router.post("/start_session", response_model=SessionStartResponse)
68
- def start_session(body: SessionStartRequest):
69
- session = session_store.create_session(body.project_name)
70
- return SessionStartResponse(session_id=session.session_id)
71
-
72
-
73
- @router.post("/chat", response_model=ChatResponse)
74
- def chat(body: ChatRequest):
75
- session: Session | None = session_store.get_session(body.session_id)
76
- if session is None:
77
- raise HTTPException(status_code=404, detail="Session not found")
78
-
79
- version = _get_live_version(session.project_name)
80
- user_input = body.user_input
81
- session.add_turn("user", user_input)
82
-
83
- # 1) Intent detection loop
84
- intent = None
85
- for it in version.intents:
86
- prompt = build_intent_prompt(version.general_prompt, it)
87
- resp = ask_llm(prompt, user_input, mode="classification")
88
- if it.name.lower() in resp.lower():
89
- intent = it
90
- break
91
-
92
- if intent is None:
93
- reply = ask_llm(version.general_prompt, user_input)
94
- session.add_turn("assistant", reply)
95
- return ChatResponse(reply=reply, session=session.to_dict())
96
-
97
- # 2) Parameter extraction
98
- vars_ = session.variables.copy()
99
- missing = [p for p in intent.parameters if p.name not in vars_]
100
- if missing:
101
- prompt = build_param_prompt(version.general_prompt, intent, missing)
102
- llm_json = ask_llm(prompt, user_input, mode="json") or {}
103
- vars_.update(llm_json)
104
-
105
- still_missing = [p.name for p in intent.parameters if p.required and p.name not in vars_]
106
- if still_missing:
107
- ask = " ".join(f"{p} değerini söyler misin?" for p in still_missing)
108
- reply = ask_llm(version.general_prompt, ask)
109
- session.variables = vars_
110
- session.awaiting_parameters = still_missing
111
- session.add_turn("assistant", reply)
112
- return ChatResponse(reply=reply, session=session.to_dict())
113
-
114
- # 3) API call
115
- api_cfg = cfg.get_api(intent.action)
116
- if api_cfg is None:
117
- raise HTTPException(status_code=500, detail="API not configured")
118
-
119
- try:
120
- resp = call_api(api_cfg, vars_)
121
- except Exception as e:
122
- log(f"❌ API error: {e}")
123
- reply = intent.fallback_error_prompt or "İşlem sırasında hata oluştu."
124
- session.add_turn("assistant", reply)
125
- return ChatResponse(reply=reply, session=session.to_dict())
126
-
127
- # 4) Humanize
128
- human_prompt = build_api_humanize_prompt(
129
- version.general_prompt,
130
- api_cfg.response_prompt or "",
131
- json.dumps(resp.json(), ensure_ascii=False, indent=2),
132
- )
133
- reply = ask_llm(human_prompt)
134
- session.variables = vars_
135
- session.add_turn("assistant", reply)
136
- return ChatResponse(reply=reply, session=session.to_dict())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
+ Flare – Chat Handler (state-machine + header session)
 
 
3
  """
4
 
5
  from __future__ import annotations
6
+ import json, re
7
+ from typing import Dict, List
8
+ from fastapi import APIRouter, HTTPException, Request
 
 
9
  from pydantic import BaseModel
10
+ from config_provider import ConfigProvider, VersionConfig, IntentConfig, ParameterConfig
11
+ from prompt_builder import (
12
+ build_detection_prompt, build_param_extract_prompt,
13
+ build_missing_param_prompt, build_api_humanize_prompt)
14
+ from validation_engine import validate
15
  from api_executor import call_api
16
  from session import session_store, Session
17
  from utils import log
18
 
19
+ cfg=ConfigProvider.get()
20
+ router=APIRouter()
21
 
22
+ _cancel_words=re.compile(r"\b(vazgeç|iptal|boşver|cancel)\b",re.I)
23
 
24
+ # -------- helper ----------
25
+ def detect_intent(version: VersionConfig, text: str) -> IntentConfig | None:
26
+ for it in version.intents:
27
+ prompt=build_detection_prompt(version.general_prompt,it)
28
+ if it.name.lower() in text.lower(): # placeholder: ask_llm(prompt,text,"classification")
29
+ return it
30
+ return None
31
 
32
+ # --------- endpoints -------
33
  class SessionStartRequest(BaseModel):
34
+ project_name:str
 
 
35
  class SessionStartResponse(BaseModel):
36
+ session_id:str
37
 
38
+ @router.post("/start_session",response_model=SessionStartResponse)
39
+ def start_session(body:SessionStartRequest):
40
+ s=session_store.create_session(body.project_name)
41
+ return SessionStartResponse(session_id=s.session_id)
42
 
43
  class ChatRequest(BaseModel):
44
+ user_input:str
 
 
 
45
  class ChatResponse(BaseModel):
46
+ reply:str
47
+ session:Dict
48
+
49
+ @router.post("/chat",response_model=ChatResponse)
50
+ def chat(req:Request, body:ChatRequest):
51
+ sid=req.headers.get("X-Session-ID")
52
+ if not sid: raise HTTPException(400,"X-Session-ID header missing")
53
+ s=session_store.get_session(sid)
54
+ if not s: raise HTTPException(404,"Session not found")
55
+
56
+ version=_get_live_version(s.project_name)
57
+ user_text=body.user_input
58
+ s.add_turn("user",user_text)
59
+
60
+ # quick cancel
61
+ if _cancel_words.search(user_text):
62
+ s.reset_flow()
63
+ reply="Elbette, başka bir konuda nasıl yardımcı olabilirim?"
64
+ s.add_turn("assistant",reply)
65
+ return ChatResponse(reply=reply,session=s.__dict__)
66
+
67
+ # ---- idle state: intent arar
68
+ if s.state=="idle":
69
+ intent=detect_intent(version,user_text)
70
+ if not intent:
71
+ reply="(LLM-sohbet cevabı)" # burada ask_llm kullanılacak
72
+ s.add_turn("assistant",reply)
73
+ return ChatResponse(reply=reply,session=s.__dict__)
74
+
75
+ s.last_intent=intent.name
76
+ s.state="await_param"
77
+ s.awaiting_parameters=[p.name for p in intent.parameters]
78
+ s.variables={} # yeni intentte öncekileri sıfırla
79
+
80
+ # ---- await_param state
81
+ if s.state=="await_param":
82
+ intent=next(i for i in version.intents if i.name==s.last_intent)
83
+ # intent değişti mi?
84
+ new_int=detect_intent(version,user_text)
85
+ if new_int and new_int.name!=intent.name:
86
+ log("🔄 new intent overrides current flow")
87
+ s.reset_flow()
88
+ s.last_intent=new_int.name
89
+ s.state="await_param" if new_int.parameters else "call_api"
90
+ s.awaiting_parameters=[p.name for p in new_int.parameters]
91
+ intent=new_int
92
+
93
+ # param extraction
94
+ for p in intent.parameters:
95
+ if p.name in s.variables: continue
96
+ if p.name in user_text.lower(): # basit demo extraction
97
+ val=user_text
98
+ if not validate(val,p):
99
+ reply=p.invalid_prompt or f"{p.caption or p.name} değerini doğrulayamadım."
100
+ s.add_turn("assistant",reply)
101
+ return ChatResponse(reply=reply,session=s.__dict__)
102
+ s.variables[p.name]=val
103
+ s.awaiting_parameters.remove(p.name)
104
+
105
+ if s.awaiting_parameters:
106
+ s.missing_ask_count+=1
107
+ if s.missing_ask_count>=2:
108
+ s.reset_flow()
109
+ reply="Başka bir konuda yardımcı olabilir miyim?"
110
+ s.add_turn("assistant",reply)
111
+ return ChatResponse(reply=reply,session=s.__dict__)
112
+
113
+ ask=build_missing_param_prompt(s.awaiting_parameters)
114
+ s.add_turn("assistant",ask)
115
+ return ChatResponse(reply=ask,session=s.__dict__)
116
+
117
+ s.state="call_api"
118
+
119
+ # ---- call_api state
120
+ if s.state=="call_api":
121
+ intent=next(i for i in version.intents if i.name==s.last_intent)
122
+ api=cfg.get_api(intent.action)
123
+ if not api: raise HTTPException(500,"API not found")
124
+
125
+ try:
126
+ resp=call_api(api,s.variables)
127
+ except Exception as e:
128
+ log(f" API error {e}")
129
+ s.reset_flow()
130
+ reply=intent.fallback_error_prompt or "Hata oluştu."
131
+ s.add_turn("assistant",reply)
132
+ return ChatResponse(reply=reply,session=s.__dict__)
133
+
134
+ s.api_raw=resp.json() # debug
135
+ s.state="humanize"
136
+
137
+ # ---- humanize
138
+ if s.state=="humanize":
139
+ intent=next(i for i in version.intents if i.name==s.last_intent)
140
+ api=cfg.get_api(intent.action)
141
+ human_prompt=build_api_humanize_prompt(
142
+ version.general_prompt,
143
+ api.response_prompt or "",
144
+ json.dumps(s.api_raw,ensure_ascii=False,indent=2)
145
+ )
146
+ reply="(LLM-humanize)" # burada ask_llm kullanılacak
147
+ s.add_turn("assistant",reply)
148
+ s.reset_flow()
149
+ return ChatResponse(reply=reply,session=s.__dict__)
150
+
151
+
152
+ def _get_live_version(project_name:str)->VersionConfig:
153
+ proj=next(p for p in cfg.projects if p.name==project_name and p.enabled)
154
+ return max((v for v in proj.versions if v.published),key=lambda x:x.id)