teleton 0.5.2 → 0.7.0
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 +243 -105
- package/dist/chunk-FNV5FF35.js +331 -0
- package/dist/chunk-LRCPA7SC.js +149 -0
- package/dist/{chunk-WUTMT6DW.js → chunk-N3F7E7DR.js} +114 -566
- package/dist/chunk-ND2X5FWB.js +368 -0
- package/dist/chunk-NERLQY2H.js +421 -0
- package/dist/{chunk-YBA6IBGT.js → chunk-OCLG5GKI.js} +24 -24
- package/dist/{chunk-WOXBZOQX.js → chunk-OGIG552S.js} +2152 -4488
- package/dist/chunk-RCMD3U65.js +141 -0
- package/dist/{chunk-O4R7V5Y2.js → chunk-RO62LO6Z.js} +11 -1
- package/dist/chunk-TCD4NZDA.js +3226 -0
- package/dist/chunk-UCN6TI25.js +143 -0
- package/dist/{chunk-WL2Q3VRD.js → chunk-UDD7FYOU.js} +12 -4
- package/dist/chunk-VAUJSSD3.js +20 -0
- package/dist/chunk-XBE4JB7C.js +8 -0
- package/dist/chunk-XBKSS6DM.js +58 -0
- package/dist/cli/index.js +1179 -412
- package/dist/client-3VWE7NC4.js +29 -0
- package/dist/{get-my-gifts-KVULMBJ3.js → get-my-gifts-RI7FAXAL.js} +3 -1
- package/dist/index.js +17 -8
- package/dist/{memory-Y5J7CXAR.js → memory-RD7ZSTRV.js} +16 -10
- package/dist/{migrate-UEQCDWL2.js → migrate-GO4NOBT7.js} +14 -6
- package/dist/{server-BQY7CM2N.js → server-OWVEZTR3.js} +869 -93
- package/dist/setup-server-C7ZTPHD5.js +934 -0
- package/dist/{task-dependency-resolver-TRPILAHM.js → task-dependency-resolver-WKZWJLLM.js} +19 -15
- package/dist/{task-executor-N7XNVK5N.js → task-executor-PD3H4MLO.js} +5 -2
- package/dist/tool-adapter-Y3TCEQOC.js +145 -0
- package/dist/tool-index-MIVK3D7H.js +250 -0
- package/dist/{transcript-7V4UNID4.js → transcript-UDJZP6NK.js} +2 -1
- package/dist/web/assets/complete-fZLnb5Ot.js +1 -0
- package/dist/web/assets/index-B_FcaX5D.css +1 -0
- package/dist/web/assets/index-CbeAP4_n.js +67 -0
- package/dist/web/assets/index.es-oXiZF7Hc.js +11 -0
- package/dist/web/assets/login-telegram-BP7CJDmx.js +1 -0
- package/dist/web/assets/run-DOrDowjK.js +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +21 -15
- package/dist/chunk-5WWR4CU3.js +0 -124
- package/dist/web/assets/index-CDMbujHf.css +0 -1
- package/dist/web/assets/index-DDX8oQ2z.js +0 -67
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import {
|
|
2
|
+
VOYAGE_API_URL
|
|
3
|
+
} from "./chunk-XBKSS6DM.js";
|
|
4
|
+
import {
|
|
5
|
+
EMBEDDING_CACHE_EVICTION_INTERVAL,
|
|
6
|
+
EMBEDDING_CACHE_EVICTION_RATIO,
|
|
7
|
+
EMBEDDING_CACHE_MAX_ENTRIES,
|
|
8
|
+
EMBEDDING_CACHE_TTL_DAYS,
|
|
9
|
+
VOYAGE_BATCH_SIZE
|
|
10
|
+
} from "./chunk-RO62LO6Z.js";
|
|
11
|
+
import {
|
|
12
|
+
fetchWithTimeout
|
|
13
|
+
} from "./chunk-VAUJSSD3.js";
|
|
14
|
+
import {
|
|
15
|
+
TELETON_ROOT
|
|
16
|
+
} from "./chunk-EYWNOHMJ.js";
|
|
17
|
+
import {
|
|
18
|
+
createLogger
|
|
19
|
+
} from "./chunk-RCMD3U65.js";
|
|
20
|
+
|
|
21
|
+
// src/memory/embeddings/provider.ts
|
|
22
|
+
var NoopEmbeddingProvider = class {
|
|
23
|
+
id = "noop";
|
|
24
|
+
model = "none";
|
|
25
|
+
dimensions = 0;
|
|
26
|
+
async embedQuery(_text) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
async embedBatch(_texts) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// src/memory/embeddings/anthropic.ts
|
|
35
|
+
var AnthropicEmbeddingProvider = class {
|
|
36
|
+
id = "anthropic";
|
|
37
|
+
model;
|
|
38
|
+
dimensions;
|
|
39
|
+
apiKey;
|
|
40
|
+
baseUrl = VOYAGE_API_URL;
|
|
41
|
+
constructor(config) {
|
|
42
|
+
this.apiKey = config.apiKey;
|
|
43
|
+
this.model = config.model ?? "voyage-3-lite";
|
|
44
|
+
const dims = {
|
|
45
|
+
"voyage-3": 1024,
|
|
46
|
+
"voyage-3-lite": 512,
|
|
47
|
+
"voyage-code-3": 1024,
|
|
48
|
+
"voyage-finance-2": 1024,
|
|
49
|
+
"voyage-multilingual-2": 1024,
|
|
50
|
+
"voyage-law-2": 1024
|
|
51
|
+
};
|
|
52
|
+
this.dimensions = dims[this.model] ?? 512;
|
|
53
|
+
}
|
|
54
|
+
async embedQuery(text) {
|
|
55
|
+
const result = await this.embed([text]);
|
|
56
|
+
return result[0] ?? [];
|
|
57
|
+
}
|
|
58
|
+
async embedBatch(texts) {
|
|
59
|
+
if (texts.length === 0) return [];
|
|
60
|
+
const batchSize = VOYAGE_BATCH_SIZE;
|
|
61
|
+
const results = [];
|
|
62
|
+
for (let i = 0; i < texts.length; i += batchSize) {
|
|
63
|
+
const batch = texts.slice(i, i + batchSize);
|
|
64
|
+
const embeddings = await this.embed(batch);
|
|
65
|
+
results.push(...embeddings);
|
|
66
|
+
}
|
|
67
|
+
return results;
|
|
68
|
+
}
|
|
69
|
+
async embed(texts) {
|
|
70
|
+
const response = await fetchWithTimeout(`${this.baseUrl}/embeddings`, {
|
|
71
|
+
method: "POST",
|
|
72
|
+
headers: {
|
|
73
|
+
"Content-Type": "application/json",
|
|
74
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
75
|
+
},
|
|
76
|
+
body: JSON.stringify({
|
|
77
|
+
input: texts,
|
|
78
|
+
model: this.model,
|
|
79
|
+
input_type: "document"
|
|
80
|
+
})
|
|
81
|
+
});
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
const error = await response.text();
|
|
84
|
+
throw new Error(`Voyage API error: ${response.status} ${error}`);
|
|
85
|
+
}
|
|
86
|
+
const data = await response.json();
|
|
87
|
+
return data.data.map((item) => item.embedding);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// src/memory/embeddings/local.ts
|
|
92
|
+
import { pipeline, env } from "@huggingface/transformers";
|
|
93
|
+
import { join } from "path";
|
|
94
|
+
var log = createLogger("Memory");
|
|
95
|
+
env.cacheDir = join(TELETON_ROOT, "models");
|
|
96
|
+
var extractorPromise = null;
|
|
97
|
+
function getExtractor(model) {
|
|
98
|
+
if (!extractorPromise) {
|
|
99
|
+
log.info(`Loading local embedding model: ${model} (cache: ${env.cacheDir})`);
|
|
100
|
+
extractorPromise = pipeline("feature-extraction", model, {
|
|
101
|
+
dtype: "fp32"
|
|
102
|
+
}).then((ext) => {
|
|
103
|
+
log.info(`Local embedding model ready`);
|
|
104
|
+
return ext;
|
|
105
|
+
}).catch((err) => {
|
|
106
|
+
log.error(`Failed to load embedding model: ${err.message}`);
|
|
107
|
+
extractorPromise = null;
|
|
108
|
+
throw err;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return extractorPromise;
|
|
112
|
+
}
|
|
113
|
+
var LocalEmbeddingProvider = class {
|
|
114
|
+
id = "local";
|
|
115
|
+
model;
|
|
116
|
+
dimensions;
|
|
117
|
+
_disabled = false;
|
|
118
|
+
constructor(config) {
|
|
119
|
+
this.model = config.model || "Xenova/all-MiniLM-L6-v2";
|
|
120
|
+
this.dimensions = 384;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Pre-download and load the model at startup.
|
|
124
|
+
* If loading fails, marks this provider as disabled (returns empty embeddings).
|
|
125
|
+
* Call this once during app init — avoids retry spam on every message.
|
|
126
|
+
* @returns true if model loaded successfully, false if fallback to noop
|
|
127
|
+
*/
|
|
128
|
+
async warmup() {
|
|
129
|
+
try {
|
|
130
|
+
await getExtractor(this.model);
|
|
131
|
+
return true;
|
|
132
|
+
} catch (err) {
|
|
133
|
+
log.warn(
|
|
134
|
+
`Local embedding model unavailable \u2014 falling back to FTS5-only search (no vector embeddings)`
|
|
135
|
+
);
|
|
136
|
+
this._disabled = true;
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async embedQuery(text) {
|
|
141
|
+
if (this._disabled) return [];
|
|
142
|
+
const extractor = await getExtractor(this.model);
|
|
143
|
+
const output = await extractor(text, { pooling: "mean", normalize: true });
|
|
144
|
+
return Array.from(output.data);
|
|
145
|
+
}
|
|
146
|
+
async embedBatch(texts) {
|
|
147
|
+
if (this._disabled) return [];
|
|
148
|
+
if (texts.length === 0) return [];
|
|
149
|
+
const extractor = await getExtractor(this.model);
|
|
150
|
+
const output = await extractor(texts, { pooling: "mean", normalize: true });
|
|
151
|
+
const data = output.data;
|
|
152
|
+
const dims = this.dimensions;
|
|
153
|
+
const results = [];
|
|
154
|
+
for (let i = 0; i < texts.length; i++) {
|
|
155
|
+
results.push(Array.from(data.slice(i * dims, (i + 1) * dims)));
|
|
156
|
+
}
|
|
157
|
+
return results;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// src/memory/embeddings/index.ts
|
|
162
|
+
import { createHash } from "crypto";
|
|
163
|
+
function createEmbeddingProvider(config) {
|
|
164
|
+
switch (config.provider) {
|
|
165
|
+
case "anthropic":
|
|
166
|
+
if (!config.apiKey) {
|
|
167
|
+
throw new Error("API key required for Anthropic embedding provider");
|
|
168
|
+
}
|
|
169
|
+
return new AnthropicEmbeddingProvider({
|
|
170
|
+
apiKey: config.apiKey,
|
|
171
|
+
model: config.model
|
|
172
|
+
});
|
|
173
|
+
case "local":
|
|
174
|
+
return new LocalEmbeddingProvider({
|
|
175
|
+
model: config.model
|
|
176
|
+
});
|
|
177
|
+
case "none":
|
|
178
|
+
return new NoopEmbeddingProvider();
|
|
179
|
+
default:
|
|
180
|
+
throw new Error(`Unknown embedding provider: ${config.provider}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function hashText(text) {
|
|
184
|
+
return createHash("sha256").update(text).digest("hex");
|
|
185
|
+
}
|
|
186
|
+
function serializeEmbedding(embedding) {
|
|
187
|
+
return Buffer.from(new Float32Array(embedding).buffer);
|
|
188
|
+
}
|
|
189
|
+
function deserializeEmbedding(data) {
|
|
190
|
+
try {
|
|
191
|
+
if (Buffer.isBuffer(data)) {
|
|
192
|
+
const floats = new Float32Array(data.buffer, data.byteOffset, data.byteLength / 4);
|
|
193
|
+
return Array.from(floats);
|
|
194
|
+
}
|
|
195
|
+
return JSON.parse(data);
|
|
196
|
+
} catch {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// src/memory/embeddings/cached.ts
|
|
202
|
+
var CachedEmbeddingProvider = class _CachedEmbeddingProvider {
|
|
203
|
+
constructor(inner, db) {
|
|
204
|
+
this.inner = inner;
|
|
205
|
+
this.db = db;
|
|
206
|
+
this.id = inner.id;
|
|
207
|
+
this.model = inner.model;
|
|
208
|
+
this.dimensions = inner.dimensions;
|
|
209
|
+
}
|
|
210
|
+
id;
|
|
211
|
+
model;
|
|
212
|
+
dimensions;
|
|
213
|
+
static log = createLogger("Memory");
|
|
214
|
+
hits = 0;
|
|
215
|
+
misses = 0;
|
|
216
|
+
ops = 0;
|
|
217
|
+
cacheGet(hash) {
|
|
218
|
+
return this.db.prepare(
|
|
219
|
+
`SELECT embedding FROM embedding_cache WHERE hash = ? AND model = ? AND provider = ?`
|
|
220
|
+
).get(hash, this.model, this.id);
|
|
221
|
+
}
|
|
222
|
+
cachePut(hash, blob) {
|
|
223
|
+
this.db.prepare(
|
|
224
|
+
`INSERT OR REPLACE INTO embedding_cache (hash, embedding, model, provider, dims, created_at, accessed_at)
|
|
225
|
+
VALUES (?, ?, ?, ?, ?, unixepoch(), unixepoch())`
|
|
226
|
+
).run(hash, blob, this.model, this.id, this.dimensions);
|
|
227
|
+
}
|
|
228
|
+
cacheTouch(hash) {
|
|
229
|
+
this.db.prepare(
|
|
230
|
+
`UPDATE embedding_cache SET accessed_at = unixepoch() WHERE hash = ? AND model = ? AND provider = ?`
|
|
231
|
+
).run(hash, this.model, this.id);
|
|
232
|
+
}
|
|
233
|
+
async warmup() {
|
|
234
|
+
return this.inner.warmup?.() ?? true;
|
|
235
|
+
}
|
|
236
|
+
async embedQuery(text) {
|
|
237
|
+
const hash = hashText(text);
|
|
238
|
+
const row = this.cacheGet(hash);
|
|
239
|
+
if (row) {
|
|
240
|
+
this.hits++;
|
|
241
|
+
this.cacheTouch(hash);
|
|
242
|
+
this.tick();
|
|
243
|
+
return deserializeEmbedding(row.embedding);
|
|
244
|
+
}
|
|
245
|
+
this.misses++;
|
|
246
|
+
const embedding = await this.inner.embedQuery(text);
|
|
247
|
+
this.cachePut(hash, serializeEmbedding(embedding));
|
|
248
|
+
this.tick();
|
|
249
|
+
return embedding;
|
|
250
|
+
}
|
|
251
|
+
async embedBatch(texts) {
|
|
252
|
+
if (texts.length === 0) return [];
|
|
253
|
+
const hashes = texts.map(hashText);
|
|
254
|
+
const results = new Array(texts.length).fill(null);
|
|
255
|
+
const missIndices = [];
|
|
256
|
+
const missTexts = [];
|
|
257
|
+
for (let i = 0; i < texts.length; i++) {
|
|
258
|
+
const row = this.cacheGet(hashes[i]);
|
|
259
|
+
if (row) {
|
|
260
|
+
this.hits++;
|
|
261
|
+
this.cacheTouch(hashes[i]);
|
|
262
|
+
results[i] = deserializeEmbedding(row.embedding);
|
|
263
|
+
} else {
|
|
264
|
+
this.misses++;
|
|
265
|
+
missIndices.push(i);
|
|
266
|
+
missTexts.push(texts[i]);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (missTexts.length > 0) {
|
|
270
|
+
const newEmbeddings = await this.inner.embedBatch(missTexts);
|
|
271
|
+
for (let j = 0; j < missIndices.length; j++) {
|
|
272
|
+
const idx = missIndices[j];
|
|
273
|
+
const embedding = newEmbeddings[j] ?? [];
|
|
274
|
+
results[idx] = embedding;
|
|
275
|
+
if (embedding.length > 0) {
|
|
276
|
+
this.cachePut(hashes[idx], serializeEmbedding(embedding));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
this.ops += texts.length;
|
|
281
|
+
this.maybeEvict();
|
|
282
|
+
this.maybeLogStats();
|
|
283
|
+
return results;
|
|
284
|
+
}
|
|
285
|
+
tick() {
|
|
286
|
+
this.ops++;
|
|
287
|
+
this.maybeEvict();
|
|
288
|
+
this.maybeLogStats();
|
|
289
|
+
}
|
|
290
|
+
maybeLogStats() {
|
|
291
|
+
const total = this.hits + this.misses;
|
|
292
|
+
if (total > 0 && total % 100 === 0) {
|
|
293
|
+
const rate = (this.hits / total * 100).toFixed(0);
|
|
294
|
+
_CachedEmbeddingProvider.log.info(
|
|
295
|
+
`Embedding cache: ${this.hits} hits, ${this.misses} misses (${rate}% hit rate)`
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
maybeEvict() {
|
|
300
|
+
if (this.ops % EMBEDDING_CACHE_EVICTION_INTERVAL !== 0) return;
|
|
301
|
+
try {
|
|
302
|
+
const cutoff = Math.floor(Date.now() / 1e3) - EMBEDDING_CACHE_TTL_DAYS * 86400;
|
|
303
|
+
this.db.prepare(`DELETE FROM embedding_cache WHERE accessed_at < ?`).run(cutoff);
|
|
304
|
+
const count = this.db.prepare(`SELECT COUNT(*) as cnt FROM embedding_cache`).get().cnt;
|
|
305
|
+
if (count > EMBEDDING_CACHE_MAX_ENTRIES) {
|
|
306
|
+
const toDelete = Math.ceil(count * EMBEDDING_CACHE_EVICTION_RATIO);
|
|
307
|
+
this.db.prepare(
|
|
308
|
+
`DELETE FROM embedding_cache WHERE (hash, model, provider) IN (
|
|
309
|
+
SELECT hash, model, provider FROM embedding_cache ORDER BY accessed_at ASC LIMIT ?
|
|
310
|
+
)`
|
|
311
|
+
).run(toDelete);
|
|
312
|
+
_CachedEmbeddingProvider.log.info(
|
|
313
|
+
`Embedding cache eviction: removed ${toDelete} entries (${count} total)`
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
} catch (err) {
|
|
317
|
+
_CachedEmbeddingProvider.log.warn({ err }, "Embedding cache eviction error");
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
export {
|
|
323
|
+
NoopEmbeddingProvider,
|
|
324
|
+
AnthropicEmbeddingProvider,
|
|
325
|
+
LocalEmbeddingProvider,
|
|
326
|
+
CachedEmbeddingProvider,
|
|
327
|
+
createEmbeddingProvider,
|
|
328
|
+
hashText,
|
|
329
|
+
serializeEmbedding,
|
|
330
|
+
deserializeEmbedding
|
|
331
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// src/config/providers.ts
|
|
2
|
+
var PROVIDER_REGISTRY = {
|
|
3
|
+
anthropic: {
|
|
4
|
+
id: "anthropic",
|
|
5
|
+
displayName: "Anthropic (Claude)",
|
|
6
|
+
envVar: "ANTHROPIC_API_KEY",
|
|
7
|
+
keyPrefix: "sk-ant-",
|
|
8
|
+
keyHint: "sk-ant-api03-...",
|
|
9
|
+
consoleUrl: "https://console.anthropic.com/",
|
|
10
|
+
defaultModel: "claude-opus-4-5-20251101",
|
|
11
|
+
utilityModel: "claude-3-5-haiku-20241022",
|
|
12
|
+
toolLimit: null,
|
|
13
|
+
piAiProvider: "anthropic"
|
|
14
|
+
},
|
|
15
|
+
openai: {
|
|
16
|
+
id: "openai",
|
|
17
|
+
displayName: "OpenAI (GPT-4o)",
|
|
18
|
+
envVar: "OPENAI_API_KEY",
|
|
19
|
+
keyPrefix: "sk-",
|
|
20
|
+
keyHint: "sk-proj-...",
|
|
21
|
+
consoleUrl: "https://platform.openai.com/api-keys",
|
|
22
|
+
defaultModel: "gpt-4o",
|
|
23
|
+
utilityModel: "gpt-4o-mini",
|
|
24
|
+
toolLimit: 128,
|
|
25
|
+
piAiProvider: "openai"
|
|
26
|
+
},
|
|
27
|
+
google: {
|
|
28
|
+
id: "google",
|
|
29
|
+
displayName: "Google (Gemini)",
|
|
30
|
+
envVar: "GOOGLE_API_KEY",
|
|
31
|
+
keyPrefix: null,
|
|
32
|
+
keyHint: "AIza...",
|
|
33
|
+
consoleUrl: "https://aistudio.google.com/apikey",
|
|
34
|
+
defaultModel: "gemini-2.5-flash",
|
|
35
|
+
utilityModel: "gemini-2.0-flash-lite",
|
|
36
|
+
toolLimit: 128,
|
|
37
|
+
piAiProvider: "google"
|
|
38
|
+
},
|
|
39
|
+
xai: {
|
|
40
|
+
id: "xai",
|
|
41
|
+
displayName: "xAI (Grok)",
|
|
42
|
+
envVar: "XAI_API_KEY",
|
|
43
|
+
keyPrefix: "xai-",
|
|
44
|
+
keyHint: "xai-...",
|
|
45
|
+
consoleUrl: "https://console.x.ai/",
|
|
46
|
+
defaultModel: "grok-3",
|
|
47
|
+
utilityModel: "grok-3-mini-fast",
|
|
48
|
+
toolLimit: 128,
|
|
49
|
+
piAiProvider: "xai"
|
|
50
|
+
},
|
|
51
|
+
groq: {
|
|
52
|
+
id: "groq",
|
|
53
|
+
displayName: "Groq",
|
|
54
|
+
envVar: "GROQ_API_KEY",
|
|
55
|
+
keyPrefix: "gsk_",
|
|
56
|
+
keyHint: "gsk_...",
|
|
57
|
+
consoleUrl: "https://console.groq.com/keys",
|
|
58
|
+
defaultModel: "llama-3.3-70b-versatile",
|
|
59
|
+
utilityModel: "llama-3.1-8b-instant",
|
|
60
|
+
toolLimit: 128,
|
|
61
|
+
piAiProvider: "groq"
|
|
62
|
+
},
|
|
63
|
+
openrouter: {
|
|
64
|
+
id: "openrouter",
|
|
65
|
+
displayName: "OpenRouter",
|
|
66
|
+
envVar: "OPENROUTER_API_KEY",
|
|
67
|
+
keyPrefix: "sk-or-",
|
|
68
|
+
keyHint: "sk-or-v1-...",
|
|
69
|
+
consoleUrl: "https://openrouter.ai/keys",
|
|
70
|
+
defaultModel: "anthropic/claude-opus-4.5",
|
|
71
|
+
utilityModel: "google/gemini-2.5-flash-lite",
|
|
72
|
+
toolLimit: 128,
|
|
73
|
+
piAiProvider: "openrouter"
|
|
74
|
+
},
|
|
75
|
+
moonshot: {
|
|
76
|
+
id: "moonshot",
|
|
77
|
+
displayName: "Moonshot (Kimi K2.5)",
|
|
78
|
+
envVar: "MOONSHOT_API_KEY",
|
|
79
|
+
keyPrefix: "sk-",
|
|
80
|
+
keyHint: "sk-...",
|
|
81
|
+
consoleUrl: "https://platform.moonshot.ai/",
|
|
82
|
+
defaultModel: "kimi-k2.5",
|
|
83
|
+
utilityModel: "kimi-k2.5",
|
|
84
|
+
toolLimit: 128,
|
|
85
|
+
piAiProvider: "moonshot"
|
|
86
|
+
},
|
|
87
|
+
mistral: {
|
|
88
|
+
id: "mistral",
|
|
89
|
+
displayName: "Mistral AI",
|
|
90
|
+
envVar: "MISTRAL_API_KEY",
|
|
91
|
+
keyPrefix: null,
|
|
92
|
+
keyHint: "...",
|
|
93
|
+
consoleUrl: "https://console.mistral.ai/api-keys",
|
|
94
|
+
defaultModel: "devstral-small-2507",
|
|
95
|
+
utilityModel: "ministral-8b-latest",
|
|
96
|
+
toolLimit: 128,
|
|
97
|
+
piAiProvider: "mistral"
|
|
98
|
+
},
|
|
99
|
+
cocoon: {
|
|
100
|
+
id: "cocoon",
|
|
101
|
+
displayName: "Cocoon Network (Decentralized)",
|
|
102
|
+
envVar: "",
|
|
103
|
+
keyPrefix: null,
|
|
104
|
+
keyHint: "No API key needed \u2014 pays in TON",
|
|
105
|
+
consoleUrl: "https://cocoon.network",
|
|
106
|
+
defaultModel: "Qwen/Qwen3-32B",
|
|
107
|
+
utilityModel: "Qwen/Qwen3-32B",
|
|
108
|
+
toolLimit: 128,
|
|
109
|
+
piAiProvider: "cocoon"
|
|
110
|
+
},
|
|
111
|
+
local: {
|
|
112
|
+
id: "local",
|
|
113
|
+
displayName: "Local (Ollama, vLLM, LM Studio...)",
|
|
114
|
+
envVar: "",
|
|
115
|
+
keyPrefix: null,
|
|
116
|
+
keyHint: "No API key needed",
|
|
117
|
+
consoleUrl: "",
|
|
118
|
+
defaultModel: "auto",
|
|
119
|
+
utilityModel: "auto",
|
|
120
|
+
toolLimit: 128,
|
|
121
|
+
piAiProvider: "local"
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
function getProviderMetadata(provider) {
|
|
125
|
+
const meta = PROVIDER_REGISTRY[provider];
|
|
126
|
+
if (!meta) {
|
|
127
|
+
throw new Error(`Unknown provider: ${provider}`);
|
|
128
|
+
}
|
|
129
|
+
return meta;
|
|
130
|
+
}
|
|
131
|
+
function getSupportedProviders() {
|
|
132
|
+
return Object.values(PROVIDER_REGISTRY);
|
|
133
|
+
}
|
|
134
|
+
function validateApiKeyFormat(provider, key) {
|
|
135
|
+
const meta = PROVIDER_REGISTRY[provider];
|
|
136
|
+
if (!meta) return `Unknown provider: ${provider}`;
|
|
137
|
+
if (provider === "cocoon" || provider === "local") return void 0;
|
|
138
|
+
if (!key || key.trim().length === 0) return "API key is required";
|
|
139
|
+
if (meta.keyPrefix && !key.startsWith(meta.keyPrefix)) {
|
|
140
|
+
return `Invalid format (should start with ${meta.keyPrefix})`;
|
|
141
|
+
}
|
|
142
|
+
return void 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export {
|
|
146
|
+
getProviderMetadata,
|
|
147
|
+
getSupportedProviders,
|
|
148
|
+
validateApiKeyFormat
|
|
149
|
+
};
|