feat(chat): scenario-based training — choice of channel/Situation + realistic per-turn evaluation

Backend:
- Channel/initiation now driven by a SCENARIO chosen at chat start, not baked into the
  persona: social (customer opens), f2f_call (seller must open, proactive), recontact
  (customer re-contacts after 1-3 months).
- /chat/start accepts {scenario}; session stores scenario + internal{turns,score}.
- persona_reply takes scenario + adapts tone; system-role transcript entries are fed to
  the persona as hidden scene notes.
- JUDGE updated for realism: good response can WIN even in hard/tough-text scenarios;
  long/no-close chats (turns >~12) lose; pushy/ignoring-need loses. Efficiency rewarded.

Frontend:
- Scenario picker before chat (choose Social / Face-to-face-call / Re-contact).
- Chat thread renders role=system as a centered time-lapse/scene note.
- Choose-scenario i18n (EN+TH).
Rebuilt dist.
This commit is contained in:
Macky
2026-08-08 11:27:05 +07:00
parent 8670addcc3
commit bd6a7ffa32
27 changed files with 314 additions and 110 deletions

View File

@@ -0,0 +1,16 @@
import{k as z,_ as T,r as c,f as i,c as h,a as e,m as k,u as a,S as $,j as v,t as o,i as s,w as x,v as b,e as V,A as S,s as U,o as w}from"./index-tI5UDFg-.js";/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const j=z("globe",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20",key:"13o1zl"}],["path",{d:"M2 12h20",key:"9i4pu4"}]]);/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const D=z("lock",[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 10 0v4",key:"fwvmzm"}]]);/**
* @license lucide-vue-next v1.0.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const E=z("user",[["path",{d:"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2",key:"975kel"}],["circle",{cx:"12",cy:"7",r:"4",key:"17ys0d"}]]),I={class:"card"},A={style:{"margin-top":"0"}},G={class:"muted"},H=["value"],O={class:"row",style:{gap:"10px","margin-top":"16px"}},q=["disabled"],F={key:0,class:"spinner"},J={class:"card",style:{"margin-top":"16px"}},K={style:{"margin-top":"0"}},Q={class:"row",style:{gap:"10px","margin-top":"16px"}},R=["disabled"],W={key:0,class:"spinner"},X={class:"card",style:{"margin-top":"16px"}},Y={style:{"margin-top":"0"}},Z={class:"row"},ee={key:0,class:"error",role:"alert"},ae={key:1,class:"ok",role:"status"},se={__name:"Settings",setup(te){var C,M;const y=c(((C=i.user)==null?void 0:C.name)||""),p=c(((M=i.user)==null?void 0:M.email)||""),u=c(""),_=c(""),r=c(""),m=c(""),g=c(!1),f=c(!1);async function B(){var d,t;r.value="",m.value="",g.value=!0;try{const n={};if(y.value&&y.value!==((d=i.user)==null?void 0:d.name)&&(n.name=y.value),p.value&&p.value!==((t=i.user)==null?void 0:t.email)&&(n.email=p.value),Object.keys(n).length){const l=await U.updateProfile(n);i.user=l.user,m.value=s.t("saved")}}catch(n){r.value=n.message}finally{g.value=!1}}async function N(){var d,t,n;if(r.value="",m.value="",u.value.length<4){r.value=s.t("passwordTooShort");return}if(u.value!==_.value){r.value=s.t("passwordMismatch");return}f.value=!0;try{const l=((d=i.user)==null?void 0:d.username)||((t=i.user)==null?void 0:t.id)||"",L=await U.setup({username:l,email:p.value||((n=i.user)==null?void 0:n.email),password:u.value});i.user=L.user,u.value="",_.value="",m.value=s.t("saved")}catch(l){r.value=l.message}finally{f.value=!1}}function P(d){s.set(d)}return(d,t)=>{var n;return w(),h("div",null,[e("h2",null,[k(a($),{size:22,"stroke-width":1.8,style:{"vertical-align":"-4px"}}),v(" "+o(a(s).t("settings")),1)]),e("div",I,[e("h3",A,[k(a(E),{size:18,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),v(" "+o(a(s).t("profile")),1)]),e("label",null,[v(o(a(s).t("username"))+" ",1),e("span",G,"("+o(a(s).t("readonly"))+")",1)]),e("input",{value:((n=a(i).user)==null?void 0:n.username)||"",disabled:""},null,8,H),e("label",null,o(a(s).t("name")),1),x(e("input",{"onUpdate:modelValue":t[0]||(t[0]=l=>y.value=l),type:"text"},null,512),[[b,y.value]]),e("label",null,o(a(s).t("email")),1),x(e("input",{"onUpdate:modelValue":t[1]||(t[1]=l=>p.value=l),type:"email"},null,512),[[b,p.value]]),e("div",O,[e("button",{class:"primary",disabled:g.value,onClick:B},[g.value?(w(),h("span",F)):V("",!0),v(o(a(s).t("saveProfile")),1)],8,q)])]),e("div",J,[e("h3",K,[k(a(D),{size:18,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),v(" "+o(a(s).t("changePassword")),1)]),e("label",null,o(a(s).t("newPassword")),1),x(e("input",{"onUpdate:modelValue":t[2]||(t[2]=l=>u.value=l),type:"password",autocomplete:"new-password"},null,512),[[b,u.value]]),e("label",null,o(a(s).t("confirmPassword")),1),x(e("input",{"onUpdate:modelValue":t[3]||(t[3]=l=>_.value=l),type:"password",autocomplete:"new-password"},null,512),[[b,_.value]]),e("div",Q,[e("button",{class:"primary",disabled:f.value||!u.value||u.value!==_.value,onClick:N},[f.value?(w(),h("span",W)):V("",!0),v(o(a(s).t("save")),1)],8,R)])]),e("div",X,[e("h3",Y,[k(a(j),{size:18,"stroke-width":1.8,style:{"vertical-align":"-3px"}}),v(" "+o(a(s).t("language")),1)]),e("div",Z,[e("button",{class:S({active:a(s).locale==="th"}),onClick:t[4]||(t[4]=l=>P("th"))},"ไทย",2),e("button",{class:S({active:a(s).locale==="en"}),onClick:t[5]||(t[5]=l=>P("en"))},"English",2)])]),r.value?(w(),h("div",ee,o(r.value),1)):V("",!0),m.value?(w(),h("div",ae,"✅ "+o(m.value),1)):V("",!0)])}}},oe=T(se,[["__scopeId","data-v-4a5a3af4"]]);export{oe as default};