@cachly-dev/mcp-server 0.10.175 → 0.10.176
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/einblendung.js +6 -6
- package/dist/src/embeddings.js +4 -4
- package/dist/src/handlers/brain.js +1 -1
- package/dist/src/index.js +81 -81
- package/dist/src/leser-sicherung.js +1 -0
- package/dist/src/leser.js +1 -1
- package/glama.json +1 -1
- package/hooks/cachly-ambient-einblendung.mjs +17 -17
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/src/einblendung.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{readFileSync as
|
|
1
|
+
import{readFileSync as w,existsSync as b,writeFileSync as v,mkdirSync as D,statSync as Y,appendFileSync as J}from"node:fs";import{dirname as k,join as h}from"node:path";import{tmpdir as m}from"node:os";import{createHash as q}from"node:crypto";import{spawn as Z}from"node:child_process";import{Einblendbestand as x,torWoerter as L}from"./einblendung-kern.js";import{rahmeEin as N}from"./einblendung-rahmen.js";import{leserText as z}from"./seltenheitsbestand.js";import{mischeMitLeser as C}from"./leser.js";import{LeserSicherung as F,dateiSpeicher as K,frageLeser as W}from"./leser-sicherung.js";import{LESER_TIEFE as G,LESER_ZEITLIMIT_MS as V}from"./rangfolge-stellschrauben.js";export*from"./einblendung-kern.js";const X=600*1e3,T=8e3,Q=60*1e3,j=120,R=G,ee=1500,$=V,te=h(m(),"cachly-ambient-netlog.jsonl");function we(e=process.cwd()){const t={apiUrl:process.env.CACHLY_API_URL||"https://api.cachly.dev",jwt:process.env.CACHLY_JWT||"",instanceId:process.env.CACHLY_BRAIN_INSTANCE_ID||""};if(t.jwt&&t.instanceId)return t;let r=e;for(let o=0;o<8;o++){const s=h(r,".mcp.json");if(b(s))try{const i=JSON.parse(w(s,"utf8"))?.mcpServers?.cachly?.env??{};if(t.apiUrl=t.apiUrl||i.CACHLY_API_URL||"https://api.cachly.dev",t.jwt=t.jwt||i.CACHLY_JWT||"",t.instanceId=t.instanceId||i.CACHLY_BRAIN_INSTANCE_ID||"",t.jwt&&t.instanceId)return t}catch{}const n=k(r);if(n===r)break;r=n}return t}function S(e){return q("sha1").update(e).digest("hex").slice(0,12)}function re(e){return h(m(),`cachly-ambient-bestand-${S(e)}.json`)}function ne(e){return h(m(),`cachly-leser-sicherung-${S(e)}.json`)}function se(e){return h(m(),`cachly-ambient-auffrischen-${S(e)}.lock`)}async function M(e,{fetchFn:t=fetch,zeitlimitMs:r=T}={}){try{const o=await t(`${e.apiUrl}/api/v1/instances/${e.instanceId}/export`,{headers:{Authorization:`Bearer ${e.jwt}`},signal:AbortSignal.timeout(r)});if(!o.ok)return null;const s=await o.json();return Array.isArray(s?.lessons)?s.lessons.map(n=>typeof n=="string"?oe(n):n).filter(n=>!!n&&typeof n=="object"):null}catch{return null}}function oe(e){try{return JSON.parse(e)}catch{return null}}function H(e,t){try{D(k(e),{recursive:!0}),v(e,JSON.stringify({at:Date.now(),lessons:t}),{mode:384})}catch{}}function O(e){try{const t=JSON.parse(w(e,"utf8"));if(Array.isArray(t?.lessons))return{at:Number(t.at)||0,lessons:t.lessons}}catch{}return null}function ie(e,t){const r=se(e.instanceId);try{if(b(r)&&Date.now()-Y(r).mtimeMs<Q)return;v(r,String(Date.now()),{mode:384})}catch{return}try{Z(process.execPath,t,{detached:!0,stdio:"ignore",env:{...process.env,CACHLY_API_URL:e.apiUrl,CACHLY_JWT:e.jwt,CACHLY_BRAIN_INSTANCE_ID:e.instanceId}}).unref()}catch{}}async function ae(e,{force:t=!1,nebenlauf:r,abrufMs:o=T,fetchFn:s}={}){if(!e.jwt||!e.instanceId)return{lessons:[],quelle:"nicht-eingerichtet"};const n=re(e.instanceId);if(t){const c=await M(e,{fetchFn:s,zeitlimitMs:o});if(c)return H(n,c),{lessons:c,quelle:"server"};const l=O(n);return l?{lessons:l.lessons,quelle:"platte-alt"}:{lessons:[],quelle:"server-nicht-erreichbar"}}const i=O(n);if(i){const c=Date.now()-i.at<X;return!c&&r&&ie(e,r),{lessons:i.lessons,quelle:c?"platte":"platte-alt"}}const u=await M(e,{fetchFn:s,zeitlimitMs:o});return u?(H(n,u),{lessons:u,quelle:"server"}):{lessons:[],quelle:"server-nicht-erreichbar"}}function E(e){return Math.ceil(String(e).length/4)}function g(e){const t=e.severity?`[${e.severity}] `:"",r=String(e.what_worked??"").replace(/\s+/g," ").trim(),o=r.length>j?`${r.slice(0,j)}\u2026`:r;return`- ${t}${e.topic}: ${o}`}const d={lessons:[],tokens:0,topScore:0,belege:0};function U(e,t,r){const o=[];let s=0;for(const{lektion:n}of e.slice(0,t)){const i=E(g(n));if(s+i>r)break;o.push(n),s+=i}return{lessons:o,tokens:s,topScore:Math.round(e[0].punkte*100)/100,belege:e[0].belege}}function be(e,t,{minBelege:r=2,topK:o=3,tokenBudget:s=600}={}){if(!Array.isArray(t)||t.length===0)return{...d};if(L(e).length===0)return{...d};const n=new x(t).sortiere(e).filter(i=>i.belege>=r);return n.length===0?{...d}:U(n,o,s)}function ce(e){return z(e,ee)}async function P(e,t,r,{zeitlimitMs:o=$,fetchFn:s}={}){if(!e?.jwt||!e?.instanceId||r.length===0)return{art:"ohne"};if(["0","off","aus","false"].includes(String(process.env.CACHLY_LESER||"").toLowerCase()))return{art:"ohne"};const n=new F(K(ne(e.instanceId)));return W({url:`${e.apiUrl}/api/v1/rerank`,jwt:e.jwt,frage:t,texte:r,instanceId:e.instanceId,zeitlimitMs:o,fetchFn:s},n)}async function ve(e,t,r,o={}){const s=await P(e,t,r,o);return s.art==="punkte"?{scores:s.scores,provider:s.provider,ms:s.ms}:null}async function le(e,t,r,{minBelege:o=2,topK:s=3,tokenBudget:n=600,tiefe:i=R,zeitlimitMs:u=$,fetchFn:c}={}){const l={...d,leser:"aus",leserMs:0};if(!Array.isArray(t)||t.length===0||L(e).length===0)return l;let a=new x(t).sortiere(e).filter(p=>p.belege>=o);if(a.length===0)return l;let y="aus",A=0;if(a.length>1&&i>0){const p=a.slice(0,i),B=a.slice(i),f=await P(r,e,p.map(I=>ce(I.lektion)),{zeitlimitMs:u,fetchFn:c});f.art==="punkte"?(a=[...C(p.map(_=>_.punkte),f.scores,1).map(_=>p[_]),...B],y=f.provider,A=f.ms):f.art==="uebersprungen"&&(y="uebersprungen")}return{...U(a,s,n),leser:y,leserMs:A}}function ue(e){if(!e.lessons.length)return"";const t=e.lessons.map(g).join(`
|
|
2
2
|
`);return`\u{1F9E0} Cachly Brain \u2014 relevant lessons for this task (auto-recall, ${e.lessons.length}):
|
|
3
|
-
${
|
|
4
|
-
(ambient-recall \xB7 ~${e.tokens} tokens \xB7 match score ${e.topScore})`}const
|
|
3
|
+
${N(t)}
|
|
4
|
+
(ambient-recall \xB7 ~${e.tokens} tokens \xB7 match score ${e.topScore})`}const pe=5;function ke(e){if(!e.length)return"";const t=e.filter(s=>{const n=String(s.topic??"");return!(/^auto:/.test(n)||/^[a-z]+:?$/.test(n))}),o=[...t.length?t:e].sort((s,n)=>(Number(n.recall_count)||0)-(Number(s.recall_count)||0)).slice(0,pe).map(g).join(`
|
|
5
5
|
`);return`\u{1F9E0} Cachly Brain \u2014 ambient recall active (${e.length} lessons). Top by proven reuse:
|
|
6
|
-
${
|
|
7
|
-
(Relevant lessons for each task are auto-injected per prompt. ~${
|
|
8
|
-
`)}catch{}return{kontext:
|
|
6
|
+
${N(o)}
|
|
7
|
+
(Relevant lessons for each task are auto-injected per prompt. ~${E(o)} tokens.)`}function xe(e,t){return t?JSON.stringify({hookSpecificOutput:{hookEventName:e,additionalContext:t}}):""}async function Le(e,t,{nebenlauf:r,abrufMs:o,leser:s=!0,session:n=null,protokoll:i=!0,fetchFn:u}={}){const{lessons:c,quelle:l}=await ae(t,{nebenlauf:r,abrufMs:o,fetchFn:u}),a=await le(e,c,t,{tiefe:s?R:0,fetchFn:u});if(i)try{J(te,JSON.stringify({at:Date.now(),session:n,prompt_tokens:E(e),injected_tokens:a.tokens,matches:a.lessons.length,top_score:a.topScore,belege:a.belege,leser:a.leser,leser_ms:a.leserMs,quelle:l,bestand:c.length})+`
|
|
8
|
+
`)}catch{}return{kontext:ue(a),auswahl:a,quelle:l,bestand:c.length}}export{T as ABRUF_TIMEOUT_MS,j as ANZEIGE_ZEICHEN,X as BESTAND_TTL_MS,$ as LESER_HOOK_ZEITLIMIT_MS,ee as LESER_MAX_ZEICHEN,R as LESER_TIEFE,te as NETZ_PROTOKOLL,pe as SITZUNG_TOP_N,re as bestandPfad,E as estimateTokens,ae as holeBestand,xe as hookAusgabe,P as leserAnfrageApi,ve as leserPunkteApi,ce as leserText,C as mischeMitLeser,Le as promptEinblendung,ue as promptKontext,g as renderLesson,we as resolveConfig,be as selectRelevant,le as selectRelevantMitLeser,ne as sicherungsPfad,ke as sitzungsKontext,ie as starteNebenlauf};
|
package/dist/src/embeddings.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
const s={apiUrl:process.env.CACHLY_API_URL??"https://api.cachly.dev",jwt:process.env.CACHLY_JWT??"",model:process.env.CACHLY_EMBED_MODEL??""};function
|
|
1
|
+
const s={apiUrl:process.env.CACHLY_API_URL??"https://api.cachly.dev",jwt:process.env.CACHLY_JWT??"",model:process.env.CACHLY_EMBED_MODEL??""};function M(r){s.jwt=r,r&&!g&&d==="none"&&(d="cachly")}const u=Number(process.env.CACHLY_EMBED_TIMEOUT_MS??8e3),p=Number(process.env.CACHLY_EMBED_TIMEOUT_MAX_MS??45e3),A=12;function h(r,n){return n==="kurz"?u:Math.min(p,u+r.length*A)}function f(){return process.env.CACHLY_JWT?"cachly":process.env.OLLAMA_BASE_URL?"ollama":"none"}const g=!!process.env.CACHLY_EMBED_PROVIDER;let d=(process.env.CACHLY_EMBED_PROVIDER??f()).toLowerCase();async function I(r,n,a){const c=h(r,n);switch(d){case"cachly":{if(!s.jwt)throw new Error(`CACHLY_JWT not set.
|
|
2
2
|
|
|
3
3
|
The "cachly" provider uses server-side embeddings via the Cachly API.
|
|
4
4
|
Set CACHLY_JWT, or use another provider via CACHLY_EMBED_PROVIDER:
|
|
5
5
|
openai \u2192 OPENAI_API_KEY
|
|
6
6
|
gemini \u2192 GEMINI_API_KEY
|
|
7
|
-
ollama \u2192 OLLAMA_BASE_URL (local, no key needed)`);const t=`${s.apiUrl}/api/v1/embed`,o={text:r};(a??s.model)&&(o.model=a??s.model);const e=process.env.CACHLY_ADMIN_KEY??"",i=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s.jwt}`,...e?{"X-Admin-Key":e}:{}},body:JSON.stringify(o),signal:AbortSignal.timeout(
|
|
7
|
+
ollama \u2192 OLLAMA_BASE_URL (local, no key needed)`);const t=`${s.apiUrl}/api/v1/embed`,o={text:r};(a??s.model)&&(o.model=a??s.model);const e=process.env.CACHLY_ADMIN_KEY??"",i=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s.jwt}`,...e?{"X-Admin-Key":e}:{}},body:JSON.stringify(o),signal:AbortSignal.timeout(c)});if(!i.ok){const _=await i.text();let l=null;const m=Number(i.headers.get("retry-after"));if(Number.isFinite(m)&&m>=0)l=m;else try{const E=JSON.parse(_);typeof E.retry_after=="number"&&E.retry_after>=0&&(l=E.retry_after)}catch{}throw Object.assign(new Error(`Cachly embed API error ${i.status}: ${_.slice(0,200)}`),{status:i.status,retryAfterSek:l})}return(await i.json()).embedding}case"mistral":{const t=process.env.MISTRAL_API_KEY;if(!t)throw new Error("MISTRAL_API_KEY not set");const o=s.model!=="text-embedding-3-small"?s.model:"mistral-embed",e=await fetch("https://api.mistral.ai/v1/embeddings",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify({model:o,input:[r]}),signal:AbortSignal.timeout(c)});if(!e.ok)throw new Error(`Mistral embedding error: ${e.statusText}`);const i=await e.json();if(!i.data?.[0]?.embedding)throw new Error("Mistral returned empty embedding response");return i.data[0].embedding}case"cohere":{const t=process.env.COHERE_API_KEY;if(!t)throw new Error("COHERE_API_KEY not set");const o=s.model!=="text-embedding-3-small"?s.model:"embed-english-v3.0",e=await fetch("https://api.cohere.com/v2/embed",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify({model:o,texts:[r],input_type:"search_query",embedding_types:["float"]}),signal:AbortSignal.timeout(c)});if(!e.ok)throw new Error(`Cohere embedding error: ${e.statusText}`);return(await e.json()).embeddings.float[0]}case"ollama":{const t=process.env.OLLAMA_BASE_URL??"http://localhost:11434",o=a??(s.model!=="text-embedding-3-small"?s.model:"nomic-embed-text"),e=await fetch(`${t}/api/embeddings`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:o,prompt:r}),signal:AbortSignal.timeout(c)});if(!e.ok)throw new Error(`Ollama embedding error: ${e.statusText}`);return(await e.json()).embedding}case"gemini":{const t=process.env.GEMINI_API_KEY;if(!t)throw new Error("GEMINI_API_KEY not set");const o=s.model!=="text-embedding-3-small"?s.model:"text-embedding-004",e=await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${o}:embedContent?key=${t}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:`models/${o}`,content:{parts:[{text:r}]}}),signal:AbortSignal.timeout(c)});if(!e.ok)throw new Error(`Gemini embedding error: ${e.statusText}`);return(await e.json()).embedding.values}case"openai":{const t=process.env.OPENAI_API_KEY;if(!t)throw new Error(`OPENAI_API_KEY not set.
|
|
8
8
|
|
|
9
9
|
Set OPENAI_API_KEY in your .env, or switch provider:
|
|
10
10
|
CACHLY_EMBED_PROVIDER=gemini (+ GEMINI_API_KEY)
|
|
11
11
|
CACHLY_EMBED_PROVIDER=ollama (local, free)
|
|
12
|
-
CACHLY_EMBED_PROVIDER=cachly (server-side, no key needed)`);const o=s.model||"text-embedding-3-small",e=await fetch("https://api.openai.com/v1/embeddings",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify({model:o,input:r}),signal:AbortSignal.timeout(
|
|
13
|
-
Supported: cachly (default), openai, mistral, cohere, ollama, gemini`)}}const b=3,
|
|
12
|
+
CACHLY_EMBED_PROVIDER=cachly (server-side, no key needed)`);const o=s.model||"text-embedding-3-small",e=await fetch("https://api.openai.com/v1/embeddings",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify({model:o,input:r}),signal:AbortSignal.timeout(c)});if(!e.ok)throw new Error(`OpenAI embedding error: ${e.statusText}`);const i=await e.json();if(!i.data?.[0]?.embedding)throw new Error("OpenAI returned empty embedding response");return i.data[0].embedding}default:throw new Error(`Unknown CACHLY_EMBED_PROVIDER="${d}".
|
|
13
|
+
Supported: cachly (default), openai, mistral, cohere, ollama, gemini`)}}const b=3,y=4;function w(r){const n=r;if(typeof n?.status=="number")return n.status===429||n.status>=500;if(n?.name==="TimeoutError"||n?.name==="AbortError")return!1;if(/fetch failed/i.test(String(n?.message??"")))return!0;const a=n?.cause?.code??"";return["ECONNRESET","ECONNREFUSED","ETIMEDOUT","EAI_AGAIN","EPIPE","UND_ERR_SOCKET"].includes(a)}function C(r,n,a){if(n>=(a==="lang"?y:b)||!w(r))return null;const t=r?.retryAfterSek;if(typeof t=="number"&&Number.isFinite(t)&&t>=0){const o=t*1e3;return a==="kurz"?o<=2e3?o:null:Math.min(o,61e3)}return a==="lang"?Math.min(1e3*2**(n-1),8e3):250*2**(n-1)}async function O(r,n){const a=n?.geduld??"kurz";if(n?.modell&&d!=="cachly"&&d!=="ollama")throw new Error(`Anbieter "${d}" kann kein abweichendes Modell anfragen (verlangt: ${n.modell}).`);let c=0;for(;;){c++;try{return await I(r,a,n?.modell)}catch(t){const o=C(t,c,a);if(o===null)throw t;await new Promise(e=>setTimeout(e,o))}}}function S(){switch(d){case"cachly":return!!s.jwt;case"mistral":return!!process.env.MISTRAL_API_KEY;case"cohere":return!!process.env.COHERE_API_KEY;case"ollama":return!0;case"gemini":return!!process.env.GEMINI_API_KEY;case"openai":return!!process.env.OPENAI_API_KEY;case"none":return!1;default:return!1}}function v(){const r={cachly:"CACHLY_JWT (server-side, no extra key needed)",openai:"OPENAI_API_KEY",mistral:"MISTRAL_API_KEY",cohere:"COHERE_API_KEY",ollama:"OLLAMA_BASE_URL (optional, default: http://localhost:11434)",gemini:"GEMINI_API_KEY"};if(d==="none")return"No embedding provider configured. Set OPENAI_API_KEY, GEMINI_API_KEY, or another provider key in .env. Brain works without embedding.";const n=r[d]??"CACHLY_JWT";return`CACHLY_EMBED_PROVIDER=${d} \u2192 requires ${n}`}export{d as EMBED_PROVIDER,O as computeEmbedding,s as embedConfig,v as embedProviderHint,S as hasEmbedProvider,w as istVoruebergehend,M as setEmbedJwt,C as wiederholungMs,h as zeitlimitMs};
|
|
@@ -14,7 +14,7 @@ ${f.join(`
|
|
|
14
14
|
|
|
15
15
|
${A.join(`
|
|
16
16
|
|
|
17
|
-
`)}`}case"smart_recall":{const y=Date.now(),{instance_id:n,query:t,threshold:r=.78,author:m="",context_files:h=[]}=U,S=Array.isArray(h)?h.filter(p=>typeof p=="string"):[],i=await Y(n);Lt(i,void 0,2);let u=new Set,A=!1;m&&(u=await ut(i,n,m).catch(()=>new Set),A=await Xe(i,n,m).catch(()=>null)==="admin");const s=await Fe(i,["cachly:ctx:*","cachly:lesson:best:*","cachly:idx:*"],t,25),w=hs(s);let C=!1,f=null;const o=await(async()=>{if(!Ae())return Ve(Xt,"kein Einbettungsdienst eingerichtet \u2014 die Suche laeuft nur ueber Woerter"),w;try{if(await $e.aktualisiere(i),$e.groesse===0)return Ve(Zt,"keine einzige Lektion hat eine Einbettung \u2014 die Suche laeuft nur ueber Woerter. Nachruesten mit eingaenge-nachruesten, pruefen mit brain_doctor"),w;const p=await Se(t);if(!p?.length)return Ve(Qt,"die Frage liess sich nicht einbetten \u2014 diese Suche laeuft nur ueber Woerter"),w;await tt.aktualisiere(i),await st.aktualisiere(i),await ze.aktualisiere(i),await Ke.aktualisiere(i);const $=$e.groesse>0?Ke.groesse/$e.groesse:0;$<kt&&$e.groesse>0&&In(i,3);const v=$>=kt?Se(t,{modell:Ge}).catch(()=>null):null,K=w.filter(P=>P.key.startsWith("cachly:lesson:best:")).map(P=>P.key.slice(19)),q=P=>Math.max($e.naehe(p,P),tt.naehe(p,P)),z=[...ze.themen()],X=(z.length>0?z:$e.aehnlichste(p,500).map(P=>P.topic)).map(P=>({t:P,n:q(P)})).sort((P,ue)=>ue.n-P.n).slice(0,xt).map(P=>P.t),ne=[...new Set([...K,...X])],J=X.slice(0,3).map(P=>$e.rohvektor(P)).filter(Boolean),me=J.length?Rs(p,J):p,D=$t(t),W=ze.statistik,ee=ne.map(P=>{const ue=new Set([...$t(ze.textVon(P))].map(Ds));return{naeheText:$e.naehe(p,P),naeheThema:tt.naehe(p,P),naeheRueckkopplung:$e.naehe(me,P),seltenheitsDeckung:W?W.deckung(D,ue):0,besterZeuge:W?W.besterZeuge(D,ue):0}});let H=Is(ee,Ls);{const P=[...H].sort((ue,_e)=>_e-ue);f=P.length>1?P[0]-P[1]:null}if(st.groesse>0){const P=ne.map(_e=>{const pe=st.besteNaehe(p,_e);return pe>=Qs?pe:-2}),ue=bt(P);H=H.map((_e,pe)=>_e+en*ue[pe])}if(v){const P=await v;if(P?.length){const ue=bt(ne.map(_e=>Ke.naehe(P,_e)));H=H.map((_e,pe)=>_e+Js*ue[pe])}}let V=ne.map((P,ue)=>({t:P,p:H[ue]})).sort((P,ue)=>ue.p-P.p).map(P=>P.t);if(ps()&&V.length>1){const P=V.slice(0,St),ue=V.slice(St),_e=new Map(ne.map((Oe,Re)=>[Oe,H[Re]])),pe=await ms(t,P.map(Oe=>ze.leserTextVon(Oe)),{instanceId:n});pe&&(V=[...gs(P.map(Re=>_e.get(Re)??0),pe).map(Re=>P[Re]),...ue])}const te=new Map(w.map(P=>[P.key,P])),he=[];for(const P of V){const ue=`cachly:lesson:best:${P}`,_e=te.get(ue);if(_e){he.push(_e),te.delete(ue);continue}const pe=await i.get(ue).catch(()=>null);pe&&he.push({key:ue,content:pe,score:0,matchedWords:[],wortBelege:0,finalScore:0,qualityBoost:1})}return he.push(...te.values()),Fs&&Ws({frage:t.slice(0,200),themen:ne,ausWortsuche:K.length,ausSinnsuche:X.length,obergrenze:xt,gezeigt:V.slice(0,10)}),C=!0,he}catch(p){return await We(i,es,`der Bedeutungspfad brach ab (${p instanceof Error?p.message:String(p)}) \u2014 diese Suche lief nur ueber Woerter`),w}})();Mt(i),vt(i,String(t??""),Date.now()-y);{const p=o.filter($=>$.key.startsWith("cachly:lesson:best:")).slice(0,3).map($=>$.key.slice(19));if(p.length>0){const $={ts:new Date().toISOString(),themen:p};(async()=>{const v=gt(n);await i.lpush(v,JSON.stringify($)),await i.ltrim(v,0,Ns-1)})().catch(()=>{})}{const $={ts:new Date().toISOString(),frage:t.slice(0,200),geliefert:p};(async()=>{const v=wt(n);await i.lpush(v,JSON.stringify($)),await i.ltrim(v,0,As-1)})().catch(()=>{})}}if(Ae()){const p=o.filter($=>$.key.startsWith("cachly:lesson:best:")).slice(0,3).map($=>$.key.slice(19));Ss(i,t,p,Se).catch(()=>{})}const N=[],_=o.filter(p=>p.key.startsWith("cachly:lesson:best:"));let b=0,I=0;for(const p of _.slice(0,5)){const $=await i.get(p.key).catch(()=>null);if($){const v=B($,null);if(v?.state==="archived")continue;if(v){const K=new Date().toISOString(),q={...v,recall_count:(v.recall_count??0)+1,last_recalled_at:K};i.set(p.key,JSON.stringify(q)).catch(()=>{});const z=ct(v)?rt(v):0;if(z>0&&i.incrbyfloat(`cachly:stats:time_saved_mins:${n}`,z).catch(()=>{}),v.author&&m&&v.author!==m){b++,i.sadd(`cachly:stats:reuse_pairs:${n}`,`${m}<-${v.author}`).catch(()=>{});const X=`person:${ye(m)}`,ne=`person:${ye(v.author)}`;be(i,X,"collaborates",ne,!0).catch(()=>{}),be(i,ne,"collaborates",X,!0).catch(()=>{})}v.outcome==="success"&&I++,v.outcome==="success"&&(v.recall_count??0)>=1&&!je(v)&&N.push({topic:p.key.replace("cachly:lesson:best:",""),severity:v.severity??"major",recall_count:(v.recall_count??0)+1,savedMins:z,ts:v.ts,author:v.author})}}}I>0&&i.set(`cachly:stats:first_recall_at:${n}`,new Date().toISOString(),"EX",365*86400,"NX").catch(()=>{}),_.length>0&&(i.incr(`cachly:stats:recalls_total:${n}`).catch(()=>{}),b>0&&i.incr(`cachly:stats:cross_author_recalls:${n}`).catch(()=>{}));const E=await de(`/api/v1/instances/${n}`).catch(()=>null),e=!E?.tier||E.tier.toLowerCase()==="free",j=[];if(!e&&E?.vector_token&&Ae())try{const p=await Se(t),$=`https://api.cachly.dev/v1/sem/${E.vector_token}`,v=await fetch(`${$}/search`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({embedding:p,namespace:"cachly:ctx",threshold:r,top_k:5}),signal:AbortSignal.timeout(8e3)});if(v.ok){const K=await v.json();for(const q of K.filter(z=>z.found&&z.id)){const z=q.id.replace("ctx:","").split(":"),X=z[0],ne=z.slice(1).join(":"),J=`cachly:ctx:${X}:${ne}`,me=await i.get(J).catch(()=>null);j.push({key:J,similarity:q.similarity??0,content:me??"(evicted)"})}}}catch{}const ie=o.map(p=>p.score),k=ie.length?Math.min(...ie):0,ge=ie.length&&Math.max(...ie)-k||1,le=p=>(p-k)/ge,F=new Map,O=C?vs(o.map(p=>le(p.score))):null,G=new Map;for(const[p,$]of o.entries()){const v=le($.score),K=O?O[p]:v;O&&F.set($.key,p),G.set($.key,{key:$.key,content:$.content,bm25Score:v,hausWert:K,hybridScore:K*(j.length>0?.7:1),matchedWords:$.matchedWords,matchType:"keyword",subQuery:$.subQuery,wortBelege:$.wortBelege})}for(const p of j){const $=G.get(p.key);$?($.semScore=p.similarity,$.hybridScore=($.hausWert??0)*.6+p.similarity*.4,$.matchType="hybrid"):G.set(p.key,{key:p.key,content:p.content,semScore:p.similarity,hybridScore:p.similarity*.5,matchType:"semantic"})}try{const p=Qe(t).filter(K=>K.length>=4).slice(0,4),$=new Set;for(const K of p){const q=i.scanStream({match:`cachly:ckg:node:*${K}*`,count:20});await new Promise((z,X)=>{q.on("data",ne=>{for(const J of ne.slice(0,5))$.add(J.replace("cachly:ckg:node:",""))}),q.on("end",z),q.on("error",X)})}const v=new Set;for(const K of[...$].slice(0,12)){const q=await i.smembers(`cachly:ckg:idx:to:${K}`);for(const z of q.slice(0,6)){const X=await i.get(z);if(!X)continue;const ne=B(X,null);if(!ne||ne.edgeType!=="fixes"||ne.confidence<.35||v.has(ne.from))continue;v.add(ne.from);const J=`cachly:lesson:best:${ne.from}`,me=G.get(J);if(me)me.ckgScore=Math.max(me.ckgScore??0,ne.confidence),me.hybridScore=me.hybridScore*.88+ne.confidence*.12,me.matchType==="keyword"&&(me.matchType="hybrid");else{const D=await i.get(J).catch(()=>null);if(!D)continue;G.set(J,{key:J,content:D,ckgScore:ne.confidence,hybridScore:ne.confidence*.3,matchType:"ckg"})}}}}catch{}if(S.length>0){const p=new Set(S.map($=>$.replace(/\\/g,"/")));for(const $ of G.values()){if(!$.key.startsWith("cachly:lesson:best:"))continue;const v=B($.content,{});(Array.isArray(v.file_paths)?v.file_paths.filter(q=>typeof q=="string").map(q=>q.replace(/\\/g,"/")):[]).some(q=>p.has(q))&&($.hybridScore*=1.15,$.contextBoost=!0)}}for(const p of G.values()){if(p.matchType!=="semantic"&&p.matchType!=="ckg")continue;const $=fs({key:p.key,content:p.content});$&&(p.hybridScore*=ds($))}const se=[...G.values()].filter(p=>{if(!p.key.startsWith("cachly:lesson:best:"))return!0;const $=Te(p.content);return!(!Ee($)||!ht($?.group,u,A))}),R=O?$s(se,F):se.sort((p,$)=>$.hybridScore-p.hybridScore),Z=[];for(const p of R){if(!p.key.startsWith("cachly:lesson:best:"))continue;const $=B(p.content,{});$.gilt_bis&&Date.parse(String($.gilt_bis))<Date.now()&&Z.push(`\u23F3 \`${p.key.replace("cachly:lesson:best:","")}\` galt bis ${String($.gilt_bis).slice(0,10)} \u2014 Geschichte, keine aktuelle Tatsache.`);const v=B(p.content,{});if(v.pruefung_gefallen_am){const K=p.key.replace("cachly:lesson:best:",""),q=String(v.pruefung_gefallen_am).slice(0,10),z=String(v.pruefung_befund??"").trim().slice(0,200);Z.push(`\u274C \`${K}\` \u2014 Pruefung am ${q} FEHLGESCHLAGEN`+(z?`: ${z}`:""))}}{const p=new Set(R.map($=>$.key));for(let $=R.length-1;$>=0;$--){const v=R[$];if(!v.key.startsWith("cachly:lesson:best:"))continue;const K=B(v.content,{});if(!K.ersetzt_durch)continue;const q=String(K.ersetzt_grund??"").trim(),z=v.key.replace("cachly:lesson:best:","");if(p.has(`cachly:lesson:best:${K.ersetzt_durch}`))R.splice($,1),Z.push(`\u21B7 \`${z}\` verdraengt \u2014 ersetzt durch \`${K.ersetzt_durch}\`${K.ersetzt_am?` am ${K.ersetzt_am.slice(0,10)}`:""}${q?` \u2014 Grund: ${q}`:" \u2014 Grund nicht erfasst"}`);else if(await i.get(`cachly:lesson:best:${K.ersetzt_durch}`)){const X=Te(await i.get(`cachly:lesson:best:${K.ersetzt_durch}`));Ee(X)?Z.push(`\u26A0\uFE0F \`${z}\` ist ersetzt durch \`${K.ersetzt_durch}\` (nicht in dieser Liste) \u2014 die gueltige Antwort: \`recall_best_solution(topic="${K.ersetzt_durch}")\``):Z.push(`\u21B7 \`${z}\`: von einer nicht team-sichtbaren Fassung ersetzt \u2014 diese Fassung bleibt die beste sichtbare Antwort.`)}else Z.push(`\u21A9\uFE0F \`${z}\` war ersetzt durch \`${K.ersetzt_durch}\` \u2014 der Nachfolger wurde geloescht, diese Fassung gilt wieder (bei Gelegenheit neu bestaetigen).`)}}const fe=[];{await $e.aktualisiere(i).catch(()=>{});const p=R.slice(0,8).filter($=>$.key.startsWith("cachly:lesson:best:"));for(let $=0;$<p.length;$++)for(let v=$+1;v<p.length;v++){const K=p[$].key.replace("cachly:lesson:best:",""),q=p[v].key.replace("cachly:lesson:best:",""),z=$e.rohvektor(K),X=$e.rohvektor(q);if(!z||!X)continue;let ne=0,J=0,me=0;for(let ee=0;ee<z.length;ee++)ne+=z[ee]*X[ee],J+=z[ee]*z[ee],me+=X[ee]*X[ee];if((J>0&&me>0?ne/(Math.sqrt(J)*Math.sqrt(me)):0)<qs)continue;const W=ee=>{const H=B(ee??"",{});return String(H.verified_at??H.ts??"").slice(0,10)||"ohne Datum"};fe.push(`\u26A0\uFE0F \`${K}\` (${W(p[$].content)}) und \`${q}\` (${W(p[v].content)}) sagen fast dasselbe \u2014 prueft, welche Fassung fuer euren Fall gilt; die neuere ist nicht automatisch die richtige.`)}fe.length>3&&(fe.length=3)}if(Z.push(...fe),R.length>=2){const p=R[0].hybridScore,$=R[Math.min(4,R.length-1)].hybridScore,v=Math.max(0,p-$);(async()=>{try{await i.lpush("cachly:recall:spread",JSON.stringify({ts:new Date().toISOString(),spread:Number(v.toFixed(4)),n:R.length})),await i.ltrim("cachly:recall:spread",0,199)}catch{}})()}const re=ts();let a="",g=null;if(re!==null){a=ss();const p=R[0]?.hybridScore??0,$=ns(R.map(v=>({key:v.key,content:v.content,punktzahl:v.hybridScore})),a,re,p);$.weggelassenesThema!==void 0?R.splice(0,1):$.markierungGesetzt&&R[0]&&$.treffer[0]&&(R[0]={...R[0],content:$.treffer[0].content}),g={frage:t,hoechstePunktzahl:p,themen:[],zulassungsfaehig:$.zulassungsfaehig,zulassungsGrund:$.zulassungsGrund,zuteilung:$.zuteilung,weggelassenesThema:$.weggelassenesThema,markierungGesetzt:$.markierungGesetzt}}const d=await Dt(n,de),c=d.reached,l=Tt({ueberLimit:c,gesamt:R.length,volleTiefe:sn}),x=process.env.CACHLY_RECALL_COMPACT==="1",L=(()=>{const p=Number.parseInt(process.env.CACHLY_RECALL_COMPACT_HITS??"",10);return Number.isFinite(p)?Math.min(20,Math.max(1,p)):5})(),M=x?Math.min(L,l.sichtbar):l.sichtbar,T=[`\u{1F9E0} **Smart Recall** for: _"${t}"_
|
|
17
|
+
`)}`}case"smart_recall":{const y=Date.now(),{instance_id:n,query:t,threshold:r=.78,author:m="",context_files:h=[]}=U,S=Array.isArray(h)?h.filter(p=>typeof p=="string"):[],i=await Y(n);Lt(i,void 0,2);let u=new Set,A=!1;m&&(u=await ut(i,n,m).catch(()=>new Set),A=await Xe(i,n,m).catch(()=>null)==="admin");const s=await Fe(i,["cachly:ctx:*","cachly:lesson:best:*","cachly:idx:*"],t,25),w=hs(s);let C=!1,f=null;const o=await(async()=>{if(!Ae())return Ve(Xt,"kein Einbettungsdienst eingerichtet \u2014 die Suche laeuft nur ueber Woerter"),w;try{if(await $e.aktualisiere(i),$e.groesse===0)return Ve(Zt,"keine einzige Lektion hat eine Einbettung \u2014 die Suche laeuft nur ueber Woerter. Nachruesten mit eingaenge-nachruesten, pruefen mit brain_doctor"),w;const p=await Se(t);if(!p?.length)return Ve(Qt,"die Frage liess sich nicht einbetten \u2014 diese Suche laeuft nur ueber Woerter"),w;await tt.aktualisiere(i),await st.aktualisiere(i),await ze.aktualisiere(i),await Ke.aktualisiere(i);const $=$e.groesse>0?Ke.groesse/$e.groesse:0;$<kt&&$e.groesse>0&&In(i,3);const v=$>=kt?Se(t,{modell:Ge}).catch(()=>null):null,K=w.filter(P=>P.key.startsWith("cachly:lesson:best:")).map(P=>P.key.slice(19)),q=P=>Math.max($e.naehe(p,P),tt.naehe(p,P)),z=[...ze.themen()],X=(z.length>0?z:$e.aehnlichste(p,500).map(P=>P.topic)).map(P=>({t:P,n:q(P)})).sort((P,ue)=>ue.n-P.n).slice(0,xt).map(P=>P.t),ne=[...new Set([...K,...X])],J=X.slice(0,3).map(P=>$e.rohvektor(P)).filter(Boolean),me=J.length?Rs(p,J):p,D=$t(t),W=ze.statistik,ee=ne.map(P=>{const ue=new Set([...$t(ze.textVon(P))].map(Ds));return{naeheText:$e.naehe(p,P),naeheThema:tt.naehe(p,P),naeheRueckkopplung:$e.naehe(me,P),seltenheitsDeckung:W?W.deckung(D,ue):0,besterZeuge:W?W.besterZeuge(D,ue):0}});let H=Is(ee,Ls);{const P=[...H].sort((ue,_e)=>_e-ue);f=P.length>1?P[0]-P[1]:null}if(st.groesse>0){const P=ne.map(_e=>{const pe=st.besteNaehe(p,_e);return pe>=Qs?pe:-2}),ue=bt(P);H=H.map((_e,pe)=>_e+en*ue[pe])}if(v){const P=await v;if(P?.length){const ue=bt(ne.map(_e=>Ke.naehe(P,_e)));H=H.map((_e,pe)=>_e+Js*ue[pe])}}let V=ne.map((P,ue)=>({t:P,p:H[ue]})).sort((P,ue)=>ue.p-P.p).map(P=>P.t);if(ps(n)&&V.length>1){const P=V.slice(0,St),ue=V.slice(St),_e=new Map(ne.map((Oe,Re)=>[Oe,H[Re]])),pe=await ms(t,P.map(Oe=>ze.leserTextVon(Oe)),{instanceId:n});pe&&(V=[...gs(P.map(Re=>_e.get(Re)??0),pe).map(Re=>P[Re]),...ue])}const te=new Map(w.map(P=>[P.key,P])),he=[];for(const P of V){const ue=`cachly:lesson:best:${P}`,_e=te.get(ue);if(_e){he.push(_e),te.delete(ue);continue}const pe=await i.get(ue).catch(()=>null);pe&&he.push({key:ue,content:pe,score:0,matchedWords:[],wortBelege:0,finalScore:0,qualityBoost:1})}return he.push(...te.values()),Fs&&Ws({frage:t.slice(0,200),themen:ne,ausWortsuche:K.length,ausSinnsuche:X.length,obergrenze:xt,gezeigt:V.slice(0,10)}),C=!0,he}catch(p){return await We(i,es,`der Bedeutungspfad brach ab (${p instanceof Error?p.message:String(p)}) \u2014 diese Suche lief nur ueber Woerter`),w}})();Mt(i),vt(i,String(t??""),Date.now()-y);{const p=o.filter($=>$.key.startsWith("cachly:lesson:best:")).slice(0,3).map($=>$.key.slice(19));if(p.length>0){const $={ts:new Date().toISOString(),themen:p};(async()=>{const v=gt(n);await i.lpush(v,JSON.stringify($)),await i.ltrim(v,0,Ns-1)})().catch(()=>{})}{const $={ts:new Date().toISOString(),frage:t.slice(0,200),geliefert:p};(async()=>{const v=wt(n);await i.lpush(v,JSON.stringify($)),await i.ltrim(v,0,As-1)})().catch(()=>{})}}if(Ae()){const p=o.filter($=>$.key.startsWith("cachly:lesson:best:")).slice(0,3).map($=>$.key.slice(19));Ss(i,t,p,Se).catch(()=>{})}const N=[],_=o.filter(p=>p.key.startsWith("cachly:lesson:best:"));let b=0,I=0;for(const p of _.slice(0,5)){const $=await i.get(p.key).catch(()=>null);if($){const v=B($,null);if(v?.state==="archived")continue;if(v){const K=new Date().toISOString(),q={...v,recall_count:(v.recall_count??0)+1,last_recalled_at:K};i.set(p.key,JSON.stringify(q)).catch(()=>{});const z=ct(v)?rt(v):0;if(z>0&&i.incrbyfloat(`cachly:stats:time_saved_mins:${n}`,z).catch(()=>{}),v.author&&m&&v.author!==m){b++,i.sadd(`cachly:stats:reuse_pairs:${n}`,`${m}<-${v.author}`).catch(()=>{});const X=`person:${ye(m)}`,ne=`person:${ye(v.author)}`;be(i,X,"collaborates",ne,!0).catch(()=>{}),be(i,ne,"collaborates",X,!0).catch(()=>{})}v.outcome==="success"&&I++,v.outcome==="success"&&(v.recall_count??0)>=1&&!je(v)&&N.push({topic:p.key.replace("cachly:lesson:best:",""),severity:v.severity??"major",recall_count:(v.recall_count??0)+1,savedMins:z,ts:v.ts,author:v.author})}}}I>0&&i.set(`cachly:stats:first_recall_at:${n}`,new Date().toISOString(),"EX",365*86400,"NX").catch(()=>{}),_.length>0&&(i.incr(`cachly:stats:recalls_total:${n}`).catch(()=>{}),b>0&&i.incr(`cachly:stats:cross_author_recalls:${n}`).catch(()=>{}));const E=await de(`/api/v1/instances/${n}`).catch(()=>null),e=!E?.tier||E.tier.toLowerCase()==="free",j=[];if(!e&&E?.vector_token&&Ae())try{const p=await Se(t),$=`https://api.cachly.dev/v1/sem/${E.vector_token}`,v=await fetch(`${$}/search`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({embedding:p,namespace:"cachly:ctx",threshold:r,top_k:5}),signal:AbortSignal.timeout(8e3)});if(v.ok){const K=await v.json();for(const q of K.filter(z=>z.found&&z.id)){const z=q.id.replace("ctx:","").split(":"),X=z[0],ne=z.slice(1).join(":"),J=`cachly:ctx:${X}:${ne}`,me=await i.get(J).catch(()=>null);j.push({key:J,similarity:q.similarity??0,content:me??"(evicted)"})}}}catch{}const ie=o.map(p=>p.score),k=ie.length?Math.min(...ie):0,ge=ie.length&&Math.max(...ie)-k||1,le=p=>(p-k)/ge,F=new Map,O=C?vs(o.map(p=>le(p.score))):null,G=new Map;for(const[p,$]of o.entries()){const v=le($.score),K=O?O[p]:v;O&&F.set($.key,p),G.set($.key,{key:$.key,content:$.content,bm25Score:v,hausWert:K,hybridScore:K*(j.length>0?.7:1),matchedWords:$.matchedWords,matchType:"keyword",subQuery:$.subQuery,wortBelege:$.wortBelege})}for(const p of j){const $=G.get(p.key);$?($.semScore=p.similarity,$.hybridScore=($.hausWert??0)*.6+p.similarity*.4,$.matchType="hybrid"):G.set(p.key,{key:p.key,content:p.content,semScore:p.similarity,hybridScore:p.similarity*.5,matchType:"semantic"})}try{const p=Qe(t).filter(K=>K.length>=4).slice(0,4),$=new Set;for(const K of p){const q=i.scanStream({match:`cachly:ckg:node:*${K}*`,count:20});await new Promise((z,X)=>{q.on("data",ne=>{for(const J of ne.slice(0,5))$.add(J.replace("cachly:ckg:node:",""))}),q.on("end",z),q.on("error",X)})}const v=new Set;for(const K of[...$].slice(0,12)){const q=await i.smembers(`cachly:ckg:idx:to:${K}`);for(const z of q.slice(0,6)){const X=await i.get(z);if(!X)continue;const ne=B(X,null);if(!ne||ne.edgeType!=="fixes"||ne.confidence<.35||v.has(ne.from))continue;v.add(ne.from);const J=`cachly:lesson:best:${ne.from}`,me=G.get(J);if(me)me.ckgScore=Math.max(me.ckgScore??0,ne.confidence),me.hybridScore=me.hybridScore*.88+ne.confidence*.12,me.matchType==="keyword"&&(me.matchType="hybrid");else{const D=await i.get(J).catch(()=>null);if(!D)continue;G.set(J,{key:J,content:D,ckgScore:ne.confidence,hybridScore:ne.confidence*.3,matchType:"ckg"})}}}}catch{}if(S.length>0){const p=new Set(S.map($=>$.replace(/\\/g,"/")));for(const $ of G.values()){if(!$.key.startsWith("cachly:lesson:best:"))continue;const v=B($.content,{});(Array.isArray(v.file_paths)?v.file_paths.filter(q=>typeof q=="string").map(q=>q.replace(/\\/g,"/")):[]).some(q=>p.has(q))&&($.hybridScore*=1.15,$.contextBoost=!0)}}for(const p of G.values()){if(p.matchType!=="semantic"&&p.matchType!=="ckg")continue;const $=fs({key:p.key,content:p.content});$&&(p.hybridScore*=ds($))}const se=[...G.values()].filter(p=>{if(!p.key.startsWith("cachly:lesson:best:"))return!0;const $=Te(p.content);return!(!Ee($)||!ht($?.group,u,A))}),R=O?$s(se,F):se.sort((p,$)=>$.hybridScore-p.hybridScore),Z=[];for(const p of R){if(!p.key.startsWith("cachly:lesson:best:"))continue;const $=B(p.content,{});$.gilt_bis&&Date.parse(String($.gilt_bis))<Date.now()&&Z.push(`\u23F3 \`${p.key.replace("cachly:lesson:best:","")}\` galt bis ${String($.gilt_bis).slice(0,10)} \u2014 Geschichte, keine aktuelle Tatsache.`);const v=B(p.content,{});if(v.pruefung_gefallen_am){const K=p.key.replace("cachly:lesson:best:",""),q=String(v.pruefung_gefallen_am).slice(0,10),z=String(v.pruefung_befund??"").trim().slice(0,200);Z.push(`\u274C \`${K}\` \u2014 Pruefung am ${q} FEHLGESCHLAGEN`+(z?`: ${z}`:""))}}{const p=new Set(R.map($=>$.key));for(let $=R.length-1;$>=0;$--){const v=R[$];if(!v.key.startsWith("cachly:lesson:best:"))continue;const K=B(v.content,{});if(!K.ersetzt_durch)continue;const q=String(K.ersetzt_grund??"").trim(),z=v.key.replace("cachly:lesson:best:","");if(p.has(`cachly:lesson:best:${K.ersetzt_durch}`))R.splice($,1),Z.push(`\u21B7 \`${z}\` verdraengt \u2014 ersetzt durch \`${K.ersetzt_durch}\`${K.ersetzt_am?` am ${K.ersetzt_am.slice(0,10)}`:""}${q?` \u2014 Grund: ${q}`:" \u2014 Grund nicht erfasst"}`);else if(await i.get(`cachly:lesson:best:${K.ersetzt_durch}`)){const X=Te(await i.get(`cachly:lesson:best:${K.ersetzt_durch}`));Ee(X)?Z.push(`\u26A0\uFE0F \`${z}\` ist ersetzt durch \`${K.ersetzt_durch}\` (nicht in dieser Liste) \u2014 die gueltige Antwort: \`recall_best_solution(topic="${K.ersetzt_durch}")\``):Z.push(`\u21B7 \`${z}\`: von einer nicht team-sichtbaren Fassung ersetzt \u2014 diese Fassung bleibt die beste sichtbare Antwort.`)}else Z.push(`\u21A9\uFE0F \`${z}\` war ersetzt durch \`${K.ersetzt_durch}\` \u2014 der Nachfolger wurde geloescht, diese Fassung gilt wieder (bei Gelegenheit neu bestaetigen).`)}}const fe=[];{await $e.aktualisiere(i).catch(()=>{});const p=R.slice(0,8).filter($=>$.key.startsWith("cachly:lesson:best:"));for(let $=0;$<p.length;$++)for(let v=$+1;v<p.length;v++){const K=p[$].key.replace("cachly:lesson:best:",""),q=p[v].key.replace("cachly:lesson:best:",""),z=$e.rohvektor(K),X=$e.rohvektor(q);if(!z||!X)continue;let ne=0,J=0,me=0;for(let ee=0;ee<z.length;ee++)ne+=z[ee]*X[ee],J+=z[ee]*z[ee],me+=X[ee]*X[ee];if((J>0&&me>0?ne/(Math.sqrt(J)*Math.sqrt(me)):0)<qs)continue;const W=ee=>{const H=B(ee??"",{});return String(H.verified_at??H.ts??"").slice(0,10)||"ohne Datum"};fe.push(`\u26A0\uFE0F \`${K}\` (${W(p[$].content)}) und \`${q}\` (${W(p[v].content)}) sagen fast dasselbe \u2014 prueft, welche Fassung fuer euren Fall gilt; die neuere ist nicht automatisch die richtige.`)}fe.length>3&&(fe.length=3)}if(Z.push(...fe),R.length>=2){const p=R[0].hybridScore,$=R[Math.min(4,R.length-1)].hybridScore,v=Math.max(0,p-$);(async()=>{try{await i.lpush("cachly:recall:spread",JSON.stringify({ts:new Date().toISOString(),spread:Number(v.toFixed(4)),n:R.length})),await i.ltrim("cachly:recall:spread",0,199)}catch{}})()}const re=ts();let a="",g=null;if(re!==null){a=ss();const p=R[0]?.hybridScore??0,$=ns(R.map(v=>({key:v.key,content:v.content,punktzahl:v.hybridScore})),a,re,p);$.weggelassenesThema!==void 0?R.splice(0,1):$.markierungGesetzt&&R[0]&&$.treffer[0]&&(R[0]={...R[0],content:$.treffer[0].content}),g={frage:t,hoechstePunktzahl:p,themen:[],zulassungsfaehig:$.zulassungsfaehig,zulassungsGrund:$.zulassungsGrund,zuteilung:$.zuteilung,weggelassenesThema:$.weggelassenesThema,markierungGesetzt:$.markierungGesetzt}}const d=await Dt(n,de),c=d.reached,l=Tt({ueberLimit:c,gesamt:R.length,volleTiefe:sn}),x=process.env.CACHLY_RECALL_COMPACT==="1",L=(()=>{const p=Number.parseInt(process.env.CACHLY_RECALL_COMPACT_HITS??"",10);return Number.isFinite(p)?Math.min(20,Math.max(1,p)):5})(),M=x?Math.min(L,l.sichtbar):l.sichtbar,T=[`\u{1F9E0} **Smart Recall** for: _"${t}"_
|
|
18
18
|
`];d.goodwillMessage&&T.push(`${d.goodwillMessage}
|
|
19
19
|
`);const oe={critical:0,major:1,minor:2},Q=N.sort((p,$)=>(oe[p.severity]??1)-(oe[$.severity]??1))[0];if(Q){const p=z=>z>=60?`${z/60}h`:`${z}min`,$=(()=>{if(!Q.ts)return null;const z=new Date(Q.ts);if(isNaN(z.getTime()))return null;const X=new Date,ne=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];return z.getFullYear()===X.getFullYear()?`${z.getDate()} ${ne[z.getMonth()]}`:`${z.getDate()} ${ne[z.getMonth()]} ${z.getFullYear()}`})(),v=Q.severity==="critical"?120:Q.severity==="major"?60:20,K=Q.author&&Q.author!==m?` \xB7 by @${Q.author}`:"",q=$?` \xB7 learned ${$} (was a ${p(v)} debug)`:"";T.push(`> \u{1F4A1} **Brain saved you ~${p(Q.savedMins)} here** \u2014 \`${Q.topic}\`${q} \xB7 recalled ${Q.recall_count}\xD7${K}
|
|
20
20
|
`)}b>0&&T.push(`> \u{1F465} **Team knowledge reuse** \u2014 ${b} of these lesson${b!==1?"s were":" was"} written by a teammate. This is the value only a shared brain delivers.
|