nebula-notebook 0.2.0 → 0.2.2
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 +35 -20
- package/dist/assets/{errorwidget-C4r2j2DQ.js → errorwidget-Wy5ajPiM.js} +1 -1
- package/dist/assets/{index-Day3QcNs.js → index-B_Bs5koo.js} +1 -1
- package/dist/assets/index-DK4nS74r.js +829 -0
- package/dist/assets/{index-D5w21_Z8.js → index-DeQN03nW.js} +1 -1
- package/dist/assets/index-DzjpmUak.css +32 -0
- package/dist/assets/{index-CsHoPQy-.js → index-g6MjT-VG.js} +1 -1
- package/dist/assets/{services-shim-D6p_A67v.js → services-shim-BirHVsih.js} +1 -1
- package/dist/index.html +2 -2
- package/node-server/dist/auth/auth-middleware.js +22 -4
- package/node-server/dist/auth/auth-service.js +37 -7
- package/node-server/dist/cluster/client-registration.d.ts +12 -1
- package/node-server/dist/cluster/client-registration.js +35 -2
- package/node-server/dist/cluster/kernel-proxy.js +19 -3
- package/node-server/dist/cluster/server-registry.d.ts +9 -0
- package/node-server/dist/cluster/server-registry.js +26 -0
- package/node-server/dist/discovery/conda-locations.d.ts +73 -0
- package/node-server/dist/discovery/conda-locations.js +427 -0
- package/node-server/dist/discovery/discovery-service.d.ts +73 -4
- package/node-server/dist/discovery/discovery-service.js +211 -127
- package/node-server/dist/discovery/types.d.ts +0 -1
- package/node-server/dist/discovery/types.js +1 -2
- package/node-server/dist/environment.d.ts +12 -0
- package/node-server/dist/environment.js +104 -0
- package/node-server/dist/fs/fs-service.d.ts +31 -1
- package/node-server/dist/fs/fs-service.js +160 -28
- package/node-server/dist/fs/types.d.ts +9 -0
- package/node-server/dist/fs/types.js +8 -0
- package/node-server/dist/index.js +71 -4
- package/node-server/dist/kernel/kernel-service.d.ts +17 -0
- package/node-server/dist/kernel/kernel-service.js +72 -4
- package/node-server/dist/kernel/kernelspec.d.ts +27 -0
- package/node-server/dist/kernel/kernelspec.js +75 -4
- package/node-server/dist/notebook/headless-handler.d.ts +10 -0
- package/node-server/dist/notebook/headless-handler.js +55 -5
- package/node-server/dist/notebook/undoRedoManager.d.ts +8 -0
- package/node-server/dist/notebook/undoRedoManager.js +39 -16
- package/node-server/dist/private-tmp.d.ts +14 -0
- package/node-server/dist/private-tmp.js +70 -0
- package/node-server/dist/routes/autocomplete.d.ts +19 -0
- package/node-server/dist/routes/autocomplete.js +294 -0
- package/node-server/dist/routes/fs.js +21 -4
- package/node-server/dist/routes/kernel.js +45 -2
- package/node-server/dist/routes/notebook.js +8 -2
- package/node-server/dist/routes/python.d.ts +1 -2
- package/node-server/dist/routes/python.js +139 -3
- package/node-server/dist/scheduler/allocation-service.d.ts +20 -0
- package/node-server/dist/scheduler/allocation-service.js +124 -8
- package/node-server/dist/scheduler/mock-scheduler.js +7 -3
- package/node-server/dist/scheduler/slurm-scheduler.js +21 -8
- package/node-server/dist/scheduler/types.d.ts +3 -2
- package/node-server/dist/terminal/agent-registry.d.ts +87 -0
- package/node-server/dist/terminal/agent-registry.js +246 -0
- package/node-server/dist/terminal/binding-store.d.ts +57 -0
- package/node-server/dist/terminal/binding-store.js +0 -0
- package/node-server/dist/terminal/pty-manager.d.ts +23 -6
- package/node-server/dist/terminal/pty-manager.js +130 -19
- package/node-server/dist/terminal/server.js +112 -21
- package/node-server/dist/terminal/types.d.ts +2 -0
- package/node-server/package.json +1 -0
- package/node_modules/nebula-autocomplete/README.md +145 -0
- package/node_modules/nebula-autocomplete/dist/backends/claude.d.ts +69 -0
- package/node_modules/nebula-autocomplete/dist/backends/claude.js +449 -0
- package/node_modules/nebula-autocomplete/dist/backends/codex.d.ts +45 -0
- package/node_modules/nebula-autocomplete/dist/backends/codex.js +108 -0
- package/node_modules/nebula-autocomplete/dist/client/fetcher.d.ts +16 -0
- package/node_modules/nebula-autocomplete/dist/client/fetcher.js +41 -0
- package/node_modules/nebula-autocomplete/dist/codemirror/ghostText.d.ts +43 -0
- package/node_modules/nebula-autocomplete/dist/codemirror/ghostText.js +175 -0
- package/node_modules/nebula-autocomplete/dist/core/engine.d.ts +17 -0
- package/node_modules/nebula-autocomplete/dist/core/engine.js +135 -0
- package/node_modules/nebula-autocomplete/dist/core/lru.d.ts +7 -0
- package/node_modules/nebula-autocomplete/dist/core/lru.js +26 -0
- package/node_modules/nebula-autocomplete/dist/core/prompt.d.ts +12 -0
- package/node_modules/nebula-autocomplete/dist/core/prompt.js +0 -0
- package/node_modules/nebula-autocomplete/dist/core/text.d.ts +53 -0
- package/node_modules/nebula-autocomplete/dist/core/text.js +169 -0
- package/node_modules/nebula-autocomplete/dist/index.d.ts +7 -0
- package/node_modules/nebula-autocomplete/dist/index.js +5 -0
- package/node_modules/nebula-autocomplete/dist/server/fastify.d.ts +40 -0
- package/node_modules/nebula-autocomplete/dist/server/fastify.js +52 -0
- package/node_modules/nebula-autocomplete/dist/transport.d.ts +55 -0
- package/node_modules/nebula-autocomplete/dist/transport.js +66 -0
- package/node_modules/nebula-autocomplete/dist/types.d.ts +103 -0
- package/node_modules/nebula-autocomplete/dist/types.js +1 -0
- package/node_modules/nebula-autocomplete/package.json +58 -0
- package/node_modules/nebula-autocomplete/server.d.ts +3 -0
- package/node_modules/nebula-autocomplete/server.js +3 -0
- package/package.json +17 -5
- package/scripts/postinstall.cjs +25 -5
- package/dist/assets/index-7-YBurka.js +0 -716
- package/dist/assets/index-CFBUnxSZ.css +0 -32
- package/node-server/dist/llm/index.d.ts +0 -5
- package/node-server/dist/llm/index.js +0 -21
- package/node-server/dist/llm/llm-service.d.ts +0 -77
- package/node-server/dist/llm/llm-service.js +0 -454
- package/node-server/dist/llm/types.d.ts +0 -40
- package/node-server/dist/llm/types.js +0 -15
- package/node-server/dist/routes/llm.d.ts +0 -8
- package/node-server/dist/routes/llm.js +0 -105
|
@@ -1,454 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Multi-Provider LLM Service
|
|
4
|
-
*
|
|
5
|
-
* Supports Google Gemini, OpenAI, and Anthropic.
|
|
6
|
-
* Node.js port of the Python LLMService.
|
|
7
|
-
*/
|
|
8
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.llmService = exports.LLMService = void 0;
|
|
13
|
-
const genai_1 = require("@google/genai");
|
|
14
|
-
const openai_1 = __importDefault(require("openai"));
|
|
15
|
-
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
16
|
-
const types_1 = require("./types");
|
|
17
|
-
class LLMService {
|
|
18
|
-
googleClient = null;
|
|
19
|
-
openaiClient = null;
|
|
20
|
-
anthropicClient = null;
|
|
21
|
-
/**
|
|
22
|
-
* Get or create Google GenAI client
|
|
23
|
-
*/
|
|
24
|
-
getGoogleClient(apiKey) {
|
|
25
|
-
if (apiKey) {
|
|
26
|
-
// Create new client with provided key (don't cache)
|
|
27
|
-
return new genai_1.GoogleGenAI({ apiKey });
|
|
28
|
-
}
|
|
29
|
-
if (!this.googleClient) {
|
|
30
|
-
const envKey = process.env.GEMINI_API_KEY;
|
|
31
|
-
if (!envKey) {
|
|
32
|
-
throw new Error('GEMINI_API_KEY not found. Configure it in settings or server environment.');
|
|
33
|
-
}
|
|
34
|
-
this.googleClient = new genai_1.GoogleGenAI({ apiKey: envKey });
|
|
35
|
-
}
|
|
36
|
-
return this.googleClient;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Get or create OpenAI client
|
|
40
|
-
*/
|
|
41
|
-
getOpenaiClient(apiKey) {
|
|
42
|
-
if (apiKey) {
|
|
43
|
-
return new openai_1.default({ apiKey });
|
|
44
|
-
}
|
|
45
|
-
if (!this.openaiClient) {
|
|
46
|
-
const envKey = process.env.OPENAI_API_KEY;
|
|
47
|
-
if (!envKey) {
|
|
48
|
-
throw new Error('OPENAI_API_KEY not found. Configure it in settings or server environment.');
|
|
49
|
-
}
|
|
50
|
-
this.openaiClient = new openai_1.default({ apiKey: envKey });
|
|
51
|
-
}
|
|
52
|
-
return this.openaiClient;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Get or create Anthropic client
|
|
56
|
-
*/
|
|
57
|
-
getAnthropicClient(apiKey) {
|
|
58
|
-
if (apiKey) {
|
|
59
|
-
return new sdk_1.default({ apiKey });
|
|
60
|
-
}
|
|
61
|
-
if (!this.anthropicClient) {
|
|
62
|
-
const envKey = process.env.ANTHROPIC_API_KEY;
|
|
63
|
-
if (!envKey) {
|
|
64
|
-
throw new Error('ANTHROPIC_API_KEY not found. Configure it in settings or server environment.');
|
|
65
|
-
}
|
|
66
|
-
this.anthropicClient = new sdk_1.default({ apiKey: envKey });
|
|
67
|
-
}
|
|
68
|
-
return this.anthropicClient;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Get available providers based on configured API keys
|
|
72
|
-
*/
|
|
73
|
-
getAvailableProviders() {
|
|
74
|
-
const available = {};
|
|
75
|
-
if (process.env.GEMINI_API_KEY) {
|
|
76
|
-
available.google = types_1.AVAILABLE_MODELS.google;
|
|
77
|
-
}
|
|
78
|
-
if (process.env.OPENAI_API_KEY) {
|
|
79
|
-
available.openai = types_1.AVAILABLE_MODELS.openai;
|
|
80
|
-
}
|
|
81
|
-
if (process.env.ANTHROPIC_API_KEY) {
|
|
82
|
-
available.anthropic = types_1.AVAILABLE_MODELS.anthropic;
|
|
83
|
-
}
|
|
84
|
-
return available;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Normalize config with defaults
|
|
88
|
-
*/
|
|
89
|
-
normalizeConfig(config) {
|
|
90
|
-
return {
|
|
91
|
-
provider: config.provider,
|
|
92
|
-
model: config.model,
|
|
93
|
-
temperature: config.temperature ?? types_1.DEFAULT_TEMPERATURE,
|
|
94
|
-
maxTokens: config.maxTokens ?? types_1.DEFAULT_MAX_TOKENS,
|
|
95
|
-
apiKey: config.apiKey,
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Repair common JSON formatting issues
|
|
100
|
-
*/
|
|
101
|
-
repairJson(s) {
|
|
102
|
-
let result = s;
|
|
103
|
-
// Replace : 'value' with : "value"
|
|
104
|
-
result = result.replace(/:\s*'([^']*)'/g, ': "$1"');
|
|
105
|
-
// Remove trailing commas before } or ]
|
|
106
|
-
result = result.replace(/,(\s*[}\]])/g, '$1');
|
|
107
|
-
return result;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Parse JSON with automatic repair on failure
|
|
111
|
-
*/
|
|
112
|
-
parseJsonWithRepair(s) {
|
|
113
|
-
try {
|
|
114
|
-
return JSON.parse(s);
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
try {
|
|
118
|
-
const repaired = this.repairJson(s);
|
|
119
|
-
return JSON.parse(repaired);
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
throw new Error('Failed to parse JSON');
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Extract JSON from a response that may contain markdown code blocks
|
|
128
|
-
*/
|
|
129
|
-
extractJsonFromResponse(response) {
|
|
130
|
-
// Try to find JSON in code block first
|
|
131
|
-
const codeBlockMatch = response.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
|
|
132
|
-
if (codeBlockMatch) {
|
|
133
|
-
return this.parseJsonWithRepair(codeBlockMatch[1]);
|
|
134
|
-
}
|
|
135
|
-
// Try to find raw JSON object
|
|
136
|
-
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
137
|
-
if (jsonMatch) {
|
|
138
|
-
return this.parseJsonWithRepair(jsonMatch[0]);
|
|
139
|
-
}
|
|
140
|
-
throw new Error('No JSON found in response');
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Generate response from Google Gemini
|
|
144
|
-
*/
|
|
145
|
-
async generateGoogle(prompt, systemPrompt, config, images) {
|
|
146
|
-
const client = this.getGoogleClient(config.apiKey);
|
|
147
|
-
// Build content parts
|
|
148
|
-
const contents = [prompt];
|
|
149
|
-
if (images) {
|
|
150
|
-
for (const img of images) {
|
|
151
|
-
contents.push({
|
|
152
|
-
inlineData: {
|
|
153
|
-
mimeType: img.mimeType || 'image/png',
|
|
154
|
-
data: img.data,
|
|
155
|
-
},
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
const response = await client.models.generateContent({
|
|
160
|
-
model: config.model,
|
|
161
|
-
contents,
|
|
162
|
-
config: {
|
|
163
|
-
systemInstruction: systemPrompt,
|
|
164
|
-
temperature: config.temperature,
|
|
165
|
-
maxOutputTokens: config.maxTokens,
|
|
166
|
-
},
|
|
167
|
-
});
|
|
168
|
-
return response.text || '';
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Generate response from OpenAI
|
|
172
|
-
*/
|
|
173
|
-
async generateOpenai(prompt, systemPrompt, config, images) {
|
|
174
|
-
const client = this.getOpenaiClient(config.apiKey);
|
|
175
|
-
const messages = [
|
|
176
|
-
{ role: 'system', content: systemPrompt },
|
|
177
|
-
];
|
|
178
|
-
// Build user message content
|
|
179
|
-
if (images && images.length > 0) {
|
|
180
|
-
const content = [
|
|
181
|
-
{ type: 'text', text: prompt },
|
|
182
|
-
];
|
|
183
|
-
for (const img of images) {
|
|
184
|
-
content.push({
|
|
185
|
-
type: 'image_url',
|
|
186
|
-
image_url: {
|
|
187
|
-
url: `data:${img.mimeType || 'image/png'};base64,${img.data}`,
|
|
188
|
-
},
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
messages.push({ role: 'user', content });
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
messages.push({ role: 'user', content: prompt });
|
|
195
|
-
}
|
|
196
|
-
const response = await client.chat.completions.create({
|
|
197
|
-
model: config.model,
|
|
198
|
-
messages,
|
|
199
|
-
temperature: config.temperature,
|
|
200
|
-
max_completion_tokens: config.maxTokens,
|
|
201
|
-
});
|
|
202
|
-
return response.choices[0]?.message?.content || '';
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Generate response from Anthropic Claude
|
|
206
|
-
*/
|
|
207
|
-
async generateAnthropic(prompt, systemPrompt, config, images) {
|
|
208
|
-
const client = this.getAnthropicClient(config.apiKey);
|
|
209
|
-
// Build user message content
|
|
210
|
-
let content;
|
|
211
|
-
if (images && images.length > 0) {
|
|
212
|
-
const parts = [];
|
|
213
|
-
for (const img of images) {
|
|
214
|
-
parts.push({
|
|
215
|
-
type: 'image',
|
|
216
|
-
source: {
|
|
217
|
-
type: 'base64',
|
|
218
|
-
media_type: img.mimeType || 'image/png',
|
|
219
|
-
data: img.data,
|
|
220
|
-
},
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
parts.push({ type: 'text', text: prompt });
|
|
224
|
-
content = parts;
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
content = prompt;
|
|
228
|
-
}
|
|
229
|
-
const response = await client.messages.create({
|
|
230
|
-
model: config.model,
|
|
231
|
-
max_tokens: config.maxTokens,
|
|
232
|
-
system: systemPrompt,
|
|
233
|
-
messages: [{ role: 'user', content }],
|
|
234
|
-
temperature: config.temperature,
|
|
235
|
-
});
|
|
236
|
-
const textBlock = response.content.find(c => c.type === 'text');
|
|
237
|
-
return textBlock && 'text' in textBlock ? textBlock.text : '';
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Generate a response from the LLM
|
|
241
|
-
*/
|
|
242
|
-
async generate(options) {
|
|
243
|
-
const { prompt, systemPrompt, config, images } = options;
|
|
244
|
-
const normalizedConfig = this.normalizeConfig(config);
|
|
245
|
-
switch (config.provider) {
|
|
246
|
-
case 'google':
|
|
247
|
-
return this.generateGoogle(prompt, systemPrompt, normalizedConfig, images);
|
|
248
|
-
case 'openai':
|
|
249
|
-
return this.generateOpenai(prompt, systemPrompt, normalizedConfig, images);
|
|
250
|
-
case 'anthropic':
|
|
251
|
-
return this.generateAnthropic(prompt, systemPrompt, normalizedConfig, images);
|
|
252
|
-
default:
|
|
253
|
-
throw new Error(`Unknown provider: ${config.provider}`);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* Generate structured JSON response
|
|
258
|
-
*/
|
|
259
|
-
async generateStructured(options) {
|
|
260
|
-
const { prompt, systemPrompt, config } = options;
|
|
261
|
-
const normalizedConfig = this.normalizeConfig(config);
|
|
262
|
-
switch (config.provider) {
|
|
263
|
-
case 'google':
|
|
264
|
-
return this.generateStructuredGoogle(prompt, systemPrompt, normalizedConfig);
|
|
265
|
-
case 'openai':
|
|
266
|
-
return this.generateStructuredOpenai(prompt, systemPrompt, normalizedConfig);
|
|
267
|
-
case 'anthropic':
|
|
268
|
-
return this.generateStructuredAnthropic(prompt, systemPrompt, normalizedConfig);
|
|
269
|
-
default:
|
|
270
|
-
throw new Error(`Unknown provider: ${config.provider}`);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
async generateStructuredGoogle(prompt, systemPrompt, config) {
|
|
274
|
-
const client = this.getGoogleClient(config.apiKey);
|
|
275
|
-
const response = await client.models.generateContent({
|
|
276
|
-
model: config.model,
|
|
277
|
-
contents: [prompt],
|
|
278
|
-
config: {
|
|
279
|
-
systemInstruction: systemPrompt,
|
|
280
|
-
temperature: config.temperature,
|
|
281
|
-
maxOutputTokens: config.maxTokens,
|
|
282
|
-
responseMimeType: 'application/json',
|
|
283
|
-
},
|
|
284
|
-
});
|
|
285
|
-
return this.extractJsonFromResponse(response.text || '');
|
|
286
|
-
}
|
|
287
|
-
async generateStructuredOpenai(prompt, systemPrompt, config) {
|
|
288
|
-
const client = this.getOpenaiClient(config.apiKey);
|
|
289
|
-
const response = await client.chat.completions.create({
|
|
290
|
-
model: config.model,
|
|
291
|
-
messages: [
|
|
292
|
-
{ role: 'system', content: systemPrompt },
|
|
293
|
-
{ role: 'user', content: prompt },
|
|
294
|
-
],
|
|
295
|
-
temperature: config.temperature,
|
|
296
|
-
max_completion_tokens: config.maxTokens,
|
|
297
|
-
response_format: { type: 'json_object' },
|
|
298
|
-
});
|
|
299
|
-
const content = response.choices[0]?.message?.content || '{}';
|
|
300
|
-
return JSON.parse(content);
|
|
301
|
-
}
|
|
302
|
-
async generateStructuredAnthropic(prompt, systemPrompt, config) {
|
|
303
|
-
const client = this.getAnthropicClient(config.apiKey);
|
|
304
|
-
// Anthropic doesn't have native JSON mode, so we enforce via prompt
|
|
305
|
-
const enhancedSystem = systemPrompt +
|
|
306
|
-
'\n\nIMPORTANT: Respond with ONLY a valid JSON object. No markdown, no explanation outside the JSON.';
|
|
307
|
-
const response = await client.messages.create({
|
|
308
|
-
model: config.model,
|
|
309
|
-
max_tokens: config.maxTokens,
|
|
310
|
-
system: enhancedSystem,
|
|
311
|
-
messages: [{ role: 'user', content: prompt }],
|
|
312
|
-
temperature: config.temperature,
|
|
313
|
-
});
|
|
314
|
-
const textBlock = response.content.find(c => c.type === 'text');
|
|
315
|
-
const text = textBlock && 'text' in textBlock ? textBlock.text : '';
|
|
316
|
-
return this.extractJsonFromResponse(text);
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Chat with conversation history
|
|
320
|
-
*/
|
|
321
|
-
async chat(options) {
|
|
322
|
-
const { message, history, systemPrompt, config, images } = options;
|
|
323
|
-
const normalizedConfig = this.normalizeConfig(config);
|
|
324
|
-
switch (config.provider) {
|
|
325
|
-
case 'google':
|
|
326
|
-
return this.chatGoogle(message, history, systemPrompt, normalizedConfig, images);
|
|
327
|
-
case 'openai':
|
|
328
|
-
return this.chatOpenai(message, history, systemPrompt, normalizedConfig, images);
|
|
329
|
-
case 'anthropic':
|
|
330
|
-
return this.chatAnthropic(message, history, systemPrompt, normalizedConfig, images);
|
|
331
|
-
default:
|
|
332
|
-
throw new Error(`Unknown provider: ${config.provider}`);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
async chatGoogle(message, history, systemPrompt, config, images) {
|
|
336
|
-
const client = this.getGoogleClient(config.apiKey);
|
|
337
|
-
// Convert history to Gemini format
|
|
338
|
-
const contents = [];
|
|
339
|
-
for (const msg of history) {
|
|
340
|
-
contents.push({
|
|
341
|
-
role: msg.role === 'user' ? 'user' : 'model',
|
|
342
|
-
parts: [{ text: msg.content }],
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
// Add current message with images
|
|
346
|
-
const currentParts = [
|
|
347
|
-
{ text: message },
|
|
348
|
-
];
|
|
349
|
-
if (images) {
|
|
350
|
-
for (const img of images) {
|
|
351
|
-
currentParts.push({
|
|
352
|
-
inlineData: {
|
|
353
|
-
mimeType: img.mimeType || 'image/png',
|
|
354
|
-
data: img.data,
|
|
355
|
-
},
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
contents.push({
|
|
360
|
-
role: 'user',
|
|
361
|
-
parts: currentParts,
|
|
362
|
-
});
|
|
363
|
-
const response = await client.models.generateContent({
|
|
364
|
-
model: config.model,
|
|
365
|
-
contents,
|
|
366
|
-
config: {
|
|
367
|
-
systemInstruction: systemPrompt,
|
|
368
|
-
temperature: config.temperature,
|
|
369
|
-
maxOutputTokens: config.maxTokens,
|
|
370
|
-
},
|
|
371
|
-
});
|
|
372
|
-
return response.text || '';
|
|
373
|
-
}
|
|
374
|
-
async chatOpenai(message, history, systemPrompt, config, images) {
|
|
375
|
-
const client = this.getOpenaiClient(config.apiKey);
|
|
376
|
-
const messages = [
|
|
377
|
-
{ role: 'system', content: systemPrompt },
|
|
378
|
-
];
|
|
379
|
-
// Add history
|
|
380
|
-
for (const msg of history) {
|
|
381
|
-
messages.push({
|
|
382
|
-
role: msg.role,
|
|
383
|
-
content: msg.content,
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
// Add current message with images
|
|
387
|
-
if (images && images.length > 0) {
|
|
388
|
-
const content = [
|
|
389
|
-
{ type: 'text', text: message },
|
|
390
|
-
];
|
|
391
|
-
for (const img of images) {
|
|
392
|
-
content.push({
|
|
393
|
-
type: 'image_url',
|
|
394
|
-
image_url: {
|
|
395
|
-
url: `data:${img.mimeType || 'image/png'};base64,${img.data}`,
|
|
396
|
-
},
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
messages.push({ role: 'user', content });
|
|
400
|
-
}
|
|
401
|
-
else {
|
|
402
|
-
messages.push({ role: 'user', content: message });
|
|
403
|
-
}
|
|
404
|
-
const response = await client.chat.completions.create({
|
|
405
|
-
model: config.model,
|
|
406
|
-
messages,
|
|
407
|
-
temperature: config.temperature,
|
|
408
|
-
max_completion_tokens: config.maxTokens,
|
|
409
|
-
});
|
|
410
|
-
return response.choices[0]?.message?.content || '';
|
|
411
|
-
}
|
|
412
|
-
async chatAnthropic(message, history, systemPrompt, config, images) {
|
|
413
|
-
const client = this.getAnthropicClient(config.apiKey);
|
|
414
|
-
const messages = [];
|
|
415
|
-
// Add history
|
|
416
|
-
for (const msg of history) {
|
|
417
|
-
messages.push({
|
|
418
|
-
role: msg.role,
|
|
419
|
-
content: msg.content,
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
// Add current message with images
|
|
423
|
-
if (images && images.length > 0) {
|
|
424
|
-
const parts = [];
|
|
425
|
-
for (const img of images) {
|
|
426
|
-
parts.push({
|
|
427
|
-
type: 'image',
|
|
428
|
-
source: {
|
|
429
|
-
type: 'base64',
|
|
430
|
-
media_type: img.mimeType || 'image/png',
|
|
431
|
-
data: img.data,
|
|
432
|
-
},
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
|
-
parts.push({ type: 'text', text: message });
|
|
436
|
-
messages.push({ role: 'user', content: parts });
|
|
437
|
-
}
|
|
438
|
-
else {
|
|
439
|
-
messages.push({ role: 'user', content: message });
|
|
440
|
-
}
|
|
441
|
-
const response = await client.messages.create({
|
|
442
|
-
model: config.model,
|
|
443
|
-
max_tokens: config.maxTokens,
|
|
444
|
-
system: systemPrompt,
|
|
445
|
-
messages,
|
|
446
|
-
temperature: config.temperature,
|
|
447
|
-
});
|
|
448
|
-
const textBlock = response.content.find(c => c.type === 'text');
|
|
449
|
-
return textBlock && 'text' in textBlock ? textBlock.text : '';
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
exports.LLMService = LLMService;
|
|
453
|
-
// Global instance
|
|
454
|
-
exports.llmService = new LLMService();
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LLM Service Types
|
|
3
|
-
*/
|
|
4
|
-
export type LLMProvider = 'google' | 'openai' | 'anthropic';
|
|
5
|
-
export interface LLMConfig {
|
|
6
|
-
provider: LLMProvider;
|
|
7
|
-
model: string;
|
|
8
|
-
temperature?: number;
|
|
9
|
-
maxTokens?: number;
|
|
10
|
-
apiKey?: string;
|
|
11
|
-
}
|
|
12
|
-
export interface ImageInput {
|
|
13
|
-
mimeType: string;
|
|
14
|
-
data: string;
|
|
15
|
-
}
|
|
16
|
-
export interface ChatMessage {
|
|
17
|
-
role: 'user' | 'assistant';
|
|
18
|
-
content: string;
|
|
19
|
-
}
|
|
20
|
-
export interface GenerateOptions {
|
|
21
|
-
prompt: string;
|
|
22
|
-
systemPrompt: string;
|
|
23
|
-
config: LLMConfig;
|
|
24
|
-
images?: ImageInput[];
|
|
25
|
-
}
|
|
26
|
-
export interface ChatOptions {
|
|
27
|
-
message: string;
|
|
28
|
-
history: ChatMessage[];
|
|
29
|
-
systemPrompt: string;
|
|
30
|
-
config: LLMConfig;
|
|
31
|
-
images?: ImageInput[];
|
|
32
|
-
}
|
|
33
|
-
export interface GenerateStructuredOptions {
|
|
34
|
-
prompt: string;
|
|
35
|
-
systemPrompt: string;
|
|
36
|
-
config: LLMConfig;
|
|
37
|
-
}
|
|
38
|
-
export declare const AVAILABLE_MODELS: Record<LLMProvider, string[]>;
|
|
39
|
-
export declare const DEFAULT_MAX_TOKENS = 4096;
|
|
40
|
-
export declare const DEFAULT_TEMPERATURE = 0.2;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* LLM Service Types
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DEFAULT_TEMPERATURE = exports.DEFAULT_MAX_TOKENS = exports.AVAILABLE_MODELS = void 0;
|
|
7
|
-
// Available models per provider (Dec 2025)
|
|
8
|
-
exports.AVAILABLE_MODELS = {
|
|
9
|
-
google: ['gemini-3.0-flash', 'gemini-3.0-pro', 'gemini-2.5-flash'],
|
|
10
|
-
openai: ['gpt-5.2', 'gpt-5-mini', 'gpt-4o'],
|
|
11
|
-
anthropic: ['claude-opus-4-5-20251101', 'claude-sonnet-4-5-20250929', 'claude-haiku-4-5-20251101'],
|
|
12
|
-
};
|
|
13
|
-
// Default configuration
|
|
14
|
-
exports.DEFAULT_MAX_TOKENS = 4096;
|
|
15
|
-
exports.DEFAULT_TEMPERATURE = 0.2;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LLM API Routes
|
|
3
|
-
*/
|
|
4
|
-
import { FastifyInstance } from 'fastify';
|
|
5
|
-
import { LLMService } from '../llm/llm-service';
|
|
6
|
-
declare const llmService: LLMService;
|
|
7
|
-
export default function llmRoutes(fastify: FastifyInstance): Promise<void>;
|
|
8
|
-
export { llmService };
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* LLM API Routes
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.llmService = void 0;
|
|
7
|
-
exports.default = llmRoutes;
|
|
8
|
-
const llm_service_1 = require("../llm/llm-service");
|
|
9
|
-
const llmService = new llm_service_1.LLMService();
|
|
10
|
-
exports.llmService = llmService;
|
|
11
|
-
async function llmRoutes(fastify) {
|
|
12
|
-
/**
|
|
13
|
-
* List available LLM providers and models
|
|
14
|
-
*/
|
|
15
|
-
fastify.get('/llm/providers', async (_request, reply) => {
|
|
16
|
-
const providers = llmService.getAvailableProviders();
|
|
17
|
-
return reply.send({ providers });
|
|
18
|
-
});
|
|
19
|
-
/**
|
|
20
|
-
* Generate text/code from LLM
|
|
21
|
-
*/
|
|
22
|
-
fastify.post('/llm/generate', async (request, reply) => {
|
|
23
|
-
try {
|
|
24
|
-
const { prompt, system_prompt, provider = 'google', model = 'gemini-2.5-flash', temperature = 0.2, images, } = request.body;
|
|
25
|
-
// Check for API key from header
|
|
26
|
-
const apiKey = request.headers['x-api-key'];
|
|
27
|
-
const apiProvider = request.headers['x-api-provider'];
|
|
28
|
-
const response = await llmService.generate({
|
|
29
|
-
prompt,
|
|
30
|
-
systemPrompt: system_prompt,
|
|
31
|
-
config: {
|
|
32
|
-
provider: provider,
|
|
33
|
-
model,
|
|
34
|
-
temperature,
|
|
35
|
-
apiKey: apiProvider === provider ? apiKey : undefined,
|
|
36
|
-
},
|
|
37
|
-
images: images,
|
|
38
|
-
});
|
|
39
|
-
return reply.send({ response });
|
|
40
|
-
}
|
|
41
|
-
catch (err) {
|
|
42
|
-
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
43
|
-
return reply.code(500).send({ detail: message });
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
/**
|
|
47
|
-
* Generate structured JSON response from LLM
|
|
48
|
-
*/
|
|
49
|
-
fastify.post('/llm/generate-structured', async (request, reply) => {
|
|
50
|
-
try {
|
|
51
|
-
const { prompt, system_prompt, provider = 'google', model = 'gemini-2.5-flash', temperature = 0.2, } = request.body;
|
|
52
|
-
// Check for API key from header
|
|
53
|
-
const apiKey = request.headers['x-api-key'];
|
|
54
|
-
const apiProvider = request.headers['x-api-provider'];
|
|
55
|
-
const response = await llmService.generateStructured({
|
|
56
|
-
prompt,
|
|
57
|
-
systemPrompt: system_prompt,
|
|
58
|
-
config: {
|
|
59
|
-
provider: provider,
|
|
60
|
-
model,
|
|
61
|
-
temperature,
|
|
62
|
-
apiKey: apiProvider === provider ? apiKey : undefined,
|
|
63
|
-
},
|
|
64
|
-
});
|
|
65
|
-
return reply.send({ response });
|
|
66
|
-
}
|
|
67
|
-
catch (err) {
|
|
68
|
-
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
69
|
-
return reply.code(500).send({ detail: message });
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
/**
|
|
73
|
-
* Chat with LLM including history
|
|
74
|
-
*/
|
|
75
|
-
fastify.post('/llm/chat', async (request, reply) => {
|
|
76
|
-
try {
|
|
77
|
-
const { message, history = [], system_prompt, provider = 'google', model = 'gemini-2.5-flash', temperature = 0.2, images, } = request.body;
|
|
78
|
-
// Check for API key from header
|
|
79
|
-
const apiKey = request.headers['x-api-key'];
|
|
80
|
-
const apiProvider = request.headers['x-api-provider'];
|
|
81
|
-
// Convert history format
|
|
82
|
-
const chatHistory = history.map((h) => ({
|
|
83
|
-
role: h.role,
|
|
84
|
-
content: h.content,
|
|
85
|
-
}));
|
|
86
|
-
const response = await llmService.chat({
|
|
87
|
-
message,
|
|
88
|
-
history: chatHistory,
|
|
89
|
-
systemPrompt: system_prompt,
|
|
90
|
-
config: {
|
|
91
|
-
provider: provider,
|
|
92
|
-
model,
|
|
93
|
-
temperature,
|
|
94
|
-
apiKey: apiProvider === provider ? apiKey : undefined,
|
|
95
|
-
},
|
|
96
|
-
images: images,
|
|
97
|
-
});
|
|
98
|
-
return reply.send({ response });
|
|
99
|
-
}
|
|
100
|
-
catch (err) {
|
|
101
|
-
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
102
|
-
return reply.code(500).send({ detail: message });
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
}
|