openkbs 0.0.53 → 0.0.55
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 +1490 -202
- package/package.json +2 -1
- package/src/actions.js +345 -1
- package/src/index.js +17 -1
- package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/app/instructions.txt +44 -9
- package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/actions.js +43 -42
- package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/handler.js +14 -8
- package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Frontend/contentRender.js +95 -12
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/README.md +64 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/app/instructions.txt +160 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/app/settings.json +7 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Events/actions.js +258 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Events/onRequest.js +13 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Events/onRequest.json +3 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Events/onResponse.js +13 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Events/onResponse.json +3 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Frontend/contentRender.js +170 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Frontend/contentRender.json +3 -0
- package/templates/.openkbs/knowledge/metadata.json +1 -1
- package/templates/CLAUDE.md +593 -222
- package/templates/app/instructions.txt +13 -1
- package/templates/app/settings.json +5 -6
- package/templates/src/Events/actions.js +43 -9
- package/templates/src/Events/handler.js +24 -25
- package/templates/webpack.contentRender.config.js +8 -2
- package/version.json +3 -3
- package/MODIFY.md +0 -132
package/README.md
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# OpenKBS · [](https://github.com/open-kbs/openkbs-chat/blob/main/LICENSE) [](https://www.npmjs.com/package/openkbs)
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
OpenKBS is an extendable AI service designed to build,
|
|
5
4
|
deploy and integrate AI agents and applications.
|
|
6
5
|
|
|
@@ -26,8 +25,11 @@ deploy and integrate AI agents and applications.
|
|
|
26
25
|
- [SDK](#sdk)
|
|
27
26
|
- [Managing Secrets](#managing-secrets)
|
|
28
27
|
- [Application Settings](#application-settings)
|
|
28
|
+
- [Message Types and Content Formats](#message-types-and-content-formats)
|
|
29
|
+
- [Supported AI Models](#supported-ai-models)
|
|
29
30
|
- [LLM Instructions](#llm-instructions)
|
|
30
31
|
- [Execution Environment](#execution-environment)
|
|
32
|
+
- [MCP Integration](#mcp-integration)
|
|
31
33
|
- [Frontend](#frontend)
|
|
32
34
|
- [Frontend Module Loading](#frontend-module-loading)
|
|
33
35
|
- [Built-in UI Libraries](#built-in-ui-libraries)
|
|
@@ -169,40 +171,7 @@ Enhance your UI with Material-UI components:
|
|
|
169
171
|
|
|
170
172
|
```bash
|
|
171
173
|
openkbs push
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
### AI-Powered Frontend Generation
|
|
175
|
-
|
|
176
|
-
OpenKBS provides simple AI-powered code generation. Use the `openkbs modify` command followed by your requirement:
|
|
177
|
-
|
|
178
|
-
```bash
|
|
179
|
-
openkbs modify "Implementing UI to manage renderSettings"
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
If you need to revert changes:
|
|
183
|
-
```bash
|
|
184
|
-
git checkout -- .
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
## Extend Backend
|
|
188
|
-
|
|
189
|
-
Extend backend functionality using `openkbs modify` followed by your requirements. Add file paths to scope AI changes to specific files:
|
|
190
|
-
|
|
191
|
-
```bash
|
|
192
|
-
openkbs modify "Implement getContent backend tool that returns text or JSON from a given URL" src/Events/actions.js app/instructions.txt
|
|
193
|
-
openkbs push
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
This adds a new backend tool in `actions.js` that:
|
|
197
|
-
- Fetches content from URLs
|
|
198
|
-
- Handles JSON and HTML responses
|
|
199
|
-
- Auto-registers in `instructions.txt` (enabling LLM to understand and use it)
|
|
200
|
-
- Available to users and LLM through chat
|
|
201
|
-
|
|
202
|
-
Example usage in chat:
|
|
203
|
-
```
|
|
204
|
-
/getContent("https://api.example.com/data")
|
|
205
|
-
```
|
|
174
|
+
`````
|
|
206
175
|
|
|
207
176
|
## Mobile & Desktop App
|
|
208
177
|
|
|
@@ -231,11 +200,13 @@ src/
|
|
|
231
200
|
│ ├── onRequest.js // Handles incoming user messages
|
|
232
201
|
│ ├── onResponse.js // Handles outgoing LLM messages
|
|
233
202
|
│ ├── onPublicAPIRequest.js // Handles public API requests
|
|
234
|
-
│ ├── onAddMessages.js
|
|
203
|
+
│ ├── onAddMessages.js // Handles messages added to the chat
|
|
204
|
+
│ ├── onCronjob.js // Scheduled task handler (runs on cron schedule)
|
|
235
205
|
│ ├── onRequest.json // Dependencies for onRequest handler
|
|
236
|
-
│ ├── onResponse.json
|
|
206
|
+
│ ├── onResponse.json // Dependencies for onResponse handler
|
|
237
207
|
│ ├── onPublicAPIRequest.json // Dependencies for onPublicAPIRequest handler
|
|
238
|
-
│
|
|
208
|
+
│ ├── onAddMessages.json // Dependencies for onAddMessages handler
|
|
209
|
+
│ └── onCronjob.json // Dependencies for onCronjob handler
|
|
239
210
|
│── Frontend/
|
|
240
211
|
│ ├── contentRender.js // Custom rendering logic for chat messages
|
|
241
212
|
│ └── contentRender.json // Dependencies for the contentRender module
|
|
@@ -262,129 +233,274 @@ The core of the OpenKBS backend framework revolves around the `onRequest` and `o
|
|
|
262
233
|
|
|
263
234
|
```javascript
|
|
264
235
|
// src/Events/actions.js
|
|
265
|
-
export const getActions = (meta) =>
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}],
|
|
274
|
-
// ... more actions
|
|
275
|
-
];
|
|
276
|
-
};
|
|
236
|
+
export const getActions = (meta) => [
|
|
237
|
+
// Commands use XML tags with JSON content
|
|
238
|
+
[/<myCommand>([\s\S]*?)<\/myCommand>/s, async (match) => {
|
|
239
|
+
const data = JSON.parse(match[1].trim());
|
|
240
|
+
// Execute custom logic, API calls, etc.
|
|
241
|
+
return { result: data.param, ...meta };
|
|
242
|
+
}]
|
|
243
|
+
];
|
|
277
244
|
|
|
278
245
|
// src/Events/onRequest.js
|
|
279
246
|
import {getActions} from './actions.js';
|
|
280
247
|
export const handler = async (event) => {
|
|
281
|
-
const actions = getActions({ _meta_actions: [] });
|
|
282
|
-
|
|
283
|
-
|
|
248
|
+
const actions = getActions({ _meta_actions: [] });
|
|
249
|
+
const lastMessage = event.payload.messages[event.payload.messages.length - 1].content;
|
|
250
|
+
for (const [regex, action] of actions) {
|
|
284
251
|
const match = lastMessage?.match(regex);
|
|
285
|
-
if (match) return await action(match, event);
|
|
252
|
+
if (match) return await action(match, event);
|
|
286
253
|
}
|
|
287
|
-
return { type: 'CONTINUE' };
|
|
254
|
+
return { type: 'CONTINUE' };
|
|
288
255
|
};
|
|
289
256
|
|
|
290
|
-
|
|
291
257
|
// src/Events/onResponse.js
|
|
292
258
|
import {getActions} from './actions.js';
|
|
293
|
-
|
|
294
259
|
export const handler = async (event) => {
|
|
295
|
-
// Example of conditional meta actions based on message count:
|
|
296
260
|
const maxSelfInvokeMessagesCount = 30;
|
|
297
261
|
const actions = getActions({
|
|
298
262
|
_meta_actions: event?.payload?.messages?.length > maxSelfInvokeMessagesCount
|
|
299
|
-
? ["
|
|
300
|
-
: ["REQUEST_CHAT_MODEL"]
|
|
263
|
+
? [] : ["REQUEST_CHAT_MODEL"]
|
|
301
264
|
});
|
|
302
|
-
|
|
303
|
-
for (
|
|
304
|
-
const lastMessage = event.payload.messages[event.payload.messages.length - 1].content;
|
|
265
|
+
const lastMessage = event.payload.messages[event.payload.messages.length - 1].content;
|
|
266
|
+
for (const [regex, action] of actions) {
|
|
305
267
|
const match = lastMessage?.match(regex);
|
|
306
268
|
if (match) return await action(match, event);
|
|
307
269
|
}
|
|
308
|
-
|
|
309
|
-
return { type: 'CONTINUE' }
|
|
270
|
+
return { type: 'CONTINUE' };
|
|
310
271
|
};
|
|
311
|
-
|
|
312
272
|
```
|
|
313
273
|
|
|
314
274
|
The `onRequest` and `onResponse` handlers are the core of customizing your OpenKBS agent's behavior. They act as middleware, intercepting messages before they reach the LLM (`onRequest`) and after the LLM generates a response (`onResponse`). This enables you to implement custom logic, interact with external APIs, and control the flow of the conversation.
|
|
315
275
|
|
|
276
|
+
#### Command Format
|
|
277
|
+
|
|
278
|
+
Commands use XML tags with JSON content inside. This format is cleaner and more flexible than regex-based parsing:
|
|
279
|
+
|
|
280
|
+
```xml
|
|
281
|
+
<commandName>
|
|
282
|
+
{
|
|
283
|
+
"param1": "value1",
|
|
284
|
+
"param2": "value2"
|
|
285
|
+
}
|
|
286
|
+
</commandName>
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
For commands without parameters, use self-closing tags:
|
|
290
|
+
```xml
|
|
291
|
+
<commandName/>
|
|
292
|
+
```
|
|
293
|
+
|
|
316
294
|
**Example:**
|
|
317
295
|
|
|
318
296
|
```javascript
|
|
319
297
|
// src/Events/actions.js
|
|
298
|
+
|
|
299
|
+
// Helper function for uploading generated images
|
|
300
|
+
const uploadGeneratedImage = async (base64Data, meta) => {
|
|
301
|
+
const fileName = `image-${Date.now()}-${Math.random().toString(36).substring(7)}.png`;
|
|
302
|
+
const uploadResult = await openkbs.uploadImage(base64Data, fileName, 'image/png');
|
|
303
|
+
return {
|
|
304
|
+
type: 'CHAT_IMAGE',
|
|
305
|
+
data: { imageUrl: uploadResult.url },
|
|
306
|
+
...meta
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
|
|
320
310
|
export const getActions = (meta) => [
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
311
|
+
|
|
312
|
+
// AI Image Generation (supports multiple models)
|
|
313
|
+
[/<createAIImage>([\s\S]*?)<\/createAIImage>/s, async (match) => {
|
|
314
|
+
try {
|
|
315
|
+
const data = JSON.parse(match[1].trim());
|
|
316
|
+
const model = data.model || "gemini-2.5-flash-image";
|
|
317
|
+
const params = { model, n: 1 };
|
|
318
|
+
|
|
319
|
+
if (data.imageUrls?.length > 0) {
|
|
320
|
+
params.imageUrls = data.imageUrls;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (model === 'gpt-image-1') {
|
|
324
|
+
const validSizes = ["1024x1024", "1536x1024", "1024x1536", "auto"];
|
|
325
|
+
params.size = validSizes.includes(data.size) ? data.size : "1024x1024";
|
|
326
|
+
params.quality = "high";
|
|
327
|
+
} else if (model === 'gemini-2.5-flash-image') {
|
|
328
|
+
const validAspectRatios = ["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"];
|
|
329
|
+
params.aspect_ratio = validAspectRatios.includes(data.aspect_ratio) ? data.aspect_ratio : "1:1";
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const image = await openkbs.generateImage(data.prompt, params);
|
|
333
|
+
return await uploadGeneratedImage(image[0].b64_json, meta);
|
|
334
|
+
} catch (error) {
|
|
335
|
+
return { error: error.message || 'Image creation failed', ...meta };
|
|
336
|
+
}
|
|
326
337
|
}],
|
|
327
338
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
339
|
+
// AI Video Generation (Sora 2)
|
|
340
|
+
[/<createAIVideo>([\s\S]*?)<\/createAIVideo>/s, async (match) => {
|
|
341
|
+
try {
|
|
342
|
+
const data = JSON.parse(match[1].trim());
|
|
343
|
+
const params = {
|
|
344
|
+
video_model: data.model || "sora-2",
|
|
345
|
+
seconds: [4, 8, 12].includes(data.seconds) ? data.seconds : 8
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
if (data.input_reference_url) {
|
|
349
|
+
params.input_reference_url = data.input_reference_url;
|
|
350
|
+
} else {
|
|
351
|
+
const validSizes = ['720x1280', '1280x720'];
|
|
352
|
+
params.size = validSizes.includes(data.size) ? data.size : '1280x720';
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const videoData = await openkbs.generateVideo(data.prompt, params);
|
|
356
|
+
|
|
357
|
+
if (videoData?.[0]?.status === 'pending') {
|
|
358
|
+
return {
|
|
359
|
+
type: 'VIDEO_PENDING',
|
|
360
|
+
data: { videoId: videoData[0].video_id, message: 'Video generation in progress...' },
|
|
361
|
+
...meta
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (videoData?.[0]?.video_url) {
|
|
366
|
+
return { type: 'CHAT_VIDEO', data: { videoUrl: videoData[0].video_url }, ...meta };
|
|
367
|
+
}
|
|
368
|
+
return { error: 'Video generation failed', ...meta };
|
|
369
|
+
} catch (error) {
|
|
370
|
+
return { error: error.message || 'Video creation failed', ...meta };
|
|
371
|
+
}
|
|
345
372
|
}],
|
|
346
373
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
374
|
+
// Continue video polling
|
|
375
|
+
[/<continueVideoPolling>([\s\S]*?)<\/continueVideoPolling>/s, async (match) => {
|
|
376
|
+
try {
|
|
377
|
+
const data = JSON.parse(match[1].trim());
|
|
378
|
+
const videoData = await openkbs.checkVideoStatus(data.videoId);
|
|
379
|
+
|
|
380
|
+
if (videoData?.[0]?.status === 'completed' && videoData[0].video_url) {
|
|
381
|
+
return { type: 'CHAT_VIDEO', data: { videoUrl: videoData[0].video_url }, ...meta };
|
|
382
|
+
} else if (videoData?.[0]?.status === 'pending') {
|
|
383
|
+
return { type: 'VIDEO_PENDING', data: { videoId: data.videoId, message: 'Still generating...' }, ...meta };
|
|
384
|
+
}
|
|
385
|
+
return { error: 'Video generation failed', ...meta };
|
|
386
|
+
} catch (error) {
|
|
387
|
+
return { error: error.message, ...meta };
|
|
351
388
|
}
|
|
352
|
-
return { data: response, ...meta };
|
|
353
389
|
}],
|
|
354
390
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
391
|
+
// Google Search
|
|
392
|
+
[/<googleSearch>([\s\S]*?)<\/googleSearch>/s, async (match) => {
|
|
393
|
+
try {
|
|
394
|
+
const data = JSON.parse(match[1].trim());
|
|
395
|
+
const response = await openkbs.googleSearch(data.query);
|
|
396
|
+
const results = response?.map(({ title, link, snippet, pagemap }) => ({
|
|
397
|
+
title, link, snippet,
|
|
398
|
+
image: pagemap?.metatags?.[0]?.["og:image"]
|
|
399
|
+
}));
|
|
400
|
+
return { data: results, ...meta };
|
|
401
|
+
} catch (e) {
|
|
402
|
+
return { error: e.message, ...meta };
|
|
359
403
|
}
|
|
360
|
-
return { data: response, ...meta };
|
|
361
404
|
}],
|
|
362
405
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
406
|
+
// Web scraping
|
|
407
|
+
[/<webpageToText>([\s\S]*?)<\/webpageToText>/s, async (match) => {
|
|
408
|
+
try {
|
|
409
|
+
const data = JSON.parse(match[1].trim());
|
|
410
|
+
let response = await openkbs.webpageToText(data.url);
|
|
411
|
+
if (response?.content?.length > 5000) {
|
|
412
|
+
response.content = response.content.substring(0, 5000);
|
|
413
|
+
}
|
|
414
|
+
return { data: response, ...meta };
|
|
415
|
+
} catch (e) {
|
|
416
|
+
return { error: e.message, ...meta };
|
|
367
417
|
}
|
|
368
|
-
return { data: response, ...meta };
|
|
369
418
|
}],
|
|
370
419
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
420
|
+
// Exchange rates with period support
|
|
421
|
+
[/<getExchangeRates>([\s\S]*?)<\/getExchangeRates>/s, async (match) => {
|
|
422
|
+
try {
|
|
423
|
+
const data = JSON.parse(match[1].trim());
|
|
424
|
+
// period can be: 'latest', 'YYYY-MM-DD' (historical), or 'YYYY-MM-DD..YYYY-MM-DD' (time series)
|
|
425
|
+
let response = await openkbs.getExchangeRates({
|
|
426
|
+
base: data.base,
|
|
427
|
+
symbols: data.symbols,
|
|
428
|
+
period: data.period || 'latest'
|
|
429
|
+
});
|
|
430
|
+
return { data: response, ...meta };
|
|
431
|
+
} catch (e) {
|
|
432
|
+
return { error: e.message, ...meta };
|
|
433
|
+
}
|
|
434
|
+
}],
|
|
435
|
+
|
|
436
|
+
// Send email
|
|
437
|
+
[/<sendMail>([\s\S]*?)<\/sendMail>/s, async (match) => {
|
|
438
|
+
try {
|
|
439
|
+
const data = JSON.parse(match[1].trim());
|
|
440
|
+
const response = await openkbs.sendMail(data.to, data.subject, data.body);
|
|
441
|
+
return { type: 'EMAIL_SENT', data: { email: data.to, subject: data.subject, response }, ...meta };
|
|
442
|
+
} catch (e) {
|
|
443
|
+
return { error: e.message, ...meta };
|
|
444
|
+
}
|
|
376
445
|
}],
|
|
377
446
|
|
|
378
|
-
//
|
|
379
|
-
[
|
|
380
|
-
|
|
381
|
-
|
|
447
|
+
// Schedule task
|
|
448
|
+
[/<scheduleTask>([\s\S]*?)<\/scheduleTask>/s, async (match) => {
|
|
449
|
+
try {
|
|
450
|
+
const data = JSON.parse(match[1].trim());
|
|
451
|
+
let scheduledTime;
|
|
452
|
+
|
|
453
|
+
if (data.time) {
|
|
454
|
+
let isoTimeStr = data.time.replace(' ', 'T');
|
|
455
|
+
if (!isoTimeStr.includes('Z') && !isoTimeStr.includes('+')) isoTimeStr += 'Z';
|
|
456
|
+
scheduledTime = new Date(isoTimeStr).getTime();
|
|
457
|
+
} else if (data.delay) {
|
|
458
|
+
let delayMs = 0;
|
|
459
|
+
if (data.delay.endsWith('h')) delayMs = parseFloat(data.delay) * 3600000;
|
|
460
|
+
else if (data.delay.endsWith('d')) delayMs = parseFloat(data.delay) * 86400000;
|
|
461
|
+
else delayMs = parseFloat(data.delay) * 60000;
|
|
462
|
+
scheduledTime = Date.now() + delayMs;
|
|
463
|
+
} else {
|
|
464
|
+
scheduledTime = Date.now() + 3600000;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const response = await openkbs.kb({
|
|
468
|
+
action: 'createScheduledTask',
|
|
469
|
+
scheduledTime: Math.floor(scheduledTime / 60000) * 60000,
|
|
470
|
+
taskPayload: { message: `[SCHEDULED_TASK] ${data.message}`, createdAt: Date.now() },
|
|
471
|
+
description: data.message.substring(0, 50)
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
return { type: 'TASK_SCHEDULED', data: { scheduledTime: new Date(scheduledTime).toISOString(), taskId: response.taskId }, ...meta };
|
|
475
|
+
} catch (e) {
|
|
476
|
+
return { error: e.message, ...meta };
|
|
477
|
+
}
|
|
478
|
+
}],
|
|
479
|
+
|
|
480
|
+
// Get scheduled tasks
|
|
481
|
+
[/<getScheduledTasks\s*\/>/s, async () => {
|
|
482
|
+
try {
|
|
483
|
+
const response = await openkbs.kb({ action: 'getScheduledTasks' });
|
|
484
|
+
return { type: 'SCHEDULED_TASKS_LIST', data: response, ...meta };
|
|
485
|
+
} catch (e) {
|
|
486
|
+
return { error: e.message, ...meta };
|
|
487
|
+
}
|
|
382
488
|
}],
|
|
383
489
|
|
|
384
|
-
//
|
|
385
|
-
[
|
|
386
|
-
|
|
387
|
-
|
|
490
|
+
// View image - adds image to LLM vision context
|
|
491
|
+
[/<viewImage>([\s\S]*?)<\/viewImage>/s, async (match) => {
|
|
492
|
+
try {
|
|
493
|
+
const data = JSON.parse(match[1].trim());
|
|
494
|
+
return {
|
|
495
|
+
data: [
|
|
496
|
+
{ type: "text", text: `Viewing image: ${data.url}` },
|
|
497
|
+
{ type: "image_url", image_url: { url: data.url } }
|
|
498
|
+
],
|
|
499
|
+
...meta
|
|
500
|
+
};
|
|
501
|
+
} catch (e) {
|
|
502
|
+
return { error: e.message, ...meta };
|
|
503
|
+
}
|
|
388
504
|
}],
|
|
389
505
|
];
|
|
390
506
|
```
|
|
@@ -394,15 +510,13 @@ export const getActions = (meta) => [
|
|
|
394
510
|
// src/Events/onRequest.js
|
|
395
511
|
import {getActions} from './actions.js';
|
|
396
512
|
|
|
397
|
-
|
|
398
513
|
export const handler = async (event) => {
|
|
399
|
-
const actions = getActions({});
|
|
514
|
+
const actions = getActions({ _meta_actions: [] });
|
|
400
515
|
for (let [regex, action] of actions) {
|
|
401
516
|
const lastMessage = event.payload.messages[event.payload.messages.length - 1].content;
|
|
402
517
|
const match = lastMessage?.match(regex);
|
|
403
518
|
if (match) return await action(match);
|
|
404
519
|
}
|
|
405
|
-
|
|
406
520
|
return { type: 'CONTINUE' }
|
|
407
521
|
};
|
|
408
522
|
```
|
|
@@ -412,14 +526,12 @@ export const handler = async (event) => {
|
|
|
412
526
|
import {getActions} from './actions.js';
|
|
413
527
|
|
|
414
528
|
export const handler = async (event) => {
|
|
415
|
-
const actions = getActions({_meta_actions: ["REQUEST_CHAT_MODEL"]});
|
|
416
|
-
|
|
529
|
+
const actions = getActions({ _meta_actions: ["REQUEST_CHAT_MODEL"] });
|
|
417
530
|
for (let [regex, action] of actions) {
|
|
418
531
|
const lastMessage = event.payload.messages[event.payload.messages.length - 1].content;
|
|
419
532
|
const match = lastMessage?.match(regex);
|
|
420
533
|
if (match) return await action(match);
|
|
421
534
|
}
|
|
422
|
-
|
|
423
535
|
return { type: 'CONTINUE' }
|
|
424
536
|
};
|
|
425
537
|
```
|
|
@@ -563,7 +675,79 @@ export const handler = async (event) => {
|
|
|
563
675
|
|
|
564
676
|
```
|
|
565
677
|
|
|
566
|
-
|
|
678
|
+
#### onCronjob Handler
|
|
679
|
+
|
|
680
|
+
The `onCronjob` handler enables scheduled task execution for your agent. It runs automatically based on a cron schedule you define, without requiring user interaction.
|
|
681
|
+
|
|
682
|
+
**How it works:**
|
|
683
|
+
1. Create `src/Events/onCronjob.js` with your handler logic
|
|
684
|
+
2. Define the schedule using `handler.CRON_SCHEDULE` at the end of the file
|
|
685
|
+
3. Deploy with `openkbs push` - the cronjob is automatically registered
|
|
686
|
+
|
|
687
|
+
**Cron Schedule Format:**
|
|
688
|
+
Standard cron syntax: `minute hour day month weekday`
|
|
689
|
+
|
|
690
|
+
- `* * * * *` - Every minute
|
|
691
|
+
- `*/5 * * * *` - Every 5 minutes
|
|
692
|
+
- `0 * * * *` - Every hour
|
|
693
|
+
- `0 0 * * *` - Daily at midnight
|
|
694
|
+
- `0 9 * * 1-5` - Weekdays at 9 AM
|
|
695
|
+
|
|
696
|
+
**Example `onCronjob.js`:**
|
|
697
|
+
|
|
698
|
+
```javascript
|
|
699
|
+
// src/Events/onCronjob.js
|
|
700
|
+
|
|
701
|
+
export const handler = async (event) => {
|
|
702
|
+
try {
|
|
703
|
+
// Fetch external data
|
|
704
|
+
const response = await fetch('https://api.example.com/status');
|
|
705
|
+
const data = await response.json();
|
|
706
|
+
|
|
707
|
+
// Process and store results
|
|
708
|
+
await openkbs.createItem({
|
|
709
|
+
itemType: 'status_check',
|
|
710
|
+
itemId: `status_${Date.now()}`,
|
|
711
|
+
body: { timestamp: new Date().toISOString(), ...data }
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
// Optionally trigger a chat for notifications
|
|
715
|
+
if (data.alertLevel > 5) {
|
|
716
|
+
await openkbs.chats({
|
|
717
|
+
chatTitle: `Alert: ${data.message}`,
|
|
718
|
+
message: JSON.stringify([
|
|
719
|
+
{ type: "text", text: `ALERT: ${data.message}\nLevel: ${data.alertLevel}` }
|
|
720
|
+
])
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
return {
|
|
725
|
+
success: true,
|
|
726
|
+
processed: data,
|
|
727
|
+
timestamp: new Date().toISOString()
|
|
728
|
+
};
|
|
729
|
+
} catch (error) {
|
|
730
|
+
return {
|
|
731
|
+
success: false,
|
|
732
|
+
error: error.message,
|
|
733
|
+
timestamp: new Date().toISOString()
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
// IMPORTANT: Define the cron schedule at the end of the file
|
|
739
|
+
handler.CRON_SCHEDULE = "*/5 * * * *"; // Runs every 5 minutes
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
**Key Points:**
|
|
743
|
+
- The `CRON_SCHEDULE` must be defined as `handler.CRON_SCHEDULE = "pattern"` at the end of the file
|
|
744
|
+
- The handler receives an empty `event` object (no user payload)
|
|
745
|
+
- Use `openkbs.chats()` to create new chat sessions for notifications
|
|
746
|
+
- All SDK methods (`openkbs.*`) are available
|
|
747
|
+
- Return an object with results for logging purposes
|
|
748
|
+
- To disable the cronjob, simply remove or rename the `onCronjob.js` file and redeploy
|
|
749
|
+
|
|
750
|
+
**Dependencies (onRequest.json, onResponse.json, onCronjob.json, etc.):**
|
|
567
751
|
|
|
568
752
|
These files specify the NPM package dependencies required for the respective event handlers. They follow the standard `package.json` format.
|
|
569
753
|
|
|
@@ -598,33 +782,84 @@ if (actionMatch) {
|
|
|
598
782
|
```
|
|
599
783
|
|
|
600
784
|
|
|
601
|
-
#### SDK
|
|
785
|
+
#### SDK (Backend `openkbs` object)
|
|
602
786
|
|
|
603
|
-
The `openkbs` object
|
|
787
|
+
The `openkbs` object is a global object available in all backend event handlers (`onRequest`, `onResponse`, `onAddMessages`, `onPublicAPIRequest`, `onCronjob`). It provides access to OpenKBS services, external APIs, and data storage.
|
|
604
788
|
|
|
605
|
-
|
|
789
|
+
**Properties:**
|
|
790
|
+
```javascript
|
|
791
|
+
openkbs.kbId // Current Knowledge Base ID
|
|
792
|
+
openkbs.clientHeaders // Client headers (e.g., openkbs.clientHeaders['x-forwarded-for'] for IP)
|
|
793
|
+
openkbs.AESKey // Encryption key for this KB
|
|
794
|
+
openkbs.chatJWT // JWT token for current chat session
|
|
795
|
+
```
|
|
606
796
|
|
|
607
|
-
|
|
797
|
+
##### Image & Video Generation
|
|
798
|
+
|
|
799
|
+
* **`openkbs.generateImage(prompt, params)`:** Generates images using AI models. Returns array with `b64_json` data.
|
|
800
|
+
- `params.model`: `"gemini-2.5-flash-image"` (default, supports image editing) or `"gpt-image-1"` (better for text in images)
|
|
801
|
+
- `params.imageUrls`: Array of reference image URLs (gemini only)
|
|
802
|
+
- `params.aspect_ratio`: For gemini: `"1:1"`, `"16:9"`, `"9:16"`, `"3:2"`, `"2:3"`, `"4:3"`, `"3:4"`, `"4:5"`, `"5:4"`, `"21:9"`
|
|
803
|
+
- `params.size`: For gpt-image-1: `"1024x1024"`, `"1536x1024"`, `"1024x1536"`, `"auto"`
|
|
804
|
+
- `params.n`: Number of images (default: 1)
|
|
805
|
+
|
|
806
|
+
* **`openkbs.generateVideo(prompt, params)`:** Generates videos using Sora 2. Returns array with `video_url` or `status: 'pending'`.
|
|
807
|
+
- `params.video_model`: `"sora-2"` (default, fast) or `"sora-2-pro"` (higher quality)
|
|
808
|
+
- `params.seconds`: `4`, `8` (default), or `12`
|
|
809
|
+
- `params.size`: `"1280x720"` (landscape) or `"720x1280"` (portrait)
|
|
810
|
+
- `params.input_reference_url`: Optional reference image URL
|
|
811
|
+
|
|
812
|
+
* **`openkbs.checkVideoStatus(videoId)`:** Check status of pending video generation.
|
|
813
|
+
|
|
814
|
+
* **`openkbs.uploadImage(base64Data, fileName, mimeType)`:** Upload image to storage. Returns `{ url }`.
|
|
815
|
+
|
|
816
|
+
##### Deep Research
|
|
817
|
+
|
|
818
|
+
* **`openkbs.deepResearch(input, params)`:** Autonomous research agent powered by Google Gemini. Performs multi-step research: plans → searches web → reads sources → iterates → synthesizes report.
|
|
819
|
+
- `input`: Research query or topic (string)
|
|
820
|
+
- `params.previous_interaction_id`: Optional - for follow-up questions on completed research
|
|
821
|
+
- Returns `{ status, interaction_id, output, usage, prepaid_credits }`
|
|
822
|
+
- Status can be `'completed'`, `'in_progress'`, or `'failed'`
|
|
823
|
+
- **Important:** Takes 5-20 minutes (up to 60 min for complex topics)
|
|
824
|
+
- **Pricing:** Requires minimum upfront charge of 50 credits (~€0.50). Additional usage charged on completion.
|
|
825
|
+
|
|
826
|
+
* **`openkbs.checkDeepResearchStatus(interactionId, prepaidCredits)`:** Check status of pending deep research.
|
|
827
|
+
- `interactionId`: From previous `deepResearch()` response
|
|
828
|
+
- `prepaidCredits`: From previous response (for correct billing)
|
|
829
|
+
- Returns same format as `deepResearch()`
|
|
830
|
+
|
|
831
|
+
##### Search & Content Extraction
|
|
832
|
+
|
|
833
|
+
* **`openkbs.googleSearch(query, params)`:** Performs Google search. Optional `params.searchType: 'image'` for image search.
|
|
834
|
+
|
|
835
|
+
* **`openkbs.webpageToText(pageURL, params)`:** Extracts text content from a webpage.
|
|
608
836
|
|
|
609
|
-
* **`openkbs.
|
|
837
|
+
* **`openkbs.documentToText(documentURL, params)`:** Extracts text from documents (PDF, DOC, etc.).
|
|
610
838
|
|
|
611
|
-
* **`openkbs.
|
|
839
|
+
* **`openkbs.imageToText(imageUrl, params)`:** OCR - extracts text from images.
|
|
612
840
|
|
|
613
|
-
|
|
841
|
+
##### Communication
|
|
614
842
|
|
|
615
|
-
* **`openkbs.
|
|
843
|
+
* **`openkbs.sendMail(email, subject, content)`:** Sends an email to the specified recipient.
|
|
616
844
|
|
|
617
|
-
* **`openkbs.
|
|
845
|
+
* **`openkbs.textToSpeech(text, params)`:** Converts text to speech. Returns `response.audioContent`.
|
|
618
846
|
|
|
619
|
-
* **`openkbs.
|
|
847
|
+
* **`openkbs.speechToText(audioURL, params)`:** Transcribes audio from a URL to text.
|
|
848
|
+
|
|
849
|
+
##### Data & Utilities
|
|
620
850
|
|
|
621
|
-
* **`openkbs.
|
|
851
|
+
* **`openkbs.getExchangeRates({ base, symbols, period })`:** Retrieves exchange rates.
|
|
852
|
+
- `base`: Base currency (e.g., `"EUR"`)
|
|
853
|
+
- `symbols`: Target currencies (e.g., `"USD,GBP"`)
|
|
854
|
+
- `period`: `"latest"` (default), `"YYYY-MM-DD"` (historical), or `"YYYY-MM-DD..YYYY-MM-DD"` (time series)
|
|
855
|
+
|
|
856
|
+
* **`openkbs.checkVAT(vatNumber)`:** Validates a VAT number against official databases.
|
|
622
857
|
|
|
623
858
|
* **`openkbs.translate(text, to)`:** Translates text to the specified target language.
|
|
624
859
|
|
|
625
860
|
* **`openkbs.detectLanguage(text, params)`:** Detects the language of the provided text.
|
|
626
861
|
|
|
627
|
-
|
|
862
|
+
##### Encryption & Storage
|
|
628
863
|
|
|
629
864
|
* **`openkbs.encrypt(plaintext)`:** Encrypts data using the provided AES key.
|
|
630
865
|
|
|
@@ -632,25 +867,162 @@ The `openkbs` object provides a set of utility functions and services to interac
|
|
|
632
867
|
|
|
633
868
|
* **`openkbs.items(data)`:** Interacts with the Items API for creating, updating, and deleting items.
|
|
634
869
|
|
|
870
|
+
* **`openkbs.createItem({ itemType, itemId, body })`:** Create a new item.
|
|
871
|
+
|
|
872
|
+
* **`openkbs.updateItem({ itemType, itemId, body })`:** Update an existing item.
|
|
873
|
+
|
|
874
|
+
* **`openkbs.deleteItem(itemId)`:** Delete an item by ID.
|
|
875
|
+
|
|
876
|
+
* **`openkbs.getItem(itemId)`:** Get a single item by ID. Returns item with auto-decrypted body.
|
|
877
|
+
|
|
878
|
+
* **`openkbs.fetchItems({ limit, itemType, beginsWith, from, to, field })`:** Fetch multiple items with filters.
|
|
879
|
+
- `limit`: Max items to return (default: 1000)
|
|
880
|
+
- `itemType`: Filter by item type
|
|
881
|
+
- `beginsWith`: Filter by itemId prefix
|
|
882
|
+
- `from`, `to`: Date range filters
|
|
883
|
+
- `field`: Field name for range query
|
|
884
|
+
|
|
635
885
|
* **`openkbs.chats(data)`:** Interacts with the Chats API.
|
|
636
886
|
|
|
637
|
-
* **`openkbs.kb(data)`:** Interacts with the Knowledge Base API.
|
|
887
|
+
* **`openkbs.kb(data)`:** Interacts with the Knowledge Base API. Actions include:
|
|
888
|
+
- `createScheduledTask`: Schedule a future task
|
|
889
|
+
- `getScheduledTasks`: List all scheduled tasks
|
|
890
|
+
- `deleteScheduledTask`: Delete a scheduled task
|
|
891
|
+
- `createPresignedURL`: Get URL for file upload
|
|
638
892
|
|
|
639
|
-
|
|
893
|
+
##### Scheduled Tasks API
|
|
640
894
|
|
|
641
|
-
|
|
895
|
+
Scheduled tasks allow your agent to execute actions at specific times in the future. When a scheduled task fires, it creates a new chat session with the task message.
|
|
896
|
+
|
|
897
|
+
**Create a scheduled task:**
|
|
898
|
+
```javascript
|
|
899
|
+
const response = await openkbs.kb({
|
|
900
|
+
action: 'createScheduledTask',
|
|
901
|
+
scheduledTime: Date.now() + 3600000, // Unix timestamp in ms (1 hour from now)
|
|
902
|
+
taskPayload: {
|
|
903
|
+
message: '[SCHEDULED_TASK] Send weekly report',
|
|
904
|
+
source: 'marketing_agent',
|
|
905
|
+
customData: { reportType: 'weekly' }
|
|
906
|
+
},
|
|
907
|
+
description: 'Weekly report task' // Short description for listing
|
|
908
|
+
});
|
|
909
|
+
// Returns: { taskId: 'task_123...' }
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
**List scheduled tasks:**
|
|
913
|
+
```javascript
|
|
914
|
+
const tasks = await openkbs.kb({ action: 'getScheduledTasks' });
|
|
915
|
+
// Returns: { items: [{ timestamp, taskPayload, description, status }] }
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
**Delete a scheduled task:**
|
|
919
|
+
```javascript
|
|
920
|
+
await openkbs.kb({
|
|
921
|
+
action: 'deleteScheduledTask',
|
|
922
|
+
timestamp: 1704067200000 // The scheduledTime of the task to delete
|
|
923
|
+
});
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
**How it works:**
|
|
927
|
+
1. Task is stored with the specified `scheduledTime`
|
|
928
|
+
2. At the scheduled time, system creates a new chat with `taskPayload.message` as the initial message
|
|
929
|
+
3. Your agent's `onRequest`/`onResponse` handlers process the message normally
|
|
930
|
+
4. Use `[SCHEDULED_TASK]` prefix in message to help your agent identify scheduled tasks
|
|
931
|
+
|
|
932
|
+
* **`openkbs.createEmbeddings(input, model)`:** Create embeddings from input text.
|
|
933
|
+
- `model`: `"text-embedding-3-large"` (default) or other OpenAI embedding models
|
|
934
|
+
- Returns: `{ embeddings, totalTokens, dimension }`
|
|
935
|
+
|
|
936
|
+
* **`openkbs.parseJSONFromText(text)`:** Utility to extract JSON object from text. Returns parsed object or null.
|
|
937
|
+
|
|
938
|
+
* **`openkbs.textToImage(prompt, params)`:** (Legacy) Generates image using Stability AI. Use `generateImage` for newer models.
|
|
939
|
+
- `params.serviceId`: `"stability.sd35Large"` (default) or `"stability.sd3Medium"`
|
|
940
|
+
- Returns: `{ ContentType, base64Data }`
|
|
642
941
|
|
|
643
942
|
**Example SDK Usage:**
|
|
644
943
|
|
|
645
944
|
```javascript
|
|
646
|
-
//
|
|
647
|
-
const
|
|
648
|
-
|
|
945
|
+
// Generate an image with Gemini
|
|
946
|
+
const images = await openkbs.generateImage('a sunset over mountains', {
|
|
947
|
+
model: 'gemini-2.5-flash-image',
|
|
948
|
+
aspect_ratio: '16:9'
|
|
949
|
+
});
|
|
950
|
+
const base64 = images[0].b64_json;
|
|
649
951
|
|
|
952
|
+
// Upload the generated image
|
|
953
|
+
const result = await openkbs.uploadImage(base64, 'sunset.png', 'image/png');
|
|
954
|
+
console.log(result.url);
|
|
650
955
|
|
|
651
|
-
//
|
|
652
|
-
const
|
|
956
|
+
// Generate a video with Sora 2
|
|
957
|
+
const video = await openkbs.generateVideo('a cat playing with yarn', {
|
|
958
|
+
video_model: 'sora-2',
|
|
959
|
+
seconds: 8,
|
|
960
|
+
size: '1280x720'
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
// Check video status if pending
|
|
964
|
+
if (video[0]?.status === 'pending') {
|
|
965
|
+
const status = await openkbs.checkVideoStatus(video[0].video_id);
|
|
966
|
+
if (status[0]?.video_url) {
|
|
967
|
+
console.log('Video ready:', status[0].video_url);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// Get exchange rates for a date range
|
|
972
|
+
const rates = await openkbs.getExchangeRates({
|
|
973
|
+
base: 'EUR',
|
|
974
|
+
symbols: 'USD,GBP,JPY',
|
|
975
|
+
period: '2024-01-01..2024-01-31'
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
// Item CRUD operations
|
|
979
|
+
await openkbs.createItem({
|
|
980
|
+
itemType: 'memory',
|
|
981
|
+
itemId: 'memory_user_settings',
|
|
982
|
+
body: { theme: 'dark', notifications: true }
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
const item = await openkbs.getItem('memory_user_settings');
|
|
986
|
+
console.log(item.item.body); // { theme: 'dark', notifications: true }
|
|
987
|
+
|
|
988
|
+
await openkbs.updateItem({
|
|
989
|
+
itemType: 'memory',
|
|
990
|
+
itemId: 'memory_user_settings',
|
|
991
|
+
body: { theme: 'light', notifications: false }
|
|
992
|
+
});
|
|
653
993
|
|
|
994
|
+
// Fetch multiple items
|
|
995
|
+
const items = await openkbs.fetchItems({
|
|
996
|
+
itemType: 'memory',
|
|
997
|
+
beginsWith: 'memory_',
|
|
998
|
+
limit: 100
|
|
999
|
+
});
|
|
1000
|
+
|
|
1001
|
+
// Schedule a task
|
|
1002
|
+
await openkbs.kb({
|
|
1003
|
+
action: 'createScheduledTask',
|
|
1004
|
+
scheduledTime: Date.now() + 3600000, // 1 hour from now
|
|
1005
|
+
taskPayload: { message: 'Reminder to check analytics' },
|
|
1006
|
+
description: 'Analytics reminder'
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
// Create embeddings
|
|
1010
|
+
const { embeddings, totalTokens } = await openkbs.createEmbeddings(
|
|
1011
|
+
'Text to embed',
|
|
1012
|
+
'text-embedding-3-large'
|
|
1013
|
+
);
|
|
1014
|
+
|
|
1015
|
+
// Access client info
|
|
1016
|
+
const clientIP = openkbs.clientHeaders['x-forwarded-for'];
|
|
1017
|
+
const userAgent = openkbs.clientHeaders['user-agent'];
|
|
1018
|
+
|
|
1019
|
+
// Encrypt/decrypt data
|
|
1020
|
+
const encryptedValue = await openkbs.encrypt(JSON.stringify(userData));
|
|
1021
|
+
const decryptedValue = JSON.parse(await openkbs.decrypt(encryptedValue));
|
|
1022
|
+
|
|
1023
|
+
// Parse JSON from LLM response
|
|
1024
|
+
const jsonData = openkbs.parseJSONFromText('Some text {"key": "value"} more text');
|
|
1025
|
+
// Returns: { key: "value" }
|
|
654
1026
|
```
|
|
655
1027
|
|
|
656
1028
|
#### Managing Secrets
|
|
@@ -686,36 +1058,220 @@ This file contains essential configuration settings for the AI agent.
|
|
|
686
1058
|
}
|
|
687
1059
|
```
|
|
688
1060
|
|
|
1061
|
+
#### Message Types and Content Formats
|
|
1062
|
+
|
|
1063
|
+
OpenKBS supports multiple message content types for rich interactions with AI models. Messages can be sent as plain text or as structured content arrays.
|
|
1064
|
+
|
|
1065
|
+
##### Plain Text Messages
|
|
1066
|
+
|
|
1067
|
+
The simplest format - just a string:
|
|
1068
|
+
|
|
1069
|
+
```javascript
|
|
1070
|
+
{
|
|
1071
|
+
"role": "user",
|
|
1072
|
+
"content": "Hello, how are you?"
|
|
1073
|
+
}
|
|
1074
|
+
```
|
|
1075
|
+
|
|
1076
|
+
##### Structured Content Arrays
|
|
1077
|
+
|
|
1078
|
+
For rich content (images, documents, videos), use a JSON array as the content:
|
|
1079
|
+
|
|
1080
|
+
```javascript
|
|
1081
|
+
{
|
|
1082
|
+
role: "user",
|
|
1083
|
+
content: JSON.stringify([
|
|
1084
|
+
{ type: "text", text: "Analyze this:" },
|
|
1085
|
+
{ type: "image_url", image_url: { url: "https://example.com/image.jpg" } }
|
|
1086
|
+
])
|
|
1087
|
+
}
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
##### Supported Content Types
|
|
1091
|
+
|
|
1092
|
+
**text** - Plain text content
|
|
1093
|
+
```javascript
|
|
1094
|
+
{ type: "text", text: "Your message here" }
|
|
1095
|
+
```
|
|
1096
|
+
|
|
1097
|
+
**image_url** - Images (JPEG, PNG, GIF, WebP, SVG) and PDFs
|
|
1098
|
+
```javascript
|
|
1099
|
+
{ type: "image_url", image_url: { url: "https://example.com/photo.jpg" } }
|
|
1100
|
+
{ type: "image_url", image_url: { url: "https://example.com/document.pdf" } }
|
|
1101
|
+
```
|
|
1102
|
+
|
|
1103
|
+
**file_url** - Video files (Gemini models only)
|
|
1104
|
+
```javascript
|
|
1105
|
+
{ type: "file_url", file_url: { url: "https://example.com/video.mp4", mimeType: "video/mp4" } }
|
|
1106
|
+
{ type: "file_url", file_url: { url: "https://youtube.com/watch?v=VIDEO_ID" } }
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
##### Content Type Examples
|
|
1110
|
+
|
|
1111
|
+
**Analyze an image:**
|
|
1112
|
+
```javascript
|
|
1113
|
+
content: JSON.stringify([
|
|
1114
|
+
{ type: "text", text: "What's in this image?" },
|
|
1115
|
+
{ type: "image_url", image_url: { url: "https://example.com/photo.jpg" } }
|
|
1116
|
+
])
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
**Compare multiple images:**
|
|
1120
|
+
```javascript
|
|
1121
|
+
content: JSON.stringify([
|
|
1122
|
+
{ type: "text", text: "Compare these:" },
|
|
1123
|
+
{ type: "image_url", image_url: { url: "https://example.com/image1.jpg" } },
|
|
1124
|
+
{ type: "image_url", image_url: { url: "https://example.com/image2.jpg" } }
|
|
1125
|
+
])
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
**Analyze a video (Gemini only):**
|
|
1129
|
+
```javascript
|
|
1130
|
+
content: JSON.stringify([
|
|
1131
|
+
{ type: "text", text: "Describe this video:" },
|
|
1132
|
+
{ type: "file_url", file_url: { url: "https://example.com/video.mp4", mimeType: "video/mp4" } }
|
|
1133
|
+
])
|
|
1134
|
+
```
|
|
1135
|
+
|
|
1136
|
+
#### Supported AI Models
|
|
1137
|
+
|
|
1138
|
+
OpenKBS provides a unified API for multiple AI providers. Configure your model in `app/settings.json`:
|
|
1139
|
+
|
|
1140
|
+
```json
|
|
1141
|
+
{
|
|
1142
|
+
"model": "claude-sonnet-4-5-20250929"
|
|
1143
|
+
}
|
|
1144
|
+
```
|
|
1145
|
+
|
|
1146
|
+
##### Model Capabilities
|
|
1147
|
+
**Anthropic Claude** (Vision, PDF)
|
|
1148
|
+
**OpenAI GPT** (Vision, PDF, Video)
|
|
1149
|
+
**Google Gemini** (Vision, PDF, Video)
|
|
1150
|
+
**Other Models** (Text only)
|
|
1151
|
+
|
|
1152
|
+
##### Usage in Actions
|
|
1153
|
+
|
|
1154
|
+
When building custom actions that return content to the LLM, use the same content type format:
|
|
1155
|
+
|
|
1156
|
+
```javascript
|
|
1157
|
+
// In actions.js - returning an image for analysis
|
|
1158
|
+
[/<viewImage>([\s\S]*?)<\/viewImage>/s, async (match) => {
|
|
1159
|
+
const data = JSON.parse(match[1].trim());
|
|
1160
|
+
return {
|
|
1161
|
+
data: [
|
|
1162
|
+
{ type: "text", text: `Viewing image: ${data.url}` },
|
|
1163
|
+
{ type: "image_url", image_url: { url: data.url } }
|
|
1164
|
+
],
|
|
1165
|
+
_meta_actions: ["REQUEST_CHAT_MODEL"]
|
|
1166
|
+
};
|
|
1167
|
+
}]
|
|
1168
|
+
|
|
1169
|
+
// Returning a video for analysis (Gemini only)
|
|
1170
|
+
[/<viewVideo>([\s\S]*?)<\/viewVideo>/s, async (match) => {
|
|
1171
|
+
const data = JSON.parse(match[1].trim());
|
|
1172
|
+
return {
|
|
1173
|
+
data: [
|
|
1174
|
+
{ type: "text", text: `Analyzing video: ${data.url}` },
|
|
1175
|
+
{ type: "file_url", file_url: { url: data.url, mimeType: "video/mp4" } }
|
|
1176
|
+
],
|
|
1177
|
+
_meta_actions: ["REQUEST_CHAT_MODEL"]
|
|
1178
|
+
};
|
|
1179
|
+
}]
|
|
1180
|
+
```
|
|
1181
|
+
|
|
689
1182
|
#### LLM Instructions
|
|
690
1183
|
`app/instructions.txt`
|
|
691
1184
|
This file contains the instructions for the LLM, guiding its behavior and interaction with custom functionalities.
|
|
692
1185
|
Clear and specific instructions ensure the LLM effectively utilizes provided actions and commands.
|
|
693
1186
|
|
|
1187
|
+
**Command Format:**
|
|
1188
|
+
Commands use XML tags with JSON content. The LLM outputs these as regular text, and the backend parses and executes them.
|
|
1189
|
+
|
|
694
1190
|
**Example Instructions:**
|
|
695
1191
|
|
|
696
1192
|
```
|
|
697
1193
|
You are an AI assistant.
|
|
698
1194
|
|
|
699
|
-
|
|
1195
|
+
LIST OF AVAILABLE COMMANDS:
|
|
1196
|
+
To execute a command, output it as text and wait for system response.
|
|
700
1197
|
|
|
701
|
-
|
|
1198
|
+
<googleSearch>
|
|
1199
|
+
{
|
|
1200
|
+
"query": "search query"
|
|
1201
|
+
}
|
|
1202
|
+
</googleSearch>
|
|
702
1203
|
Description: """
|
|
703
|
-
Get results from Google Search API.
|
|
1204
|
+
Get results from the Google Search API.
|
|
704
1205
|
"""
|
|
705
1206
|
$InputLabel = """Let me Search in Google!"""
|
|
706
|
-
$InputValue = """Search
|
|
1207
|
+
$InputValue = """Search for latest news"""
|
|
1208
|
+
|
|
1209
|
+
<createAIImage>
|
|
1210
|
+
{
|
|
1211
|
+
"model": "gemini-2.5-flash-image",
|
|
1212
|
+
"aspect_ratio": "16:9",
|
|
1213
|
+
"prompt": "image description"
|
|
1214
|
+
}
|
|
1215
|
+
</createAIImage>
|
|
1216
|
+
Description: """
|
|
1217
|
+
Generate AI images. Models: gemini-2.5-flash-image (supports image editing, aspect ratios), gpt-image-1 (better for text).
|
|
1218
|
+
"""
|
|
1219
|
+
|
|
1220
|
+
<createAIVideo>
|
|
1221
|
+
{
|
|
1222
|
+
"model": "sora-2",
|
|
1223
|
+
"size": "1280x720",
|
|
1224
|
+
"seconds": 8,
|
|
1225
|
+
"prompt": "video description"
|
|
1226
|
+
}
|
|
1227
|
+
</createAIVideo>
|
|
1228
|
+
Description: """
|
|
1229
|
+
Generate AI videos with Sora 2. Duration: 4, 8, or 12 seconds.
|
|
1230
|
+
"""
|
|
707
1231
|
|
|
708
|
-
|
|
1232
|
+
<getExchangeRates>
|
|
1233
|
+
{
|
|
1234
|
+
"base": "EUR",
|
|
1235
|
+
"symbols": "USD,GBP",
|
|
1236
|
+
"period": "latest"
|
|
1237
|
+
}
|
|
1238
|
+
</getExchangeRates>
|
|
1239
|
+
Description: """
|
|
1240
|
+
Get exchange rates. Period: 'latest', 'YYYY-MM-DD' (historical), or 'YYYY-MM-DD..YYYY-MM-DD' (time series).
|
|
1241
|
+
"""
|
|
1242
|
+
|
|
1243
|
+
<scheduleTask>
|
|
1244
|
+
{
|
|
1245
|
+
"delay": "2h",
|
|
1246
|
+
"message": "Task description"
|
|
1247
|
+
}
|
|
1248
|
+
</scheduleTask>
|
|
1249
|
+
Description: """
|
|
1250
|
+
Schedule a future task. Use delay (minutes, "2h", "1d") or specific time (UTC).
|
|
1251
|
+
"""
|
|
1252
|
+
|
|
1253
|
+
<getScheduledTasks/>
|
|
1254
|
+
Description: """
|
|
1255
|
+
List all scheduled tasks.
|
|
1256
|
+
"""
|
|
1257
|
+
|
|
1258
|
+
<viewImage>
|
|
1259
|
+
{
|
|
1260
|
+
"url": "https://example.com/image.jpg"
|
|
1261
|
+
}
|
|
1262
|
+
</viewImage>
|
|
1263
|
+
Description: """
|
|
1264
|
+
Add an image to the vision context. Use when you have an image URL that needs to be analyzed.
|
|
1265
|
+
The image will be added to the conversation for visual analysis.
|
|
1266
|
+
"""
|
|
709
1267
|
|
|
710
1268
|
$Comment = """
|
|
711
1269
|
Any instructions or comments placed here will be removed before sending to the LLM.
|
|
712
1270
|
These can span multiple lines and contain any characters, code, or formatting.
|
|
713
1271
|
"""
|
|
714
|
-
...
|
|
715
|
-
|
|
716
1272
|
```
|
|
717
1273
|
|
|
718
|
-
Command definitions may include \$InputLabel and \$InputValue which are
|
|
1274
|
+
Command definitions may include \$InputLabel and \$InputValue which are invisible to the LLM:
|
|
719
1275
|
|
|
720
1276
|
`$InputLabel` - Text displayed as a selectable option in the chat interface.
|
|
721
1277
|
|
|
@@ -723,7 +1279,7 @@ Command definitions may include \$InputLabel and \$InputValue which are invisiab
|
|
|
723
1279
|
|
|
724
1280
|
These features provide quick command access and pre-populate inputs, enhancing user interaction.
|
|
725
1281
|
|
|
726
|
-
`zipMessages` and
|
|
1282
|
+
`zipMessages` and `unzipMessages` command instructions allow the LLM to manage the chat size by summarizing or restoring portions of the conversation, optimizing context retention and token usage.
|
|
727
1283
|
|
|
728
1284
|
Instructions:
|
|
729
1285
|
```
|
|
@@ -738,12 +1294,41 @@ Include message IDs with optional summaries to maintain context while using fewe
|
|
|
738
1294
|
```
|
|
739
1295
|
/unzipMessages([{ "MSG_ID" : 1234567890123 }])
|
|
740
1296
|
Description: """
|
|
741
|
-
Uncompresses the message associated with the provided `MSG_ID`, restoring its original content to the chat.
|
|
1297
|
+
Uncompresses the message associated with the provided `MSG_ID`, restoring its original content to the chat.
|
|
742
1298
|
"""
|
|
743
1299
|
```
|
|
744
1300
|
|
|
745
1301
|
**Important:** `MSG_ID` value must be a unique identifier present anywhere in the message content. The OpenKBS platform automatically handles the zipping and unzipping of messages based on these commands. No custom implementation is required.
|
|
746
1302
|
|
|
1303
|
+
#### Instruction Variables
|
|
1304
|
+
|
|
1305
|
+
OpenKBS provides dynamic variables that can be used in `app/instructions.txt` to inject runtime information:
|
|
1306
|
+
|
|
1307
|
+
- `{{kbId}}` - Current Knowledge Base ID → `abc123xyz`
|
|
1308
|
+
- `{{openkbsDateNow}}` - Current UTC time (ISO format) → `2025-01-15T14:30:00.000Z`
|
|
1309
|
+
- `{{openkbsTimestamp}}` - Current Unix timestamp (ms) → `1736948200000`
|
|
1310
|
+
- `{{openkbsDate:locale:timezone}}` - Formatted date with locale and timezone → `15/01/2025, 16:30:00`
|
|
1311
|
+
- `{{openkbsDateTZ:timezone}}` - Formatted date with timezone (en-GB locale) → `15/01/2025, 16:30:00`
|
|
1312
|
+
|
|
1313
|
+
**Example usage in instructions.txt:**
|
|
1314
|
+
|
|
1315
|
+
```
|
|
1316
|
+
You are a helpful assistant.
|
|
1317
|
+
|
|
1318
|
+
## Current Time Information
|
|
1319
|
+
- UTC Time: {{openkbsDateNow}}
|
|
1320
|
+
- Local Time (Bulgaria): {{openkbsDate:bg-BG:Europe/Sofia}}
|
|
1321
|
+
- US Eastern Time: {{openkbsDate:en-US:America/New_York}}
|
|
1322
|
+
- Unix Timestamp: {{openkbsTimestamp}}
|
|
1323
|
+
- Agent ID: {{kbId}}
|
|
1324
|
+
|
|
1325
|
+
Use the current time to provide time-aware responses.
|
|
1326
|
+
```
|
|
1327
|
+
|
|
1328
|
+
**Locale examples:** `en-US`, `en-GB`, `bg-BG`, `de-DE`, `fr-FR`, `ja-JP`
|
|
1329
|
+
|
|
1330
|
+
**Timezone examples:** `UTC`, `Europe/Sofia`, `America/New_York`, `Asia/Tokyo`, `Europe/London`
|
|
1331
|
+
|
|
747
1332
|
#### Execution Environment
|
|
748
1333
|
|
|
749
1334
|
The OpenKBS backend provides a pre-configured execution environment for your event handlers, including a set of globally available objects and libraries. This eliminates the need to explicitly declare these as dependencies in your `onRequest.json` or `onResponse.json` files. These predefined resources facilitate various operations:
|
|
@@ -768,6 +1353,53 @@ Here's a breakdown of the key objects and utilities available within the OpenKBS
|
|
|
768
1353
|
|
|
769
1354
|
* **`JSON5`:** A more permissive JSON parser that supports comments, trailing commas, single quotes, and other convenient features not found in standard JSON. Useful for parsing configuration files or user input.
|
|
770
1355
|
|
|
1356
|
+
#### MCP Integration
|
|
1357
|
+
|
|
1358
|
+
Connect your agent to external tools using [Model Context Protocol (MCP)](https://modelcontextprotocol.io/).
|
|
1359
|
+
|
|
1360
|
+
**Configure in `app/settings.json`:**
|
|
1361
|
+
```json
|
|
1362
|
+
{
|
|
1363
|
+
"options": {
|
|
1364
|
+
"mcpServers": {
|
|
1365
|
+
"brave-search": {},
|
|
1366
|
+
"github": {},
|
|
1367
|
+
"slack": {}
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
```
|
|
1372
|
+
|
|
1373
|
+
**Add required secrets** (Settings → Secrets):
|
|
1374
|
+
- `brave-search`: `BRAVE_API_KEY`
|
|
1375
|
+
- `github`: `GITHUB_PERSONAL_ACCESS_TOKEN`
|
|
1376
|
+
- `slack`: `SLACK_BOT_TOKEN`, `SLACK_TEAM_ID`
|
|
1377
|
+
|
|
1378
|
+
**How it works:**
|
|
1379
|
+
1. Tools auto-discovered and injected into system prompt
|
|
1380
|
+
2. LLM outputs: `<mcp_brave-search_brave_web_search>{"query": "..."}</mcp_brave-search_brave_web_search>`
|
|
1381
|
+
3. Handler in `actions.js`:
|
|
1382
|
+
|
|
1383
|
+
```javascript
|
|
1384
|
+
[/<mcp_([a-z0-9-]+)_([a-z0-9_]+)>([\s\S]*?)<\/mcp_\1_\2>/s, async (match) => {
|
|
1385
|
+
const [, server, tool, argsJson] = match;
|
|
1386
|
+
const result = await openkbs.mcp.callTool(server, tool, JSON.parse(argsJson || '{}'));
|
|
1387
|
+
return { data: result?.content || [], _meta_actions: ['REQUEST_CHAT_MODEL'] };
|
|
1388
|
+
}]
|
|
1389
|
+
```
|
|
1390
|
+
|
|
1391
|
+
**Custom MCP Server:**
|
|
1392
|
+
```json
|
|
1393
|
+
{
|
|
1394
|
+
"options": {
|
|
1395
|
+
"mcpServerUrl": "https://your-mcp-server.com",
|
|
1396
|
+
"mcpServers": { "your-server": {} }
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
```
|
|
1400
|
+
|
|
1401
|
+
Full tutorial: [MCP Integration Tutorial](https://openkbs.com/tutorials/mcp-integration/)
|
|
1402
|
+
|
|
771
1403
|
### Frontend
|
|
772
1404
|
|
|
773
1405
|
The OpenKBS frontend framework is built using React and provides a flexible and extensible platform for building custom chat interfaces. It allows developers to customize the appearance and behavior of the chat through a `contentRender` module, which can be dynamically loaded and used to extend the core platform.
|
|
@@ -781,70 +1413,233 @@ The frontend framework dynamically loads the `contentRender.js` module. This mod
|
|
|
781
1413
|
|
|
782
1414
|
The `contentRender.js` file is the heart of frontend customization. It can export several key functions:
|
|
783
1415
|
|
|
784
|
-
- **`onRenderChatMessage(params)`:** This function is called every time a chat message is rendered. It receives an object with various parameters
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
1416
|
+
- **`onRenderChatMessage(params)`:** This function is called every time a chat message is rendered. It receives an object with various parameters:
|
|
1417
|
+
|
|
1418
|
+
**Message Context:**
|
|
1419
|
+
- `msgIndex` - Index of the message being rendered
|
|
1420
|
+
- `messages` - Entire array of chat messages
|
|
1421
|
+
- `setMessages` - Function to update the messages state
|
|
1422
|
+
- `KB` - Knowledge Base object containing application settings
|
|
1423
|
+
- `kbUserData` - Function returning KB user data (chatUsername, etc.)
|
|
1424
|
+
|
|
1425
|
+
**UI Controls:**
|
|
1426
|
+
- `setSystemAlert` - Display system alerts: `setSystemAlert({ severity: 'success', message: 'Done!' })`
|
|
1427
|
+
- `setBlockingLoading` - Show/hide loading indicator: `setBlockingLoading(true)`
|
|
1428
|
+
- `blockingLoading` - Boolean indicating if loading indicator is active
|
|
1429
|
+
- `setInputValue` - Set the chat input field value
|
|
1430
|
+
- `blockAutoscroll` - Control auto-scrolling behavior
|
|
1431
|
+
|
|
1432
|
+
**API & Communication:**
|
|
1433
|
+
- `RequestChatAPI` - Send message to chat API: `await RequestChatAPI([...messages, newMsg])`
|
|
1434
|
+
- `axios` - Axios library for HTTP requests
|
|
1435
|
+
- `chatAPI` - API to access chat data
|
|
1436
|
+
- `itemsAPI` - Functions for manipulating KB items
|
|
1437
|
+
|
|
1438
|
+
**DOM References:**
|
|
1439
|
+
- `iframeRef` - Reference to iframe element
|
|
1440
|
+
- `chatContainerRef` - Reference to chat container element
|
|
1441
|
+
- `sendButtonRef` - Reference to send button element
|
|
1442
|
+
- `sendButtonRippleRef` - Reference to send button ripple effect
|
|
1443
|
+
- `newChatButtonRef` - Reference to new chat button
|
|
1444
|
+
|
|
1445
|
+
**Utilities & Libraries:**
|
|
1446
|
+
- `generateMsgId` - Generates unique message ID
|
|
1447
|
+
- `markdownHandler` - Markdown rendering utilities
|
|
1448
|
+
- `createEmbeddingItem` - Functions to create embeddings
|
|
1449
|
+
- `executeNodejs` - Execute custom JavaScript code inside a VM
|
|
1450
|
+
- `initDB` - Initialize IndexedDB
|
|
1451
|
+
- `indexedDB` - IndexedDB wrapper for local data access
|
|
1452
|
+
- `Files` - File management utilities (same as `openkbs.Files`)
|
|
1453
|
+
- `uploadFileAPI` - Upload files to storage
|
|
1454
|
+
|
|
1455
|
+
**Rendering Libraries (pre-loaded):**
|
|
1456
|
+
- `theme` - MUI theme object
|
|
1457
|
+
- `ReactPrismjs` - Syntax highlighting component
|
|
1458
|
+
- `CopyToClipboard` - Clipboard copy component
|
|
1459
|
+
- `APIResponseComponent` - Standard API response renderer
|
|
1460
|
+
- `CodeViewer` - Code display component
|
|
1461
|
+
- `textDetectionsImageFiles` - OCR detection results
|
|
1462
|
+
|
|
1463
|
+
**Return Value:** Return a React component to render the message, or `null` to use default rendering. Return `JSON.stringify({ type: 'HIDDEN_MESSAGE' })` to hide a message completely.
|
|
1464
|
+
|
|
1465
|
+
**Example Usage:**
|
|
1466
|
+
```javascript
|
|
1467
|
+
const onRenderChatMessage = async (params) => {
|
|
1468
|
+
const { content, role } = params.messages[params.msgIndex];
|
|
1469
|
+
const { msgIndex, messages, setSystemAlert, RequestChatAPI,
|
|
1470
|
+
kbUserData, generateMsgId, markdownHandler } = params;
|
|
1471
|
+
|
|
1472
|
+
// Hide system messages
|
|
1473
|
+
if (role === 'system') {
|
|
1474
|
+
return JSON.stringify({ type: 'HIDDEN_MESSAGE' });
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// Custom rendering for specific content
|
|
1478
|
+
if (content.includes('<myCommand>')) {
|
|
1479
|
+
return <MyCustomComponent content={content} />;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
// Send a follow-up message
|
|
1483
|
+
const sendFollowUp = async () => {
|
|
1484
|
+
await RequestChatAPI([...messages, {
|
|
1485
|
+
role: 'user',
|
|
1486
|
+
content: 'Follow up message',
|
|
1487
|
+
userId: kbUserData().chatUsername,
|
|
1488
|
+
msgId: generateMsgId()
|
|
1489
|
+
}]);
|
|
1490
|
+
};
|
|
1491
|
+
|
|
1492
|
+
return null; // Use default rendering
|
|
1493
|
+
};
|
|
1494
|
+
```
|
|
809
1495
|
|
|
810
1496
|
- **`Header(props)`:** This React component is rendered at the top of the chat interface. It receives the same `params` object as `onRenderChatMessage`. It can be used to add custom UI elements or controls to the chat header. If not defined, the standard OpenKBS chat header is displayed.
|
|
811
1497
|
|
|
812
1498
|
- **`onDeleteChatMessage(params)`:** This async function is triggered when a chat message is deleted. This function receives a `params` object similar to the `onRenderChatMessage` function but also includes `chatId`, `message` (the message being deleted), and can be used to perform cleanup actions related to custom rendered content. If not defined, a default delete message function is executed.
|
|
813
1499
|
|
|
814
|
-
**Example `contentRender.js
|
|
1500
|
+
**Example `contentRender.js` with Command Rendering:**
|
|
815
1501
|
|
|
816
1502
|
```javascript
|
|
817
1503
|
import React from 'react';
|
|
1504
|
+
import { Box, Tooltip, Typography, Zoom } from '@mui/material';
|
|
1505
|
+
import SearchIcon from '@mui/icons-material/Search';
|
|
1506
|
+
import ImageIcon from '@mui/icons-material/Image';
|
|
1507
|
+
import VideoLibraryIcon from '@mui/icons-material/VideoLibrary';
|
|
1508
|
+
|
|
1509
|
+
// Define command patterns to detect
|
|
1510
|
+
const COMMAND_PATTERNS = [
|
|
1511
|
+
/<createAIImage>[\s\S]*?<\/createAIImage>/,
|
|
1512
|
+
/<createAIVideo>[\s\S]*?<\/createAIVideo>/,
|
|
1513
|
+
/<googleSearch>[\s\S]*?<\/googleSearch>/
|
|
1514
|
+
];
|
|
818
1515
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
1516
|
+
// Icon mapping for commands
|
|
1517
|
+
const commandIcons = {
|
|
1518
|
+
createAIImage: ImageIcon,
|
|
1519
|
+
createAIVideo: VideoLibraryIcon,
|
|
1520
|
+
googleSearch: SearchIcon
|
|
1521
|
+
};
|
|
1522
|
+
|
|
1523
|
+
// Parse commands from content
|
|
1524
|
+
const parseCommands = (content) => {
|
|
1525
|
+
const commands = [];
|
|
1526
|
+
const regex = /<(\w+)>([\s\S]*?)<\/\1>/g;
|
|
1527
|
+
let match;
|
|
1528
|
+
while ((match = regex.exec(content)) !== null) {
|
|
1529
|
+
try {
|
|
1530
|
+
commands.push({
|
|
1531
|
+
name: match[1],
|
|
1532
|
+
data: JSON.parse(match[2].trim())
|
|
1533
|
+
});
|
|
1534
|
+
} catch (e) {}
|
|
828
1535
|
}
|
|
829
|
-
|
|
1536
|
+
return commands;
|
|
830
1537
|
};
|
|
831
1538
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
1539
|
+
// Render command as icon with tooltip
|
|
1540
|
+
const CommandIcon = ({ command }) => {
|
|
1541
|
+
const Icon = commandIcons[command.name] || SearchIcon;
|
|
1542
|
+
return (
|
|
1543
|
+
<Tooltip title={<pre>{JSON.stringify(command.data, null, 2)}</pre>} arrow>
|
|
1544
|
+
<Box sx={{
|
|
1545
|
+
display: 'inline-flex',
|
|
1546
|
+
width: 32, height: 32,
|
|
1547
|
+
borderRadius: '50%',
|
|
1548
|
+
backgroundColor: 'rgba(76, 175, 80, 0.1)',
|
|
1549
|
+
border: '2px solid rgba(76, 175, 80, 0.3)',
|
|
1550
|
+
alignItems: 'center',
|
|
1551
|
+
justifyContent: 'center',
|
|
1552
|
+
mx: 0.5
|
|
1553
|
+
}}>
|
|
1554
|
+
<Icon sx={{ fontSize: 16, color: '#4CAF50' }} />
|
|
1555
|
+
</Box>
|
|
1556
|
+
</Tooltip>
|
|
1557
|
+
);
|
|
839
1558
|
};
|
|
840
1559
|
|
|
841
|
-
const
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
1560
|
+
const onRenderChatMessage = async (params) => {
|
|
1561
|
+
const { content, role } = params.messages[params.msgIndex];
|
|
1562
|
+
|
|
1563
|
+
// Check for commands in content
|
|
1564
|
+
const hasCommand = COMMAND_PATTERNS.some(p => p.test(content));
|
|
1565
|
+
if (hasCommand) {
|
|
1566
|
+
const commands = parseCommands(content);
|
|
1567
|
+
return (
|
|
1568
|
+
<Box>
|
|
1569
|
+
{commands.map((cmd, i) => <CommandIcon key={i} command={cmd} />)}
|
|
1570
|
+
</Box>
|
|
1571
|
+
);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
return null; // Use default rendering
|
|
845
1575
|
};
|
|
846
1576
|
|
|
847
|
-
const
|
|
1577
|
+
const Header = ({ setRenderSettings, openkbs, setSystemAlert }) => {
|
|
1578
|
+
const [panelOpen, setPanelOpen] = React.useState(false);
|
|
1579
|
+
|
|
1580
|
+
React.useEffect(() => {
|
|
1581
|
+
setRenderSettings({
|
|
1582
|
+
disableBalanceView: false,
|
|
1583
|
+
disableEmojiButton: true,
|
|
1584
|
+
backgroundOpacity: 0.02
|
|
1585
|
+
});
|
|
1586
|
+
}, [setRenderSettings]);
|
|
1587
|
+
|
|
1588
|
+
return (
|
|
1589
|
+
<>
|
|
1590
|
+
{/* Settings button */}
|
|
1591
|
+
<Box
|
|
1592
|
+
onClick={() => setPanelOpen(true)}
|
|
1593
|
+
sx={{
|
|
1594
|
+
position: 'absolute',
|
|
1595
|
+
top: 90, left: 340,
|
|
1596
|
+
width: 40, height: 40,
|
|
1597
|
+
borderRadius: '50%',
|
|
1598
|
+
backgroundColor: 'white',
|
|
1599
|
+
border: '1px solid #e0e0e0',
|
|
1600
|
+
display: 'flex',
|
|
1601
|
+
alignItems: 'center',
|
|
1602
|
+
justifyContent: 'center',
|
|
1603
|
+
cursor: 'pointer',
|
|
1604
|
+
zIndex: 1200
|
|
1605
|
+
}}
|
|
1606
|
+
>
|
|
1607
|
+
⚙️
|
|
1608
|
+
</Box>
|
|
1609
|
+
|
|
1610
|
+
{/* Simple settings panel */}
|
|
1611
|
+
{panelOpen && (
|
|
1612
|
+
<Box
|
|
1613
|
+
onClick={() => setPanelOpen(false)}
|
|
1614
|
+
sx={{
|
|
1615
|
+
position: 'fixed',
|
|
1616
|
+
inset: 0,
|
|
1617
|
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
1618
|
+
zIndex: 1300,
|
|
1619
|
+
display: 'flex',
|
|
1620
|
+
alignItems: 'center',
|
|
1621
|
+
justifyContent: 'center'
|
|
1622
|
+
}}
|
|
1623
|
+
>
|
|
1624
|
+
<Box
|
|
1625
|
+
onClick={e => e.stopPropagation()}
|
|
1626
|
+
sx={{
|
|
1627
|
+
width: 400,
|
|
1628
|
+
backgroundColor: 'white',
|
|
1629
|
+
borderRadius: 2,
|
|
1630
|
+
p: 3
|
|
1631
|
+
}}
|
|
1632
|
+
>
|
|
1633
|
+
<Typography variant="h6">Settings</Typography>
|
|
1634
|
+
<Typography>Your custom admin panel here</Typography>
|
|
1635
|
+
</Box>
|
|
1636
|
+
</Box>
|
|
1637
|
+
)}
|
|
1638
|
+
</>
|
|
1639
|
+
);
|
|
1640
|
+
};
|
|
1641
|
+
|
|
1642
|
+
const exports = { onRenderChatMessage, Header };
|
|
848
1643
|
window.contentRender = exports;
|
|
849
1644
|
export default exports;
|
|
850
1645
|
```
|
|
@@ -1043,6 +1838,499 @@ const onRenderChatMessage = async (params) => {
|
|
|
1043
1838
|
};
|
|
1044
1839
|
```
|
|
1045
1840
|
|
|
1841
|
+
#### Header Component Props
|
|
1842
|
+
|
|
1843
|
+
The `Header` component receives all available props from the OpenKBS chat interface. These props enable full control over chat behavior, UI state, and data access.
|
|
1844
|
+
|
|
1845
|
+
**All Available Props:**
|
|
1846
|
+
|
|
1847
|
+
- `messages` (Array) - Current chat messages array
|
|
1848
|
+
- `setMessages` (Function) - Update messages array (e.g., add welcome message)
|
|
1849
|
+
- `KB` (Object) - Current Knowledge Base data
|
|
1850
|
+
- `openkbs` (Object) - OpenKBS SDK object (see Frontend SDK section)
|
|
1851
|
+
- `setRenderSettings` (Function) - Configure UI rendering options
|
|
1852
|
+
- `setSystemAlert` (Function) - Show alert notifications
|
|
1853
|
+
- `setBlockingLoading` (Function) - Show/hide full-screen loading overlay
|
|
1854
|
+
- `RequestChatAPI` (Function) - Send messages to chat API
|
|
1855
|
+
- `chatAPI` (Object) - Chat API utilities
|
|
1856
|
+
- `itemsAPI` (Object) - Items API (low-level)
|
|
1857
|
+
- `Files` (Object) - Files API module
|
|
1858
|
+
- `indexedDB` (Object) - IndexedDB interface for local storage
|
|
1859
|
+
- `initDB` (Function) - Initialize IndexedDB
|
|
1860
|
+
- `kbUserData` (Function) - Get current user data
|
|
1861
|
+
- `navigateToChat` (Function) - Navigate to different chat
|
|
1862
|
+
- `setIsContextItemsOpen` (Function) - Toggle context items panel
|
|
1863
|
+
- `isContextItemsOpen` (Boolean) - Context items panel state
|
|
1864
|
+
- `chatContainerRef` (Ref) - Reference to chat container element
|
|
1865
|
+
- `renderSettings` (Object) - Current render settings
|
|
1866
|
+
- `blockingLoading` (Boolean) - Loading state
|
|
1867
|
+
- `blockAutoscroll` (Boolean) - Auto-scroll state
|
|
1868
|
+
- `axios` (Object) - Axios HTTP client
|
|
1869
|
+
|
|
1870
|
+
**Common Usage Patterns:**
|
|
1871
|
+
|
|
1872
|
+
*setRenderSettings* - Configure UI behavior:
|
|
1873
|
+
```javascript
|
|
1874
|
+
const Header = ({ setRenderSettings }) => {
|
|
1875
|
+
useEffect(() => {
|
|
1876
|
+
setRenderSettings({
|
|
1877
|
+
disableBalanceView: false,
|
|
1878
|
+
disableEmojiButton: true,
|
|
1879
|
+
disableChatModelsSelect: true,
|
|
1880
|
+
disableShareButton: true,
|
|
1881
|
+
disableMultichat: true,
|
|
1882
|
+
disableMobileLeftButton: true,
|
|
1883
|
+
disableTextToSpeechButton: true,
|
|
1884
|
+
disableInitialScroll: true,
|
|
1885
|
+
backgroundOpacity: 0.02,
|
|
1886
|
+
customStreamingLoader: true,
|
|
1887
|
+
inputLabelsQuickSend: true,
|
|
1888
|
+
setMessageWidth: (content) => content.includes('<html') ? '90%' : undefined
|
|
1889
|
+
});
|
|
1890
|
+
}, [setRenderSettings]);
|
|
1891
|
+
};
|
|
1892
|
+
```
|
|
1893
|
+
|
|
1894
|
+
*setSystemAlert* - Show notifications:
|
|
1895
|
+
```javascript
|
|
1896
|
+
const Header = ({ setSystemAlert }) => {
|
|
1897
|
+
const showSuccess = () => {
|
|
1898
|
+
setSystemAlert({
|
|
1899
|
+
msg: 'Operation completed successfully',
|
|
1900
|
+
type: 'success', // 'success', 'error', 'warning', 'info'
|
|
1901
|
+
duration: 3000 // milliseconds
|
|
1902
|
+
});
|
|
1903
|
+
};
|
|
1904
|
+
|
|
1905
|
+
const showError = (error) => {
|
|
1906
|
+
setSystemAlert({
|
|
1907
|
+
msg: error.message,
|
|
1908
|
+
type: 'error',
|
|
1909
|
+
duration: 5000
|
|
1910
|
+
});
|
|
1911
|
+
};
|
|
1912
|
+
};
|
|
1913
|
+
```
|
|
1914
|
+
|
|
1915
|
+
*setBlockingLoading* - Full-screen loading overlay:
|
|
1916
|
+
```javascript
|
|
1917
|
+
const Header = ({ setBlockingLoading, openkbs }) => {
|
|
1918
|
+
const loadData = async () => {
|
|
1919
|
+
setBlockingLoading(true); // Show loading
|
|
1920
|
+
// Or with custom text:
|
|
1921
|
+
// setBlockingLoading({ text: 'Loading files...' });
|
|
1922
|
+
|
|
1923
|
+
try {
|
|
1924
|
+
const files = await openkbs.Files.listFiles('files');
|
|
1925
|
+
// Process files...
|
|
1926
|
+
} finally {
|
|
1927
|
+
setBlockingLoading(false); // Hide loading
|
|
1928
|
+
}
|
|
1929
|
+
};
|
|
1930
|
+
};
|
|
1931
|
+
```
|
|
1932
|
+
|
|
1933
|
+
*RequestChatAPI* - Send messages programmatically:
|
|
1934
|
+
```javascript
|
|
1935
|
+
const Header = ({ RequestChatAPI, messages, kbUserData, navigateToChat }) => {
|
|
1936
|
+
const sendQuickMessage = (prompt) => {
|
|
1937
|
+
navigateToChat(null); // Start new chat
|
|
1938
|
+
RequestChatAPI([{
|
|
1939
|
+
role: 'user',
|
|
1940
|
+
content: prompt,
|
|
1941
|
+
userId: kbUserData().chatUsername,
|
|
1942
|
+
msgId: `${Date.now()}-${Math.floor(100000 + Math.random() * 900000)}`
|
|
1943
|
+
}]);
|
|
1944
|
+
};
|
|
1945
|
+
|
|
1946
|
+
return (
|
|
1947
|
+
<button onClick={() => sendQuickMessage('Help me create a marketing plan')}>
|
|
1948
|
+
Quick Action
|
|
1949
|
+
</button>
|
|
1950
|
+
);
|
|
1951
|
+
};
|
|
1952
|
+
```
|
|
1953
|
+
|
|
1954
|
+
*setMessages* - Initialize welcome message:
|
|
1955
|
+
```javascript
|
|
1956
|
+
const Header = ({ messages, setMessages, openkbs }) => {
|
|
1957
|
+
useEffect(() => {
|
|
1958
|
+
const initWelcome = async () => {
|
|
1959
|
+
// Only for new chats
|
|
1960
|
+
if (!messages || messages.length === 0) {
|
|
1961
|
+
const profile = await openkbs.getItem('memory_profile');
|
|
1962
|
+
|
|
1963
|
+
if (!profile?.item) {
|
|
1964
|
+
setMessages([{
|
|
1965
|
+
msgId: `${Date.now()}-${Math.floor(100000 + Math.random() * 900000)}`,
|
|
1966
|
+
role: 'assistant',
|
|
1967
|
+
content: 'Welcome! Tell me about your business to get started.'
|
|
1968
|
+
}]);
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
};
|
|
1972
|
+
|
|
1973
|
+
initWelcome();
|
|
1974
|
+
}, [messages, setMessages, openkbs]);
|
|
1975
|
+
};
|
|
1976
|
+
```
|
|
1977
|
+
|
|
1978
|
+
*navigateToChat* - Chat navigation:
|
|
1979
|
+
```javascript
|
|
1980
|
+
const Header = ({ navigateToChat }) => {
|
|
1981
|
+
// Navigate to specific chat
|
|
1982
|
+
const goToChat = (chatId) => navigateToChat(chatId);
|
|
1983
|
+
|
|
1984
|
+
// Start new chat
|
|
1985
|
+
const newChat = () => navigateToChat(null);
|
|
1986
|
+
};
|
|
1987
|
+
```
|
|
1988
|
+
|
|
1989
|
+
**Complete Header Example:**
|
|
1990
|
+
|
|
1991
|
+
```javascript
|
|
1992
|
+
import React, { useEffect, useState } from 'react';
|
|
1993
|
+
import { IconButton, Dialog, Box, Button } from '@mui/material';
|
|
1994
|
+
import SettingsIcon from '@mui/icons-material/Settings';
|
|
1995
|
+
|
|
1996
|
+
const Header = ({
|
|
1997
|
+
setRenderSettings,
|
|
1998
|
+
openkbs,
|
|
1999
|
+
setSystemAlert,
|
|
2000
|
+
setBlockingLoading,
|
|
2001
|
+
messages,
|
|
2002
|
+
setMessages,
|
|
2003
|
+
RequestChatAPI,
|
|
2004
|
+
kbUserData,
|
|
2005
|
+
navigateToChat
|
|
2006
|
+
}) => {
|
|
2007
|
+
const [panelOpen, setPanelOpen] = useState(false);
|
|
2008
|
+
const [files, setFiles] = useState([]);
|
|
2009
|
+
|
|
2010
|
+
// Configure UI on mount
|
|
2011
|
+
useEffect(() => {
|
|
2012
|
+
setRenderSettings({
|
|
2013
|
+
disableEmojiButton: true,
|
|
2014
|
+
disableChatModelsSelect: true,
|
|
2015
|
+
backgroundOpacity: 0.02
|
|
2016
|
+
});
|
|
2017
|
+
}, [setRenderSettings]);
|
|
2018
|
+
|
|
2019
|
+
// Initialize welcome message for new chats
|
|
2020
|
+
useEffect(() => {
|
|
2021
|
+
if (!messages?.length && openkbs) {
|
|
2022
|
+
setMessages([{
|
|
2023
|
+
msgId: `${Date.now()}-000000`,
|
|
2024
|
+
role: 'assistant',
|
|
2025
|
+
content: 'Welcome! How can I help you today?'
|
|
2026
|
+
}]);
|
|
2027
|
+
}
|
|
2028
|
+
}, [messages, setMessages, openkbs]);
|
|
2029
|
+
|
|
2030
|
+
// Load files when panel opens
|
|
2031
|
+
const loadFiles = async () => {
|
|
2032
|
+
setBlockingLoading(true);
|
|
2033
|
+
try {
|
|
2034
|
+
const result = await openkbs.Files.listFiles('files');
|
|
2035
|
+
setFiles(result || []);
|
|
2036
|
+
} catch (e) {
|
|
2037
|
+
setSystemAlert({ msg: e.message, type: 'error', duration: 5000 });
|
|
2038
|
+
} finally {
|
|
2039
|
+
setBlockingLoading(false);
|
|
2040
|
+
}
|
|
2041
|
+
};
|
|
2042
|
+
|
|
2043
|
+
// Quick action handler
|
|
2044
|
+
const quickAction = (prompt) => {
|
|
2045
|
+
navigateToChat(null);
|
|
2046
|
+
RequestChatAPI([{
|
|
2047
|
+
role: 'user',
|
|
2048
|
+
content: prompt,
|
|
2049
|
+
userId: kbUserData().chatUsername,
|
|
2050
|
+
msgId: `${Date.now()}-${Math.floor(Math.random() * 900000)}`
|
|
2051
|
+
}]);
|
|
2052
|
+
};
|
|
2053
|
+
|
|
2054
|
+
return (
|
|
2055
|
+
<>
|
|
2056
|
+
<IconButton
|
|
2057
|
+
onClick={() => { setPanelOpen(true); loadFiles(); }}
|
|
2058
|
+
sx={{ position: 'absolute', top: 90, left: 340, zIndex: 1200 }}
|
|
2059
|
+
>
|
|
2060
|
+
<SettingsIcon />
|
|
2061
|
+
</IconButton>
|
|
2062
|
+
|
|
2063
|
+
<Button
|
|
2064
|
+
onClick={() => quickAction('Create a marketing plan')}
|
|
2065
|
+
sx={{ position: 'absolute', top: 90, left: 400, zIndex: 1200 }}
|
|
2066
|
+
>
|
|
2067
|
+
Quick Action
|
|
2068
|
+
</Button>
|
|
2069
|
+
|
|
2070
|
+
<Dialog open={panelOpen} onClose={() => setPanelOpen(false)}>
|
|
2071
|
+
<Box sx={{ p: 2 }}>
|
|
2072
|
+
<h3>Files ({files.length})</h3>
|
|
2073
|
+
{files.map((f, i) => <div key={i}>{f.name}</div>)}
|
|
2074
|
+
</Box>
|
|
2075
|
+
</Dialog>
|
|
2076
|
+
</>
|
|
2077
|
+
);
|
|
2078
|
+
};
|
|
2079
|
+
|
|
2080
|
+
const exports = { Header };
|
|
2081
|
+
window.contentRender = exports;
|
|
2082
|
+
export default exports;
|
|
2083
|
+
```
|
|
2084
|
+
|
|
2085
|
+
#### Frontend SDK (`openkbs` object)
|
|
2086
|
+
|
|
2087
|
+
The `openkbs` object is passed to `Header` and `onRenderChatMessage` functions, providing access to item storage, file management, and KB sharing APIs.
|
|
2088
|
+
|
|
2089
|
+
**Properties:**
|
|
2090
|
+
```javascript
|
|
2091
|
+
const Header = ({ openkbs }) => {
|
|
2092
|
+
console.log(openkbs.kbId); // Current KB ID
|
|
2093
|
+
console.log(openkbs.KBData); // Full KB configuration
|
|
2094
|
+
console.log(openkbs.Files); // Files API module
|
|
2095
|
+
console.log(openkbs.itemsAPI); // Items API module (low-level)
|
|
2096
|
+
console.log(openkbs.KBAPI); // KB API module
|
|
2097
|
+
};
|
|
2098
|
+
```
|
|
2099
|
+
|
|
2100
|
+
##### Item CRUD Operations
|
|
2101
|
+
|
|
2102
|
+
```javascript
|
|
2103
|
+
// Create an item
|
|
2104
|
+
await openkbs.createItem({
|
|
2105
|
+
itemType: 'memory',
|
|
2106
|
+
itemId: 'memory_user_preferences',
|
|
2107
|
+
body: { theme: 'dark', language: 'en' } // Auto-encrypted
|
|
2108
|
+
});
|
|
2109
|
+
|
|
2110
|
+
// Update an item
|
|
2111
|
+
await openkbs.updateItem({
|
|
2112
|
+
itemType: 'memory',
|
|
2113
|
+
itemId: 'memory_user_preferences',
|
|
2114
|
+
body: { theme: 'light', language: 'bg' }
|
|
2115
|
+
});
|
|
2116
|
+
|
|
2117
|
+
// Get a single item (auto-decrypted)
|
|
2118
|
+
const result = await openkbs.getItem('memory_user_preferences');
|
|
2119
|
+
console.log(result.item.body); // { theme: 'light', language: 'bg' }
|
|
2120
|
+
|
|
2121
|
+
// Delete an item
|
|
2122
|
+
await openkbs.deleteItem('memory_user_preferences');
|
|
2123
|
+
|
|
2124
|
+
// Fetch multiple items with filters
|
|
2125
|
+
const items = await openkbs.fetchItems({
|
|
2126
|
+
itemType: 'memory', // Filter by item type
|
|
2127
|
+
limit: 100, // Max items to return
|
|
2128
|
+
beginsWith: 'memory_', // Filter by ID prefix
|
|
2129
|
+
from: '2024-01-01', // Range start (for date fields)
|
|
2130
|
+
to: '2024-12-31', // Range end
|
|
2131
|
+
field: 'createdAt', // Field for range query
|
|
2132
|
+
});
|
|
2133
|
+
|
|
2134
|
+
// Items are returned with decrypted bodies
|
|
2135
|
+
items.items.forEach(({ item, meta }) => {
|
|
2136
|
+
console.log(meta.itemId, item.body);
|
|
2137
|
+
});
|
|
2138
|
+
```
|
|
2139
|
+
|
|
2140
|
+
##### Encryption Utilities
|
|
2141
|
+
|
|
2142
|
+
```javascript
|
|
2143
|
+
// Encrypt sensitive data
|
|
2144
|
+
const encrypted = await openkbs.encrypt('sensitive data');
|
|
2145
|
+
|
|
2146
|
+
// Decrypt data
|
|
2147
|
+
const decrypted = await openkbs.decrypt(encrypted);
|
|
2148
|
+
```
|
|
2149
|
+
|
|
2150
|
+
##### Files API (`openkbs.Files`)
|
|
2151
|
+
|
|
2152
|
+
```javascript
|
|
2153
|
+
// List files in a namespace
|
|
2154
|
+
const files = await openkbs.Files.listFiles('files');
|
|
2155
|
+
|
|
2156
|
+
// Upload a file
|
|
2157
|
+
const file = new File(['content'], 'example.txt', { type: 'text/plain' });
|
|
2158
|
+
await openkbs.Files.uploadFileAPI(file, 'files', (progress) => {
|
|
2159
|
+
console.log(`Upload progress: ${progress}%`);
|
|
2160
|
+
});
|
|
2161
|
+
|
|
2162
|
+
// Create presigned URL for upload/download
|
|
2163
|
+
const url = await openkbs.Files.createPresignedURL(
|
|
2164
|
+
'files', // namespace
|
|
2165
|
+
'putObject', // 'putObject' or 'getObject'
|
|
2166
|
+
'myfile.pdf', // filename
|
|
2167
|
+
'application/pdf' // content type
|
|
2168
|
+
);
|
|
2169
|
+
|
|
2170
|
+
// Delete a file
|
|
2171
|
+
await openkbs.Files.deleteRawKBFile('filename.txt', 'files');
|
|
2172
|
+
|
|
2173
|
+
// Rename a file
|
|
2174
|
+
await openkbs.Files.renameFile(
|
|
2175
|
+
'files/kbId/old-name.txt', // old path
|
|
2176
|
+
'new-name.txt', // new name
|
|
2177
|
+
'files' // namespace
|
|
2178
|
+
);
|
|
2179
|
+
|
|
2180
|
+
// Secrets management
|
|
2181
|
+
const secrets = await openkbs.Files.listSecrets();
|
|
2182
|
+
await openkbs.Files.createSecretWithKBToken('API_KEY', 'secret-value');
|
|
2183
|
+
await openkbs.Files.deleteSecret('API_KEY');
|
|
2184
|
+
|
|
2185
|
+
// File versions
|
|
2186
|
+
const versions = await openkbs.Files.listVersions('myfile.js', 'functions');
|
|
2187
|
+
await openkbs.Files.restoreVersion('version-id', 'myfile.js', 'functions');
|
|
2188
|
+
```
|
|
2189
|
+
|
|
2190
|
+
##### KB API (`openkbs.KBAPI`)
|
|
2191
|
+
|
|
2192
|
+
```javascript
|
|
2193
|
+
// Get KB configuration
|
|
2194
|
+
const kb = await openkbs.KBAPI.getKB();
|
|
2195
|
+
|
|
2196
|
+
// Share KB with another user (by email)
|
|
2197
|
+
await openkbs.KBAPI.shareKBWith('user@example.com');
|
|
2198
|
+
|
|
2199
|
+
// Get list of users KB is shared with
|
|
2200
|
+
const shares = await openkbs.KBAPI.getKBShares();
|
|
2201
|
+
// Returns: { sharedWith: ['user1@example.com', 'user2@example.com'] }
|
|
2202
|
+
|
|
2203
|
+
// Remove share
|
|
2204
|
+
await openkbs.KBAPI.unshareKBWith('user@example.com');
|
|
2205
|
+
|
|
2206
|
+
// API Keys management
|
|
2207
|
+
const keys = await openkbs.KBAPI.getAPIKeys();
|
|
2208
|
+
const newKey = await openkbs.KBAPI.createAPIKey('My API Key', { resources: '*' });
|
|
2209
|
+
await openkbs.KBAPI.deleteAPIKey('api-key-id');
|
|
2210
|
+
|
|
2211
|
+
// Update KB variable
|
|
2212
|
+
await openkbs.KBAPI.updateVariable('customSetting', 'value');
|
|
2213
|
+
|
|
2214
|
+
// Search items (semantic search if embeddings enabled)
|
|
2215
|
+
const results = await openkbs.KBAPI.searchItems('search query', openkbs.kbId, 100);
|
|
2216
|
+
```
|
|
2217
|
+
|
|
2218
|
+
##### Complete Header Example with Admin Panel
|
|
2219
|
+
|
|
2220
|
+
```javascript
|
|
2221
|
+
import React, { useState, useEffect } from 'react';
|
|
2222
|
+
import { Box, IconButton, Dialog, Tabs, Tab, List, ListItem,
|
|
2223
|
+
ListItemText, TextField, Button, Typography } from '@mui/material';
|
|
2224
|
+
import SettingsIcon from '@mui/icons-material/Settings';
|
|
2225
|
+
|
|
2226
|
+
const Header = ({ openkbs, setRenderSettings, setSystemAlert, setBlockingLoading }) => {
|
|
2227
|
+
const [open, setOpen] = useState(false);
|
|
2228
|
+
const [tab, setTab] = useState(0);
|
|
2229
|
+
const [files, setFiles] = useState([]);
|
|
2230
|
+
const [shares, setShares] = useState([]);
|
|
2231
|
+
const [email, setEmail] = useState('');
|
|
2232
|
+
|
|
2233
|
+
useEffect(() => {
|
|
2234
|
+
setRenderSettings({
|
|
2235
|
+
disableEmojiButton: true,
|
|
2236
|
+
backgroundOpacity: 0.02
|
|
2237
|
+
});
|
|
2238
|
+
}, []);
|
|
2239
|
+
|
|
2240
|
+
// Load files
|
|
2241
|
+
const loadFiles = async () => {
|
|
2242
|
+
setBlockingLoading(true);
|
|
2243
|
+
try {
|
|
2244
|
+
const result = await openkbs.Files.listFiles('files');
|
|
2245
|
+
setFiles(result || []);
|
|
2246
|
+
} finally {
|
|
2247
|
+
setBlockingLoading(false);
|
|
2248
|
+
}
|
|
2249
|
+
};
|
|
2250
|
+
|
|
2251
|
+
// Load shares
|
|
2252
|
+
const loadShares = async () => {
|
|
2253
|
+
const result = await openkbs.KBAPI.getKBShares();
|
|
2254
|
+
setShares(result?.sharedWith || []);
|
|
2255
|
+
};
|
|
2256
|
+
|
|
2257
|
+
// Share with user
|
|
2258
|
+
const shareWith = async () => {
|
|
2259
|
+
try {
|
|
2260
|
+
await openkbs.KBAPI.shareKBWith(email);
|
|
2261
|
+
setSystemAlert({ msg: `Shared with ${email}`, type: 'success', duration: 3000 });
|
|
2262
|
+
setEmail('');
|
|
2263
|
+
loadShares();
|
|
2264
|
+
} catch (e) {
|
|
2265
|
+
setSystemAlert({ msg: e.message, type: 'error', duration: 5000 });
|
|
2266
|
+
}
|
|
2267
|
+
};
|
|
2268
|
+
|
|
2269
|
+
useEffect(() => {
|
|
2270
|
+
if (open && tab === 0) loadFiles();
|
|
2271
|
+
if (open && tab === 1) loadShares();
|
|
2272
|
+
}, [open, tab]);
|
|
2273
|
+
|
|
2274
|
+
return (
|
|
2275
|
+
<>
|
|
2276
|
+
<IconButton
|
|
2277
|
+
onClick={() => setOpen(true)}
|
|
2278
|
+
sx={{ position: 'absolute', top: 80, right: 20, zIndex: 1200 }}
|
|
2279
|
+
>
|
|
2280
|
+
<SettingsIcon />
|
|
2281
|
+
</IconButton>
|
|
2282
|
+
|
|
2283
|
+
<Dialog open={open} onClose={() => setOpen(false)} maxWidth="md" fullWidth>
|
|
2284
|
+
<Box sx={{ p: 2 }}>
|
|
2285
|
+
<Typography variant="h6">Settings</Typography>
|
|
2286
|
+
<Tabs value={tab} onChange={(e, v) => setTab(v)}>
|
|
2287
|
+
<Tab label="Files" />
|
|
2288
|
+
<Tab label="Sharing" />
|
|
2289
|
+
</Tabs>
|
|
2290
|
+
|
|
2291
|
+
{tab === 0 && (
|
|
2292
|
+
<List>
|
|
2293
|
+
{files.map((file, i) => (
|
|
2294
|
+
<ListItem key={i}>
|
|
2295
|
+
<ListItemText primary={file.name} />
|
|
2296
|
+
</ListItem>
|
|
2297
|
+
))}
|
|
2298
|
+
</List>
|
|
2299
|
+
)}
|
|
2300
|
+
|
|
2301
|
+
{tab === 1 && (
|
|
2302
|
+
<Box>
|
|
2303
|
+
<Box sx={{ display: 'flex', gap: 1, my: 2 }}>
|
|
2304
|
+
<TextField
|
|
2305
|
+
fullWidth
|
|
2306
|
+
label="Email"
|
|
2307
|
+
value={email}
|
|
2308
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
2309
|
+
/>
|
|
2310
|
+
<Button variant="contained" onClick={shareWith}>
|
|
2311
|
+
Share
|
|
2312
|
+
</Button>
|
|
2313
|
+
</Box>
|
|
2314
|
+
<List>
|
|
2315
|
+
{shares.map((s, i) => (
|
|
2316
|
+
<ListItem key={i}>
|
|
2317
|
+
<ListItemText primary={s} />
|
|
2318
|
+
</ListItem>
|
|
2319
|
+
))}
|
|
2320
|
+
</List>
|
|
2321
|
+
</Box>
|
|
2322
|
+
)}
|
|
2323
|
+
</Box>
|
|
2324
|
+
</Dialog>
|
|
2325
|
+
</>
|
|
2326
|
+
);
|
|
2327
|
+
};
|
|
2328
|
+
|
|
2329
|
+
const exports = { Header };
|
|
2330
|
+
window.contentRender = exports;
|
|
2331
|
+
export default exports;
|
|
2332
|
+
```
|
|
2333
|
+
|
|
1046
2334
|
## API
|
|
1047
2335
|
|
|
1048
2336
|
OpenKBS provides APIs to interact programmatically with your application. These APIs allow you to perform actions like starting new chats, retrieving chat messages, and managing data within your application. Data exchanged with these APIs is encrypted and decrypted using AES-256 encryption.
|
|
@@ -1387,4 +2675,4 @@ We welcome contributions from the community! Please feel free to submit issues,
|
|
|
1387
2675
|
|
|
1388
2676
|
## Contact
|
|
1389
2677
|
|
|
1390
|
-
For more information, visit our [official website](https://openkbs.com) or join our community discussions on [GitHub](https://github.com/open-kbs/openkbs/discussions).
|
|
2678
|
+
For more information, visit our [official website](https://openkbs.com) or join our community discussions on [GitHub](https://github.com/open-kbs/openkbs/discussions).
|