openkbs 0.0.34 → 0.0.36
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/src/actions.js +115 -1
- package/src/index.js +22 -1
- package/src/utils.js +18 -1
- package/templates/.openkbs/knowledge/README.md +34 -0
- package/templates/.openkbs/knowledge/examples/ai-copywriter/app/icon.png +0 -0
- package/templates/.openkbs/knowledge/examples/ai-copywriter/app/instructions.txt +61 -0
- package/templates/.openkbs/knowledge/examples/ai-copywriter/app/settings.json +13 -0
- package/templates/.openkbs/knowledge/examples/ai-copywriter/run_agent.js +120 -0
- package/templates/.openkbs/knowledge/examples/ai-copywriter/src/Events/actions.js +152 -0
- package/templates/.openkbs/knowledge/examples/ai-copywriter/src/Events/onResponse.js +59 -0
- package/templates/.openkbs/knowledge/examples/ai-copywriter/src/Events/onResponse.json +4 -0
- package/templates/.openkbs/knowledge/examples/ai-copywriter/src/Frontend/contentRender.js +26 -0
- package/templates/.openkbs/knowledge/examples/ai-copywriter/src/Frontend/contentRender.json +10 -0
- package/templates/.openkbs/knowledge/metadata.json +3 -0
- package/templates/CLAUDE.md +15 -0
- package/ON_PREMISES.md +0 -215
- package/examples/cloud-master/actions.js +0 -34
- package/examples/cloud-master/ai1.png +0 -0
- package/examples/cloud-master/ai2.png +0 -0
- package/examples/cloud-master/ai3.png +0 -0
- package/examples/cloud-master/backup.png +0 -0
- package/examples/cloud-master/contentRender.js +0 -255
- package/examples/cloud-master/instructions.txt +0 -77
- package/examples/cloud-master/llama-loaded.png +0 -0
- package/examples/cloud-master/sd3-loaded.png +0 -0
package/package.json
CHANGED
package/src/actions.js
CHANGED
|
@@ -538,6 +538,119 @@ async function downloadModifyAction() {
|
|
|
538
538
|
});
|
|
539
539
|
}
|
|
540
540
|
|
|
541
|
+
async function updateKnowledgeAction() {
|
|
542
|
+
try {
|
|
543
|
+
const knowledgeDir = path.join(process.cwd(), '.openkbs', 'knowledge');
|
|
544
|
+
const metadataPath = path.join(knowledgeDir, 'metadata.json');
|
|
545
|
+
|
|
546
|
+
// Check if .openkbs/knowledge directory exists
|
|
547
|
+
if (!fs.existsSync(knowledgeDir)) {
|
|
548
|
+
console.red('Knowledge directory not found. Please ensure you are in an OpenKBS project directory.');
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Get local metadata version
|
|
553
|
+
let localVersion = null;
|
|
554
|
+
if (fs.existsSync(metadataPath)) {
|
|
555
|
+
try {
|
|
556
|
+
const localMetadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
|
|
557
|
+
localVersion = localMetadata.version;
|
|
558
|
+
} catch (error) {
|
|
559
|
+
console.red('Error reading local metadata.json:', error.message);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Check remote version from S3
|
|
565
|
+
const { S3Client, GetObjectCommand } = require("@aws-sdk/client-s3");
|
|
566
|
+
const s3Client = new S3Client({ region: 'us-east-1' });
|
|
567
|
+
const bucket = 'openkbs-downloads';
|
|
568
|
+
const remoteMetadataKey = 'templates/.openkbs/knowledge/metadata.json';
|
|
569
|
+
|
|
570
|
+
let remoteVersion = null;
|
|
571
|
+
try {
|
|
572
|
+
const response = await s3Client.send(new GetObjectCommand({
|
|
573
|
+
Bucket: bucket,
|
|
574
|
+
Key: remoteMetadataKey
|
|
575
|
+
}));
|
|
576
|
+
|
|
577
|
+
const remoteMetadataContent = await response.Body.transformToString();
|
|
578
|
+
const remoteMetadata = JSON.parse(remoteMetadataContent);
|
|
579
|
+
remoteVersion = remoteMetadata.version;
|
|
580
|
+
} catch (error) {
|
|
581
|
+
console.red('Error fetching remote metadata:', error.message);
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Compare versions
|
|
586
|
+
if (localVersion === remoteVersion) {
|
|
587
|
+
console.green('Knowledge base is already up to date.');
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
console.log(`Updating knowledge base from version ${localVersion || 'unknown'} to ${remoteVersion}...`);
|
|
592
|
+
|
|
593
|
+
// Download updated knowledge files from S3
|
|
594
|
+
await downloadKnowledgeFromS3(knowledgeDir);
|
|
595
|
+
|
|
596
|
+
console.green('Knowledge base updated successfully!');
|
|
597
|
+
|
|
598
|
+
} catch (error) {
|
|
599
|
+
console.red('Error updating knowledge base:', error.message);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
async function downloadKnowledgeFromS3(targetDir) {
|
|
604
|
+
const { S3Client, ListObjectsV2Command, GetObjectCommand } = require("@aws-sdk/client-s3");
|
|
605
|
+
const s3Client = new S3Client({ region: 'us-east-1' });
|
|
606
|
+
const bucket = 'openkbs-downloads';
|
|
607
|
+
const prefix = 'templates/.openkbs/knowledge/';
|
|
608
|
+
|
|
609
|
+
try {
|
|
610
|
+
// List all objects in knowledge folder
|
|
611
|
+
const listResponse = await s3Client.send(new ListObjectsV2Command({
|
|
612
|
+
Bucket: bucket,
|
|
613
|
+
Prefix: prefix
|
|
614
|
+
}));
|
|
615
|
+
|
|
616
|
+
if (!listResponse.Contents || listResponse.Contents.length === 0) {
|
|
617
|
+
console.yellow('No knowledge files found in remote repository.');
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Download all files in parallel
|
|
622
|
+
const downloadPromises = listResponse.Contents.map(async (obj) => {
|
|
623
|
+
const key = obj.Key;
|
|
624
|
+
const relativePath = key.substring(prefix.length);
|
|
625
|
+
|
|
626
|
+
// Skip if it's a directory marker
|
|
627
|
+
if (relativePath.endsWith('/') || relativePath === '') return;
|
|
628
|
+
|
|
629
|
+
const localPath = path.join(targetDir, relativePath);
|
|
630
|
+
|
|
631
|
+
// Ensure directory exists
|
|
632
|
+
await fs.ensureDir(path.dirname(localPath));
|
|
633
|
+
|
|
634
|
+
// Download file
|
|
635
|
+
const response = await s3Client.send(new GetObjectCommand({
|
|
636
|
+
Bucket: bucket,
|
|
637
|
+
Key: key
|
|
638
|
+
}));
|
|
639
|
+
|
|
640
|
+
const fileContent = await response.Body.transformToByteArray();
|
|
641
|
+
await fs.writeFile(localPath, fileContent);
|
|
642
|
+
|
|
643
|
+
console.log(`Downloaded: ${relativePath}`);
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
await Promise.all(downloadPromises);
|
|
647
|
+
|
|
648
|
+
} catch (error) {
|
|
649
|
+
console.red('Error downloading knowledge files from S3:', error.message);
|
|
650
|
+
throw error;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
541
654
|
module.exports = {
|
|
542
655
|
signAction,
|
|
543
656
|
loginAction,
|
|
@@ -554,5 +667,6 @@ module.exports = {
|
|
|
554
667
|
logoutAction,
|
|
555
668
|
installFrontendPackageAction,
|
|
556
669
|
modifyAction,
|
|
557
|
-
downloadModifyAction
|
|
670
|
+
downloadModifyAction,
|
|
671
|
+
updateKnowledgeAction
|
|
558
672
|
};
|
package/src/index.js
CHANGED
|
@@ -12,7 +12,8 @@ const {
|
|
|
12
12
|
deleteKBAction,
|
|
13
13
|
deleteFileAction,
|
|
14
14
|
describeAction, deployAction, createByTemplateAction, initByTemplateAction,
|
|
15
|
-
logoutAction, installFrontendPackageAction, modifyAction, downloadModifyAction
|
|
15
|
+
logoutAction, installFrontendPackageAction, modifyAction, downloadModifyAction,
|
|
16
|
+
updateKnowledgeAction
|
|
16
17
|
} = require('./actions');
|
|
17
18
|
|
|
18
19
|
|
|
@@ -153,4 +154,24 @@ This will download the MODIFY.md template file from the OpenKBS GitHub repositor
|
|
|
153
154
|
This file will be automatically included when you run the 'openkbs modify' command.
|
|
154
155
|
`);
|
|
155
156
|
|
|
157
|
+
program
|
|
158
|
+
.command('update')
|
|
159
|
+
.argument('<target>', 'The target to update (currently supports: knowledge)')
|
|
160
|
+
.description('Update project components from remote repository')
|
|
161
|
+
.action((target) => {
|
|
162
|
+
if (target === 'knowledge') {
|
|
163
|
+
updateKnowledgeAction();
|
|
164
|
+
} else {
|
|
165
|
+
console.error(`Unknown update target: ${target}. Currently supported: knowledge`);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
.addHelpText('after', `
|
|
170
|
+
Examples:
|
|
171
|
+
$ openkbs update knowledge
|
|
172
|
+
|
|
173
|
+
This will check if your local .openkbs/knowledge directory is up to date with the remote repository
|
|
174
|
+
and update it if necessary.
|
|
175
|
+
`);
|
|
176
|
+
|
|
156
177
|
program.parse(process.argv);
|
package/src/utils.js
CHANGED
|
@@ -970,8 +970,14 @@ async function uploadFiles(namespaces, kbId, kbToken, location = 'origin', targe
|
|
|
970
970
|
const localDir = dist ? path.join(process.cwd(), 'cache') : path.join(process.cwd(), 'src');
|
|
971
971
|
const filesMap = await buildLocalFilesMap(localDir, namespaces);
|
|
972
972
|
let filesToUpload = [];
|
|
973
|
+
const isInOpenKBSFolder = (fileName) => fileName.startsWith('.openkbs/') || fileName.includes('/.openkbs/');
|
|
973
974
|
|
|
974
975
|
if (targetFile) {
|
|
976
|
+
// Skip if target file is in .openkbs folder
|
|
977
|
+
if (isInOpenKBSFolder(targetFile)) {
|
|
978
|
+
console.log(`Skipping file in .openkbs folder: ${targetFile}`);
|
|
979
|
+
return false;
|
|
980
|
+
}
|
|
975
981
|
const isDistFile = targetFile.startsWith('Events/dist') || targetFile.startsWith('Frontend/dist');
|
|
976
982
|
if (filesMap[targetFile] && ((dist && isDistFile) || (!dist && !isDistFile))) {
|
|
977
983
|
filesToUpload.push({ ...filesMap[targetFile], fileName: targetFile });
|
|
@@ -979,8 +985,10 @@ async function uploadFiles(namespaces, kbId, kbToken, location = 'origin', targe
|
|
|
979
985
|
return false; // File not found locally or not in the correct folder
|
|
980
986
|
}
|
|
981
987
|
} else {
|
|
982
|
-
// No target file, upload all files based on the dist parameter
|
|
988
|
+
// No target file, upload all files based on the dist parameter, excluding .openkbs files
|
|
983
989
|
filesToUpload = Object.keys(filesMap).filter(fileName => {
|
|
990
|
+
// Skip files in .openkbs folder
|
|
991
|
+
if (isInOpenKBSFolder(fileName)) return false;
|
|
984
992
|
const isDistFile = fileName.startsWith('Events/dist') || fileName.startsWith('Frontend/dist');
|
|
985
993
|
return dist ? isDistFile : !isDistFile;
|
|
986
994
|
}).map(fileName => {
|
|
@@ -1077,6 +1085,15 @@ async function downloadTemplatesFromS3(targetDir) {
|
|
|
1077
1085
|
|
|
1078
1086
|
// Wait for all downloads to complete
|
|
1079
1087
|
await Promise.all(downloadPromises);
|
|
1088
|
+
|
|
1089
|
+
// Add .openkbs/* to .gitignore
|
|
1090
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
1091
|
+
if (await fs.pathExists(gitignorePath)) {
|
|
1092
|
+
let content = await fs.readFile(gitignorePath, 'utf8');
|
|
1093
|
+
if (!content.includes('.openkbs/*')) {
|
|
1094
|
+
await fs.writeFile(gitignorePath, content.trim() + '\n.openkbs/*\n');
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1080
1097
|
}
|
|
1081
1098
|
|
|
1082
1099
|
const generateKey = (passphrase) => {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# OpenKBS Development Guidelines
|
|
2
|
+
|
|
3
|
+
## Development Flow
|
|
4
|
+
|
|
5
|
+
1. **Read existing code first:**
|
|
6
|
+
- `./app/` folder (settings, instructions, etc.)
|
|
7
|
+
- `./src/` folder (all Events and Frontend files)
|
|
8
|
+
|
|
9
|
+
2. **Assess implementation requirements:**
|
|
10
|
+
- **Minor changes only**: Simple modifications to existing code that don't require framework knowledge
|
|
11
|
+
- **Framework implementation needed**: New features, new event handlers, new frontend components, or placeholder app development
|
|
12
|
+
|
|
13
|
+
3. **For framework implementation:**
|
|
14
|
+
- Read all source code in `.openkbs/knowledge/examples/`
|
|
15
|
+
- Study complete working applications to understand OpenKBS patterns
|
|
16
|
+
- Never guess framework methods or variables - always reference examples
|
|
17
|
+
|
|
18
|
+
## When Examples Are Required
|
|
19
|
+
|
|
20
|
+
- New OpenKBS application development (placeholder app)
|
|
21
|
+
- Adding new event handlers (onRequest, onResponse, etc.)
|
|
22
|
+
- Creating new frontend components
|
|
23
|
+
- Implementing OpenKBS-specific functionality
|
|
24
|
+
- Any framework-related implementation
|
|
25
|
+
|
|
26
|
+
## When Examples May Not Be Needed
|
|
27
|
+
|
|
28
|
+
- Simple text changes to existing files
|
|
29
|
+
- Minor bug fixes in existing code
|
|
30
|
+
- Configuration adjustments that don't involve framework methods
|
|
31
|
+
|
|
32
|
+
## Critical Rule
|
|
33
|
+
|
|
34
|
+
**When in doubt, read the examples.** They contain all necessary framework knowledge for proper OpenKBS implementation.
|
|
Binary file
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
You are an autonomous agent for crafting intelligent, creative, and consistent content:
|
|
2
|
+
|
|
3
|
+
Job:
|
|
4
|
+
- Find and open at least one site that have correct, reliable and detailed information about the product
|
|
5
|
+
- paraphrase and enrich it and generate a SEO friendly product description
|
|
6
|
+
- Always search the official website of the manufacturer (Brand) and find information about the product from there.
|
|
7
|
+
- If you see additional instructions in PROCESS_PRODUCT, execute them.
|
|
8
|
+
- When you have obtained all the necessary information, output JOB_COMPLETED response.
|
|
9
|
+
|
|
10
|
+
Guidelines:
|
|
11
|
+
- use webpageToText command to read websites content in details
|
|
12
|
+
- Always output one command per response and wait for the response before continuing
|
|
13
|
+
- If an API call fails, so that you can't extract a required data, retry with different website or search query
|
|
14
|
+
- When searching for product information, use the format: Product Name + Product Code.
|
|
15
|
+
- Once all necessary information is obtained, deliver the final result using the following JSON format
|
|
16
|
+
|
|
17
|
+
Output Format Success:
|
|
18
|
+
{
|
|
19
|
+
"type": "JOB_COMPLETED",
|
|
20
|
+
"productID": "[productID]",
|
|
21
|
+
"productDescription": "[short plain text description]",
|
|
22
|
+
"metaDescription": "...",
|
|
23
|
+
"images": [{"url": "...", "title": "..."}, ...],
|
|
24
|
+
"attributes": {"size": "L", ...},
|
|
25
|
+
"videos": [
|
|
26
|
+
{"url": "[video1_url]", "title": "[video1_title]"},
|
|
27
|
+
{"url": "[video2_url]", "title": "[video2_title]"}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
Description: """
|
|
32
|
+
Use this JSON format to output the final job response
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
Output Format Failed:
|
|
36
|
+
{"type": "JOB_FAILED", "reason": "fail reason summary"}
|
|
37
|
+
Description: """
|
|
38
|
+
Output this JSON format if you can't extract the required data
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
List of API commands you can use to accomplish the Task:
|
|
42
|
+
|
|
43
|
+
/googleSearch("query")
|
|
44
|
+
Description: """
|
|
45
|
+
Get results from Google Search API.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
/youtubeSearch("query")
|
|
49
|
+
Description: """
|
|
50
|
+
Get results from youtube Search API.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
/googleImageSearch("query")
|
|
54
|
+
Description: """
|
|
55
|
+
Get results from google Image Search
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
/webpageToText("URL")
|
|
59
|
+
Description: """
|
|
60
|
+
Use this API to open/read a web pages like product pages.
|
|
61
|
+
"""
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
const https = require('https');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const readline = require('readline');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const settings = JSON.parse(fs.readFileSync('app/settings.json', 'utf8'));
|
|
7
|
+
const secretsPath = path.join('.openkbs', 'secrets.json');
|
|
8
|
+
let apiKey;
|
|
9
|
+
|
|
10
|
+
const message = `PROCESS_PRODUCT:
|
|
11
|
+
Product Name: iPhone 14 Pro Max
|
|
12
|
+
Product Code: MQ9X3RX/A
|
|
13
|
+
ID: 97649
|
|
14
|
+
|
|
15
|
+
find at least 2 images and 2 videos
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
if (!settings.kbId) {
|
|
19
|
+
console.log('First use: "openkbs push" To create the agent')
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Function to read the API key from secrets.json
|
|
24
|
+
function getApiKey() {
|
|
25
|
+
if (fs.existsSync(secretsPath)) {
|
|
26
|
+
const secrets = JSON.parse(fs.readFileSync(secretsPath, 'utf8'));
|
|
27
|
+
return secrets.apiKey;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function promptForApiKey() {
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
const rl = readline.createInterface({
|
|
35
|
+
input: process.stdin,
|
|
36
|
+
output: process.stdout
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
console.log(`Please generate an API key from: https://${settings.kbId}.apps.openkbs.com/?tab=access&createAPIKey=api-${+new Date()}`);
|
|
40
|
+
|
|
41
|
+
rl.question('Enter your API key: ', (key) => {
|
|
42
|
+
rl.close();
|
|
43
|
+
resolve(key);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
rl._writeToOutput = (str) => rl.output.write(rl.line ? "*" : str);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function saveApiKey(key) {
|
|
51
|
+
if (!fs.existsSync('.openkbs')) {
|
|
52
|
+
fs.mkdirSync('.openkbs');
|
|
53
|
+
}
|
|
54
|
+
fs.writeFileSync(secretsPath, JSON.stringify({ apiKey: key }, null, 2));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function main() {
|
|
58
|
+
apiKey = getApiKey();
|
|
59
|
+
|
|
60
|
+
if (!apiKey) {
|
|
61
|
+
apiKey = await promptForApiKey();
|
|
62
|
+
console.log('\n')
|
|
63
|
+
saveApiKey(apiKey);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const app = { kbId: settings.kbId, apiKey };
|
|
67
|
+
|
|
68
|
+
const chatTitle = "Task " + new Date().getTime();
|
|
69
|
+
startJob(chatTitle, message, app).then(chatId => {
|
|
70
|
+
console.log(`Job ${chatId} created.\nWorking ...`);
|
|
71
|
+
pollForMessages(chatId, app);
|
|
72
|
+
}).catch(console.error);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function startJob(chatTitle, data, app) {
|
|
76
|
+
return makeRequest('https://chat.openkbs.com/', { ...app, chatTitle, message: data })
|
|
77
|
+
.then(res => {
|
|
78
|
+
try {
|
|
79
|
+
return JSON.parse(res)[0].createdChatId;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
fs.unlinkSync(secretsPath);
|
|
82
|
+
throw 'Authentication failed.';
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function makeRequest(url, payload) {
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
const { hostname, pathname } = new URL(url);
|
|
90
|
+
const req = https.request({
|
|
91
|
+
hostname, path: pathname, method: 'POST',
|
|
92
|
+
headers: { 'Content-Type': 'application/json' }
|
|
93
|
+
}, res => {
|
|
94
|
+
let data = '';
|
|
95
|
+
res.on('data', chunk => data += chunk);
|
|
96
|
+
res.on('end', () => resolve(data));
|
|
97
|
+
}).on('error', reject);
|
|
98
|
+
|
|
99
|
+
req.write(JSON.stringify(payload));
|
|
100
|
+
req.end();
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function pollForMessages(chatId, app) {
|
|
105
|
+
const payload = { ...app, action: 'getChatMessages', chatId, decryptContent: true };
|
|
106
|
+
const interval = setInterval(() => {
|
|
107
|
+
makeRequest('https://chat.openkbs.com/', payload).then(jsonString => {
|
|
108
|
+
const messages = JSON.parse(jsonString)[0].data.messages;
|
|
109
|
+
for (const message of messages) {
|
|
110
|
+
if (message.role === 'system' && /{"type"\s*:\s*"(JOB_COMPLETED|JOB_FAILED)".*?}/s.test(message.content)) {
|
|
111
|
+
console.log(JSON.parse(message.content).data[0]);
|
|
112
|
+
clearInterval(interval);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}).catch(console.error);
|
|
117
|
+
}, 1000);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
main();
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
const extractJSONFromText = (text) => {
|
|
2
|
+
let braceCount = 0, startIndex = text.indexOf('{');
|
|
3
|
+
if (startIndex === -1) return null;
|
|
4
|
+
|
|
5
|
+
for (let i = startIndex; i < text.length; i++) {
|
|
6
|
+
if (text[i] === '{') braceCount++;
|
|
7
|
+
if (text[i] === '}' && --braceCount === 0) {
|
|
8
|
+
try {
|
|
9
|
+
return JSON.parse(text.slice(startIndex, i + 1));
|
|
10
|
+
} catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const getActions = () => [
|
|
19
|
+
[/[\s\S]*"type"\s*:\s*"JOB_COMPLETED"[\s\S]*/, async (match, event) => {
|
|
20
|
+
const parsedData = extractJSONFromText(match[0]);
|
|
21
|
+
if (parsedData && parsedData.type === "JOB_COMPLETED") {
|
|
22
|
+
await openkbs.chats({
|
|
23
|
+
action: "updateChat",
|
|
24
|
+
title: await openkbs.encrypt(parsedData?.name),
|
|
25
|
+
chatIcon: '🟢',
|
|
26
|
+
chatId: event?.payload?.chatId
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
return parsedData;
|
|
30
|
+
}
|
|
31
|
+
}],
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
[/[\s\S]*"type"\s*:\s*"JOB_FAILED"[\s\S]*/, async (match, event) => {
|
|
35
|
+
const json = extractJSONFromText(match[0]);
|
|
36
|
+
if (json && json.type === "JOB_FAILED") {
|
|
37
|
+
await openkbs.chats({
|
|
38
|
+
action: "updateChat",
|
|
39
|
+
title: await openkbs.encrypt(json.reason),
|
|
40
|
+
chatIcon: '🔴',
|
|
41
|
+
chatId: event?.payload?.chatId
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
}],
|
|
45
|
+
|
|
46
|
+
[/\/?googleSearch\("(.*?)"(?:,\s*(\d+))?\)/, async (match) => {
|
|
47
|
+
const q = match[1];
|
|
48
|
+
const limit = match[2] ? parseInt(match[2]) : 5;
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const response = await openkbs.googleSearch(q, {});
|
|
52
|
+
const data = response?.map(({ title, link, snippet, pagemap }) => ({
|
|
53
|
+
title, link, snippet, image: pagemap?.metatags?.[0]?.["og:image"]
|
|
54
|
+
})).slice(0, limit);
|
|
55
|
+
|
|
56
|
+
if(!data?.length) return { data: { error: "No results found" } };
|
|
57
|
+
return { data };
|
|
58
|
+
} catch (e) {
|
|
59
|
+
return { error: e.message };
|
|
60
|
+
}
|
|
61
|
+
}],
|
|
62
|
+
|
|
63
|
+
[/\/?youtubeSearch\("(.*?)"(?:,\s*(\d+))?\)/, async (match) => {
|
|
64
|
+
const q = match[1];
|
|
65
|
+
const limit = match[2] ? parseInt(match[2]) : 5;
|
|
66
|
+
try {
|
|
67
|
+
const response = await openkbs.googleSearch(q + ' site:youtube.com', { videoOnly: true });
|
|
68
|
+
const data = response?.map(({ title, link, snippet, pagemap }) => ({
|
|
69
|
+
title,
|
|
70
|
+
link: link.replace('www.youtube.com/watch?v=', 'youtu.be/'),
|
|
71
|
+
snippet,
|
|
72
|
+
thumbnail: pagemap?.videoobject?.[0]?.thumbnailurl || pagemap?.metatags?.[0]?.["og:image"],
|
|
73
|
+
duration: pagemap?.videoobject?.[0]?.duration,
|
|
74
|
+
channel: pagemap?.metatags?.[0]?.["og:site_name"],
|
|
75
|
+
})).filter(item => item.link.includes('youtu')).slice(0, limit);
|
|
76
|
+
|
|
77
|
+
if(!data?.length) return { data: { error: "No YouTube videos found" } };
|
|
78
|
+
return { data };
|
|
79
|
+
} catch (e) {
|
|
80
|
+
return { error: e.message };
|
|
81
|
+
}
|
|
82
|
+
}],
|
|
83
|
+
|
|
84
|
+
[/\/?googleImageSearch\("(.*?)"(?:,\s*(\d+))?\)/, async (match) => {
|
|
85
|
+
const q = match[1];
|
|
86
|
+
const limit = match[2] ? parseInt(match[2]) : 10;
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const response = await openkbs.googleSearch(q, { searchType: 'image' });
|
|
90
|
+
const data = response?.map(({ title, link, snippet, pagemap }) => {
|
|
91
|
+
const imageObj = pagemap?.cse_image?.[0];
|
|
92
|
+
const thumbnail = imageObj?.src || pagemap?.metatags?.[0]?.["og:image"] || link;
|
|
93
|
+
return {
|
|
94
|
+
title,
|
|
95
|
+
link: link,
|
|
96
|
+
snippet,
|
|
97
|
+
image: thumbnail
|
|
98
|
+
};
|
|
99
|
+
})?.slice(0, limit);
|
|
100
|
+
|
|
101
|
+
if (!data?.length) return { data: { error: "No image results found" } };
|
|
102
|
+
return { data };
|
|
103
|
+
|
|
104
|
+
} catch (e) {
|
|
105
|
+
return { error: e.message };
|
|
106
|
+
}
|
|
107
|
+
}],
|
|
108
|
+
|
|
109
|
+
[/\/?webpageToText\("(.*)"\)/, async (match) => {
|
|
110
|
+
try {
|
|
111
|
+
let response = await openkbs.webpageToText(match[1]);
|
|
112
|
+
|
|
113
|
+
// limit output length
|
|
114
|
+
if (response?.content?.length > 5000) {
|
|
115
|
+
response.content = response.content.substring(0, 5000);
|
|
116
|
+
}
|
|
117
|
+
if(!response?.url) return { data: { error: "Unable to read website" } };
|
|
118
|
+
return { data: response };
|
|
119
|
+
} catch (e) {
|
|
120
|
+
return { error: e.response?.data || e };
|
|
121
|
+
}
|
|
122
|
+
}],
|
|
123
|
+
|
|
124
|
+
[/\/?documentToText\("(.*)"\)/, async (match) => {
|
|
125
|
+
try {
|
|
126
|
+
let response = await openkbs.documentToText(match[1]);
|
|
127
|
+
|
|
128
|
+
// limit output length
|
|
129
|
+
if (response?.text?.length > 5000) {
|
|
130
|
+
response.text = response.text.substring(0, 5000);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return { data: response };
|
|
134
|
+
} catch (e) {
|
|
135
|
+
return { error: e.response.data };
|
|
136
|
+
}
|
|
137
|
+
}],
|
|
138
|
+
|
|
139
|
+
[/\/?imageToText\("(.*)"\)/, async (match) => {
|
|
140
|
+
try {
|
|
141
|
+
let response = await openkbs.imageToText(match[1]);
|
|
142
|
+
|
|
143
|
+
if (response?.detections?.[0]?.txt) {
|
|
144
|
+
response = { detections: response?.detections?.[0]?.txt };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return { data: response };
|
|
148
|
+
} catch (e) {
|
|
149
|
+
return { error: e.response.data };
|
|
150
|
+
}
|
|
151
|
+
}],
|
|
152
|
+
];
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {getActions} from './actions.js';
|
|
2
|
+
|
|
3
|
+
export const handler = async (event) => {
|
|
4
|
+
const maxSelfInvokeMessagesCount = 60;
|
|
5
|
+
const lastMessage = event.payload.messages[event.payload.messages.length - 1].content;
|
|
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
|
+
|
|
11
|
+
const actions = getActions();
|
|
12
|
+
|
|
13
|
+
const matchingActions = actions.reduce((acc, [regex, action]) => {
|
|
14
|
+
const matches = [...lastMessage.matchAll(new RegExp(regex, 'g'))];
|
|
15
|
+
matches.forEach(match => {
|
|
16
|
+
acc.push(action(match, event));
|
|
17
|
+
});
|
|
18
|
+
return acc;
|
|
19
|
+
}, []);
|
|
20
|
+
|
|
21
|
+
const isJobFinished = isJobCompleted || isJobFailed;
|
|
22
|
+
|
|
23
|
+
const meta = {
|
|
24
|
+
_meta_actions:
|
|
25
|
+
(
|
|
26
|
+
event?.payload?.messages?.length > maxSelfInvokeMessagesCount ||
|
|
27
|
+
isJobFinished && (matchingActions.length === 1 || lastMessage.role === 'system')
|
|
28
|
+
)
|
|
29
|
+
? []
|
|
30
|
+
: ["REQUEST_CHAT_MODEL"]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (matchingActions.length > 0) {
|
|
34
|
+
try {
|
|
35
|
+
const results = await Promise.all(matchingActions);
|
|
36
|
+
|
|
37
|
+
if (results?.[0]?.data?.some?.(o => o?.type === 'image_url')) {
|
|
38
|
+
return {
|
|
39
|
+
...results[0],
|
|
40
|
+
...meta
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
type: 'RESPONSE',
|
|
46
|
+
data: results,
|
|
47
|
+
...meta
|
|
48
|
+
};
|
|
49
|
+
} catch (error) {
|
|
50
|
+
return {
|
|
51
|
+
type: 'ERROR',
|
|
52
|
+
error: error.message,
|
|
53
|
+
...meta
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { type: 'CONTINUE' };
|
|
59
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
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;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Claude Code Instructions
|
|
2
|
+
|
|
3
|
+
## Initial Setup
|
|
4
|
+
1. First, update the knowledge base to ensure you have the latest information:
|
|
5
|
+
```bash
|
|
6
|
+
openkbs update knowledge
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
2. Then read the project contribution guidelines:
|
|
10
|
+
```bash
|
|
11
|
+
# Read the knowledge base README for detailed instructions
|
|
12
|
+
.openkbs/knowledge/README.md
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This file contains comprehensive instructions on how to contribute to this OpenKBS project.
|
package/ON_PREMISES.md
DELETED
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
### Running the Backend Locally (On-Premises)
|
|
3
|
-
|
|
4
|
-
To run the backend services of your AI application locally, follow these steps. This allows you to manage chat services, code execution, and AI LLM services on your own infrastructure.
|
|
5
|
-
|
|
6
|
-
#### Running the Chat Service Locally
|
|
7
|
-
|
|
8
|
-
1. **Start the Chat Service**:
|
|
9
|
-
- Open a new terminal and navigate to the root folder of your application.
|
|
10
|
-
- Run the following command:
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
npm run chat
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
- If LocalStack is not installed, you will receive instructions on how to install it based on your platform.
|
|
17
|
-
- Open another terminal, navigate to `/tmp`, and install LocalStack using the suggested commands and retrun `npm run chat`
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
4. **Configure OpenAI Key**:
|
|
21
|
-
- Enter your `OPENAI_KEY` when prompted. This key will be stored at `~/.openkbs/.env`.
|
|
22
|
-
|
|
23
|
-
5. **Access the Local Chat Service**:
|
|
24
|
-
- Refresh your browser at `http://{kbId}.apps.localhost:38593/chat`.
|
|
25
|
-
- You will see "On-Premises" in green text, indicating that your OpenKBS instance is using the local chat server to communicate with the OpenAI streaming API.
|
|
26
|
-
- You can remove the cloud models options by setting `"enableCloudModels": false` in `config.json`
|
|
27
|
-
|
|
28
|
-
#### Running the Code Execution Service Locally
|
|
29
|
-
|
|
30
|
-
1. **Start the Code Execution Service**:
|
|
31
|
-
- Open another terminal tab, navigate to the root folder of your KB app, and run:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
npm run code
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
2. **Enter Secrets**:
|
|
38
|
-
- You may be prompted to enter any secret placeholders in your `./src/Events/actions.js`. By default, this includes `googlesearch_api_key` and `googlesearch_engine_id`.
|
|
39
|
-
- You can press enter to skip, but for using Google Search as an AI tool, it's recommended to fill them. Google provides 100 free searches per day.
|
|
40
|
-
|
|
41
|
-
Congratulations! The LLM can now execute NodeJS code directly on your machine!
|
|
42
|
-
|
|
43
|
-
#### Enhancing Your Application with Code Execution
|
|
44
|
-
|
|
45
|
-
To utilize the code execution feature, follow these steps:
|
|
46
|
-
|
|
47
|
-
1. **Update `contentRender.js`**:
|
|
48
|
-
- Modify your local `contentRender.js` file to match the version found at [contentRender.js](./examples/cloud-master/contentRender.js). This update will provide the necessary UI components for local code execution and response rendering.
|
|
49
|
-
|
|
50
|
-
2. **Update `instructions.txt`**:
|
|
51
|
-
- Edit your local `instructions.txt` file to include the instructions found at [instructions.txt](./examples/cloud-master/instructions.txt). These instructions will guide the LLM on how to output code and other API commands for execution by the OpenKBS framework.
|
|
52
|
-
|
|
53
|
-
3. **Push the new instructions**:
|
|
54
|
-
- we have to push the instructions which are stored encrypted at OpenKBS registry:
|
|
55
|
-
```bash
|
|
56
|
-
openkbs push origin app/instructions.txt
|
|
57
|
-
```
|
|
58
|
-
- push to localstack to build and deploy all Node.js events - ./src/Events
|
|
59
|
-
```bash
|
|
60
|
-
openkbs push localstack
|
|
61
|
-
```
|
|
62
|
-
4. **Requesting the AI to Perform Tasks on Your PC and AWS Cloud**:
|
|
63
|
-
- Instruct the AI to list your desktop files, review the code, click `execute`, and click `send`:
|
|
64
|
-
```
|
|
65
|
-
List my desktop files
|
|
66
|
-
```
|
|
67
|
-
- Instruct the AI to create an S3 bucket and backup your desktop images to it:
|
|
68
|
-
```
|
|
69
|
-
Create an S3 bucket and back up my desktop images to it
|
|
70
|
-
```
|
|
71
|
-

|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## Installing openkbs-ai-server and Integrating Llama 3.1 and Stable Diffusion 3 Locally
|
|
75
|
-
|
|
76
|
-

|
|
77
|
-
|
|
78
|
-
To set up the `openkbs-ai-server` and integrate advanced AI models like Llama 3.1 and Stable Diffusion 3 on your local machine, follow the steps outlined below.
|
|
79
|
-
|
|
80
|
-
### Prerequisites
|
|
81
|
-
|
|
82
|
-
Ensure you have the following prerequisites installed and configured:
|
|
83
|
-
|
|
84
|
-
- Ubuntu 22.04 or a compatible Linux distribution.
|
|
85
|
-
- Python 3.10 and virtual environment tools.
|
|
86
|
-
- Node.js and npm.
|
|
87
|
-
- NVIDIA or AMD GPU drivers, depending on your hardware.
|
|
88
|
-
|
|
89
|
-
Please follow the installation on [GitHub](https://github.com/open-kbs/openkbs-ai-server).
|
|
90
|
-
|
|
91
|
-
### Step 1: Checkout, Build, and Run
|
|
92
|
-
|
|
93
|
-
Clone the `openkbs-ai-server` repository and set up the environment:
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
git clone git@github.com:open-kbs/openkbs-ai-server.git
|
|
97
|
-
cd openkbs-ai-server/cluster
|
|
98
|
-
npm i
|
|
99
|
-
cd ..
|
|
100
|
-
python -m venv .env
|
|
101
|
-
source .env/bin/activate
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
**IMPORTANT: SELECT THE CORRECT GPU INSTRUCTIONS BELOW. DO NOT EXECUTE BOTH.**
|
|
105
|
-
|
|
106
|
-
#### **FOR AMD GPUS:**
|
|
107
|
-
|
|
108
|
-
**ONLY FOLLOW THESE INSTRUCTIONS IF YOU HAVE AN AMD GPU.**
|
|
109
|
-
|
|
110
|
-
Install necessary libraries and Python packages:
|
|
111
|
-
|
|
112
|
-
```bash
|
|
113
|
-
sudo apt-get install -y libjpeg-dev libpng-dev
|
|
114
|
-
pip install wheel setuptools
|
|
115
|
-
pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/rocm6.1/
|
|
116
|
-
pip install -r ./models/requirements_AMD.txt
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
#### **FOR NVIDIA GPUS:**
|
|
120
|
-
|
|
121
|
-
**ONLY FOLLOW THESE INSTRUCTIONS IF YOU HAVE AN NVIDIA GPU.**
|
|
122
|
-
|
|
123
|
-
Install the required Python packages:
|
|
124
|
-
|
|
125
|
-
```bash
|
|
126
|
-
pip install torch
|
|
127
|
-
pip install -r ./models/requirements_NVIDIA.txt
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Step 2: Configure Hugging Face Authentication
|
|
131
|
-
|
|
132
|
-
Log in to Hugging Face to access the AI models:
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
huggingface-cli login
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
Enter your Hugging Face token when prompted.
|
|
139
|
-
|
|
140
|
-
### Step 3: Install Global Node.js Packages
|
|
141
|
-
|
|
142
|
-
Install global Node.js packages required for running the server:
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
npm install -g pm2 nodemon react-scripts
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### Step 4: Start the AI Server
|
|
149
|
-
|
|
150
|
-
Launch the AI server using the provided script:
|
|
151
|
-
|
|
152
|
-
```bash
|
|
153
|
-
./start.sh
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
This command will start both the frontend and backend services using pm2. Your default web browser should automatically open to [http://localhost:7080/register](http://localhost:7080/register), where you can register the admin account for the AI server.
|
|
157
|
-
|
|
158
|
-
### Step 5: Install AI Models
|
|
159
|
-
|
|
160
|
-
In the AI server admin panel, search for and install the following models:
|
|
161
|
-
|
|
162
|
-
- **Llama-3.1-8B-Instruct**: Ensure you have access to [Llama-3.1-8B-Instruct](https://huggingface.co/meta-llama/Llama-3.1-8B-Instruct) on Hugging Face.
|
|
163
|
-
- **Stable Diffusion 3**: Ensure you have access to [Stable Diffusion 3](https://huggingface.co/stabilityai/stable-diffusion-3-medium) on Hugging Face.
|
|
164
|
-
|
|
165
|
-
After installation, restart your chat server to apply the changes.
|
|
166
|
-
|
|
167
|
-
### Step 6: Integrate Stable Diffusion under Events actions, so that Llama can call it
|
|
168
|
-
|
|
169
|
-
Go to your app root folder
|
|
170
|
-
```bash
|
|
171
|
-
cd my-pc-agent
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
Add to `./src/Events/actions.js`
|
|
175
|
-
```javascript
|
|
176
|
-
[/\/?textToImage\("(.*)"\)/, async (match) => {
|
|
177
|
-
try {
|
|
178
|
-
const response = await axios.get(`http://localhost:8080/pipe/stabilityai--stable-diffusion-3-medium-diffusers--default?prompt=${encodeURIComponent(match[1])}`, {
|
|
179
|
-
responseType: 'arraybuffer'
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
const base64Data = Buffer.from(response.data, 'binary').toString('base64');
|
|
183
|
-
const contentType = response.headers['content-type'];
|
|
184
|
-
const imageSrc = `data:${contentType};base64,${base64Data}`;
|
|
185
|
-
|
|
186
|
-
return { type: 'SAVED_CHAT_IMAGE', imageSrc, ...meta };
|
|
187
|
-
} catch (error) {
|
|
188
|
-
console.error('Error fetching image:', error);
|
|
189
|
-
throw error; // or handle the error as needed
|
|
190
|
-
}
|
|
191
|
-
}]
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
Push the changes:
|
|
195
|
-
```bash
|
|
196
|
-
openkbs push localstack
|
|
197
|
-
```
|
|
198
|
-
### Step 7: Test Llama agent
|
|
199
|
-
|
|
200
|
-
Once the models are installed and the server is running, select `Llama-3.1-8B-Inst` in your Chat Models selection and type in the chat:
|
|
201
|
-
|
|
202
|
-
```
|
|
203
|
-
Hey Llama, search Google for the latest AI news and wait, then generate news image. Finally, use a template function to create an HTML page hosted on the S3 bucket 'ai-news-openkbs'.
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-

|
|
207
|
-
|
|
208
|
-

|
|
209
|
-
|
|
210
|
-

|
|
211
|
-
|
|
212
|
-

|
|
213
|
-
|
|
214
|
-

|
|
215
|
-
Have fun!
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export const getActions = (meta) => [
|
|
2
|
-
[/\/?googleSearch\("(.*)"\)/, async (match) => {
|
|
3
|
-
const q = match[1];
|
|
4
|
-
try {
|
|
5
|
-
const noSecrets = '{{secrets.googlesearch_api_key}}'.includes('secrets.googlesearch_api_key');
|
|
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;
|
|
10
|
-
const data = response?.map(({ title, link, snippet, pagemap }) => ({
|
|
11
|
-
title, link, snippet, image: pagemap?.metatags?.[0]?.["og:image"]
|
|
12
|
-
}));
|
|
13
|
-
return { data, ...meta };
|
|
14
|
-
} catch (e) {
|
|
15
|
-
return { error: e.response.data, ...meta };
|
|
16
|
-
}
|
|
17
|
-
}],
|
|
18
|
-
[/\/?textToImage\("(.*)"\)/, async (match) => {
|
|
19
|
-
try {
|
|
20
|
-
const response = await axios.get(`http://localhost:8080/pipe/stabilityai--stable-diffusion-3-medium-diffusers--default?prompt=${encodeURIComponent(match[1])}`, {
|
|
21
|
-
responseType: 'arraybuffer'
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const base64Data = Buffer.from(response.data, 'binary').toString('base64');
|
|
25
|
-
const contentType = response.headers['content-type'];
|
|
26
|
-
const imageSrc = `data:${contentType};base64,${base64Data}`;
|
|
27
|
-
|
|
28
|
-
return { type: 'SAVED_CHAT_IMAGE', imageSrc, ...meta };
|
|
29
|
-
} catch (error) {
|
|
30
|
-
console.error('Error fetching image:', error);
|
|
31
|
-
throw error; // or handle the error as needed
|
|
32
|
-
}
|
|
33
|
-
}]
|
|
34
|
-
];
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef } from "react";
|
|
2
|
-
import { IconButton, Tooltip, LinearProgress } from '@mui/material';
|
|
3
|
-
import CloseIcon from "@mui/icons-material/Close";
|
|
4
|
-
import SendIcon from "@mui/icons-material/Send";
|
|
5
|
-
import { FileCopy, PlayArrow } from "@mui/icons-material";
|
|
6
|
-
import { AppBar, Toolbar, Typography } from '@mui/material';
|
|
7
|
-
import { MoreVert as MenuIcon, AccountCircle as AccountIcon } from '@mui/icons-material';
|
|
8
|
-
|
|
9
|
-
// Inject custom styles
|
|
10
|
-
const style = document.createElement('style');
|
|
11
|
-
style.innerHTML = `
|
|
12
|
-
.codeContainer, .codeContainer code {
|
|
13
|
-
background-color: #0d0d0d !important;
|
|
14
|
-
color: white !important;
|
|
15
|
-
text-shadow: none !important;
|
|
16
|
-
border-radius: 10px !important;
|
|
17
|
-
font-size: 13px !important;
|
|
18
|
-
}
|
|
19
|
-
.codeContainer * {
|
|
20
|
-
background-color: #0d0d0d !important;
|
|
21
|
-
}
|
|
22
|
-
@keyframes pulseButton {
|
|
23
|
-
0% { transform: scale(1); }
|
|
24
|
-
50% { transform: scale(1.5); }
|
|
25
|
-
100% { transform: scale(1); }
|
|
26
|
-
}
|
|
27
|
-
.pulseButton {
|
|
28
|
-
animation: 0.7s pulse 2;
|
|
29
|
-
}
|
|
30
|
-
`;
|
|
31
|
-
document.head.appendChild(style);
|
|
32
|
-
|
|
33
|
-
// Utility function to parse JSON safely
|
|
34
|
-
function parseJSON(str) {
|
|
35
|
-
try {
|
|
36
|
-
if (str && typeof str === 'string' && (str[0] === '{' || str[0] === '[')) {
|
|
37
|
-
return JSON.parse(str);
|
|
38
|
-
}
|
|
39
|
-
} catch {}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Check if code contains ES6 syntax or JSX
|
|
43
|
-
export const containsES6SyntaxOrJSX = (code) => {
|
|
44
|
-
const es6Patterns = [/\bimport\b/, /\bexport\b/];
|
|
45
|
-
const jsxPattern = /<([A-Za-z][A-Za-z0-9]*)(\s+[A-Za-z0-9\-]+(\s*=\s*{[^}]*}|="[^"]*"|='[^']*'|=[^>\s]+)?)*\s*\/?>/;
|
|
46
|
-
return es6Patterns.some(pattern => pattern.test(code)) || jsxPattern.test(code);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const isMobile = window.innerWidth < 960;
|
|
50
|
-
|
|
51
|
-
export function CodeViewer(props) {
|
|
52
|
-
const [response, setResponse] = useState(null);
|
|
53
|
-
const [codeRunLoading, setCodeRunLoading] = useState(false);
|
|
54
|
-
const [isLongContent, setIsLongContent] = useState(false);
|
|
55
|
-
const [prismHeight, setPrismHeight] = useState(0);
|
|
56
|
-
const responseRef = useRef(null);
|
|
57
|
-
const prismRef = useRef(null);
|
|
58
|
-
const [tooltipOpen, setTooltipOpen] = useState(true);
|
|
59
|
-
|
|
60
|
-
const handleTooltipClose = () => setTooltipOpen(false);
|
|
61
|
-
const handleTooltipOpen = () => setTooltipOpen(true);
|
|
62
|
-
|
|
63
|
-
const { source, onClose, styleCopy, noCopy, limitedWidth, styleClose, execute, ...restProps } = props;
|
|
64
|
-
const { ReactPrismjs, APIResponseComponent, CopyToClipboard } = props.params;
|
|
65
|
-
const { RequestChatAPI, kbUserData, generateMsgId, msgIndex, messages, setSystemAlert, KB, axios } = props.params;
|
|
66
|
-
|
|
67
|
-
const isLastMessage = msgIndex >= (messages?.filter(msg => msg?.content)?.length - 1);
|
|
68
|
-
const canExecute = execute && props?.language?.includes('javascript') && !containsES6SyntaxOrJSX(source);
|
|
69
|
-
|
|
70
|
-
useEffect(() => {
|
|
71
|
-
const lineCount = source ? source.split('\n').length : 0;
|
|
72
|
-
setIsLongContent(lineCount > 25);
|
|
73
|
-
}, [source]);
|
|
74
|
-
|
|
75
|
-
useEffect(() => {
|
|
76
|
-
if (response && responseRef.current) {
|
|
77
|
-
responseRef.current.scrollIntoView({ behavior: 'smooth' });
|
|
78
|
-
}
|
|
79
|
-
}, [response]);
|
|
80
|
-
|
|
81
|
-
useEffect(() => {
|
|
82
|
-
if (prismRef.current) {
|
|
83
|
-
setPrismHeight(prismRef.current.clientHeight);
|
|
84
|
-
}
|
|
85
|
-
}, [source]);
|
|
86
|
-
|
|
87
|
-
const runCode = async () => {
|
|
88
|
-
try {
|
|
89
|
-
const code = source.includes('module.exports')
|
|
90
|
-
? source
|
|
91
|
-
: `const handler = async (event) => { ${source} }; module.exports = { handler };`;
|
|
92
|
-
setCodeRunLoading(true);
|
|
93
|
-
const response = await axios.post(`http://localhost:38595`, {
|
|
94
|
-
walletPrivateKey: KB?.walletPrivateKey,
|
|
95
|
-
walletPublicKey: KB?.walletPublicKey,
|
|
96
|
-
accountId: KB?.accountId,
|
|
97
|
-
userCode:code,
|
|
98
|
-
AESKey: KB?.key
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
setCodeRunLoading(false);
|
|
102
|
-
setResponse(response?.data?.error ? JSON.stringify(response.data) : JSON.stringify(response.data));
|
|
103
|
-
} catch (error) {
|
|
104
|
-
setCodeRunLoading(false);
|
|
105
|
-
setResponse(`Error: ${error?.response?.status || error.message}`);
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const preStyle = {
|
|
110
|
-
whiteSpace: limitedWidth ? 'pre-wrap' : undefined,
|
|
111
|
-
wordBreak: limitedWidth ? 'break-all' : undefined,
|
|
112
|
-
...restProps.style
|
|
113
|
-
};
|
|
114
|
-
const colorIcon = '#e7e7e7';
|
|
115
|
-
const oneliner = !(source?.split?.('\n')?.length > 1);
|
|
116
|
-
|
|
117
|
-
const handleOnCopy = () => {
|
|
118
|
-
setSystemAlert && setSystemAlert({ msg: 'Copied to clipboard', type: 'success', duration: 1500 });
|
|
119
|
-
props?.onCopy && props.onCopy();
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const formattedResponse = parseJSON(response) || { response }
|
|
123
|
-
|
|
124
|
-
return (
|
|
125
|
-
<div style={{ paddingBottom: 2, position: 'relative', display: 'inline-block', maxWidth: '100%', minWidth: '350px', overflowX: 'auto' }}>
|
|
126
|
-
{oneliner && <div style={{ height: '40px' }}></div>}
|
|
127
|
-
{onClose && (
|
|
128
|
-
<IconButton onClick={onClose} style={styleClose}>
|
|
129
|
-
<CloseIcon style={{ opacity: 0.7, fontSize: '1rem', color: colorIcon }} />
|
|
130
|
-
</IconButton>
|
|
131
|
-
)}
|
|
132
|
-
<div ref={prismRef}>
|
|
133
|
-
<ReactPrismjs {...{ source, style: preStyle, ...restProps }} />
|
|
134
|
-
</div>
|
|
135
|
-
{!codeRunLoading && (
|
|
136
|
-
<>
|
|
137
|
-
<CopyToClipboard text={source || ''}>
|
|
138
|
-
<IconButton onClick={handleOnCopy} style={{ position: 'absolute', top: `${prismHeight - 30}px`, right: '0px', zIndex: 10, color: 'rgba(11, 11, 11, 0.54)' }}>
|
|
139
|
-
{!noCopy && <FileCopy style={{ opacity: 0.7, color: colorIcon }} />}
|
|
140
|
-
</IconButton>
|
|
141
|
-
</CopyToClipboard>
|
|
142
|
-
{canExecute && (
|
|
143
|
-
<Tooltip title="Execute" arrow onMouseEnter={handleTooltipClose} onMouseLeave={handleTooltipOpen} open={isLastMessage && !response && !codeRunLoading && tooltipOpen} placement={"top"}>
|
|
144
|
-
<IconButton onClick={runCode} style={{ position: 'absolute', top: `${prismHeight - 30}px`, right: '36px', animation: isLastMessage && !response && !codeRunLoading && 'pulseButton 1.5s infinite', ...styleCopy }}>
|
|
145
|
-
<PlayArrow style={{ color: '#e7e7e7' }} />
|
|
146
|
-
</IconButton>
|
|
147
|
-
</Tooltip>
|
|
148
|
-
)}
|
|
149
|
-
</>
|
|
150
|
-
)}
|
|
151
|
-
{codeRunLoading ? (
|
|
152
|
-
<LinearProgress />
|
|
153
|
-
) : (
|
|
154
|
-
response && (
|
|
155
|
-
<div ref={responseRef} style={{ position: 'relative', paddingTop: '30px' }}>
|
|
156
|
-
<APIResponseComponent open={false} showResponseSize={true} JSONData={formattedResponse} enableClipboard={true} />
|
|
157
|
-
<>
|
|
158
|
-
<CopyToClipboard text={response || ''}>
|
|
159
|
-
<IconButton onClick={handleOnCopy} style={{ position: 'absolute', top: '35px', right: '0px' }}>
|
|
160
|
-
<FileCopy style={{ opacity: 0.7, color: '#3D86C9' }} />
|
|
161
|
-
</IconButton>
|
|
162
|
-
</CopyToClipboard>
|
|
163
|
-
{isLastMessage && (
|
|
164
|
-
<Tooltip title="Send Response" arrow open={isLastMessage} placement={"top"}>
|
|
165
|
-
<IconButton onClick={async () => {
|
|
166
|
-
await RequestChatAPI([...messages, {
|
|
167
|
-
role: 'user',
|
|
168
|
-
content: typeof response === 'number' ? response.toString() : response,
|
|
169
|
-
userId: kbUserData().chatUsername,
|
|
170
|
-
msgId: generateMsgId()
|
|
171
|
-
}]);
|
|
172
|
-
}} style={{ position: 'absolute', top: '35px', right: '36px', animation: 'pulseButton 1.5s infinite' }}>
|
|
173
|
-
<SendIcon style={{ opacity: 0.9, color: '#3D86C9' }} />
|
|
174
|
-
</IconButton>
|
|
175
|
-
</Tooltip>
|
|
176
|
-
)}
|
|
177
|
-
</>
|
|
178
|
-
</div>
|
|
179
|
-
)
|
|
180
|
-
)}
|
|
181
|
-
</div>
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const onRenderChatMessage = async (params) => {
|
|
186
|
-
const { content } = params.messages[params.msgIndex];
|
|
187
|
-
if (content.match(/```/)) {
|
|
188
|
-
let language = null;
|
|
189
|
-
const output = [];
|
|
190
|
-
|
|
191
|
-
content.split('\n').forEach(line => {
|
|
192
|
-
if (!language) {
|
|
193
|
-
language = /```(?<language>\w+)/g.exec(line)?.groups?.language;
|
|
194
|
-
output.push(language ? { language, code: '' } : line);
|
|
195
|
-
} else if (line.match(/```/)) {
|
|
196
|
-
language = null;
|
|
197
|
-
} else {
|
|
198
|
-
output[output.length - 1].code += line + '\n';
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
return output.map((o, i) =>
|
|
203
|
-
typeof o === 'string'
|
|
204
|
-
? <p key={`a${i}`} style={{ marginTop: '0px', marginBottom: '0px' }}>{o}</p>
|
|
205
|
-
: <div key={`a${i}`}>
|
|
206
|
-
<CodeViewer
|
|
207
|
-
key={`ab${i}`}
|
|
208
|
-
params={{ ...params, i }}
|
|
209
|
-
limitedWidth={isMobile}
|
|
210
|
-
execute={true}
|
|
211
|
-
className="codeContainer"
|
|
212
|
-
language={o.language}
|
|
213
|
-
source={o.code}
|
|
214
|
-
/>
|
|
215
|
-
</div>
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const Header = ({ setRenderSettings}) => {
|
|
221
|
-
useEffect(() => {
|
|
222
|
-
setRenderSettings({
|
|
223
|
-
disableShareButton: true,
|
|
224
|
-
disableBalanceView: true,
|
|
225
|
-
disableSentLabel: false,
|
|
226
|
-
disableChatAvatar: false,
|
|
227
|
-
disableChatModelsSelect: false,
|
|
228
|
-
disableContextItems: false,
|
|
229
|
-
disableCopyButton: false,
|
|
230
|
-
disableEmojiButton: false,
|
|
231
|
-
disableTextToSpeechButton: false,
|
|
232
|
-
disableMobileLeftButton: false,
|
|
233
|
-
});
|
|
234
|
-
}, [setRenderSettings]);
|
|
235
|
-
|
|
236
|
-
return (
|
|
237
|
-
<AppBar position="absolute" style={{ zIndex: 2000, flexGrow: 1, textAlign: 'left' }}>
|
|
238
|
-
<Toolbar>
|
|
239
|
-
<IconButton edge="start" color="inherit" aria-label="menu" style={{ marginRight: '16px' }}>
|
|
240
|
-
<MenuIcon />
|
|
241
|
-
</IconButton>
|
|
242
|
-
<Typography variant="h6" style={{ flexGrow: 1 }}>
|
|
243
|
-
My PC Agent
|
|
244
|
-
</Typography>
|
|
245
|
-
<IconButton edge="end" color="inherit" aria-label="account">
|
|
246
|
-
<AccountIcon />
|
|
247
|
-
</IconButton>
|
|
248
|
-
</Toolbar>
|
|
249
|
-
</AppBar>
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const exports = { onRenderChatMessage, Header };
|
|
254
|
-
window.contentRender = exports;
|
|
255
|
-
export default exports;
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
You are an AI assistant, assist users with their inquiries and tasks efficiently.
|
|
2
|
-
- Always output one action per message and wait for a response before continuing.
|
|
3
|
-
- When asked to generate HTML, always enclose the response in `<html>{your implementation}</html>` tags.
|
|
4
|
-
|
|
5
|
-
API_COMMANDS_INSTRUCTIONS:
|
|
6
|
-
|
|
7
|
-
Guidelines:
|
|
8
|
-
- always ouput any commands on by one, and wait for the response
|
|
9
|
-
|
|
10
|
-
Here are some API commands you can use to assist users:
|
|
11
|
-
|
|
12
|
-
/googleSearch("query")
|
|
13
|
-
Description: """
|
|
14
|
-
Get results from Google Search API.
|
|
15
|
-
"""
|
|
16
|
-
$InputLabel = """Let me Search in Google!"""
|
|
17
|
-
$InputValue = """Search in google for the latest news"""
|
|
18
|
-
|
|
19
|
-
/textToImage("image prompt")
|
|
20
|
-
Description: """
|
|
21
|
-
Generates images by text prompt (English only supported).
|
|
22
|
-
|
|
23
|
-
A good prompt needs to be detailed and specific.
|
|
24
|
-
A good process is to look through a list of keyword categories and decide whether you want to use any of them.
|
|
25
|
-
The keyword categories are:
|
|
26
|
-
|
|
27
|
-
Subject - The subject is what you want to see in the image. A common mistake is not writing enough about the subjects. (e.g., beautiful and powerful mysterious sorceress, smile, sitting on a rock, lightning magic, hat, detailed leather clothing with gemstones, dress, castle background)
|
|
28
|
-
Medium - Medium is the material used to make artwork. Some examples are illustration, oil painting, 3D rendering, and photography. Medium has a strong effect because one keyword alone can dramatically change the style. (e.g., digital art)
|
|
29
|
-
Style - The style refers to the artistic style of the image. Examples include impressionist, surrealist, pop art, etc. (e.g., impressionist, surrealist, pop art)
|
|
30
|
-
Resolution - Resolution represents how sharp and detailed the image is. Let’s add keywords highly detailed and sharp focus. (e.g., highly detailed, sharp focus, 8k)
|
|
31
|
-
Additional details - Additional details are sweeteners added to modify an image. We will add sci-fi and dystopian to add some vibe to the image. (e.g., sci-fi, dystopian)
|
|
32
|
-
Color - You can control the overall color of the image by adding color keywords. The colors you specified may appear as a tone or in objects. (e.g., iridescent gold)
|
|
33
|
-
Lighting - Any photographer would tell you lighting is key to creating successful images. Lighting keywords can have a huge effect on how the image looks. Let’s add studio lighting to make it studio photo-like. (e.g., studio lighting)
|
|
34
|
-
Remarks - You may have noticed the images are already pretty good with only a few keywords added. More is not always better when building a prompt. You often don’t need many keywords to get good images.
|
|
35
|
-
"""
|
|
36
|
-
$InputLabel = """Let me Generate Image!"""
|
|
37
|
-
$InputValue = """Generate a cover image for a health and weight loss campaign"""
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
FUNCTION_EXECUTION_INSTRUCTIONS:
|
|
41
|
-
|
|
42
|
-
You can also use Node.js code to perform tasks on the user's local PC or perform AWS code using the provided template functions.
|
|
43
|
-
|
|
44
|
-
Guidelines:
|
|
45
|
-
|
|
46
|
-
- Output only one Template Function per message.
|
|
47
|
-
- Functions always export the handler function at the end.
|
|
48
|
-
- Once you output a function, it will be executed (as it is) on the user's PC and you will get the response.
|
|
49
|
-
- Before performing any specific operations, first explore the available resources to understand their structure and contents. This could involve listing available items, such as tables in a database, files in a directory, or objects in a cloud storage bucket.
|
|
50
|
-
- Once the resources are identified, gather detailed information about their structure and attributes. This might include describing the schema of a database table, examining metadata of files, or reviewing properties of cloud resources.
|
|
51
|
-
- If the initial exploration does not provide sufficient information, inspect a sample of the resource to gain a deeper understanding of its structure and contents. This could involve selecting a few records from a database, opening a few files, or downloading a few objects from cloud storage.
|
|
52
|
-
- Avoid using placeholder variables in the code. Instead, request the necessary information from the user to generate fully functional and executable code.
|
|
53
|
-
- Common env variables process.env.HOME
|
|
54
|
-
|
|
55
|
-
By following these steps, you can ensure a comprehensive understanding of the resources you are working with
|
|
56
|
-
|
|
57
|
-
Template Functions:
|
|
58
|
-
|
|
59
|
-
```javascript
|
|
60
|
-
const AWS = require('aws-sdk'); // require example 1
|
|
61
|
-
const dns = require('dns'); // require example 2
|
|
62
|
-
const { promisify } = require('util'); // require example 3
|
|
63
|
-
|
|
64
|
-
AWS.config.update({ region: 'us-east-1' });
|
|
65
|
-
|
|
66
|
-
const handler = async () => {
|
|
67
|
-
// Generate code here to perform the desired operations and get a response.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// code Examples:
|
|
71
|
-
// return await new AWS.S3().listBuckets().promise();
|
|
72
|
-
// return await promisify(dns.resolveAny)('example.net')
|
|
73
|
-
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
module.exports = { handler };
|
|
77
|
-
```
|
|
Binary file
|
|
Binary file
|