@cachly-dev/mcp-server 0.10.128 → 0.10.130
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/README.md +2 -2
- package/dist/index.js +1 -7
- package/dist/packages/telegram-notify/client.js +2 -75
- package/dist/src/ambient-cli.js +7 -253
- package/dist/src/ambient-deps.js +1 -55
- package/dist/src/ambient-hooks.js +5 -278
- package/dist/src/ambient-ledger.js +6 -95
- package/dist/src/ambient-memory.js +1 -67
- package/dist/src/ambient-recall.js +1 -221
- package/dist/src/aussetzer.js +1 -82
- package/dist/src/auth.js +9 -140
- package/dist/src/autor-abzeichen.js +1 -56
- package/dist/src/autor.js +1 -136
- package/dist/src/bauabdruck.js +1 -93
- package/dist/src/bedeutung.js +2 -323
- package/dist/src/bench/cachly-bench.js +4 -249
- package/dist/src/bench/external-corpus.js +4 -135
- package/dist/src/bench/fixtures.js +1 -225
- package/dist/src/brain-export.js +16 -119
- package/dist/src/brain-watch-hook.js +8 -86
- package/dist/src/cachly-url.js +1 -21
- package/dist/src/ckg.js +1 -108
- package/dist/src/cls-hook.js +8 -94
- package/dist/src/confidence.js +1 -50
- package/dist/src/credentials.js +1 -85
- package/dist/src/deutsch.js +1 -223
- package/dist/src/doctor.js +2 -248
- package/dist/src/editor.js +1 -63
- package/dist/src/eingaenge-nachruesten.js +1 -290
- package/dist/src/eingaenge.js +2 -228
- package/dist/src/embeddings.js +13 -223
- package/dist/src/feldreparatur.js +3 -0
- package/dist/src/first-contact.js +4 -108
- package/dist/src/first-recall-proof.js +4 -41
- package/dist/src/funnel-milestones.js +2 -48
- package/dist/src/geliefert.js +1 -114
- package/dist/src/handlers/advanced.js +38 -537
- package/dist/src/handlers/brain.js +69 -4181
- package/dist/src/handlers/cache.js +59 -638
- package/dist/src/handlers/context.js +17 -182
- package/dist/src/handlers/fedbrain.js +40 -1436
- package/dist/src/handlers/instances.js +16 -201
- package/dist/src/handlers/roadmap.js +13 -175
- package/dist/src/handlers/share.js +19 -530
- package/dist/src/handlers/syndicate.js +38 -904
- package/dist/src/handlers/team.js +89 -1850
- package/dist/src/handlers/viz.js +3 -148
- package/dist/src/index.js +321 -4281
- package/dist/src/interferenz.js +1 -0
- package/dist/src/kostprobe.js +7 -125
- package/dist/src/lesson-preview.js +2 -133
- package/dist/src/namespace.js +1 -128
- package/dist/src/notifier.js +1 -6
- package/dist/src/rangfolge-stellschrauben.js +1 -0
- package/dist/src/rangfolge.js +1 -214
- package/dist/src/raumkorrektur.js +1 -197
- package/dist/src/recall-tiefe.js +1 -42
- package/dist/src/rerank.js +1 -154
- package/dist/src/resonanz.js +1 -0
- package/dist/src/search.js +1 -2104
- package/dist/src/seltenheitsbestand.js +1 -92
- package/dist/src/speicherlage.js +1 -84
- package/dist/src/spuren.js +1 -150
- package/dist/src/starter-corpus.js +1 -165
- package/dist/src/telemetry-types.js +0 -13
- package/dist/src/tools.js +10 -2465
- package/dist/src/toolspecs.js +1 -121
- package/dist/src/upgrade-nudge.js +1 -29
- package/dist/src/utils.js +1 -89
- package/dist/src/versuch.js +1 -250
- package/dist/src/vorspann.js +1 -146
- package/dist/src/wartung/vektoren-verdichten.js +1 -200
- package/dist/src/werkzeug-nutzung.js +1 -72
- package/dist/src/wertbeitrag.js +1 -73
- package/dist/src/wirkungsspur.js +1 -0
- package/glama.json +1 -1
- package/package.json +8 -7
- package/server.json +3 -3
- package/dist/index.d.ts +0 -1
- package/dist/index.js.map +0 -1
- package/dist/packages/telegram-notify/client.d.ts +0 -19
- package/dist/packages/telegram-notify/client.js.map +0 -1
- package/dist/src/ambient-cli.d.ts +0 -101
- package/dist/src/ambient-cli.js.map +0 -1
- package/dist/src/ambient-deps.d.ts +0 -26
- package/dist/src/ambient-deps.js.map +0 -1
- package/dist/src/ambient-hooks.d.ts +0 -137
- package/dist/src/ambient-hooks.js.map +0 -1
- package/dist/src/ambient-ledger.d.ts +0 -19
- package/dist/src/ambient-ledger.js.map +0 -1
- package/dist/src/ambient-memory.d.ts +0 -12
- package/dist/src/ambient-memory.js.map +0 -1
- package/dist/src/ambient-recall.d.ts +0 -127
- package/dist/src/ambient-recall.js.map +0 -1
- package/dist/src/aussetzer.d.ts +0 -67
- package/dist/src/aussetzer.js.map +0 -1
- package/dist/src/auth.d.ts +0 -43
- package/dist/src/auth.js.map +0 -1
- package/dist/src/autor-abzeichen.d.ts +0 -41
- package/dist/src/autor-abzeichen.js.map +0 -1
- package/dist/src/autor.d.ts +0 -101
- package/dist/src/autor.js.map +0 -1
- package/dist/src/bauabdruck-daten.generiert.json +0 -5
- package/dist/src/bauabdruck.d.ts +0 -38
- package/dist/src/bauabdruck.js.map +0 -1
- package/dist/src/bedeutung.d.ts +0 -168
- package/dist/src/bedeutung.js.map +0 -1
- package/dist/src/bench/cachly-bench.d.ts +0 -61
- package/dist/src/bench/cachly-bench.js.map +0 -1
- package/dist/src/bench/external-corpus.d.ts +0 -44
- package/dist/src/bench/external-corpus.js.map +0 -1
- package/dist/src/bench/fixtures.d.ts +0 -37
- package/dist/src/bench/fixtures.js.map +0 -1
- package/dist/src/brain-export.d.ts +0 -64
- package/dist/src/brain-export.js.map +0 -1
- package/dist/src/brain-watch-hook.d.ts +0 -21
- package/dist/src/brain-watch-hook.js.map +0 -1
- package/dist/src/cachly-url.d.ts +0 -68
- package/dist/src/cachly-url.js.map +0 -1
- package/dist/src/ckg.d.ts +0 -54
- package/dist/src/ckg.js.map +0 -1
- package/dist/src/cls-hook.d.ts +0 -15
- package/dist/src/cls-hook.js.map +0 -1
- package/dist/src/confidence.d.ts +0 -19
- package/dist/src/confidence.js.map +0 -1
- package/dist/src/credentials.d.ts +0 -37
- package/dist/src/credentials.js.map +0 -1
- package/dist/src/deutsch.d.ts +0 -98
- package/dist/src/deutsch.js.map +0 -1
- package/dist/src/doctor.d.ts +0 -47
- package/dist/src/doctor.js.map +0 -1
- package/dist/src/editor.d.ts +0 -9
- package/dist/src/editor.js.map +0 -1
- package/dist/src/eingaenge-nachruesten.d.ts +0 -41
- package/dist/src/eingaenge-nachruesten.js.map +0 -1
- package/dist/src/eingaenge.d.ts +0 -135
- package/dist/src/eingaenge.js.map +0 -1
- package/dist/src/embeddings.d.ts +0 -48
- package/dist/src/embeddings.js.map +0 -1
- package/dist/src/first-contact.d.ts +0 -56
- package/dist/src/first-contact.js.map +0 -1
- package/dist/src/first-recall-proof.d.ts +0 -19
- package/dist/src/first-recall-proof.js.map +0 -1
- package/dist/src/funnel-milestones.d.ts +0 -5
- package/dist/src/funnel-milestones.js.map +0 -1
- package/dist/src/geliefert.d.ts +0 -63
- package/dist/src/geliefert.js.map +0 -1
- package/dist/src/handlers/advanced.d.ts +0 -6
- package/dist/src/handlers/advanced.js.map +0 -1
- package/dist/src/handlers/brain.d.ts +0 -41
- package/dist/src/handlers/brain.js.map +0 -1
- package/dist/src/handlers/cache.d.ts +0 -6
- package/dist/src/handlers/cache.js.map +0 -1
- package/dist/src/handlers/context.d.ts +0 -6
- package/dist/src/handlers/context.js.map +0 -1
- package/dist/src/handlers/fedbrain.d.ts +0 -18
- package/dist/src/handlers/fedbrain.js.map +0 -1
- package/dist/src/handlers/instances.d.ts +0 -33
- package/dist/src/handlers/instances.js.map +0 -1
- package/dist/src/handlers/roadmap.d.ts +0 -6
- package/dist/src/handlers/roadmap.js.map +0 -1
- package/dist/src/handlers/share.d.ts +0 -8
- package/dist/src/handlers/share.js.map +0 -1
- package/dist/src/handlers/syndicate.d.ts +0 -6
- package/dist/src/handlers/syndicate.js.map +0 -1
- package/dist/src/handlers/team.d.ts +0 -45
- package/dist/src/handlers/team.js.map +0 -1
- package/dist/src/handlers/viz.d.ts +0 -6
- package/dist/src/handlers/viz.js.map +0 -1
- package/dist/src/index.d.ts +0 -93
- package/dist/src/index.js.map +0 -1
- package/dist/src/kostprobe.d.ts +0 -25
- package/dist/src/kostprobe.js.map +0 -1
- package/dist/src/lesson-preview.d.ts +0 -81
- package/dist/src/lesson-preview.js.map +0 -1
- package/dist/src/namespace.d.ts +0 -20
- package/dist/src/namespace.js.map +0 -1
- package/dist/src/notifier.d.ts +0 -5
- package/dist/src/notifier.js.map +0 -1
- package/dist/src/rangfolge.d.ts +0 -169
- package/dist/src/rangfolge.js.map +0 -1
- package/dist/src/raumkorrektur.d.ts +0 -100
- package/dist/src/raumkorrektur.js.map +0 -1
- package/dist/src/recall-tiefe.d.ts +0 -48
- package/dist/src/recall-tiefe.js.map +0 -1
- package/dist/src/rerank.d.ts +0 -48
- package/dist/src/rerank.js.map +0 -1
- package/dist/src/search.d.ts +0 -153
- package/dist/src/search.js.map +0 -1
- package/dist/src/seltenheitsbestand.d.ts +0 -53
- package/dist/src/seltenheitsbestand.js.map +0 -1
- package/dist/src/speicherlage.d.ts +0 -51
- package/dist/src/speicherlage.js.map +0 -1
- package/dist/src/spuren.d.ts +0 -97
- package/dist/src/spuren.js.map +0 -1
- package/dist/src/starter-corpus.d.ts +0 -28
- package/dist/src/starter-corpus.js.map +0 -1
- package/dist/src/telemetry-types.d.ts +0 -87
- package/dist/src/telemetry-types.js.map +0 -1
- package/dist/src/tools.d.ts +0 -2933
- package/dist/src/tools.js.map +0 -1
- package/dist/src/toolspecs.d.ts +0 -43
- package/dist/src/toolspecs.js.map +0 -1
- package/dist/src/upgrade-nudge.d.ts +0 -15
- package/dist/src/upgrade-nudge.js.map +0 -1
- package/dist/src/utils.d.ts +0 -34
- package/dist/src/utils.js.map +0 -1
- package/dist/src/versuch.d.ts +0 -179
- package/dist/src/versuch.js.map +0 -1
- package/dist/src/vorspann.d.ts +0 -73
- package/dist/src/vorspann.js.map +0 -1
- package/dist/src/wartung/vektoren-verdichten.d.ts +0 -50
- package/dist/src/wartung/vektoren-verdichten.js.map +0 -1
- package/dist/src/werkzeug-nutzung.d.ts +0 -62
- package/dist/src/werkzeug-nutzung.js.map +0 -1
- package/dist/src/wertbeitrag.d.ts +0 -57
- package/dist/src/wertbeitrag.js.map +0 -1
package/dist/src/embeddings.js
CHANGED
|
@@ -1,223 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* │ Brain works WITHOUT embedding (keyword search + exact key lookup). │
|
|
15
|
-
* │ Embedding is an optional boost for semantic_search/index_project. │
|
|
16
|
-
* └──────────────────────────────────────────────────────────────────────┘
|
|
17
|
-
*
|
|
18
|
-
* Mutable config — call setEmbedJwt() when the JWT changes (device flow).
|
|
19
|
-
*/
|
|
20
|
-
// ── Mutable config (synced with index.ts JWT on device-flow auth) ────────────
|
|
21
|
-
export const embedConfig = {
|
|
22
|
-
apiUrl: process.env.CACHLY_API_URL ?? 'https://api.cachly.dev',
|
|
23
|
-
jwt: process.env.CACHLY_JWT ?? '',
|
|
24
|
-
model: process.env.CACHLY_EMBED_MODEL ?? '',
|
|
25
|
-
};
|
|
26
|
-
/** Call this when JWT is refreshed (e.g. after device flow). */
|
|
27
|
-
export function setEmbedJwt(jwt) {
|
|
28
|
-
embedConfig.jwt = jwt;
|
|
29
|
-
}
|
|
30
|
-
// Hard timeout for any embedding-provider HTTP call. Embedding is an OPTIONAL boost
|
|
31
|
-
// for semantic recall — a slow/unreachable provider must never hang the agent turn.
|
|
32
|
-
// Callers already treat embedding failures as non-fatal (keyword search still works).
|
|
33
|
-
const EMBED_TIMEOUT_MS = Number(process.env.CACHLY_EMBED_TIMEOUT_MS ?? 8_000);
|
|
34
|
-
// ── Provider auto-detection ───────────────────────────────────────────────────
|
|
35
|
-
function detectEmbedProvider() {
|
|
36
|
-
// Client-side keys first (direct, fast, no server roundtrip)
|
|
37
|
-
if (process.env.OPENAI_API_KEY)
|
|
38
|
-
return 'openai';
|
|
39
|
-
if (process.env.GEMINI_API_KEY)
|
|
40
|
-
return 'gemini';
|
|
41
|
-
if (process.env.MISTRAL_API_KEY)
|
|
42
|
-
return 'mistral';
|
|
43
|
-
if (process.env.COHERE_API_KEY)
|
|
44
|
-
return 'cohere';
|
|
45
|
-
if (process.env.OLLAMA_BASE_URL)
|
|
46
|
-
return 'ollama';
|
|
47
|
-
// Server-side fallback — no API key needed, but adds latency
|
|
48
|
-
if (process.env.CACHLY_JWT)
|
|
49
|
-
return 'cachly';
|
|
50
|
-
return 'none'; // no provider → embedding disabled, brain still works via exact keys
|
|
51
|
-
}
|
|
52
|
-
export const EMBED_PROVIDER = (process.env.CACHLY_EMBED_PROVIDER ?? detectEmbedProvider()).toLowerCase();
|
|
53
|
-
// ── Multi-provider embedding ─────────────────────────────────────────────────
|
|
54
|
-
/**
|
|
55
|
-
* Compute an embedding vector for `text` using the configured provider.
|
|
56
|
-
*
|
|
57
|
-
* Client-side (recommended — set one API key in your .env):
|
|
58
|
-
* openai – OPENAI_API_KEY · text-embedding-3-small
|
|
59
|
-
* gemini – GEMINI_API_KEY · text-embedding-004
|
|
60
|
-
* mistral – MISTRAL_API_KEY · mistral-embed
|
|
61
|
-
* cohere – COHERE_API_KEY · embed-english-v3.0
|
|
62
|
-
* ollama – OLLAMA_BASE_URL · nomic-embed-text (local, free)
|
|
63
|
-
*
|
|
64
|
-
* Server-side fallback (no key needed on client):
|
|
65
|
-
* cachly – POST /api/v1/embed (requires CACHLY_JWT)
|
|
66
|
-
*
|
|
67
|
-
* Note: Brain works fully WITHOUT embedding (keyword search + exact keys).
|
|
68
|
-
* Embedding is an OPTIONAL boost for semantic_search and index_project.
|
|
69
|
-
*/
|
|
70
|
-
export async function computeEmbedding(text) {
|
|
71
|
-
switch (EMBED_PROVIDER) {
|
|
72
|
-
case 'cachly': {
|
|
73
|
-
// Server-side embedding — the Cachly API computes the embedding
|
|
74
|
-
// using whatever provider is configured on the server. No client-side API key needed.
|
|
75
|
-
if (!embedConfig.jwt)
|
|
76
|
-
throw new Error('CACHLY_JWT not set.\n\n' +
|
|
77
|
-
'The "cachly" provider uses server-side embeddings via the Cachly API.\n' +
|
|
78
|
-
'Set CACHLY_JWT, or use another provider via CACHLY_EMBED_PROVIDER:\n' +
|
|
79
|
-
' openai → OPENAI_API_KEY\n' +
|
|
80
|
-
' gemini → GEMINI_API_KEY\n' +
|
|
81
|
-
' ollama → OLLAMA_BASE_URL (local, no key needed)');
|
|
82
|
-
const url = `${embedConfig.apiUrl}/api/v1/embed`;
|
|
83
|
-
const body = { text };
|
|
84
|
-
if (embedConfig.model)
|
|
85
|
-
body.model = embedConfig.model;
|
|
86
|
-
const res = await fetch(url, {
|
|
87
|
-
method: 'POST',
|
|
88
|
-
headers: {
|
|
89
|
-
'Content-Type': 'application/json',
|
|
90
|
-
'Authorization': `Bearer ${embedConfig.jwt}`,
|
|
91
|
-
},
|
|
92
|
-
body: JSON.stringify(body),
|
|
93
|
-
signal: AbortSignal.timeout(EMBED_TIMEOUT_MS),
|
|
94
|
-
});
|
|
95
|
-
if (!res.ok) {
|
|
96
|
-
const errBody = await res.text();
|
|
97
|
-
throw new Error(`Cachly embed API error ${res.status}: ${errBody}`);
|
|
98
|
-
}
|
|
99
|
-
const json = (await res.json());
|
|
100
|
-
return json.embedding;
|
|
101
|
-
}
|
|
102
|
-
case 'mistral': {
|
|
103
|
-
const key = process.env.MISTRAL_API_KEY;
|
|
104
|
-
if (!key)
|
|
105
|
-
throw new Error('MISTRAL_API_KEY not set');
|
|
106
|
-
const model = embedConfig.model !== 'text-embedding-3-small' ? embedConfig.model : 'mistral-embed';
|
|
107
|
-
const res = await fetch('https://api.mistral.ai/v1/embeddings', {
|
|
108
|
-
method: 'POST',
|
|
109
|
-
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${key}` },
|
|
110
|
-
body: JSON.stringify({ model, input: [text] }),
|
|
111
|
-
signal: AbortSignal.timeout(EMBED_TIMEOUT_MS),
|
|
112
|
-
});
|
|
113
|
-
if (!res.ok)
|
|
114
|
-
throw new Error(`Mistral embedding error: ${res.statusText}`);
|
|
115
|
-
const json = (await res.json());
|
|
116
|
-
if (!json.data?.[0]?.embedding)
|
|
117
|
-
throw new Error('Mistral returned empty embedding response');
|
|
118
|
-
return json.data[0].embedding;
|
|
119
|
-
}
|
|
120
|
-
case 'cohere': {
|
|
121
|
-
const key = process.env.COHERE_API_KEY;
|
|
122
|
-
if (!key)
|
|
123
|
-
throw new Error('COHERE_API_KEY not set');
|
|
124
|
-
const model = embedConfig.model !== 'text-embedding-3-small' ? embedConfig.model : 'embed-english-v3.0';
|
|
125
|
-
const res = await fetch('https://api.cohere.com/v2/embed', {
|
|
126
|
-
method: 'POST',
|
|
127
|
-
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${key}` },
|
|
128
|
-
body: JSON.stringify({ model, texts: [text], input_type: 'search_query', embedding_types: ['float'] }),
|
|
129
|
-
signal: AbortSignal.timeout(EMBED_TIMEOUT_MS),
|
|
130
|
-
});
|
|
131
|
-
if (!res.ok)
|
|
132
|
-
throw new Error(`Cohere embedding error: ${res.statusText}`);
|
|
133
|
-
const json = (await res.json());
|
|
134
|
-
return json.embeddings.float[0];
|
|
135
|
-
}
|
|
136
|
-
case 'ollama': {
|
|
137
|
-
const base = process.env.OLLAMA_BASE_URL ?? 'http://localhost:11434';
|
|
138
|
-
const model = embedConfig.model !== 'text-embedding-3-small' ? embedConfig.model : 'nomic-embed-text';
|
|
139
|
-
const res = await fetch(`${base}/api/embeddings`, {
|
|
140
|
-
method: 'POST',
|
|
141
|
-
headers: { 'Content-Type': 'application/json' },
|
|
142
|
-
body: JSON.stringify({ model, prompt: text }),
|
|
143
|
-
signal: AbortSignal.timeout(EMBED_TIMEOUT_MS),
|
|
144
|
-
});
|
|
145
|
-
if (!res.ok)
|
|
146
|
-
throw new Error(`Ollama embedding error: ${res.statusText}`);
|
|
147
|
-
const json = (await res.json());
|
|
148
|
-
return json.embedding;
|
|
149
|
-
}
|
|
150
|
-
case 'gemini': {
|
|
151
|
-
const key = process.env.GEMINI_API_KEY;
|
|
152
|
-
if (!key)
|
|
153
|
-
throw new Error('GEMINI_API_KEY not set');
|
|
154
|
-
const model = embedConfig.model !== 'text-embedding-3-small' ? embedConfig.model : 'text-embedding-004';
|
|
155
|
-
const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${model}:embedContent?key=${key}`, {
|
|
156
|
-
method: 'POST',
|
|
157
|
-
headers: { 'Content-Type': 'application/json' },
|
|
158
|
-
body: JSON.stringify({ model: `models/${model}`, content: { parts: [{ text }] } }),
|
|
159
|
-
signal: AbortSignal.timeout(EMBED_TIMEOUT_MS),
|
|
160
|
-
});
|
|
161
|
-
if (!res.ok)
|
|
162
|
-
throw new Error(`Gemini embedding error: ${res.statusText}`);
|
|
163
|
-
const json = (await res.json());
|
|
164
|
-
return json.embedding.values;
|
|
165
|
-
}
|
|
166
|
-
case 'openai': {
|
|
167
|
-
const key = process.env.OPENAI_API_KEY;
|
|
168
|
-
if (!key)
|
|
169
|
-
throw new Error('OPENAI_API_KEY not set.\n\n' +
|
|
170
|
-
'Set OPENAI_API_KEY in your .env, or switch provider:\n' +
|
|
171
|
-
' CACHLY_EMBED_PROVIDER=gemini (+ GEMINI_API_KEY)\n' +
|
|
172
|
-
' CACHLY_EMBED_PROVIDER=ollama (local, free)\n' +
|
|
173
|
-
' CACHLY_EMBED_PROVIDER=cachly (server-side, no key needed)');
|
|
174
|
-
const model = embedConfig.model || 'text-embedding-3-small';
|
|
175
|
-
const res = await fetch('https://api.openai.com/v1/embeddings', {
|
|
176
|
-
method: 'POST',
|
|
177
|
-
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${key}` },
|
|
178
|
-
body: JSON.stringify({ model, input: text }),
|
|
179
|
-
signal: AbortSignal.timeout(EMBED_TIMEOUT_MS),
|
|
180
|
-
});
|
|
181
|
-
if (!res.ok)
|
|
182
|
-
throw new Error(`OpenAI embedding error: ${res.statusText}`);
|
|
183
|
-
const json = (await res.json());
|
|
184
|
-
if (!json.data?.[0]?.embedding)
|
|
185
|
-
throw new Error('OpenAI returned empty embedding response');
|
|
186
|
-
return json.data[0].embedding;
|
|
187
|
-
}
|
|
188
|
-
default: {
|
|
189
|
-
throw new Error(`Unknown CACHLY_EMBED_PROVIDER="${EMBED_PROVIDER}".\n` +
|
|
190
|
-
'Supported: cachly (default), openai, mistral, cohere, ollama, gemini');
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
/** Returns true if the configured embedding provider has its required key/URL set. */
|
|
195
|
-
export function hasEmbedProvider() {
|
|
196
|
-
switch (EMBED_PROVIDER) {
|
|
197
|
-
case 'cachly': return !!embedConfig.jwt;
|
|
198
|
-
case 'mistral': return !!process.env.MISTRAL_API_KEY;
|
|
199
|
-
case 'cohere': return !!process.env.COHERE_API_KEY;
|
|
200
|
-
case 'ollama': return true; // OLLAMA_BASE_URL is optional (defaults to localhost)
|
|
201
|
-
case 'gemini': return !!process.env.GEMINI_API_KEY;
|
|
202
|
-
case 'openai': return !!process.env.OPENAI_API_KEY;
|
|
203
|
-
case 'none': return false;
|
|
204
|
-
default: return false;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
/** Returns a human-readable description of the current embed provider/model for error messages. */
|
|
208
|
-
export function embedProviderHint() {
|
|
209
|
-
const providerKeys = {
|
|
210
|
-
cachly: 'CACHLY_JWT (server-side, no extra key needed)',
|
|
211
|
-
openai: 'OPENAI_API_KEY',
|
|
212
|
-
mistral: 'MISTRAL_API_KEY',
|
|
213
|
-
cohere: 'COHERE_API_KEY',
|
|
214
|
-
ollama: 'OLLAMA_BASE_URL (optional, default: http://localhost:11434)',
|
|
215
|
-
gemini: 'GEMINI_API_KEY',
|
|
216
|
-
};
|
|
217
|
-
if (EMBED_PROVIDER === 'none') {
|
|
218
|
-
return 'No embedding provider configured. Set OPENAI_API_KEY, GEMINI_API_KEY, or another provider key in .env. Brain works without embedding.';
|
|
219
|
-
}
|
|
220
|
-
const key = providerKeys[EMBED_PROVIDER] ?? 'CACHLY_JWT';
|
|
221
|
-
return `CACHLY_EMBED_PROVIDER=${EMBED_PROVIDER} → requires ${key}`;
|
|
222
|
-
}
|
|
223
|
-
//# sourceMappingURL=embeddings.js.map
|
|
1
|
+
const t={apiUrl:process.env.CACHLY_API_URL??"https://api.cachly.dev",jwt:process.env.CACHLY_JWT??"",model:process.env.CACHLY_EMBED_MODEL??""};function m(r){t.jwt=r}const i=Number(process.env.CACHLY_EMBED_TIMEOUT_MS??8e3);function c(){return process.env.CACHLY_JWT?"cachly":process.env.OLLAMA_BASE_URL?"ollama":"none"}const a=(process.env.CACHLY_EMBED_PROVIDER??c()).toLowerCase();async function l(r){switch(a){case"cachly":{if(!t.jwt)throw new Error(`CACHLY_JWT not set.
|
|
2
|
+
|
|
3
|
+
The "cachly" provider uses server-side embeddings via the Cachly API.
|
|
4
|
+
Set CACHLY_JWT, or use another provider via CACHLY_EMBED_PROVIDER:
|
|
5
|
+
openai \u2192 OPENAI_API_KEY
|
|
6
|
+
gemini \u2192 GEMINI_API_KEY
|
|
7
|
+
ollama \u2192 OLLAMA_BASE_URL (local, no key needed)`);const o=`${t.apiUrl}/api/v1/embed`,n={text:r};t.model&&(n.model=t.model);const e=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.jwt}`},body:JSON.stringify(n),signal:AbortSignal.timeout(i)});if(!e.ok){const d=await e.text();throw new Error(`Cachly embed API error ${e.status}: ${d}`)}return(await e.json()).embedding}case"mistral":{const o=process.env.MISTRAL_API_KEY;if(!o)throw new Error("MISTRAL_API_KEY not set");const n=t.model!=="text-embedding-3-small"?t.model:"mistral-embed",e=await fetch("https://api.mistral.ai/v1/embeddings",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({model:n,input:[r]}),signal:AbortSignal.timeout(i)});if(!e.ok)throw new Error(`Mistral embedding error: ${e.statusText}`);const s=await e.json();if(!s.data?.[0]?.embedding)throw new Error("Mistral returned empty embedding response");return s.data[0].embedding}case"cohere":{const o=process.env.COHERE_API_KEY;if(!o)throw new Error("COHERE_API_KEY not set");const n=t.model!=="text-embedding-3-small"?t.model:"embed-english-v3.0",e=await fetch("https://api.cohere.com/v2/embed",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({model:n,texts:[r],input_type:"search_query",embedding_types:["float"]}),signal:AbortSignal.timeout(i)});if(!e.ok)throw new Error(`Cohere embedding error: ${e.statusText}`);return(await e.json()).embeddings.float[0]}case"ollama":{const o=process.env.OLLAMA_BASE_URL??"http://localhost:11434",n=t.model!=="text-embedding-3-small"?t.model:"nomic-embed-text",e=await fetch(`${o}/api/embeddings`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:n,prompt:r}),signal:AbortSignal.timeout(i)});if(!e.ok)throw new Error(`Ollama embedding error: ${e.statusText}`);return(await e.json()).embedding}case"gemini":{const o=process.env.GEMINI_API_KEY;if(!o)throw new Error("GEMINI_API_KEY not set");const n=t.model!=="text-embedding-3-small"?t.model:"text-embedding-004",e=await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${n}:embedContent?key=${o}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:`models/${n}`,content:{parts:[{text:r}]}}),signal:AbortSignal.timeout(i)});if(!e.ok)throw new Error(`Gemini embedding error: ${e.statusText}`);return(await e.json()).embedding.values}case"openai":{const o=process.env.OPENAI_API_KEY;if(!o)throw new Error(`OPENAI_API_KEY not set.
|
|
8
|
+
|
|
9
|
+
Set OPENAI_API_KEY in your .env, or switch provider:
|
|
10
|
+
CACHLY_EMBED_PROVIDER=gemini (+ GEMINI_API_KEY)
|
|
11
|
+
CACHLY_EMBED_PROVIDER=ollama (local, free)
|
|
12
|
+
CACHLY_EMBED_PROVIDER=cachly (server-side, no key needed)`);const n=t.model||"text-embedding-3-small",e=await fetch("https://api.openai.com/v1/embeddings",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({model:n,input:r}),signal:AbortSignal.timeout(i)});if(!e.ok)throw new Error(`OpenAI embedding error: ${e.statusText}`);const s=await e.json();if(!s.data?.[0]?.embedding)throw new Error("OpenAI returned empty embedding response");return s.data[0].embedding}default:throw new Error(`Unknown CACHLY_EMBED_PROVIDER="${a}".
|
|
13
|
+
Supported: cachly (default), openai, mistral, cohere, ollama, gemini`)}}function E(){switch(a){case"cachly":return!!t.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 p(){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(a==="none")return"No embedding provider configured. Set OPENAI_API_KEY, GEMINI_API_KEY, or another provider key in .env. Brain works without embedding.";const o=r[a]??"CACHLY_JWT";return`CACHLY_EMBED_PROVIDER=${a} \u2192 requires ${o}`}export{a as EMBED_PROVIDER,l as computeEmbedding,t as embedConfig,p as embedProviderHint,E as hasEmbedProvider,m as setEmbedJwt};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
const d=["what_worked","what_failed","context"],a=new RegExp(String.raw`<\/?(?:parameter\s+name\s*=\s*["']?)?(${d.join("|")})["']?\s*\/?>`,"gi");function w(t){return t?(a.lastIndex=0,a.test(t)):!1}function x(t){if(!w(t.what_worked)&&!w(t.what_failed)&&!w(t.context))return t;const n={what_worked:"",what_failed:t.what_failed??"",context:t.context??""},o=t.what_worked??"",i=[];let s=0,l="what_worked";a.lastIndex=0;for(let e=a.exec(o);e!==null;e=a.exec(o)){i.push({ziel:l,text:o.slice(s,e.index)});const r=e[1].toLowerCase();l=e[0].startsWith("</")?l:r,s=e.index+e[0].length}i.push({ziel:l,text:o.slice(s)});for(const e of i){const r=e.text.trim();r&&(n[e.ziel]?n[e.ziel]=`${n[e.ziel]}
|
|
2
|
+
|
|
3
|
+
${r}`:n[e.ziel]=r)}const c=e=>e.replace(a," ").replace(/[ \t]{2,}/g," ").trim();return{what_worked:c(n.what_worked),what_failed:n.what_failed?c(n.what_failed):t.what_failed,context:n.context?c(n.context):t.context}}export{w as hatFeldmarke,x as repariereFelder};
|
|
@@ -1,108 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* The very first `brain_from_git` run is a new user's first contact with the
|
|
6
|
-
* Brain, and it decides whether they ever reach the "aha". The response must,
|
|
7
|
-
* in one message:
|
|
8
|
-
* (a) summarize what was just learned (counts by category + severity),
|
|
9
|
-
* (b) prove recall already works — show the top hit of a real search that
|
|
10
|
-
* ran against a topic seeded seconds ago,
|
|
11
|
-
* (c) hand the user 2-3 copy-pasteable next queries, and
|
|
12
|
-
* (d) degrade gracefully for empty/small repos (clear guidance, not empty stats).
|
|
13
|
-
*
|
|
14
|
-
* Pure string building — no I/O, no schema/tool changes — so the onboarding
|
|
15
|
-
* bench and its CI test can assert the exact first-contact response shape.
|
|
16
|
-
*/
|
|
17
|
-
/** Below this many ingested lessons we treat the repo as "small" and add growth guidance. */
|
|
18
|
-
export const SMALL_SEED_THRESHOLD = 5;
|
|
19
|
-
export function formatSeedDuration(ms) {
|
|
20
|
-
if (ms < 1000)
|
|
21
|
-
return `${Math.max(ms, 1)}ms`;
|
|
22
|
-
if (ms < 60_000)
|
|
23
|
-
return `${(ms / 1000).toFixed(1)}s`;
|
|
24
|
-
return `${Math.floor(ms / 60_000)}m ${Math.round((ms % 60_000) / 1000)}s`;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Turn seeded topics (`category:some-domain`) into up to `max` copy-pasteable
|
|
28
|
-
* `smart_recall` calls a brand-new user can paste as their literal next message.
|
|
29
|
-
* Deduplicates by domain so three "fix:*" topics don't yield three identical queries.
|
|
30
|
-
*/
|
|
31
|
-
export function suggestRecallQueries(topics, instanceId, max = 3) {
|
|
32
|
-
const seen = new Set();
|
|
33
|
-
const out = [];
|
|
34
|
-
for (const topic of topics) {
|
|
35
|
-
const domain = (topic.includes(':') ? topic.slice(topic.indexOf(':') + 1) : topic)
|
|
36
|
-
.replace(/[-_]+/g, ' ')
|
|
37
|
-
.trim();
|
|
38
|
-
if (!domain || seen.has(domain))
|
|
39
|
-
continue;
|
|
40
|
-
seen.add(domain);
|
|
41
|
-
out.push(`smart_recall(instance_id="${instanceId}", query="${domain}")`);
|
|
42
|
-
if (out.length >= max)
|
|
43
|
-
break;
|
|
44
|
-
}
|
|
45
|
-
return out;
|
|
46
|
-
}
|
|
47
|
-
/** Steps that take an empty/tiny Brain to a first recall hit in under a minute. */
|
|
48
|
-
function growthSteps(instanceId) {
|
|
49
|
-
return [
|
|
50
|
-
` 1. \`brain_seed_starter(instance_id="${instanceId}")\` — load 16 curated engineering lessons instantly`,
|
|
51
|
-
` 2. \`smart_recall(instance_id="${instanceId}", query="docker build slow")\` — see your first recall hit`,
|
|
52
|
-
` 3. \`learn_from_attempts(...)\` after your next fix — the Brain grows with every lesson from here on`,
|
|
53
|
-
];
|
|
54
|
-
}
|
|
55
|
-
export function buildFirstContactReport(r) {
|
|
56
|
-
const lines = [];
|
|
57
|
-
lines.push(`🔁 **brain_from_git: ${r.repoDir}**`);
|
|
58
|
-
if (r.isIncremental && r.lastSha) {
|
|
59
|
-
lines.push(`🔄 Incremental mode — only new commits since \`${r.lastSha.slice(0, 8)}\` were processed`);
|
|
60
|
-
}
|
|
61
|
-
lines.push('');
|
|
62
|
-
lines.push(`📂 Branch: \`${r.revRange}\` | Processed: **${r.processed}** commits | ` +
|
|
63
|
-
`Ingested: **${r.ingested}** lessons | Skipped: ${r.skipped} | ` +
|
|
64
|
-
`⏱️ Seeding took ${formatSeedDuration(r.durationMs)}`);
|
|
65
|
-
lines.push('');
|
|
66
|
-
// ── Empty repo / nothing ingested — guidance instead of empty stats ─────────
|
|
67
|
-
if (r.ingested === 0) {
|
|
68
|
-
if (r.emptyReason)
|
|
69
|
-
lines.push(`⚠️ ${r.emptyReason}`);
|
|
70
|
-
lines.push(`🌱 **Nothing to learn from this git history yet** — normal for fresh, shallow, or squash-only repos.`);
|
|
71
|
-
lines.push(`Get to your first recall in under a minute:`);
|
|
72
|
-
lines.push(...growthSteps(r.instanceId));
|
|
73
|
-
return lines.join('\n');
|
|
74
|
-
}
|
|
75
|
-
// ── (a) Seeded summary — what the Brain just learned ────────────────────────
|
|
76
|
-
lines.push(`**What your Brain just learned (by category):**`);
|
|
77
|
-
lines.push(r.categories.map(([k, v]) => ` • **${k}** (${v})`).join('\n'));
|
|
78
|
-
if (r.severities && r.severities.length > 0) {
|
|
79
|
-
lines.push(`**By severity:** ${r.severities.map(([k, v]) => `${k} ${v}`).join(' · ')}`);
|
|
80
|
-
}
|
|
81
|
-
// ── (b) Proof of value — a real recall against a just-seeded topic ──────────
|
|
82
|
-
if (r.proof) {
|
|
83
|
-
lines.push('');
|
|
84
|
-
lines.push(`🎯 **Proof — your first recall already works.** We just searched your fresh Brain for:`);
|
|
85
|
-
lines.push(` "${r.proof.query}"`);
|
|
86
|
-
lines.push(` → Top hit: **${r.proof.topic}** — ${r.proof.snippet}`);
|
|
87
|
-
}
|
|
88
|
-
// ── (c) Suggested next queries — copy-pasteable ──────────────────────────────
|
|
89
|
-
if (r.suggestedQueries.length > 0) {
|
|
90
|
-
lines.push('');
|
|
91
|
-
lines.push(`**Try these next (copy-paste):**`);
|
|
92
|
-
for (const q of r.suggestedQueries)
|
|
93
|
-
lines.push(` • \`${q}\``);
|
|
94
|
-
}
|
|
95
|
-
// ── (d) Small-repo guidance ──────────────────────────────────────────────────
|
|
96
|
-
if (r.ingested < SMALL_SEED_THRESHOLD) {
|
|
97
|
-
lines.push('');
|
|
98
|
-
lines.push(`🌱 **Small git history — only ${r.ingested} lesson${r.ingested === 1 ? '' : 's'} so far.** Top it up:`);
|
|
99
|
-
lines.push(...growthSteps(r.instanceId));
|
|
100
|
-
}
|
|
101
|
-
lines.push('');
|
|
102
|
-
lines.push(`💡 New lessons are stored with confidence 0.55 (auto-inferred).`);
|
|
103
|
-
lines.push(`💡 As you confirm them via \`learn_from_attempts\`, confidence rises automatically.`);
|
|
104
|
-
lines.push(`🔍 Explore: \`brain_search(query="fix")\` | \`ckg_inspect(concept="deploy")\``);
|
|
105
|
-
lines.push(`💾 Next run will automatically continue from the latest commit (incremental mode).`);
|
|
106
|
-
return lines.join('\n');
|
|
107
|
-
}
|
|
108
|
-
//# sourceMappingURL=first-contact.js.map
|
|
1
|
+
const p=5;function u(e){return e<1e3?`${Math.max(e,1)}ms`:e<6e4?`${(e/1e3).toFixed(1)}s`:`${Math.floor(e/6e4)}m ${Math.round(e%6e4/1e3)}s`}function c(e,s,t=3){const o=new Set,r=[];for(const n of e){const i=(n.includes(":")?n.slice(n.indexOf(":")+1):n).replace(/[-_]+/g," ").trim();if(!(!i||o.has(i))&&(o.add(i),r.push(`smart_recall(instance_id="${s}", query="${i}")`),r.length>=t))break}return r}function a(e){return[` 1. \`brain_seed_starter(instance_id="${e}")\` \u2014 load 16 curated engineering lessons instantly`,` 2. \`smart_recall(instance_id="${e}", query="docker build slow")\` \u2014 see your first recall hit`," 3. `learn_from_attempts(...)` after your next fix \u2014 the Brain grows with every lesson from here on"]}function h(e){const s=[];if(s.push(`\u{1F501} **brain_from_git: ${e.repoDir}**`),e.isIncremental&&e.lastSha&&s.push(`\u{1F504} Incremental mode \u2014 only new commits since \`${e.lastSha.slice(0,8)}\` were processed`),s.push(""),s.push(`\u{1F4C2} Branch: \`${e.revRange}\` | Processed: **${e.processed}** commits | Ingested: **${e.ingested}** lessons | Skipped: ${e.skipped} | \u23F1\uFE0F Seeding took ${u(e.durationMs)}`),s.push(""),e.ingested===0)return e.emptyReason&&s.push(`\u26A0\uFE0F ${e.emptyReason}`),s.push("\u{1F331} **Nothing to learn from this git history yet** \u2014 normal for fresh, shallow, or squash-only repos."),s.push("Get to your first recall in under a minute:"),s.push(...a(e.instanceId)),s.join(`
|
|
2
|
+
`);if(s.push("**What your Brain just learned (by category):**"),s.push(e.categories.map(([t,o])=>` \u2022 **${t}** (${o})`).join(`
|
|
3
|
+
`)),e.severities&&e.severities.length>0&&s.push(`**By severity:** ${e.severities.map(([t,o])=>`${t} ${o}`).join(" \xB7 ")}`),e.proof&&(s.push(""),s.push("\u{1F3AF} **Proof \u2014 your first recall already works.** We just searched your fresh Brain for:"),s.push(` "${e.proof.query}"`),s.push(` \u2192 Top hit: **${e.proof.topic}** \u2014 ${e.proof.snippet}`)),e.suggestedQueries.length>0){s.push(""),s.push("**Try these next (copy-paste):**");for(const t of e.suggestedQueries)s.push(` \u2022 \`${t}\``)}return e.ingested<5&&(s.push(""),s.push(`\u{1F331} **Small git history \u2014 only ${e.ingested} lesson${e.ingested===1?"":"s"} so far.** Top it up:`),s.push(...a(e.instanceId))),s.push(""),s.push("\u{1F4A1} New lessons are stored with confidence 0.55 (auto-inferred)."),s.push("\u{1F4A1} As you confirm them via `learn_from_attempts`, confidence rises automatically."),s.push('\u{1F50D} Explore: `brain_search(query="fix")` | `ckg_inspect(concept="deploy")`'),s.push("\u{1F4BE} Next run will automatically continue from the latest commit (incremental mode)."),s.join(`
|
|
4
|
+
`)}export{p as SMALL_SEED_THRESHOLD,h as buildFirstContactReport,u as formatSeedDuration,c as suggestRecallQueries};
|
|
@@ -1,41 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* can call them without risking the 3s onboarding budget.
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* Finds the "Proof — your first recall already works" block inside a
|
|
9
|
-
* `brain_from_git` result text and returns it verbatim (the block runs from
|
|
10
|
-
* the matching line up to the next blank line). Returns `null` when the text
|
|
11
|
-
* carries no such block — never invents a proof.
|
|
12
|
-
*/
|
|
13
|
-
export function extractFirstRecallProof(text) {
|
|
14
|
-
if (!text)
|
|
15
|
-
return null;
|
|
16
|
-
const lines = text.split('\n');
|
|
17
|
-
const startIdx = lines.findIndex((line) => /first recall already works/i.test(line));
|
|
18
|
-
if (startIdx === -1)
|
|
19
|
-
return null;
|
|
20
|
-
const block = [];
|
|
21
|
-
for (let i = startIdx; i < lines.length; i++) {
|
|
22
|
-
if (i > startIdx && lines[i].trim() === '')
|
|
23
|
-
break;
|
|
24
|
-
block.push(lines[i]);
|
|
25
|
-
}
|
|
26
|
-
const joined = block.join('\n').trim();
|
|
27
|
-
return joined || null;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Renders the first-recall proof for the seed path: a real recall hit
|
|
31
|
-
* against the just-seeded starter corpus, shown as evidence instead of a
|
|
32
|
-
* bare "seeded ✓".
|
|
33
|
-
*/
|
|
34
|
-
export function renderFirstRecallProof(topic, what) {
|
|
35
|
-
const snippet = what.trim();
|
|
36
|
-
return [
|
|
37
|
-
'🎯 Proof — your first recall already works.',
|
|
38
|
-
` Top hit: **${topic}** — ${snippet}`,
|
|
39
|
-
].join('\n');
|
|
40
|
-
}
|
|
41
|
-
//# sourceMappingURL=first-recall-proof.js.map
|
|
1
|
+
function l(o){if(!o)return null;const n=o.split(`
|
|
2
|
+
`),t=n.findIndex(r=>/first recall already works/i.test(r));if(t===-1)return null;const e=[];for(let r=t;r<n.length&&!(r>t&&n[r].trim()==="");r++)e.push(n[r]);return e.join(`
|
|
3
|
+
`).trim()||null}function s(o,n){const t=n.trim();return["\u{1F3AF} Proof \u2014 your first recall already works.",` Top hit: **${o}** \u2014 ${t}`].join(`
|
|
4
|
+
`)}export{l as extractFirstRecallProof,s as renderFirstRecallProof};
|
|
@@ -1,48 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
// Milestones like first_call_success were guarded by an in-memory flag only —
|
|
4
|
-
// but ambient hooks spawn a FRESH server process per prompt, so every hook
|
|
5
|
-
// invocation re-sent "first call" and the backend saw 66 "first" calls from
|
|
6
|
-
// one user (and Telegram pinged the founder on every one). Persisting the
|
|
7
|
-
// marker in ~/.cachly/ makes "first" mean first-on-this-machine across
|
|
8
|
-
// process restarts. Best-effort: any filesystem problem degrades to the old
|
|
9
|
-
// in-memory behaviour (the backend now dedupes too), never throws.
|
|
10
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
11
|
-
import { homedir } from 'node:os';
|
|
12
|
-
import { dirname, join } from 'node:path';
|
|
13
|
-
export function defaultMilestonePath() {
|
|
14
|
-
return join(homedir(), '.cachly', 'funnel-milestones.json');
|
|
15
|
-
}
|
|
16
|
-
function readAll(path) {
|
|
17
|
-
try {
|
|
18
|
-
if (!existsSync(path))
|
|
19
|
-
return {};
|
|
20
|
-
const parsed = JSON.parse(readFileSync(path, 'utf-8'));
|
|
21
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
22
|
-
return parsed;
|
|
23
|
-
}
|
|
24
|
-
return {};
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
return {}; // corrupt file → treat as empty, next mark rewrites it
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
/** True when this milestone was already sent from this machine. */
|
|
31
|
-
export function milestoneSent(name, path = defaultMilestonePath()) {
|
|
32
|
-
return name in readAll(path);
|
|
33
|
-
}
|
|
34
|
-
/** Record a milestone (idempotent). Never throws. */
|
|
35
|
-
export function markMilestoneSent(name, path = defaultMilestonePath()) {
|
|
36
|
-
try {
|
|
37
|
-
const all = readAll(path);
|
|
38
|
-
if (name in all)
|
|
39
|
-
return;
|
|
40
|
-
all[name] = new Date().toISOString();
|
|
41
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
42
|
-
writeFileSync(path, JSON.stringify(all, null, 2) + '\n', 'utf-8');
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
/* best-effort — the server-side dedup is the safety net */
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
//# sourceMappingURL=funnel-milestones.js.map
|
|
1
|
+
import{existsSync as o,mkdirSync as c,readFileSync as u,writeFileSync as f}from"node:fs";import{homedir as l}from"node:os";import{dirname as s,join as a}from"node:path";function n(){return a(l(),".cachly","funnel-milestones.json")}function i(t){try{if(!o(t))return{};const r=JSON.parse(u(t,"utf-8"));return r&&typeof r=="object"&&!Array.isArray(r)?r:{}}catch{return{}}}function d(t,r=n()){return t in i(r)}function p(t,r=n()){try{const e=i(r);if(t in e)return;e[t]=new Date().toISOString(),c(s(r),{recursive:!0}),f(r,JSON.stringify(e,null,2)+`
|
|
2
|
+
`,"utf-8")}catch{}}export{n as defaultMilestonePath,p as markMilestoneSent,d as milestoneSent};
|
package/dist/src/geliefert.js
CHANGED
|
@@ -1,114 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
/**
|
|
3
|
-
* Wie viel Text kam aus dem Gedaechtnis statt aus dem Modell?
|
|
4
|
-
*
|
|
5
|
-
* ANLASS 19.08.2026, Heinrich: "Bei der Schaetzung der Tokenersparnis sollten
|
|
6
|
-
* wir schauen, dass wir nicht nur schaetzen, denn das nervt Leute sehr stark.
|
|
7
|
-
* Vielleicht machen wir eine weitere Metrik rein? Sowas wie Token, die bei den
|
|
8
|
-
* Calls aus dem Brain rausgenommen wurden und nicht aus der AI vom Vendor
|
|
9
|
-
* kamen?"
|
|
10
|
-
*
|
|
11
|
-
* ── Warum das die bessere Zahl ist ──────────────────────────────────────────
|
|
12
|
-
*
|
|
13
|
-
* Die bisherige Zahl "583k Token gespart" ist eine RECHNUNG: 1.200 Token je
|
|
14
|
-
* wiederverwendeter Lektion mal 486 Abrufe. Der Faktor 1.200 ist geraten, und
|
|
15
|
-
* jeder darf ihn bestreiten. Die Zahl hier ist eine SUMME: die Laenge der
|
|
16
|
-
* Antworten, die das Brain wirklich zurueckgegeben hat. Da gibt es nichts zu
|
|
17
|
-
* bestreiten — hoechstens die Frage, was sie bedeutet.
|
|
18
|
-
*
|
|
19
|
-
* ── Was sie NICHT sagt ──────────────────────────────────────────────────────
|
|
20
|
-
*
|
|
21
|
-
* Sie ist KEIN Beweis, dass das Modell diese Token sonst selbst erzeugt haette.
|
|
22
|
-
* Vielleicht haette es die Antwort gar nicht gefunden, vielleicht kuerzer
|
|
23
|
-
* geantwortet. Sie sagt genau eines, und das laesst sich nachrechnen: so viel
|
|
24
|
-
* Text kam als Antwort aus dem Gedaechtnis. Wer mehr behauptet, schaetzt
|
|
25
|
-
* wieder.
|
|
26
|
-
*
|
|
27
|
-
* Deshalb steht die Schaetzung weiter daneben — aber klein, mit ihrer Annahme
|
|
28
|
-
* im selben Satz, und neben einer Zahl, die keine braucht.
|
|
29
|
-
*/
|
|
30
|
-
/**
|
|
31
|
-
* Nur ABRUFE zaehlen, nicht jede Antwort.
|
|
32
|
-
*
|
|
33
|
-
* `learn_from_attempts` gibt eine Bestaetigung zurueck, `create_instance` eine
|
|
34
|
-
* Kennung — das ist kein geliefertes Wissen. Gezaehlt wird, was Gespeichertes
|
|
35
|
-
* herausgibt. Steht ein Werkzeug nicht auf dieser Liste, zaehlt es nicht mit;
|
|
36
|
-
* die Zahl ist dadurch eher zu klein als zu gross, und das ist die richtige
|
|
37
|
-
* Richtung fuer eine Zahl, mit der man wirbt.
|
|
38
|
-
*/
|
|
39
|
-
export const LIEFERNDE_WERKZEUGE = new Set([
|
|
40
|
-
'smart_recall',
|
|
41
|
-
'recall_best_solution',
|
|
42
|
-
'recall_context',
|
|
43
|
-
'recall_at',
|
|
44
|
-
'causal_trace',
|
|
45
|
-
'semantic_search',
|
|
46
|
-
'brain_search',
|
|
47
|
-
'team_recall',
|
|
48
|
-
'global_recall',
|
|
49
|
-
'session_start',
|
|
50
|
-
'session_start_summary',
|
|
51
|
-
'brain_briefing',
|
|
52
|
-
'compact_recover',
|
|
53
|
-
'session_handoff',
|
|
54
|
-
'crystal_view',
|
|
55
|
-
'brain_graph',
|
|
56
|
-
'trace_dependency',
|
|
57
|
-
'brain_who_knows',
|
|
58
|
-
'fedbrain_search',
|
|
59
|
-
'syndicate_search',
|
|
60
|
-
]);
|
|
61
|
-
export function istLieferung(werkzeugName) {
|
|
62
|
-
return LIEFERNDE_WERKZEUGE.has(werkzeugName);
|
|
63
|
-
}
|
|
64
|
-
/** Redis-Schluessel je Brain: Summe der gelieferten Token. */
|
|
65
|
-
export function lieferSchluessel(instanceId) {
|
|
66
|
-
return `cachly:stats:delivered_tokens:${instanceId}`;
|
|
67
|
-
}
|
|
68
|
-
/** Redis-Schluessel je Brain: Abrufe gesamt und davon mit Treffer. */
|
|
69
|
-
export function trefferSchluessel(instanceId) {
|
|
70
|
-
return `cachly:stats:recall_hits:${instanceId}`;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Hatte dieser Abruf einen Treffer?
|
|
74
|
-
*
|
|
75
|
-
* Erkannt an dem, was die Antwort SAGT, nicht an ihrer Laenge: eine hoefliche
|
|
76
|
-
* Absage ("keine passende Lektion gefunden") ist laenger als mancher Treffer.
|
|
77
|
-
* Die Muster stammen aus den Antworttexten der Abruf-Werkzeuge selbst.
|
|
78
|
-
*/
|
|
79
|
-
const LEERE_ANTWORT = /(no (relevant |matching |cached )?(lessons?|results?|context|memory|match)|keine? (passende|relevante)|nothing (found|cached)|0 results|Results \(0\b|empty)/i;
|
|
80
|
-
export function hatTreffer(antwort) {
|
|
81
|
-
if (!antwort || antwort.trim().length === 0)
|
|
82
|
-
return false;
|
|
83
|
-
return !LEERE_ANTWORT.test(antwort);
|
|
84
|
-
}
|
|
85
|
-
/** Die Token dieser Antwort — dieselbe Schaetzfunktion wie beim Einblende-Budget. */
|
|
86
|
-
export function tokenDerAntwort(antwort) {
|
|
87
|
-
return estimateTokens(antwort);
|
|
88
|
-
}
|
|
89
|
-
export function lieferBild(roh) {
|
|
90
|
-
const zahl = (v) => {
|
|
91
|
-
const n = typeof v === 'number' ? v : Number(v ?? 0);
|
|
92
|
-
return Number.isFinite(n) && n > 0 ? Math.floor(n) : 0;
|
|
93
|
-
};
|
|
94
|
-
const token = zahl(roh.token);
|
|
95
|
-
const abrufe = zahl(roh.abrufe);
|
|
96
|
-
const treffer = Math.min(zahl(roh.treffer), abrufe);
|
|
97
|
-
return { token, abrufe, treffer, quote: abrufe > 0 ? treffer / abrufe : 0 };
|
|
98
|
-
}
|
|
99
|
-
/** Grosse Zahlen so, wie ein Mensch sie liest. */
|
|
100
|
-
export function inWorten(bild) {
|
|
101
|
-
if (bild.abrufe === 0) {
|
|
102
|
-
return 'Aus dem Brain geliefert: noch nichts — es gab bisher keinen Abruf.';
|
|
103
|
-
}
|
|
104
|
-
const t = bild.token >= 1_000_000
|
|
105
|
-
? `${(bild.token / 1_000_000).toFixed(2)} Mio.`
|
|
106
|
-
: bild.token >= 1_000
|
|
107
|
-
? `${Math.round(bild.token / 1_000)}k`
|
|
108
|
-
: `${bild.token}`;
|
|
109
|
-
const prozent = Math.round(bild.quote * 100);
|
|
110
|
-
return (`Aus dem Brain geliefert: ${t} Token in ${bild.abrufe} Abrufen. ` +
|
|
111
|
-
`${bild.treffer} davon fanden etwas (${prozent} %). ` +
|
|
112
|
-
`Gemessen, nicht gerechnet — es ist die Summe der Antwortlaengen, keine Annahme.`);
|
|
113
|
-
}
|
|
114
|
-
//# sourceMappingURL=geliefert.js.map
|
|
1
|
+
import{estimateTokens as c}from"./ambient-recall.js";const i=new Set(["smart_recall","recall_best_solution","recall_context","recall_at","causal_trace","semantic_search","brain_search","team_recall","global_recall","session_start","session_start_summary","brain_briefing","compact_recover","session_handoff","crystal_view","brain_graph","trace_dependency","brain_who_knows","fedbrain_search","syndicate_search"]);function l(e){return i.has(e)}function _(e){return`cachly:stats:delivered_tokens:${e}`}function h(e){return`cachly:stats:recall_hits:${e}`}const u=/(no (relevant |matching |cached )?(lessons?|results?|context|memory|match)|keine? (passende|relevante)|nothing (found|cached)|0 results|Results \(0\b|empty)/i;function m(e){return!e||e.trim().length===0?!1:!u.test(e)}function k(e){return c(e)}function p(e){const t=s=>{const o=typeof s=="number"?s:Number(s??0);return Number.isFinite(o)&&o>0?Math.floor(o):0},r=t(e.token),n=t(e.abrufe),a=Math.min(t(e.treffer),n);return{token:r,abrufe:n,treffer:a,quote:n>0?a/n:0}}function d(e){if(e.abrufe===0)return"Aus dem Brain geliefert: noch nichts \u2014 es gab bisher keinen Abruf.";const t=e.token>=1e6?`${(e.token/1e6).toFixed(2)} Mio.`:e.token>=1e3?`${Math.round(e.token/1e3)}k`:`${e.token}`,r=Math.round(e.quote*100);return`Aus dem Brain geliefert: ${t} Token in ${e.abrufe} Abrufen. ${e.treffer} davon fanden etwas (${r} %). Gemessen, nicht gerechnet \u2014 es ist die Summe der Antwortlaengen, keine Annahme.`}export{i as LIEFERNDE_WERKZEUGE,m as hatTreffer,d as inWorten,l as istLieferung,p as lieferBild,_ as lieferSchluessel,k as tokenDerAntwort,h as trefferSchluessel};
|