openkbs 0.0.53 → 0.0.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1490 -202
- package/package.json +2 -1
- package/src/actions.js +345 -1
- package/src/index.js +17 -1
- package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/app/instructions.txt +44 -9
- package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/actions.js +43 -42
- package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/handler.js +14 -8
- package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Frontend/contentRender.js +95 -12
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/README.md +64 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/app/instructions.txt +160 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/app/settings.json +7 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Events/actions.js +258 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Events/onRequest.js +13 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Events/onRequest.json +3 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Events/onResponse.js +13 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Events/onResponse.json +3 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Frontend/contentRender.js +170 -0
- package/templates/.openkbs/knowledge/examples/ai-marketing-agent/src/Frontend/contentRender.json +3 -0
- package/templates/.openkbs/knowledge/metadata.json +1 -1
- package/templates/CLAUDE.md +593 -222
- package/templates/app/instructions.txt +13 -1
- package/templates/app/settings.json +5 -6
- package/templates/src/Events/actions.js +43 -9
- package/templates/src/Events/handler.js +24 -25
- package/templates/webpack.contentRender.config.js +8 -2
- package/version.json +3 -3
- package/MODIFY.md +0 -132
package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Frontend/contentRender.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import React, { useEffect } from "react";
|
|
2
2
|
import JsonView from '@uiw/react-json-view';
|
|
3
|
-
import { Chip } from '@mui/material';
|
|
3
|
+
import { Box, Tooltip, Chip } from '@mui/material';
|
|
4
|
+
import SearchIcon from '@mui/icons-material/Search';
|
|
5
|
+
import LanguageIcon from '@mui/icons-material/Language';
|
|
6
|
+
import ImageIcon from '@mui/icons-material/Image';
|
|
7
|
+
import YouTubeIcon from '@mui/icons-material/YouTube';
|
|
8
|
+
import ArticleIcon from '@mui/icons-material/Article';
|
|
4
9
|
|
|
5
10
|
const extractJSONFromText = (text) => {
|
|
6
11
|
let braceCount = 0, startIndex = text.indexOf('{');
|
|
@@ -19,24 +24,102 @@ const extractJSONFromText = (text) => {
|
|
|
19
24
|
return null;
|
|
20
25
|
}
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
// Command patterns for XML+JSON format
|
|
28
|
+
const COMMAND_PATTERNS = [
|
|
29
|
+
/<googleSearch>[\s\S]*?<\/googleSearch>/,
|
|
30
|
+
/<youtubeSearch>[\s\S]*?<\/youtubeSearch>/,
|
|
31
|
+
/<googleImageSearch>[\s\S]*?<\/googleImageSearch>/,
|
|
32
|
+
/<webpageToText>[\s\S]*?<\/webpageToText>/,
|
|
33
|
+
/<documentToText>[\s\S]*?<\/documentToText>/,
|
|
34
|
+
/<imageToText>[\s\S]*?<\/imageToText>/
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
// Icon mapping for commands
|
|
38
|
+
const commandIcons = {
|
|
39
|
+
googleSearch: SearchIcon,
|
|
40
|
+
youtubeSearch: YouTubeIcon,
|
|
41
|
+
googleImageSearch: ImageIcon,
|
|
42
|
+
webpageToText: LanguageIcon,
|
|
43
|
+
documentToText: ArticleIcon,
|
|
44
|
+
imageToText: ImageIcon
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Parse commands from content
|
|
48
|
+
const parseCommands = (content) => {
|
|
49
|
+
const commands = [];
|
|
50
|
+
const regex = /<(\w+)>([\s\S]*?)<\/\1>/g;
|
|
51
|
+
let match;
|
|
52
|
+
while ((match = regex.exec(content)) !== null) {
|
|
53
|
+
try {
|
|
54
|
+
commands.push({
|
|
55
|
+
name: match[1],
|
|
56
|
+
data: JSON.parse(match[2].trim())
|
|
57
|
+
});
|
|
58
|
+
} catch (e) {
|
|
59
|
+
commands.push({ name: match[1], data: match[2].trim() });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return commands;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Render command as icon with tooltip
|
|
66
|
+
const CommandIcon = ({ command }) => {
|
|
67
|
+
const Icon = commandIcons[command.name] || SearchIcon;
|
|
68
|
+
return (
|
|
69
|
+
<Tooltip
|
|
70
|
+
title={
|
|
71
|
+
<Box sx={{ p: 1 }}>
|
|
72
|
+
<Box sx={{ fontWeight: 'bold', color: '#4CAF50', mb: 0.5 }}>{command.name}</Box>
|
|
73
|
+
<pre style={{ margin: 0, fontSize: '10px' }}>
|
|
74
|
+
{typeof command.data === 'object'
|
|
75
|
+
? JSON.stringify(command.data, null, 2)
|
|
76
|
+
: command.data}
|
|
77
|
+
</pre>
|
|
78
|
+
</Box>
|
|
79
|
+
}
|
|
80
|
+
arrow
|
|
81
|
+
>
|
|
82
|
+
<Box sx={{
|
|
83
|
+
display: 'inline-flex',
|
|
84
|
+
width: 32, height: 32,
|
|
85
|
+
borderRadius: '50%',
|
|
86
|
+
backgroundColor: 'rgba(76, 175, 80, 0.1)',
|
|
87
|
+
border: '2px solid rgba(76, 175, 80, 0.3)',
|
|
88
|
+
alignItems: 'center',
|
|
89
|
+
justifyContent: 'center',
|
|
90
|
+
mx: 0.5,
|
|
91
|
+
cursor: 'pointer',
|
|
92
|
+
'&:hover': {
|
|
93
|
+
backgroundColor: 'rgba(76, 175, 80, 0.2)',
|
|
94
|
+
transform: 'scale(1.1)'
|
|
95
|
+
},
|
|
96
|
+
transition: 'all 0.2s'
|
|
97
|
+
}}>
|
|
98
|
+
<Icon sx={{ fontSize: 16, color: '#4CAF50' }} />
|
|
99
|
+
</Box>
|
|
100
|
+
</Tooltip>
|
|
101
|
+
);
|
|
102
|
+
};
|
|
25
103
|
|
|
26
104
|
// do NOT useState() directly in this function, it is not a React component
|
|
27
105
|
const onRenderChatMessage = async (params) => {
|
|
28
106
|
const { content } = params.messages[params.msgIndex];
|
|
29
107
|
const JSONData = extractJSONFromText(content);
|
|
30
|
-
|
|
31
|
-
|
|
108
|
+
|
|
109
|
+
// Render JOB_COMPLETED as JSON view
|
|
32
110
|
if (JSONData?.type === 'JOB_COMPLETED') {
|
|
33
111
|
return <JsonView value={JSONData} />
|
|
34
112
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
)
|
|
113
|
+
|
|
114
|
+
// Check for commands in content
|
|
115
|
+
const hasCommand = COMMAND_PATTERNS.some(p => p.test(content));
|
|
116
|
+
if (hasCommand) {
|
|
117
|
+
const commands = parseCommands(content);
|
|
118
|
+
return (
|
|
119
|
+
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
|
|
120
|
+
{commands.map((cmd, i) => <CommandIcon key={i} command={cmd} />)}
|
|
121
|
+
</Box>
|
|
122
|
+
);
|
|
40
123
|
}
|
|
41
124
|
|
|
42
125
|
// return undefined to use default message render
|
|
@@ -61,4 +144,4 @@ const Header = ({ setRenderSettings }) => {
|
|
|
61
144
|
|
|
62
145
|
const exports = { onRenderChatMessage, Header };
|
|
63
146
|
window.contentRender = exports;
|
|
64
|
-
export default exports;
|
|
147
|
+
export default exports;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# AI Marketing Agent
|
|
2
|
+
|
|
3
|
+
A comprehensive AI marketing assistant that can create content, generate images and videos, search the web, send emails, and schedule tasks.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **AI Image Generation**: Gemini 2.5 Flash Image and GPT-Image-1 models
|
|
8
|
+
- **AI Video Generation**: Sora 2 and Sora 2 Pro models
|
|
9
|
+
- **Web Search**: Google Search and Image Search
|
|
10
|
+
- **Email Marketing**: Send emails directly from chat
|
|
11
|
+
- **Task Scheduling**: Schedule future reminders and tasks
|
|
12
|
+
- **Web Publishing**: Create and publish HTML landing pages
|
|
13
|
+
- **Memory System**: Persistent storage for user preferences and content
|
|
14
|
+
|
|
15
|
+
## Command Format
|
|
16
|
+
|
|
17
|
+
All commands use XML tags with JSON content:
|
|
18
|
+
|
|
19
|
+
```xml
|
|
20
|
+
<commandName>
|
|
21
|
+
{
|
|
22
|
+
"param1": "value1",
|
|
23
|
+
"param2": "value2"
|
|
24
|
+
}
|
|
25
|
+
</commandName>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Available Commands
|
|
29
|
+
|
|
30
|
+
### Content Creation
|
|
31
|
+
- `<createAIImage>` - Generate images with AI
|
|
32
|
+
- `<createAIVideo>` - Generate videos with Sora 2
|
|
33
|
+
- `<publishWebPage>` - Publish HTML landing pages
|
|
34
|
+
|
|
35
|
+
### Search & Research
|
|
36
|
+
- `<googleSearch>` - Web search
|
|
37
|
+
- `<googleImageSearch>` - Image search
|
|
38
|
+
- `<webpageToText>` - Extract text from webpages
|
|
39
|
+
- `<viewImage>` - View image in context
|
|
40
|
+
|
|
41
|
+
### Communication
|
|
42
|
+
- `<sendMail>` - Send emails
|
|
43
|
+
|
|
44
|
+
### Task Management
|
|
45
|
+
- `<scheduleTask>` - Schedule future tasks
|
|
46
|
+
- `<getScheduledTasks/>` - List scheduled tasks
|
|
47
|
+
|
|
48
|
+
### Memory
|
|
49
|
+
- `<setMemory>` - Save to memory
|
|
50
|
+
- `<deleteItem>` - Delete from memory
|
|
51
|
+
|
|
52
|
+
## Architecture
|
|
53
|
+
|
|
54
|
+
- `src/Events/actions.js` - Command implementations
|
|
55
|
+
- `src/Events/onRequest.js` - User message handler
|
|
56
|
+
- `src/Events/onResponse.js` - LLM response handler
|
|
57
|
+
- `src/Frontend/contentRender.js` - UI customization
|
|
58
|
+
- `app/instructions.txt` - LLM instructions
|
|
59
|
+
|
|
60
|
+
## Deployment
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
openkbs push
|
|
64
|
+
```
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
You are an AI Marketing Assistant that helps users create content, images, videos, and marketing materials.
|
|
2
|
+
|
|
3
|
+
## Current Time
|
|
4
|
+
- UTC: {{openkbsDateNow}}
|
|
5
|
+
- Local: {{openkbsDate:en-US:UTC}}
|
|
6
|
+
|
|
7
|
+
## Core Capabilities
|
|
8
|
+
1. **AI Image Generation** - Create images with Gemini or GPT-Image
|
|
9
|
+
2. **AI Video Generation** - Create videos with Sora 2
|
|
10
|
+
3. **Web Search** - Search Google for information and images
|
|
11
|
+
4. **Email Marketing** - Send marketing emails
|
|
12
|
+
5. **Task Scheduling** - Schedule future reminders and tasks
|
|
13
|
+
6. **Web Publishing** - Create and publish landing pages
|
|
14
|
+
7. **Memory** - Remember user preferences and content ideas
|
|
15
|
+
|
|
16
|
+
## AVAILABLE COMMANDS
|
|
17
|
+
To execute a command, output it and wait for the system response.
|
|
18
|
+
|
|
19
|
+
<setMemory>
|
|
20
|
+
{
|
|
21
|
+
"itemId": "memory_key_name",
|
|
22
|
+
"value": "any value or object",
|
|
23
|
+
"expirationInMinutes": 1440
|
|
24
|
+
}
|
|
25
|
+
</setMemory>
|
|
26
|
+
Description: """
|
|
27
|
+
Save information to memory. itemId must start with 'memory_'. Optional expiration.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
<deleteItem>
|
|
31
|
+
{
|
|
32
|
+
"itemId": "memory_key_name"
|
|
33
|
+
}
|
|
34
|
+
</deleteItem>
|
|
35
|
+
Description: """
|
|
36
|
+
Delete a memory item by its itemId.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
<createAIImage>
|
|
40
|
+
{
|
|
41
|
+
"model": "gemini-2.5-flash-image",
|
|
42
|
+
"aspect_ratio": "16:9",
|
|
43
|
+
"prompt": "detailed image description"
|
|
44
|
+
}
|
|
45
|
+
</createAIImage>
|
|
46
|
+
Description: """
|
|
47
|
+
Generate AI images.
|
|
48
|
+
- gemini-2.5-flash-image: aspect_ratio (1:1, 16:9, 9:16, 3:2, 4:3, etc.), supports imageUrls for editing
|
|
49
|
+
- gpt-image-1: size (1024x1024, 1536x1024, 1024x1536), better for text in images
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
<createAIVideo>
|
|
53
|
+
{
|
|
54
|
+
"model": "sora-2",
|
|
55
|
+
"size": "1280x720",
|
|
56
|
+
"seconds": 8,
|
|
57
|
+
"prompt": "detailed video description"
|
|
58
|
+
}
|
|
59
|
+
</createAIVideo>
|
|
60
|
+
Description: """
|
|
61
|
+
Generate AI videos with Sora 2.
|
|
62
|
+
- model: sora-2 (fast) or sora-2-pro (higher quality)
|
|
63
|
+
- size: 1280x720 (landscape) or 720x1280 (portrait)
|
|
64
|
+
- seconds: 4, 8, or 12
|
|
65
|
+
- input_reference_url: optional reference image
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
<continueVideoPolling>
|
|
69
|
+
{
|
|
70
|
+
"videoId": "video_id_here"
|
|
71
|
+
}
|
|
72
|
+
</continueVideoPolling>
|
|
73
|
+
Description: """
|
|
74
|
+
Check status of pending video generation.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
<viewImage>
|
|
78
|
+
{
|
|
79
|
+
"url": "https://example.com/image.jpg"
|
|
80
|
+
}
|
|
81
|
+
</viewImage>
|
|
82
|
+
Description: """
|
|
83
|
+
Add an image to the conversation for visual analysis.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
<googleSearch>
|
|
87
|
+
{
|
|
88
|
+
"query": "search query"
|
|
89
|
+
}
|
|
90
|
+
</googleSearch>
|
|
91
|
+
Description: """
|
|
92
|
+
Search Google for information.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
<googleImageSearch>
|
|
96
|
+
{
|
|
97
|
+
"query": "search query",
|
|
98
|
+
"limit": 5
|
|
99
|
+
}
|
|
100
|
+
</googleImageSearch>
|
|
101
|
+
Description: """
|
|
102
|
+
Search Google for images.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
<webpageToText>
|
|
106
|
+
{
|
|
107
|
+
"url": "https://example.com/page"
|
|
108
|
+
}
|
|
109
|
+
</webpageToText>
|
|
110
|
+
Description: """
|
|
111
|
+
Extract text content from a webpage.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
<sendMail>
|
|
115
|
+
{
|
|
116
|
+
"to": "email@example.com",
|
|
117
|
+
"subject": "Subject line",
|
|
118
|
+
"body": "Email body content"
|
|
119
|
+
}
|
|
120
|
+
</sendMail>
|
|
121
|
+
Description: """
|
|
122
|
+
Send an email.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
<scheduleTask>
|
|
126
|
+
{
|
|
127
|
+
"delay": "2h",
|
|
128
|
+
"message": "Task description"
|
|
129
|
+
}
|
|
130
|
+
</scheduleTask>
|
|
131
|
+
Description: """
|
|
132
|
+
Schedule a future task.
|
|
133
|
+
- delay: minutes (number), "2h" (hours), or "1d" (days)
|
|
134
|
+
- time: specific UTC time "2025-01-15T14:00:00Z"
|
|
135
|
+
Task will trigger a new chat at scheduled time.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
<getScheduledTasks/>
|
|
139
|
+
Description: """
|
|
140
|
+
List all scheduled tasks.
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
<publishWebPage>
|
|
144
|
+
<!DOCTYPE html>
|
|
145
|
+
<html>
|
|
146
|
+
<head><title>Page Title</title></head>
|
|
147
|
+
<body>
|
|
148
|
+
<h1>Content</h1>
|
|
149
|
+
</body>
|
|
150
|
+
</html>
|
|
151
|
+
</publishWebPage>
|
|
152
|
+
Description: """
|
|
153
|
+
Publish an HTML landing page. Title must be in English for filename generation.
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
## Guidelines
|
|
157
|
+
- Always confirm actions with the user before executing
|
|
158
|
+
- Save important user information to memory
|
|
159
|
+
- When creating images/videos, provide detailed prompts
|
|
160
|
+
- For scheduled tasks, confirm the time with the user
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
// Memory helpers
|
|
2
|
+
const setMemoryValue = async (itemId, value, expirationInMinutes) => {
|
|
3
|
+
const body = {
|
|
4
|
+
value,
|
|
5
|
+
lastUpdated: new Date().toISOString()
|
|
6
|
+
};
|
|
7
|
+
if (expirationInMinutes) {
|
|
8
|
+
body.exp = new Date(Date.now() + expirationInMinutes * 60000).toISOString();
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
await openkbs.updateItem({ itemType: 'memory', itemId, body });
|
|
12
|
+
} catch {
|
|
13
|
+
await openkbs.createItem({ itemType: 'memory', itemId, body });
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Upload generated image helper
|
|
18
|
+
const uploadGeneratedImage = async (base64Data, meta) => {
|
|
19
|
+
const fileName = `image-${Date.now()}-${Math.random().toString(36).substring(7)}.png`;
|
|
20
|
+
const uploadResult = await openkbs.uploadImage(base64Data, fileName, 'image/png');
|
|
21
|
+
return { type: 'CHAT_IMAGE', data: { imageUrl: uploadResult.url }, ...meta };
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const getActions = (meta) => [
|
|
25
|
+
// Memory Management
|
|
26
|
+
[/<setMemory>([\s\S]*?)<\/setMemory>/s, async (match) => {
|
|
27
|
+
try {
|
|
28
|
+
const data = JSON.parse(match[1].trim());
|
|
29
|
+
if (!data.itemId?.startsWith('memory_')) {
|
|
30
|
+
return { type: "MEMORY_ERROR", error: "itemId must start with 'memory_'", ...meta };
|
|
31
|
+
}
|
|
32
|
+
await setMemoryValue(data.itemId, data.value, data.expirationInMinutes);
|
|
33
|
+
return { type: "MEMORY_UPDATED", itemId: data.itemId, ...meta };
|
|
34
|
+
} catch (e) {
|
|
35
|
+
return { type: "MEMORY_ERROR", error: e.message, ...meta };
|
|
36
|
+
}
|
|
37
|
+
}],
|
|
38
|
+
|
|
39
|
+
[/<deleteItem>([\s\S]*?)<\/deleteItem>/s, async (match) => {
|
|
40
|
+
try {
|
|
41
|
+
const data = JSON.parse(match[1].trim());
|
|
42
|
+
await openkbs.deleteItem(data.itemId);
|
|
43
|
+
return { type: "ITEM_DELETED", itemId: data.itemId, ...meta };
|
|
44
|
+
} catch (e) {
|
|
45
|
+
return { type: "DELETE_ERROR", error: e.message, ...meta };
|
|
46
|
+
}
|
|
47
|
+
}],
|
|
48
|
+
|
|
49
|
+
// AI Image Generation
|
|
50
|
+
[/<createAIImage>([\s\S]*?)<\/createAIImage>/s, async (match) => {
|
|
51
|
+
try {
|
|
52
|
+
const data = JSON.parse(match[1].trim());
|
|
53
|
+
const model = data.model || "gemini-2.5-flash-image";
|
|
54
|
+
const params = { model, n: 1 };
|
|
55
|
+
|
|
56
|
+
if (data.imageUrls?.length > 0) params.imageUrls = data.imageUrls;
|
|
57
|
+
|
|
58
|
+
if (model === 'gpt-image-1') {
|
|
59
|
+
const validSizes = ["1024x1024", "1536x1024", "1024x1536", "auto"];
|
|
60
|
+
params.size = validSizes.includes(data.size) ? data.size : "1024x1024";
|
|
61
|
+
params.quality = "high";
|
|
62
|
+
} else if (model === 'gemini-2.5-flash-image') {
|
|
63
|
+
const validRatios = ["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"];
|
|
64
|
+
params.aspect_ratio = validRatios.includes(data.aspect_ratio) ? data.aspect_ratio : "1:1";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const image = await openkbs.generateImage(data.prompt, params);
|
|
68
|
+
return await uploadGeneratedImage(image[0].b64_json, meta);
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return { error: e.message || 'Image creation failed', ...meta };
|
|
71
|
+
}
|
|
72
|
+
}],
|
|
73
|
+
|
|
74
|
+
// AI Video Generation
|
|
75
|
+
[/<createAIVideo>([\s\S]*?)<\/createAIVideo>/s, async (match) => {
|
|
76
|
+
try {
|
|
77
|
+
const data = JSON.parse(match[1].trim());
|
|
78
|
+
const params = {
|
|
79
|
+
video_model: data.model || "sora-2",
|
|
80
|
+
seconds: [4, 8, 12].includes(data.seconds) ? data.seconds : 8
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
if (data.input_reference_url) {
|
|
84
|
+
params.input_reference_url = data.input_reference_url;
|
|
85
|
+
} else {
|
|
86
|
+
params.size = ['720x1280', '1280x720'].includes(data.size) ? data.size : '1280x720';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const videoData = await openkbs.generateVideo(data.prompt, params);
|
|
90
|
+
|
|
91
|
+
if (videoData?.[0]?.status === 'pending') {
|
|
92
|
+
return { type: 'VIDEO_PENDING', data: { videoId: videoData[0].video_id }, ...meta };
|
|
93
|
+
}
|
|
94
|
+
if (videoData?.[0]?.video_url) {
|
|
95
|
+
return { type: 'CHAT_VIDEO', data: { videoUrl: videoData[0].video_url }, ...meta };
|
|
96
|
+
}
|
|
97
|
+
return { error: 'Video generation failed', ...meta };
|
|
98
|
+
} catch (e) {
|
|
99
|
+
return { error: e.message, ...meta };
|
|
100
|
+
}
|
|
101
|
+
}],
|
|
102
|
+
|
|
103
|
+
[/<continueVideoPolling>([\s\S]*?)<\/continueVideoPolling>/s, async (match) => {
|
|
104
|
+
try {
|
|
105
|
+
const data = JSON.parse(match[1].trim());
|
|
106
|
+
const videoData = await openkbs.checkVideoStatus(data.videoId);
|
|
107
|
+
|
|
108
|
+
if (videoData?.[0]?.status === 'completed' && videoData[0].video_url) {
|
|
109
|
+
return { type: 'CHAT_VIDEO', data: { videoUrl: videoData[0].video_url }, ...meta };
|
|
110
|
+
} else if (videoData?.[0]?.status === 'pending') {
|
|
111
|
+
return { type: 'VIDEO_PENDING', data: { videoId: data.videoId }, ...meta };
|
|
112
|
+
}
|
|
113
|
+
return { error: 'Video generation failed', ...meta };
|
|
114
|
+
} catch (e) {
|
|
115
|
+
return { error: e.message, ...meta };
|
|
116
|
+
}
|
|
117
|
+
}],
|
|
118
|
+
|
|
119
|
+
// View Image (adds to LLM vision context)
|
|
120
|
+
[/<viewImage>([\s\S]*?)<\/viewImage>/s, async (match) => {
|
|
121
|
+
try {
|
|
122
|
+
const data = JSON.parse(match[1].trim());
|
|
123
|
+
return {
|
|
124
|
+
data: [
|
|
125
|
+
{ type: "text", text: `Viewing: ${data.url}` },
|
|
126
|
+
{ type: "image_url", image_url: { url: data.url } }
|
|
127
|
+
],
|
|
128
|
+
...meta
|
|
129
|
+
};
|
|
130
|
+
} catch (e) {
|
|
131
|
+
return { type: "VIEW_IMAGE_ERROR", error: e.message, ...meta };
|
|
132
|
+
}
|
|
133
|
+
}],
|
|
134
|
+
|
|
135
|
+
// Web scraping
|
|
136
|
+
[/<webpageToText>([\s\S]*?)<\/webpageToText>/s, async (match) => {
|
|
137
|
+
try {
|
|
138
|
+
const data = JSON.parse(match[1].trim());
|
|
139
|
+
let response = await openkbs.webpageToText(data.url);
|
|
140
|
+
if (response?.content?.length > 5000) {
|
|
141
|
+
response.content = response.content.substring(0, 5000);
|
|
142
|
+
}
|
|
143
|
+
return { data: response, ...meta };
|
|
144
|
+
} catch (e) {
|
|
145
|
+
return { error: e.message, ...meta };
|
|
146
|
+
}
|
|
147
|
+
}],
|
|
148
|
+
|
|
149
|
+
// Google Search
|
|
150
|
+
[/<googleSearch>([\s\S]*?)<\/googleSearch>/s, async (match) => {
|
|
151
|
+
try {
|
|
152
|
+
const data = JSON.parse(match[1].trim());
|
|
153
|
+
const response = await openkbs.googleSearch(data.query);
|
|
154
|
+
const results = response?.map(({ title, link, snippet, pagemap }) => ({
|
|
155
|
+
title, link, snippet, image: pagemap?.metatags?.[0]?.["og:image"]
|
|
156
|
+
}));
|
|
157
|
+
return { data: results, ...meta };
|
|
158
|
+
} catch (e) {
|
|
159
|
+
return { error: e.message, ...meta };
|
|
160
|
+
}
|
|
161
|
+
}],
|
|
162
|
+
|
|
163
|
+
// Google Image Search
|
|
164
|
+
[/<googleImageSearch>([\s\S]*?)<\/googleImageSearch>/s, async (match) => {
|
|
165
|
+
try {
|
|
166
|
+
const data = JSON.parse(match[1].trim());
|
|
167
|
+
const response = await openkbs.googleSearch(data.query, { searchType: 'image' });
|
|
168
|
+
const results = response?.map(({ title, link, pagemap }) => ({
|
|
169
|
+
title, link, image: pagemap?.cse_image?.[0]?.src || link
|
|
170
|
+
}))?.slice(0, data.limit || 10);
|
|
171
|
+
return { data: results, ...meta };
|
|
172
|
+
} catch (e) {
|
|
173
|
+
return { error: e.message, ...meta };
|
|
174
|
+
}
|
|
175
|
+
}],
|
|
176
|
+
|
|
177
|
+
// Send Email
|
|
178
|
+
[/<sendMail>([\s\S]*?)<\/sendMail>/s, async (match) => {
|
|
179
|
+
try {
|
|
180
|
+
const data = JSON.parse(match[1].trim());
|
|
181
|
+
await openkbs.sendMail(data.to, data.subject, data.body);
|
|
182
|
+
return { type: 'EMAIL_SENT', data: { to: data.to, subject: data.subject }, ...meta };
|
|
183
|
+
} catch (e) {
|
|
184
|
+
return { error: e.message, ...meta };
|
|
185
|
+
}
|
|
186
|
+
}],
|
|
187
|
+
|
|
188
|
+
// Schedule Task
|
|
189
|
+
[/<scheduleTask>([\s\S]*?)<\/scheduleTask>/s, async (match) => {
|
|
190
|
+
try {
|
|
191
|
+
const data = JSON.parse(match[1].trim());
|
|
192
|
+
let scheduledTime;
|
|
193
|
+
|
|
194
|
+
if (data.time) {
|
|
195
|
+
let isoTimeStr = data.time.replace(' ', 'T');
|
|
196
|
+
if (!isoTimeStr.includes('Z') && !isoTimeStr.includes('+')) isoTimeStr += 'Z';
|
|
197
|
+
scheduledTime = new Date(isoTimeStr).getTime();
|
|
198
|
+
} else if (data.delay) {
|
|
199
|
+
let delayMs = 0;
|
|
200
|
+
if (data.delay.endsWith('h')) delayMs = parseFloat(data.delay) * 3600000;
|
|
201
|
+
else if (data.delay.endsWith('d')) delayMs = parseFloat(data.delay) * 86400000;
|
|
202
|
+
else delayMs = parseFloat(data.delay) * 60000;
|
|
203
|
+
scheduledTime = Date.now() + delayMs;
|
|
204
|
+
} else {
|
|
205
|
+
scheduledTime = Date.now() + 3600000;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const response = await openkbs.kb({
|
|
209
|
+
action: 'createScheduledTask',
|
|
210
|
+
scheduledTime: Math.floor(scheduledTime / 60000) * 60000,
|
|
211
|
+
taskPayload: { message: `[SCHEDULED_TASK] ${data.message}`, createdAt: Date.now() },
|
|
212
|
+
description: data.message.substring(0, 50)
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
return { type: 'TASK_SCHEDULED', data: { scheduledTime: new Date(scheduledTime).toISOString(), taskId: response.taskId }, ...meta };
|
|
216
|
+
} catch (e) {
|
|
217
|
+
return { error: e.message, ...meta };
|
|
218
|
+
}
|
|
219
|
+
}],
|
|
220
|
+
|
|
221
|
+
// Get Scheduled Tasks
|
|
222
|
+
[/<getScheduledTasks\s*\/>/s, async () => {
|
|
223
|
+
try {
|
|
224
|
+
const response = await openkbs.kb({ action: 'getScheduledTasks' });
|
|
225
|
+
return { type: 'SCHEDULED_TASKS_LIST', data: response, ...meta };
|
|
226
|
+
} catch (e) {
|
|
227
|
+
return { error: e.message, ...meta };
|
|
228
|
+
}
|
|
229
|
+
}],
|
|
230
|
+
|
|
231
|
+
// Web Page Publishing
|
|
232
|
+
[/<publishWebPage>([\s\S]*?)<\/publishWebPage>/s, async (match) => {
|
|
233
|
+
try {
|
|
234
|
+
const htmlContent = match[1].trim();
|
|
235
|
+
const titleMatch = htmlContent.match(/<title>(.*?)<\/title>/i);
|
|
236
|
+
const title = titleMatch ? titleMatch[1] : 'Page';
|
|
237
|
+
const filename = `${title.toLowerCase().replace(/[^a-z0-9]+/g, '_')}_${Date.now()}.html`;
|
|
238
|
+
|
|
239
|
+
const presignedUrl = await openkbs.kb({
|
|
240
|
+
action: 'createPresignedURL',
|
|
241
|
+
namespace: 'files',
|
|
242
|
+
fileName: filename,
|
|
243
|
+
fileType: 'text/html',
|
|
244
|
+
presignedOperation: 'putObject'
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const htmlBuffer = Buffer.from(htmlContent, 'utf8');
|
|
248
|
+
await axios.put(presignedUrl, htmlBuffer, {
|
|
249
|
+
headers: { 'Content-Type': 'text/html', 'Content-Length': htmlBuffer.length }
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
const publicUrl = `https://web.file.vpc1.us/files/${openkbs.kbId}/${filename}`;
|
|
253
|
+
return { type: 'WEB_PAGE_PUBLISHED', data: { url: publicUrl, title }, ...meta };
|
|
254
|
+
} catch (e) {
|
|
255
|
+
return { type: 'PUBLISH_ERROR', error: e.message, ...meta };
|
|
256
|
+
}
|
|
257
|
+
}]
|
|
258
|
+
];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {getActions} from './actions.js';
|
|
2
|
+
|
|
3
|
+
export const handler = async (event) => {
|
|
4
|
+
const actions = getActions({ _meta_actions: [] });
|
|
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
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {getActions} from './actions.js';
|
|
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
|
+
};
|