openkbs 0.0.52 → 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 +1496 -192
- package/package.json +2 -1
- package/src/actions.js +407 -13
- package/src/index.js +38 -2
- package/src/utils.js +1 -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,117 +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
|
+
}
|
|
445
|
+
}],
|
|
446
|
+
|
|
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
|
+
}
|
|
488
|
+
}],
|
|
489
|
+
|
|
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
|
+
}
|
|
376
504
|
}],
|
|
377
505
|
];
|
|
378
506
|
```
|
|
@@ -382,15 +510,13 @@ export const getActions = (meta) => [
|
|
|
382
510
|
// src/Events/onRequest.js
|
|
383
511
|
import {getActions} from './actions.js';
|
|
384
512
|
|
|
385
|
-
|
|
386
513
|
export const handler = async (event) => {
|
|
387
|
-
const actions = getActions({});
|
|
514
|
+
const actions = getActions({ _meta_actions: [] });
|
|
388
515
|
for (let [regex, action] of actions) {
|
|
389
516
|
const lastMessage = event.payload.messages[event.payload.messages.length - 1].content;
|
|
390
517
|
const match = lastMessage?.match(regex);
|
|
391
518
|
if (match) return await action(match);
|
|
392
519
|
}
|
|
393
|
-
|
|
394
520
|
return { type: 'CONTINUE' }
|
|
395
521
|
};
|
|
396
522
|
```
|
|
@@ -400,14 +526,12 @@ export const handler = async (event) => {
|
|
|
400
526
|
import {getActions} from './actions.js';
|
|
401
527
|
|
|
402
528
|
export const handler = async (event) => {
|
|
403
|
-
const actions = getActions({_meta_actions: ["REQUEST_CHAT_MODEL"]});
|
|
404
|
-
|
|
529
|
+
const actions = getActions({ _meta_actions: ["REQUEST_CHAT_MODEL"] });
|
|
405
530
|
for (let [regex, action] of actions) {
|
|
406
531
|
const lastMessage = event.payload.messages[event.payload.messages.length - 1].content;
|
|
407
532
|
const match = lastMessage?.match(regex);
|
|
408
533
|
if (match) return await action(match);
|
|
409
534
|
}
|
|
410
|
-
|
|
411
535
|
return { type: 'CONTINUE' }
|
|
412
536
|
};
|
|
413
537
|
```
|
|
@@ -551,7 +675,79 @@ export const handler = async (event) => {
|
|
|
551
675
|
|
|
552
676
|
```
|
|
553
677
|
|
|
554
|
-
|
|
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.):**
|
|
555
751
|
|
|
556
752
|
These files specify the NPM package dependencies required for the respective event handlers. They follow the standard `package.json` format.
|
|
557
753
|
|
|
@@ -586,29 +782,84 @@ if (actionMatch) {
|
|
|
586
782
|
```
|
|
587
783
|
|
|
588
784
|
|
|
589
|
-
#### SDK
|
|
785
|
+
#### SDK (Backend `openkbs` object)
|
|
590
786
|
|
|
591
|
-
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.
|
|
592
788
|
|
|
593
|
-
|
|
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
|
+
```
|
|
594
796
|
|
|
595
|
-
|
|
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
|
|
596
832
|
|
|
597
|
-
* **`openkbs.
|
|
833
|
+
* **`openkbs.googleSearch(query, params)`:** Performs Google search. Optional `params.searchType: 'image'` for image search.
|
|
598
834
|
|
|
599
|
-
* **`openkbs.
|
|
835
|
+
* **`openkbs.webpageToText(pageURL, params)`:** Extracts text content from a webpage.
|
|
600
836
|
|
|
601
|
-
* **`openkbs.
|
|
837
|
+
* **`openkbs.documentToText(documentURL, params)`:** Extracts text from documents (PDF, DOC, etc.).
|
|
602
838
|
|
|
603
|
-
* **`openkbs.
|
|
839
|
+
* **`openkbs.imageToText(imageUrl, params)`:** OCR - extracts text from images.
|
|
604
840
|
|
|
605
|
-
|
|
841
|
+
##### Communication
|
|
842
|
+
|
|
843
|
+
* **`openkbs.sendMail(email, subject, content)`:** Sends an email to the specified recipient.
|
|
844
|
+
|
|
845
|
+
* **`openkbs.textToSpeech(text, params)`:** Converts text to speech. Returns `response.audioContent`.
|
|
846
|
+
|
|
847
|
+
* **`openkbs.speechToText(audioURL, params)`:** Transcribes audio from a URL to text.
|
|
848
|
+
|
|
849
|
+
##### Data & Utilities
|
|
850
|
+
|
|
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.
|
|
606
857
|
|
|
607
858
|
* **`openkbs.translate(text, to)`:** Translates text to the specified target language.
|
|
608
859
|
|
|
609
860
|
* **`openkbs.detectLanguage(text, params)`:** Detects the language of the provided text.
|
|
610
861
|
|
|
611
|
-
|
|
862
|
+
##### Encryption & Storage
|
|
612
863
|
|
|
613
864
|
* **`openkbs.encrypt(plaintext)`:** Encrypts data using the provided AES key.
|
|
614
865
|
|
|
@@ -616,25 +867,162 @@ The `openkbs` object provides a set of utility functions and services to interac
|
|
|
616
867
|
|
|
617
868
|
* **`openkbs.items(data)`:** Interacts with the Items API for creating, updating, and deleting items.
|
|
618
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
|
+
|
|
619
885
|
* **`openkbs.chats(data)`:** Interacts with the Chats API.
|
|
620
886
|
|
|
621
|
-
* **`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
|
|
892
|
+
|
|
893
|
+
##### Scheduled Tasks API
|
|
894
|
+
|
|
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.
|
|
622
896
|
|
|
623
|
-
|
|
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
|
|
624
931
|
|
|
625
|
-
* **`openkbs.createEmbeddings(input, model)`:** Create embeddings from input text
|
|
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 }`
|
|
626
941
|
|
|
627
942
|
**Example SDK Usage:**
|
|
628
943
|
|
|
629
944
|
```javascript
|
|
630
|
-
//
|
|
631
|
-
const
|
|
632
|
-
|
|
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;
|
|
951
|
+
|
|
952
|
+
// Upload the generated image
|
|
953
|
+
const result = await openkbs.uploadImage(base64, 'sunset.png', 'image/png');
|
|
954
|
+
console.log(result.url);
|
|
955
|
+
|
|
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
|
+
});
|
|
633
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
|
+
}
|
|
634
970
|
|
|
635
|
-
//
|
|
636
|
-
const
|
|
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
|
+
});
|
|
637
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
|
+
});
|
|
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" }
|
|
638
1026
|
```
|
|
639
1027
|
|
|
640
1028
|
#### Managing Secrets
|
|
@@ -670,36 +1058,220 @@ This file contains essential configuration settings for the AI agent.
|
|
|
670
1058
|
}
|
|
671
1059
|
```
|
|
672
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
|
+
|
|
673
1182
|
#### LLM Instructions
|
|
674
1183
|
`app/instructions.txt`
|
|
675
1184
|
This file contains the instructions for the LLM, guiding its behavior and interaction with custom functionalities.
|
|
676
1185
|
Clear and specific instructions ensure the LLM effectively utilizes provided actions and commands.
|
|
677
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
|
+
|
|
678
1190
|
**Example Instructions:**
|
|
679
1191
|
|
|
680
1192
|
```
|
|
681
1193
|
You are an AI assistant.
|
|
682
1194
|
|
|
683
|
-
|
|
1195
|
+
LIST OF AVAILABLE COMMANDS:
|
|
1196
|
+
To execute a command, output it as text and wait for system response.
|
|
684
1197
|
|
|
685
|
-
|
|
1198
|
+
<googleSearch>
|
|
1199
|
+
{
|
|
1200
|
+
"query": "search query"
|
|
1201
|
+
}
|
|
1202
|
+
</googleSearch>
|
|
686
1203
|
Description: """
|
|
687
|
-
Get results from Google Search API.
|
|
1204
|
+
Get results from the Google Search API.
|
|
688
1205
|
"""
|
|
689
1206
|
$InputLabel = """Let me Search in Google!"""
|
|
690
|
-
$InputValue = """Search
|
|
1207
|
+
$InputValue = """Search for latest news"""
|
|
691
1208
|
|
|
692
|
-
|
|
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
|
+
"""
|
|
1231
|
+
|
|
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
|
+
"""
|
|
693
1267
|
|
|
694
1268
|
$Comment = """
|
|
695
1269
|
Any instructions or comments placed here will be removed before sending to the LLM.
|
|
696
1270
|
These can span multiple lines and contain any characters, code, or formatting.
|
|
697
1271
|
"""
|
|
698
|
-
...
|
|
699
|
-
|
|
700
1272
|
```
|
|
701
1273
|
|
|
702
|
-
Command definitions may include \$InputLabel and \$InputValue which are
|
|
1274
|
+
Command definitions may include \$InputLabel and \$InputValue which are invisible to the LLM:
|
|
703
1275
|
|
|
704
1276
|
`$InputLabel` - Text displayed as a selectable option in the chat interface.
|
|
705
1277
|
|
|
@@ -707,7 +1279,7 @@ Command definitions may include \$InputLabel and \$InputValue which are invisiab
|
|
|
707
1279
|
|
|
708
1280
|
These features provide quick command access and pre-populate inputs, enhancing user interaction.
|
|
709
1281
|
|
|
710
|
-
`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.
|
|
711
1283
|
|
|
712
1284
|
Instructions:
|
|
713
1285
|
```
|
|
@@ -722,12 +1294,41 @@ Include message IDs with optional summaries to maintain context while using fewe
|
|
|
722
1294
|
```
|
|
723
1295
|
/unzipMessages([{ "MSG_ID" : 1234567890123 }])
|
|
724
1296
|
Description: """
|
|
725
|
-
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.
|
|
726
1298
|
"""
|
|
727
1299
|
```
|
|
728
1300
|
|
|
729
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.
|
|
730
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
|
+
|
|
731
1332
|
#### Execution Environment
|
|
732
1333
|
|
|
733
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:
|
|
@@ -752,6 +1353,53 @@ Here's a breakdown of the key objects and utilities available within the OpenKBS
|
|
|
752
1353
|
|
|
753
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.
|
|
754
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
|
+
|
|
755
1403
|
### Frontend
|
|
756
1404
|
|
|
757
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.
|
|
@@ -765,70 +1413,233 @@ The frontend framework dynamically loads the `contentRender.js` module. This mod
|
|
|
765
1413
|
|
|
766
1414
|
The `contentRender.js` file is the heart of frontend customization. It can export several key functions:
|
|
767
1415
|
|
|
768
|
-
- **`onRenderChatMessage(params)`:** This function is called every time a chat message is rendered. It receives an object with various parameters
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
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
|
+
```
|
|
793
1495
|
|
|
794
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.
|
|
795
1497
|
|
|
796
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.
|
|
797
1499
|
|
|
798
|
-
**Example `contentRender.js
|
|
1500
|
+
**Example `contentRender.js` with Command Rendering:**
|
|
799
1501
|
|
|
800
1502
|
```javascript
|
|
801
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
|
+
];
|
|
802
1515
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
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) {}
|
|
812
1535
|
}
|
|
813
|
-
|
|
1536
|
+
return commands;
|
|
814
1537
|
};
|
|
815
1538
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
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
|
+
);
|
|
823
1558
|
};
|
|
824
1559
|
|
|
825
|
-
const
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
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
|
|
829
1575
|
};
|
|
830
1576
|
|
|
831
|
-
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 };
|
|
832
1643
|
window.contentRender = exports;
|
|
833
1644
|
export default exports;
|
|
834
1645
|
```
|
|
@@ -1027,6 +1838,499 @@ const onRenderChatMessage = async (params) => {
|
|
|
1027
1838
|
};
|
|
1028
1839
|
```
|
|
1029
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
|
+
|
|
1030
2334
|
## API
|
|
1031
2335
|
|
|
1032
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.
|
|
@@ -1371,4 +2675,4 @@ We welcome contributions from the community! Please feel free to submit issues,
|
|
|
1371
2675
|
|
|
1372
2676
|
## Contact
|
|
1373
2677
|
|
|
1374
|
-
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).
|