openkbs 0.0.35 → 0.0.37
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/package.json +1 -1
- package/scripts/deploy-templates.js +8 -28
- package/src/utils.js +9 -1
- package/templates/.openkbs/knowledge/README.md +44 -21
- package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/app/icon.png +0 -0
- package/templates/.openkbs/knowledge/examples/{ai-copywriter → ai-copywriter-agent}/app/instructions.txt +1 -1
- package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/app/settings.json +6 -0
- package/templates/.openkbs/knowledge/examples/{ai-copywriter/run_agent.js → ai-copywriter-agent/run_job.js} +7 -3
- package/templates/.openkbs/knowledge/examples/{ai-copywriter → ai-copywriter-agent}/src/Events/actions.js +10 -21
- package/templates/.openkbs/knowledge/examples/{ai-copywriter/src/Events/onResponse.js → ai-copywriter-agent/src/Events/handler.js} +6 -10
- package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/onRequest.js +3 -0
- package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/onRequest.json +5 -0
- package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/onResponse.js +3 -0
- package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/onResponse.json +5 -0
- package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Frontend/contentRender.js +64 -0
- package/templates/.openkbs/knowledge/examples/{ai-copywriter → ai-copywriter-agent}/src/Frontend/contentRender.json +2 -1
- package/templates/.openkbs/knowledge/metadata.json +1 -1
- package/templates/CLAUDE.md +9 -5
- package/templates/app/instructions.txt +1 -10
- package/templates/app/settings.json +2 -2
- package/templates/src/Events/actions.js +8 -10
- package/templates/src/Events/handler.js +49 -0
- package/templates/src/Events/onRequest.js +2 -13
- package/templates/src/Events/onRequest.json +5 -0
- package/templates/src/Events/onResponse.js +2 -12
- package/templates/{.openkbs/knowledge/examples/ai-copywriter/src → src}/Events/onResponse.json +1 -0
- package/templates/.openkbs/knowledge/examples/ai-copywriter/app/icon.png +0 -0
- package/templates/.openkbs/knowledge/examples/ai-copywriter/app/settings.json +0 -13
- package/templates/.openkbs/knowledge/examples/ai-copywriter/src/Frontend/contentRender.js +0 -26
package/package.json
CHANGED
|
@@ -1,41 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
|
|
6
5
|
|
|
7
|
-
const s3Client = new S3Client({ region: 'us-east-1' });
|
|
8
6
|
const BUCKET = 'openkbs-downloads';
|
|
9
7
|
const TEMPLATES_DIR = path.join(__dirname, '../templates');
|
|
10
8
|
|
|
11
|
-
async function uploadDirectory(localPath, s3Prefix) {
|
|
12
|
-
const items = await fs.readdir(localPath, { withFileTypes: true });
|
|
13
|
-
|
|
14
|
-
for (const item of items) {
|
|
15
|
-
const itemPath = path.join(localPath, item.name);
|
|
16
|
-
const s3Key = `${s3Prefix}/${item.name}`;
|
|
17
|
-
|
|
18
|
-
if (item.isDirectory()) {
|
|
19
|
-
await uploadDirectory(itemPath, s3Key);
|
|
20
|
-
} else {
|
|
21
|
-
const fileContent = await fs.readFile(itemPath);
|
|
22
|
-
await s3Client.send(new PutObjectCommand({
|
|
23
|
-
Bucket: BUCKET,
|
|
24
|
-
Key: s3Key,
|
|
25
|
-
Body: fileContent
|
|
26
|
-
}));
|
|
27
|
-
console.log(`Uploaded: ${s3Key}`);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
9
|
async function deployTemplates() {
|
|
33
10
|
try {
|
|
34
|
-
console.log('
|
|
35
|
-
|
|
36
|
-
|
|
11
|
+
console.log('Syncing templates to S3...');
|
|
12
|
+
|
|
13
|
+
const syncCommand = `aws s3 sync "${TEMPLATES_DIR}" s3://${BUCKET}/templates --delete`;
|
|
14
|
+
execSync(syncCommand, { stdio: 'inherit' });
|
|
15
|
+
|
|
16
|
+
console.log('Templates synced successfully!');
|
|
37
17
|
} catch (error) {
|
|
38
|
-
console.error('Error
|
|
18
|
+
console.error('Error syncing templates:', error);
|
|
39
19
|
process.exit(1);
|
|
40
20
|
}
|
|
41
21
|
}
|
package/src/utils.js
CHANGED
|
@@ -1080,11 +1080,19 @@ async function downloadTemplatesFromS3(targetDir) {
|
|
|
1080
1080
|
|
|
1081
1081
|
const fileContent = await streamToBuffer(response.Body);
|
|
1082
1082
|
await fs.writeFile(localPath, fileContent);
|
|
1083
|
-
console.log(relativePath);
|
|
1084
1083
|
});
|
|
1085
1084
|
|
|
1086
1085
|
// Wait for all downloads to complete
|
|
1087
1086
|
await Promise.all(downloadPromises);
|
|
1087
|
+
|
|
1088
|
+
// Add .openkbs/* to .gitignore
|
|
1089
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
1090
|
+
if (await fs.pathExists(gitignorePath)) {
|
|
1091
|
+
let content = await fs.readFile(gitignorePath, 'utf8');
|
|
1092
|
+
if (!content.includes('.openkbs/*')) {
|
|
1093
|
+
await fs.writeFile(gitignorePath, content.trim() + '\n.openkbs/*\n');
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1088
1096
|
}
|
|
1089
1097
|
|
|
1090
1098
|
const generateKey = (passphrase) => {
|
|
@@ -1,34 +1,57 @@
|
|
|
1
|
-
# OpenKBS Development Guidelines
|
|
1
|
+
# OpenKBS Agent Development Guidelines
|
|
2
|
+
|
|
3
|
+
## MANDATORY FIRST STEP
|
|
4
|
+
**READ EVERY SINGLE FILE IN THE EXAMPLES FOLDER**
|
|
5
|
+
|
|
6
|
+
Read the content of ALL files in `.openkbs/knowledge/examples/` directory and ALL subdirectories (without the icon.png)
|
|
2
7
|
|
|
3
8
|
## Development Flow
|
|
4
9
|
|
|
5
|
-
1. **Read
|
|
10
|
+
1. **Read ALL example files first** to get familiar with OpenKBS framework for building AI agents
|
|
11
|
+
2. **Read existing agent code:**
|
|
6
12
|
- `./app/` folder (settings, instructions, etc.)
|
|
7
13
|
- `./src/` folder (all Events and Frontend files)
|
|
14
|
+
- `./run_job.js` any files starting with "run"
|
|
15
|
+
3. **Implement requested features using knowledge from examples**
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## **Critical** Rules (**IMPORTANT**)
|
|
19
|
+
- Never skip reading examples
|
|
20
|
+
- Study the complete working examples to understand OpenKBS patterns
|
|
21
|
+
- Never guess framework methods, settings or variables — always reference the examples.
|
|
22
|
+
- Think hard before the implementation
|
|
23
|
+
|
|
24
|
+
## Development Guidelines
|
|
25
|
+
- If develop new agent from scratch, implement the run_job.js script as start point to backend-to-backend integrations
|
|
26
|
+
- To add npm dependency to backend handlers, add it to onRequest.json and onResponse.json
|
|
27
|
+
- To add npm dependency to the frontend, add it to contentRender.json
|
|
28
|
+
- Valid values for the _meta_actions key are [] or ["REQUEST_CHAT_MODEL"].
|
|
29
|
+
- Add and use npm dependencies only if necessary, some of those shown in the examples are purely demonstrative
|
|
8
30
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- **Framework implementation needed**: New features, new event handlers, new frontend components, or placeholder app development
|
|
31
|
+
### Backend
|
|
32
|
+
The OpenKBS backend framework is for developing AI agents with custom tools, using Node.js. It integrates with chat services via `onRequest` and `onResponse` handlers for custom actions and service integration.
|
|
12
33
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
34
|
+
#### Backend Handlers
|
|
35
|
+
The framework's core uses `onRequest` and `onResponse` handlers as middleware for message tool call parsing and execution.
|
|
36
|
+
- **`onResponse` Handler:** Activated after the LLM generates a message, enabling command extraction, and action execution.
|
|
37
|
+
- **`onRequest` Handler:** Triggered on user message to allow the user to execute action
|
|
17
38
|
|
|
18
|
-
|
|
39
|
+
#### NPM Dependencies for onRequest.js or onResponse.js Backend Handlers
|
|
40
|
+
1. If a file imports an NPM dependency and is then imported by onRequest.js or onResponse.js, this dependency must be defined in the handler's corresponding json file
|
|
41
|
+
Example: If actions.js imports mysql2 and onResponse.js imports actions.js, then mysql2 must be in onResponse.json:
|
|
42
|
+
{
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"mysql2": "^3.14.2"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
19
47
|
|
|
20
|
-
|
|
21
|
-
- Adding new event handlers (onRequest, onResponse, etc.)
|
|
22
|
-
- Creating new frontend components
|
|
23
|
-
- Implementing OpenKBS-specific functionality
|
|
24
|
-
- Any framework-related implementation
|
|
48
|
+
Similarly, we need to create onRequest.json for onRequest.js as each handler have separate Node.js build with separate dependencies
|
|
25
49
|
|
|
26
|
-
## When Examples May Not Be Needed
|
|
27
50
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- Configuration adjustments that don't involve framework methods
|
|
51
|
+
### Frontend Overview
|
|
52
|
+
The OpenKBS frontend framework, built with React and MUI, offers a flexible platform for custom chat interfaces. Developers can customize chat appearance and behavior via the `contentRender` module.
|
|
31
53
|
|
|
32
|
-
|
|
54
|
+
#### contentRender
|
|
55
|
+
The `contentRender.js` file is central to frontend customization, exporting key functions for interface adjustments.
|
|
56
|
+
- **`onRenderChatMessage(params)`:** function called every time a chat message is rendered.
|
|
33
57
|
|
|
34
|
-
**When in doubt, read the examples.** They contain all necessary framework knowledge for proper OpenKBS implementation.
|
|
@@ -7,7 +7,7 @@ Job:
|
|
|
7
7
|
- If you see additional instructions in PROCESS_PRODUCT, execute them.
|
|
8
8
|
- When you have obtained all the necessary information, output JOB_COMPLETED response.
|
|
9
9
|
|
|
10
|
-
Guidelines:
|
|
10
|
+
Important Guidelines:
|
|
11
11
|
- use webpageToText command to read websites content in details
|
|
12
12
|
- Always output one command per response and wait for the response before continuing
|
|
13
13
|
- If an API call fails, so that you can't extract a required data, retry with different website or search query
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Start new agent job via API call to openkbs cloud service
|
|
2
|
+
|
|
1
3
|
const https = require('https');
|
|
2
4
|
const fs = require('fs');
|
|
3
5
|
const readline = require('readline');
|
|
@@ -7,7 +9,9 @@ const settings = JSON.parse(fs.readFileSync('app/settings.json', 'utf8'));
|
|
|
7
9
|
const secretsPath = path.join('.openkbs', 'secrets.json');
|
|
8
10
|
let apiKey;
|
|
9
11
|
|
|
10
|
-
const message = `
|
|
12
|
+
const message = `Today's Date: ${new Date().toLocaleDateString()}
|
|
13
|
+
|
|
14
|
+
PROCESS_PRODUCT:
|
|
11
15
|
Product Name: iPhone 14 Pro Max
|
|
12
16
|
Product Code: MQ9X3RX/A
|
|
13
17
|
ID: 97649
|
|
@@ -20,7 +24,6 @@ if (!settings.kbId) {
|
|
|
20
24
|
return;
|
|
21
25
|
}
|
|
22
26
|
|
|
23
|
-
// Function to read the API key from secrets.json
|
|
24
27
|
function getApiKey() {
|
|
25
28
|
if (fs.existsSync(secretsPath)) {
|
|
26
29
|
const secrets = JSON.parse(fs.readFileSync(secretsPath, 'utf8'));
|
|
@@ -108,7 +111,8 @@ function pollForMessages(chatId, app) {
|
|
|
108
111
|
const messages = JSON.parse(jsonString)[0].data.messages;
|
|
109
112
|
for (const message of messages) {
|
|
110
113
|
if (message.role === 'system' && /{"type"\s*:\s*"(JOB_COMPLETED|JOB_FAILED)".*?}/s.test(message.content)) {
|
|
111
|
-
|
|
114
|
+
// return the final response found in the data results array generated by the backendHandler
|
|
115
|
+
console.log(JSON.parse(message.content)?.data?.find(item => item.type === 'JOB_COMPLETED' || item.type === 'JOB_FAILED'));
|
|
112
116
|
clearInterval(interval);
|
|
113
117
|
return;
|
|
114
118
|
}
|
|
@@ -32,37 +32,33 @@ export const getActions = () => [
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
[/[\s\S]*"type"\s*:\s*"JOB_FAILED"[\s\S]*/, async (match, event) => {
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
35
|
+
const parsedData = extractJSONFromText(match[0]);
|
|
36
|
+
if (parsedData && parsedData.type === "JOB_FAILED") {
|
|
37
37
|
await openkbs.chats({
|
|
38
38
|
action: "updateChat",
|
|
39
|
-
title: await openkbs.encrypt(
|
|
39
|
+
title: await openkbs.encrypt(parsedData.reason),
|
|
40
40
|
chatIcon: '🔴',
|
|
41
41
|
chatId: event?.payload?.chatId
|
|
42
42
|
})
|
|
43
|
+
return parsedData;
|
|
43
44
|
}
|
|
44
45
|
}],
|
|
45
46
|
|
|
46
|
-
[/\/?googleSearch\("(.*?)"
|
|
47
|
+
[/\/?googleSearch\("(.*?)"\)/, async (match) => {
|
|
47
48
|
const q = match[1];
|
|
48
|
-
const limit = match[2] ? parseInt(match[2]) : 5;
|
|
49
|
-
|
|
50
49
|
try {
|
|
51
50
|
const response = await openkbs.googleSearch(q, {});
|
|
52
51
|
const data = response?.map(({ title, link, snippet, pagemap }) => ({
|
|
53
52
|
title, link, snippet, image: pagemap?.metatags?.[0]?.["og:image"]
|
|
54
|
-
}))
|
|
55
|
-
|
|
56
|
-
if(!data?.length) return { data: { error: "No results found" } };
|
|
53
|
+
}));
|
|
57
54
|
return { data };
|
|
58
55
|
} catch (e) {
|
|
59
56
|
return { error: e.message };
|
|
60
57
|
}
|
|
61
58
|
}],
|
|
62
59
|
|
|
63
|
-
[/\/?youtubeSearch\("(.*?)"
|
|
60
|
+
[/\/?youtubeSearch\("(.*?)"\)/, async (match) => {
|
|
64
61
|
const q = match[1];
|
|
65
|
-
const limit = match[2] ? parseInt(match[2]) : 5;
|
|
66
62
|
try {
|
|
67
63
|
const response = await openkbs.googleSearch(q + ' site:youtube.com', { videoOnly: true });
|
|
68
64
|
const data = response?.map(({ title, link, snippet, pagemap }) => ({
|
|
@@ -72,19 +68,15 @@ export const getActions = () => [
|
|
|
72
68
|
thumbnail: pagemap?.videoobject?.[0]?.thumbnailurl || pagemap?.metatags?.[0]?.["og:image"],
|
|
73
69
|
duration: pagemap?.videoobject?.[0]?.duration,
|
|
74
70
|
channel: pagemap?.metatags?.[0]?.["og:site_name"],
|
|
75
|
-
})).filter(item => item.link.includes('youtu'))
|
|
76
|
-
|
|
77
|
-
if(!data?.length) return { data: { error: "No YouTube videos found" } };
|
|
71
|
+
})).filter(item => item.link.includes('youtu'));
|
|
78
72
|
return { data };
|
|
79
73
|
} catch (e) {
|
|
80
74
|
return { error: e.message };
|
|
81
75
|
}
|
|
82
76
|
}],
|
|
83
77
|
|
|
84
|
-
[/\/?googleImageSearch\("(.*?)"
|
|
78
|
+
[/\/?googleImageSearch\("(.*?)"\)/, async (match) => {
|
|
85
79
|
const q = match[1];
|
|
86
|
-
const limit = match[2] ? parseInt(match[2]) : 10;
|
|
87
|
-
|
|
88
80
|
try {
|
|
89
81
|
const response = await openkbs.googleSearch(q, { searchType: 'image' });
|
|
90
82
|
const data = response?.map(({ title, link, snippet, pagemap }) => {
|
|
@@ -96,11 +88,8 @@ export const getActions = () => [
|
|
|
96
88
|
snippet,
|
|
97
89
|
image: thumbnail
|
|
98
90
|
};
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
if (!data?.length) return { data: { error: "No image results found" } };
|
|
91
|
+
});
|
|
102
92
|
return { data };
|
|
103
|
-
|
|
104
93
|
} catch (e) {
|
|
105
94
|
return { error: e.message };
|
|
106
95
|
}
|
|
@@ -1,30 +1,26 @@
|
|
|
1
1
|
import {getActions} from './actions.js';
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const backendHandler = async (event) => {
|
|
4
4
|
const maxSelfInvokeMessagesCount = 60;
|
|
5
|
-
const lastMessage = event.payload.messages[event.payload.messages.length - 1]
|
|
6
|
-
|
|
7
|
-
// Check if this is a JOB_COMPLETED or JOB_FAILED event
|
|
8
|
-
const isJobCompleted = /"type"\s*:\s*"JOB_COMPLETED"/.test(lastMessage);
|
|
9
|
-
const isJobFailed = /"type"\s*:\s*"JOB_FAILED"/.test(lastMessage);
|
|
10
|
-
|
|
5
|
+
const lastMessage = event.payload.messages[event.payload.messages.length - 1];
|
|
11
6
|
const actions = getActions();
|
|
12
7
|
|
|
13
8
|
const matchingActions = actions.reduce((acc, [regex, action]) => {
|
|
14
|
-
const matches = [...lastMessage.matchAll(new RegExp(regex, 'g'))];
|
|
9
|
+
const matches = [...lastMessage.content.matchAll(new RegExp(regex, 'g'))];
|
|
15
10
|
matches.forEach(match => {
|
|
16
11
|
acc.push(action(match, event));
|
|
17
12
|
});
|
|
18
13
|
return acc;
|
|
19
14
|
}, []);
|
|
20
15
|
|
|
21
|
-
const isJobFinished =
|
|
16
|
+
const isJobFinished = /"JOB_COMPLETED"|"JOB_FAILED"/.test(lastMessage.content);
|
|
22
17
|
|
|
23
18
|
const meta = {
|
|
24
19
|
_meta_actions:
|
|
25
20
|
(
|
|
21
|
+
// disable post-response interactions if limit is exceeded or final job response
|
|
26
22
|
event?.payload?.messages?.length > maxSelfInvokeMessagesCount ||
|
|
27
|
-
isJobFinished &&
|
|
23
|
+
isJobFinished && lastMessage.role === 'system'
|
|
28
24
|
)
|
|
29
25
|
? []
|
|
30
26
|
: ["REQUEST_CHAT_MODEL"]
|
package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Frontend/contentRender.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React, { useEffect } from "react";
|
|
2
|
+
import JsonView from '@uiw/react-json-view';
|
|
3
|
+
import { Chip } from '@mui/material';
|
|
4
|
+
|
|
5
|
+
const extractJSONFromText = (text) => {
|
|
6
|
+
let braceCount = 0, startIndex = text.indexOf('{');
|
|
7
|
+
if (startIndex === -1) return null;
|
|
8
|
+
|
|
9
|
+
for (let i = startIndex; i < text.length; i++) {
|
|
10
|
+
if (text[i] === '{') braceCount++;
|
|
11
|
+
if (text[i] === '}' && --braceCount === 0) {
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(text.slice(startIndex, i + 1));
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const parseCommands = (text) => {
|
|
23
|
+
return text.match(/\/\w+\("([^"]+)"\)/g) || [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// do NOT useState() directly in this function, it is not a React component
|
|
27
|
+
const onRenderChatMessage = async (params) => {
|
|
28
|
+
const { content } = params.messages[params.msgIndex];
|
|
29
|
+
const JSONData = extractJSONFromText(content);
|
|
30
|
+
const commands = parseCommands(content);
|
|
31
|
+
|
|
32
|
+
if (JSONData?.type === 'JOB_COMPLETED') {
|
|
33
|
+
return <JsonView value={JSONData} />
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (commands.length > 0) {
|
|
37
|
+
return commands.map((cmd, i) => (
|
|
38
|
+
<Chip key={i} label={cmd} size="small" />
|
|
39
|
+
));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// return undefined to use default message render
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const Header = ({ setRenderSettings }) => {
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
setRenderSettings({
|
|
48
|
+
inputLabelsQuickSend: true,
|
|
49
|
+
disableBalanceView: false,
|
|
50
|
+
disableSentLabel: false,
|
|
51
|
+
disableChatAvatar: false,
|
|
52
|
+
disableChatModelsSelect: false,
|
|
53
|
+
disableContextItems: false,
|
|
54
|
+
disableCopyButton: false,
|
|
55
|
+
disableEmojiButton: false,
|
|
56
|
+
disableTextToSpeechButton: false,
|
|
57
|
+
disableMobileLeftButton: false,
|
|
58
|
+
});
|
|
59
|
+
}, [setRenderSettings]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const exports = { onRenderChatMessage, Header };
|
|
63
|
+
window.contentRender = exports;
|
|
64
|
+
export default exports;
|
package/templates/CLAUDE.md
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
# Claude Code Instructions
|
|
2
2
|
|
|
3
|
-
##
|
|
4
|
-
|
|
3
|
+
## MANDATORY FIRST STEPS
|
|
4
|
+
**CRITICAL**: Before taking ANY action, implementing ANY feature, or answering ANY question about this project:
|
|
5
|
+
|
|
6
|
+
1. **FIRST**: Update the knowledge base:
|
|
5
7
|
```bash
|
|
6
8
|
openkbs update knowledge
|
|
7
9
|
```
|
|
8
10
|
|
|
9
|
-
2.
|
|
11
|
+
2. **IMMEDIATELY AFTER**: Read the knowledge base README:
|
|
10
12
|
```bash
|
|
11
|
-
#
|
|
13
|
+
# THIS MUST BE YOUR SECOND ACTION - READ THIS FILE FOR MORE INSTRUCTIONS
|
|
12
14
|
.openkbs/knowledge/README.md
|
|
13
15
|
```
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
**DO NOT PROCEED WITHOUT COMPLETING BOTH STEPS IN ORDER**
|
|
18
|
+
|
|
19
|
+
`.openkbs/knowledge/README.md` contains comprehensive instructions on how to implement openkbs agents
|
|
@@ -1,10 +1 @@
|
|
|
1
|
-
You are an AI assistant, assist users with their inquiries and tasks efficiently.
|
|
2
|
-
|
|
3
|
-
Here are some APIs you can use to assist users:
|
|
4
|
-
|
|
5
|
-
/googleSearch("query")
|
|
6
|
-
Description: """
|
|
7
|
-
Get results from Google Search API.
|
|
8
|
-
"""
|
|
9
|
-
$InputLabel = """Let me Search in Google!"""
|
|
10
|
-
$InputValue = """Search in google for the latest news"""
|
|
1
|
+
You are an AI assistant, assist users with their inquiries and tasks efficiently.
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// This boilerplate code is a starting point for development.
|
|
2
|
+
export const getActions = () => [
|
|
3
|
+
[/\/?googleSearch\("(.*?)"\)/, async (match) => {
|
|
3
4
|
const q = match[1];
|
|
4
5
|
try {
|
|
5
|
-
const
|
|
6
|
-
const params = { q, ...(noSecrets ? {} : { key: '{{secrets.googlesearch_api_key}}', cx: '{{secrets.googlesearch_engine_id}}' }) };
|
|
7
|
-
const response = noSecrets
|
|
8
|
-
? await openkbs.googleSearch(params.q, params)
|
|
9
|
-
: (await axios.get('https://www.googleapis.com/customsearch/v1', { params }))?.data?.items;
|
|
6
|
+
const response = await openkbs.googleSearch(q, {});
|
|
10
7
|
const data = response?.map(({ title, link, snippet, pagemap }) => ({
|
|
11
8
|
title, link, snippet, image: pagemap?.metatags?.[0]?.["og:image"]
|
|
12
9
|
}));
|
|
13
|
-
return { data
|
|
10
|
+
return { data };
|
|
14
11
|
} catch (e) {
|
|
15
|
-
return { error: e.
|
|
12
|
+
return { error: e.message };
|
|
16
13
|
}
|
|
17
|
-
}]
|
|
14
|
+
}],
|
|
15
|
+
// add more actions here
|
|
18
16
|
];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {getActions} from './actions.js';
|
|
2
|
+
|
|
3
|
+
export const backendHandler = async (event) => {
|
|
4
|
+
const maxSelfInvokeMessagesCount = 60;
|
|
5
|
+
const lastMessage = event.payload.messages[event.payload.messages.length - 1];
|
|
6
|
+
const actions = getActions();
|
|
7
|
+
|
|
8
|
+
const matchingActions = actions.reduce((acc, [regex, action]) => {
|
|
9
|
+
const matches = [...lastMessage.content.matchAll(new RegExp(regex, 'g'))];
|
|
10
|
+
matches.forEach(match => {
|
|
11
|
+
acc.push(action(match, event));
|
|
12
|
+
});
|
|
13
|
+
return acc;
|
|
14
|
+
}, []);
|
|
15
|
+
|
|
16
|
+
const meta = {
|
|
17
|
+
_meta_actions:
|
|
18
|
+
event?.payload?.messages?.length > maxSelfInvokeMessagesCount
|
|
19
|
+
? [] // Ends the chat
|
|
20
|
+
: ["REQUEST_CHAT_MODEL"] // Triggers the LLM to react after the response
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (matchingActions.length > 0) {
|
|
24
|
+
try {
|
|
25
|
+
const results = await Promise.all(matchingActions);
|
|
26
|
+
|
|
27
|
+
if (results?.[0]?.data?.some?.(o => o?.type === 'image_url')) {
|
|
28
|
+
return {
|
|
29
|
+
...results[0],
|
|
30
|
+
...meta
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
type: 'RESPONSE',
|
|
36
|
+
data: results,
|
|
37
|
+
...meta
|
|
38
|
+
};
|
|
39
|
+
} catch (error) {
|
|
40
|
+
return {
|
|
41
|
+
type: 'ERROR',
|
|
42
|
+
error: error.message,
|
|
43
|
+
...meta
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { type: 'CONTINUE' };
|
|
49
|
+
};
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {backendHandler} from './handler';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
export const handler = async (event) => {
|
|
5
|
-
const actions = getActions({});
|
|
6
|
-
|
|
7
|
-
for (let [regex, action] of actions) {
|
|
8
|
-
const lastMessage = event.payload.messages[event.payload.messages.length - 1].content;
|
|
9
|
-
const match = lastMessage?.match(regex);
|
|
10
|
-
if (match) return await action(match);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
return { type: 'CONTINUE' }
|
|
14
|
-
};
|
|
3
|
+
export const handler = async (event) => backendHandler(event)
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {backendHandler} from './handler';
|
|
2
2
|
|
|
3
|
-
export const handler = async (event) =>
|
|
4
|
-
const actions = getActions({_meta_actions: ["REQUEST_CHAT_MODEL"]});
|
|
5
|
-
|
|
6
|
-
for (let [regex, action] of actions) {
|
|
7
|
-
const lastMessage = event.payload.messages[event.payload.messages.length - 1].content;
|
|
8
|
-
const match = lastMessage?.match(regex);
|
|
9
|
-
if (match) return await action(match);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
return { type: 'CONTINUE' }
|
|
13
|
-
};
|
|
3
|
+
export const handler = async (event) => backendHandler(event)
|
|
Binary file
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"chatVendor": "anthropic",
|
|
3
|
-
"kbDescription": "An autonomous AI powerhouse for content generation",
|
|
4
|
-
"kbTitle": "Copywriter",
|
|
5
|
-
"model": "claude-3-5-haiku-20241022",
|
|
6
|
-
"inputTools": [
|
|
7
|
-
"imageToText"
|
|
8
|
-
],
|
|
9
|
-
"embeddingModel": "none",
|
|
10
|
-
"embeddingDimension": "none",
|
|
11
|
-
"searchEngine": "IndexedDB",
|
|
12
|
-
"itemTypes": {}
|
|
13
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import React, { useEffect } from "react";
|
|
2
|
-
|
|
3
|
-
const onRenderChatMessage = async (params) => {
|
|
4
|
-
return;
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
const Header = ({ setRenderSettings }) => {
|
|
8
|
-
useEffect(() => {
|
|
9
|
-
setRenderSettings({
|
|
10
|
-
inputLabelsQuickSend: true,
|
|
11
|
-
disableBalanceView: false,
|
|
12
|
-
disableSentLabel: false,
|
|
13
|
-
disableChatAvatar: false,
|
|
14
|
-
disableChatModelsSelect: false,
|
|
15
|
-
disableContextItems: false,
|
|
16
|
-
disableCopyButton: false,
|
|
17
|
-
disableEmojiButton: false,
|
|
18
|
-
disableTextToSpeechButton: false,
|
|
19
|
-
disableMobileLeftButton: false,
|
|
20
|
-
});
|
|
21
|
-
}, [setRenderSettings]);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const exports = { onRenderChatMessage, Header };
|
|
25
|
-
window.contentRender = exports;
|
|
26
|
-
export default exports;
|