openkbs 0.0.36 → 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.
Files changed (29) hide show
  1. package/package.json +1 -1
  2. package/scripts/deploy-templates.js +8 -28
  3. package/src/utils.js +0 -1
  4. package/templates/.openkbs/knowledge/README.md +44 -21
  5. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/app/icon.png +0 -0
  6. package/templates/.openkbs/knowledge/examples/{ai-copywriter → ai-copywriter-agent}/app/instructions.txt +1 -1
  7. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/app/settings.json +6 -0
  8. package/templates/.openkbs/knowledge/examples/{ai-copywriter/run_agent.js → ai-copywriter-agent/run_job.js} +7 -3
  9. package/templates/.openkbs/knowledge/examples/{ai-copywriter → ai-copywriter-agent}/src/Events/actions.js +10 -21
  10. package/templates/.openkbs/knowledge/examples/{ai-copywriter/src/Events/onResponse.js → ai-copywriter-agent/src/Events/handler.js} +6 -10
  11. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/onRequest.js +3 -0
  12. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/onRequest.json +5 -0
  13. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/onResponse.js +3 -0
  14. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/onResponse.json +5 -0
  15. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Frontend/contentRender.js +64 -0
  16. package/templates/.openkbs/knowledge/examples/{ai-copywriter → ai-copywriter-agent}/src/Frontend/contentRender.json +2 -1
  17. package/templates/.openkbs/knowledge/metadata.json +1 -1
  18. package/templates/CLAUDE.md +9 -5
  19. package/templates/app/instructions.txt +1 -10
  20. package/templates/app/settings.json +2 -2
  21. package/templates/src/Events/actions.js +8 -10
  22. package/templates/src/Events/handler.js +49 -0
  23. package/templates/src/Events/onRequest.js +2 -13
  24. package/templates/src/Events/onRequest.json +5 -0
  25. package/templates/src/Events/onResponse.js +2 -12
  26. package/templates/{.openkbs/knowledge/examples/ai-copywriter/src → src}/Events/onResponse.json +1 -0
  27. package/templates/.openkbs/knowledge/examples/ai-copywriter/app/icon.png +0 -0
  28. package/templates/.openkbs/knowledge/examples/ai-copywriter/app/settings.json +0 -13
  29. package/templates/.openkbs/knowledge/examples/ai-copywriter/src/Frontend/contentRender.js +0 -26
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openkbs",
3
- "version": "0.0.36",
3
+ "version": "0.0.37",
4
4
  "description": "OpenKBS - Command Line Interface",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -1,41 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs-extra');
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('Deploying templates to S3...');
35
- await uploadDirectory(TEMPLATES_DIR, 'templates');
36
- console.log('Templates deployed successfully!');
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 deploying templates:', error);
18
+ console.error('Error syncing templates:', error);
39
19
  process.exit(1);
40
20
  }
41
21
  }
package/src/utils.js CHANGED
@@ -1080,7 +1080,6 @@ 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
@@ -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 existing code first:**
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
- 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
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
- 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
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
- ## When Examples Are Required
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
- - 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
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
- - Simple text changes to existing files
29
- - Minor bug fixes in existing code
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
- ## Critical Rule
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
@@ -0,0 +1,6 @@
1
+ {
2
+ "chatVendor": "anthropic",
3
+ "kbDescription": "An autonomous AI powerhouse for content generation",
4
+ "kbTitle": "Copywriter",
5
+ "model": "claude-3-5-haiku-20241022"
6
+ }
@@ -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 = `PROCESS_PRODUCT:
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
- console.log(JSON.parse(message.content).data[0]);
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 json = extractJSONFromText(match[0]);
36
- if (json && json.type === "JOB_FAILED") {
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(json.reason),
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\("(.*?)"(?:,\s*(\d+))?\)/, async (match) => {
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
- })).slice(0, limit);
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\("(.*?)"(?:,\s*(\d+))?\)/, async (match) => {
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')).slice(0, limit);
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\("(.*?)"(?:,\s*(\d+))?\)/, async (match) => {
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
- })?.slice(0, limit);
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 handler = async (event) => {
3
+ export const backendHandler = async (event) => {
4
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
-
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 = isJobCompleted || isJobFailed;
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 && (matchingActions.length === 1 || lastMessage.role === 'system')
23
+ isJobFinished && lastMessage.role === 'system'
28
24
  )
29
25
  ? []
30
26
  : ["REQUEST_CHAT_MODEL"]
@@ -0,0 +1,3 @@
1
+ import {backendHandler} from './handler';
2
+
3
+ export const handler = async (event) => backendHandler(event)
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "mysql2": "^3.11.4"
4
+ }
5
+ }
@@ -0,0 +1,3 @@
1
+ import {backendHandler} from './handler';
2
+
3
+ export const handler = async (event) => backendHandler(event)
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "mysql2": "^3.11.4"
4
+ }
5
+ }
@@ -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;
@@ -5,6 +5,7 @@
5
5
  "@mui/material": "^5.16.1 (fixed)",
6
6
  "@mui/icons-material": "^5.16.1 (fixed)",
7
7
  "@emotion/react": "^11.10.6 (fixed)",
8
- "@emotion/styled": "^11.10.6 (fixed)"
8
+ "@emotion/styled": "^11.10.6 (fixed)",
9
+ "@uiw/react-json-view": "^2.0.0-alpha.33"
9
10
  }
10
11
  }
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.0.1"
2
+ "version": "0.0.8"
3
3
  }
@@ -1,15 +1,19 @@
1
1
  # Claude Code Instructions
2
2
 
3
- ## Initial Setup
4
- 1. First, update the knowledge base to ensure you have the latest information:
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. Then read the project contribution guidelines:
11
+ 2. **IMMEDIATELY AFTER**: Read the knowledge base README:
10
12
  ```bash
11
- # Read the knowledge base README for detailed instructions
13
+ # THIS MUST BE YOUR SECOND ACTION - READ THIS FILE FOR MORE INSTRUCTIONS
12
14
  .openkbs/knowledge/README.md
13
15
  ```
14
16
 
15
- This file contains comprehensive instructions on how to contribute to this OpenKBS project.
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,8 +1,8 @@
1
1
  {
2
- "chatVendor": "openai",
2
+ "chatVendor": "anthropic",
3
3
  "kbDescription": "{{{openkbsAppName}}}",
4
4
  "kbTitle": "{{{openkbsAppName}}}",
5
- "model": "gpt-4o",
5
+ "model": "claude-sonnet-4-20250514",
6
6
  "inputTools": [
7
7
  "imageToText",
8
8
  "speechToText"
@@ -1,18 +1,16 @@
1
- export const getActions = (meta) => [
2
- [/\/?googleSearch\("(.*)"\)/, async (match) => {
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 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;
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, ...meta };
10
+ return { data };
14
11
  } catch (e) {
15
- return { error: e.response.data, ...meta };
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 {getActions} from './actions.js';
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)
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+
4
+ }
5
+ }
@@ -1,13 +1,3 @@
1
- import {getActions} from './actions.js';
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)
@@ -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;