1import{l as r,m as l,B as c,q as t,u as m,J as i,K as n,z as k,F as _,v as N,y as ee,Y as te,aa as h,ab as E,p as w,ac as ae,ag as oe,W as se,X as re,r as b,a6 as le,b as de,e as C}from"./vue-vendor-B52ivs5F.js";import{u as ie,b as T}from"./query-vendor-Ab0nfCYP.js";import{b as ne}from"./voice-D-H8nkyF.js";import{k as ce,j as ue}from"./cost-tracking-CWSqU3hw.js";import{m as ge,n as pe,o as me,p as ye,q as xe}from"./knox-BBPew7c8.js";import{a as be}from"./swal-Dad2D9N9.js";import{t as g}from"./index-NM9cAr_v.js";import{_ as ve}from"./KnoxAdminTabs-eC8apC-I.js";import{M as fe,ak as ke,I as _e,i as he,bQ as we,a5 as Ce}from"./ui-vendor-D-1UBobO.js";import"./date-vendor-HBGOQ70J.js";import"./twilio-DAuME2Y9.js";import"./charts-D4hDxUnJ.js";import"./socket-CzTGKfeH.js";const Me={class:"flex h-full flex-col"},Se={class:"flex items-center justify-between border-b border-gray-200 bg-white px-6 py-4 dark:border-gray-700 dark:bg-gray-800"},Ke={class:"flex items-center gap-2"},Ne=["disabled"],Te={class:"flex-1 overflow-y-auto p-6"},Ve={class:"mb-4 flex items-start gap-2 rounded-lg border border-teal-200 bg-teal-50 px-4 py-3 text-sm text-teal-800 dark:border-teal-900/50 dark:bg-teal-900/20 dark:text-teal-200"},qe={key:0,class:"flex items-center justify-center py-20"},$e={key:1,class:"rounded-lg border border-dashed border-gray-300 p-10 text-center dark:border-gray-700"},Ae={key:2},De={class:"mb-2 text-[11px] font-semibold tracking-wide text-gray-500 uppercase dark:text-gray-400"},Pe={class:"overflow-hidden rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800"},Re={class:"w-full text-sm"},ze={class:"divide-y divide-gray-100 dark:divide-gray-700"},Ee={class:"px-4 py-2.5"},Ie={class:"font-medium text-gray-900 dark:text-white"},Le={class:"text-[11px] text-gray-400"},je={class:"px-4 py-2.5 text-gray-600 dark:text-gray-300"},Be={class:"px-4 py-2.5 text-center"},Fe={key:0,class:"inline-flex items-center gap-1 rounded-full bg-teal-50 px-2 py-0.5 text-[10px] font-medium text-teal-700 dark:bg-teal-900/30 dark:text-teal-400"},Qe=["onClick","disabled"],Ue={class:"px-4 py-2.5 text-right"},Oe=["onClick"],Xe=["onClick","disabled","title"],Ge={key:0},He={colspan:"4",class:"px-4 py-6 text-center text-xs text-gray-400"},Je={class:"w-full max-w-md rounded-xl border border-gray-200 bg-white p-6 shadow-xl dark:border-gray-700 dark:bg-gray-800"},We={class:"mb-4 text-lg font-semibold text-gray-900 dark:text-white"},Ye={class:"space-y-3"},Ze={class:"block text-xs"},et={key:0,class:"py-3 text-center text-xs text-gray-400"},tt={key:1,class:"rounded-lg border border-amber-200 bg-amber-50 px-3 py-2.5 text-xs text-amber-800 dark:border-amber-900/40 dark:bg-amber-900/20 dark:text-amber-200"},at={key:2,class:"block text-xs"},ot=["value"],st={class:"mt-1 block text-[10px] text-gray-400"},rt={class:"block text-xs"},lt={key:1,class:"mt-2 flex items-center gap-2 text-xs"},dt={class:"mt-5 flex justify-end gap-2"},it=["disabled"],wt={__name:"KnoxModels",setup(nt){const M=ie(),y=b(null),v=b(!1),u=b(null),f=b(!1),x=b(!1);async function I(){if(!x.value){x.value=!0;try{const{data:a}=await ne(),e=`Nox agent ${a.agentAction}. Tools: +${a.toolsCreated?.length||0} new, ~${a.toolsUpdated?.length||0} updated, -${a.toolsRemoved?.length||0} removed.`;g.success(e)}catch(a){const e=a?.response?.data?.upstream?.message||a?.response?.data?.message||a?.message||"Sync failed";g.error(`Nox voice sync failed: ${e}`)}finally{x.value=!1}}}const L=[{key:"chat",label:"Chat models (answers, ingestion, context)"},{key:"narration",label:"Narration models (recording analysis, signals)"},{key:"embedding",label:"Embedding models"},{key:"transcript_enrichment",label:"Transcript enrichment"}],o=de({catalog_model_id:"",provider:"openai",model_id:"",display_name:"",role:"chat",is_default:!1}),{data:V,isLoading:j}=T({queryKey:["admin","knox","models"],queryFn:xe}),{data:q,isLoading:B}=T({queryKey:["admin","cost-tracking","catalog-models"],queryFn:ce}),{data:F}=T({queryKey:["admin","cost-tracking","catalog-providers"],queryFn:ue}),Q=C(()=>B.value),$=a=>(V.value||[]).filter(e=>e.role===a),U=new Set(["openai","anthropic"]);function A(a){return a==="embedding"?"embedding":"llm"}const D=C(()=>{const a=new Map;for(const e of F.value||[])a.set(e.id,e);return a});function S(a){return D.value.get(a)?.name||""}function P(a){return D.value.get(a)?.kind||""}const R=C(()=>{const a=A(o.role);return(q.value||[]).filter(e=>e.is_active!==!1).filter(e=>e.kind===a).filter(e=>U.has(P(e.provider_id))).sort((e,p)=>{const s=S(e.provider_id),d=S(p.provider_id);return s!==d?s.localeCompare(d):(e.name||"").localeCompare(p.name||"")})}),O=C(()=>(u.value||!!o.catalog_model_id&&o.model_id.trim().length>0)&&o.display_name.trim().length>0);function X(){u.value=null,Object.assign(o,{catalog_model_id:"",provider:"openai",model_id:"",display_name:"",role:"chat",is_default:!1}),v.value=!0}function G(a){u.value=a,Object.assign(o,{catalog_model_id:"",provider:a.provider,model_id:a.model_id,display_name:a.display_name,role:a.role,is_default:a.is_default}),v.value=!0}function H(){o.catalog_model_id="",o.provider="openai",o.model_id="",o.display_name=""}function J(){const a=(q.value||[]).find(e=>e.id===o.catalog_model_id);if(!a){o.provider="openai",o.model_id="",o.display_name="";return}o.provider=P(a.provider_id)||"openai",o.model_id=a.name,o.display_name=(a.display_label||a.name||"").trim()}function K(){v.value=!1,u.value=null}async function W(){f.value=!0;try{u.value?(await ge(u.value.id,{display_name:o.display_name.trim()}),g.success("Model updated")):(await pe({provider:o.provider,model_id:o.model_id.trim(),display_name:o.display_name.trim(),role:o.role,is_default:o.is_default}),g.success("Model added")),M.invalidateQueries({queryKey:["admin","knox","models"]}),K()}catch(a){g.error(a?.response?.data?.message||"Save failed")}finally{f.value=!1}}async function Y(a){y.value=a.id;try{await me(a.id),g.success(`${a.display_name} is now the selected ${a.role} model`),M.invalidateQueries({queryKey:["admin","knox","models"]})}catch(e){g.error(e?.response?.data?.message||"Failed to select")}finally{y.value=null}}
1async function Z(a){if((await be(`"${a.display_name}"`,{text:"Historical usage stays attributed. This action cannot be undone."})).isConfirmed){y.value=a.id;try{await ye(a.id),g.success("Model deleted"),M.invalidateQueries({queryKey:["admin","knox","models"]})}catch(p){g.error(p?.response?.data?.message||"Delete failed")}finally{y.value=null}}}return(a,e)=>{const p=le("router-link");return r(),l("div",Me,[c(ve),t("div",Se,[e[5]||(e[5]=t("div",null,[t("h1",{class:"text-xl font-bold text-gray-900 dark:text-white"},"AI Models"),t("p",{class:"mt-0.5 text-xs text-gray-500 dark:text-gray-400"}," Manage the catalog of AI models available to Nox. Exactly one model per role is the active selection. ")],-1)),t("div",Ke,[t("button",{onClick:I,disabled:x.value,class:"flex items-center gap-1.5 rounded-lg border border-teal-600 bg-white px-4 py-2 text-sm font-medium text-teal-700 transition-colors hover:bg-teal-50 disabled:opacity-50 dark:bg-gray-800 dark:text-teal-300 dark:hover:bg-gray-700",title:"Recreate the shared Nox ElevenLabs agent + tools. Idempotent."},[c(m(fe),{size:16}),i(" "+n(x.value?"Syncingâ¦":"Sync Voice Agent"),1)],8,Ne),t("button",{onClick:X,class:"flex items-center gap-1.5 rounded-lg bg-teal-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-teal-700"},[c(m(ke),{size:16}),e[4]||(e[4]=i(" Add Model ",-1))])])]),t("div",Te,[t("div",Ve,[c(m(_e),{size:16,class:"mt-0.5 shrink-0"}),t("p",null,[e[7]||(e[7]=i(" This page selects which model each Nox role uses (chat, narration, embedding, transcript enrichment). ",-1)),e[8]||(e[8]=t("strong",null,"Pricing",-1)),e[9]||(e[9]=i(" for these models is managed in ",-1)),c(p,{to:"/admin/cost-tracking/models",class:"font-medium underline decoration-dotted underline-offset-2 hover:text-teal-900 dark:hover:text-teal-100"},{default:k(()=>[...e[6]||(e[6]=[i("Cost Tracking â Models",-1)])]),_:1}),e[10]||(e[10]=i(". ",-1))])]),m(j)?(r(),l("div",qe,[...e[11]||(e[11]=[t("div",{class:"h-10 w-10 animate-spin rounded-full border-4 border-teal-200 border-t-teal-600"},null,-1)])])):m(V)?.length?(r(),l("div",Ae,[(r(),l(_,null,N(L,s=>t("div",{key:s.key,class:"mb-8"},[t("h2",De,n(s.label),1),t("div",Pe,[t("table",Re,[e[14]||(e[14]=t("thead",{class:"bg-gray-50 text-[11px] font-semibold tracking-wide text-gray-500 uppercase dark:bg-gray-900/50 dark:text-gray-400"},[t("tr",null,[t("th",{class:"px-4 py-2 text-left"},"Model"),t("th",{class:"px-4 py-2 text-left"},"Provider"),t("th",{class:"px-4 py-2 text-center"},"Selected"),t("th",{class:"px-4 py-2 text-right"},"Actions")])],-1)),t("tbody",ze,[(r(!0),l(_,null,N($(s.key),d=>(r(),l("tr",{key:d.id,class:"hover:bg-gray-50/70 dark:hover:bg-gray-700/30"},[t("td",Ee,[t("div",Ie,n(d.display_name),1),t("div",Le,n(d.model_id),1)]),t("td",je,n(d.provider),1),t("td",Be,[d.is_default?(r(),l("span",Fe,[c(m(he),{size:10}),e[13]||(e[13]=i(" Selected ",-1))])):(r(),l("button",{key:1,onClick:z=>Y(d),disabled:y.value===d.id,class:"rounded border border-gray-300 px-2 py-0.5 text-[10px] text-gray-500 hover:border-teal-400 hover:text-teal-600 disabled:opacity-50 dark:border-gray-600 dark:text-gray-400"}," Select ",8,Qe))]),t("td",Ue,[t("button",{onClick:z=>G(d),class:"mr-1 rounded p-1.5 text-gray-400 hover:bg-gray-100 hover:text-gray-600 dark:hover:bg-gray-700 dark:hover:text-gray-300"},[c(m(we),{size:14})],8,Oe),t("button",{onClick:z=>Z(d),disabled:d.is_default||y.value===d.id,class:"rounded p-1.5 text-gray-400 hover:bg-red-50 hover:text-red-500 disabled:cursor-not-allowed disabled:opacity-30 dark:hover:bg-red-900/30",title:d.is_default?"Select another model first":"Delete"},[c(m(Ce),{size:14})],8,Xe)])]))),128)),$(s.key).length===0?(r(),l("tr",Ge,[t("td",He," No "+n(s.label.toLowerCase())+" models yet. ",1)])):w("",!0)])])])])),64))])):(r(),l("div",$e,[...e[12]||(e[12]=[t("p",{class:"text-sm text-gray-500 dark:text-gray-400"}," No models yet. Add one to get started. ",-1)])]))]),(r(),ee(re,{to:"body"},[c(se,{name:"fade"},{default:k(()=>[v.value?(r(),l("div",{key:0,class:"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",onClick:te(K,["self"])},[t("div",Je,[t("h3",We,n(u.value?"Edit Model":"Add Model"),1),t("div",Ye,[u.value?w("",!0):(r(),l(_,{key:0},[t("label",Ze,[e[16]||(e[16]=t("span",{class:"text-gray-600 dark:text-gray-400"},"Role",-1)),h(t("select",{"onUpdate:modelValue":e[0]||(e[0]=s=>o.role=s),onChange:H,class:"mt-1 w-full rounded-lg border border-gray-300 px-2 py-1.5 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-white"},[...e[15]||(e[15]=[t("option",{value:"chat"}
1,"Chat (answers, ingestion, context)",-1),t("option",{value:"narration"},"Narration (recording analysis)",-1),t("option",{value:"embedding"},"Embedding",-1),t("option",{value:"transcript_enrichment"},"Transcript enrichment",-1)])],544),[[E,o.role]])]),Q.value?(r(),l("div",et," Loading catalog⦠")):R.value.length===0?(r(),l("div",tt,[e[18]||(e[18]=i(" No ",-1)),t("strong",null,n(A(o.role)),1),e[19]||(e[19]=i(" models from openai/anthropic in the shared catalog. ",-1)),c(p,{to:"/admin/cost-tracking/models",class:"ml-1 font-medium underline decoration-dotted underline-offset-2"},{default:k(()=>[...e[17]||(e[17]=[i("Add one in Cost Tracking â Models",-1)])]),_:1}),e[20]||(e[20]=i(" first, then come back here to attach it to a Knox role. ",-1))])):(r(),l("label",at,[e[25]||(e[25]=t("span",{class:"text-gray-600 dark:text-gray-400"},"Catalog model",-1)),h(t("select",{"onUpdate:modelValue":e[1]||(e[1]=s=>o.catalog_model_id=s),onChange:J,class:"mt-1 w-full rounded-lg border border-gray-300 px-2 py-1.5 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-white"},[e[21]||(e[21]=t("option",{value:""},"â select a model â",-1)),(r(!0),l(_,null,N(R.value,s=>(r(),l("option",{key:s.id,value:s.id},n(s.name)+" · "+n(S(s.provider_id))+" · "+n(s.kind),9,ot))),128))],544),[[E,o.catalog_model_id]]),t("span",st,[e[23]||(e[23]=i(" Models are managed in ",-1)),c(p,{to:"/admin/cost-tracking/models",class:"underline decoration-dotted underline-offset-2"},{default:k(()=>[...e[22]||(e[22]=[i("Cost Tracking â Models",-1)])]),_:1}),e[24]||(e[24]=i(". This page only attaches a catalog model to a Knox role. ",-1))])]))],64)),t("label",rt,[e[26]||(e[26]=t("span",{class:"text-gray-600 dark:text-gray-400"},"Display Name",-1)),h(t("input",{"onUpdate:modelValue":e[2]||(e[2]=s=>o.display_name=s),type:"text",placeholder:"e.g. GPT-4o mini",class:"mt-1 w-full rounded-lg border border-gray-300 px-2 py-1.5 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-white"},null,512),[[ae,o.display_name]])]),u.value?w("",!0):(r(),l("label",lt,[h(t("input",{"onUpdate:modelValue":e[3]||(e[3]=s=>o.is_default=s),type:"checkbox",class:"rounded"},null,512),[[oe,o.is_default]]),e[27]||(e[27]=t("span",{class:"text-gray-600 dark:text-gray-400"},"Select this as the active model for its role",-1))]))]),t("div",dt,[t("button",{onClick:K,class:"rounded-lg border border-gray-300 px-4 py-2 text-sm dark:border-gray-600 dark:text-gray-300"}," Cancel "),t("button",{onClick:W,disabled:!O.value||f.value,class:"rounded-lg bg-teal-600 px-4 py-2 text-sm font-medium text-white hover:bg-teal-700 disabled:opacity-50"},n(f.value?"Savingâ¦":u.value?"Save":"Create"),9,it)])])])):w("",!0)]),_:1})]))])}}};export{wt as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.