Compare commits
9 commits
98fb0b31a6
...
20e502e0cb
| Author | SHA1 | Date | |
|---|---|---|---|
| 20e502e0cb | |||
| 17a614974c | |||
| c5fc6c709b | |||
| fc2458c4c1 | |||
| d6b9016564 | |||
| c11160d37d | |||
| bfeba96a38 | |||
| c0e14819fe | |||
| 5adb6efec4 |
49 changed files with 1156 additions and 1740 deletions
|
|
@ -30,6 +30,11 @@ embedding-url,http://localhost:8082
|
||||||
embedding-model,../../../../data/llm/bge-small-en-v1.5-f32.gguf
|
embedding-model,../../../../data/llm/bge-small-en-v1.5-f32.gguf
|
||||||
,
|
,
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
# VECTOR DATABASE CONFIGURATION
|
||||||
|
# ============================================================================
|
||||||
|
vectordb-url,https://localhost:6333
|
||||||
|
,
|
||||||
|
# ============================================================================
|
||||||
# LLM SERVER CONFIGURATION
|
# LLM SERVER CONFIGURATION
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
llm-server,true
|
llm-server,true
|
||||||
|
|
|
||||||
|
697
dev-chat.html
697
dev-chat.html
|
|
@ -1,697 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Dev Chat Widget</title>
|
|
||||||
<style>
|
|
||||||
/* Dev Chat Floating Button */
|
|
||||||
#dev-chat-btn {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 20px;
|
|
||||||
right: 20px;
|
|
||||||
width: 56px;
|
|
||||||
height: 56px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.4);
|
|
||||||
z-index: 99999;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
transition: transform 0.2s, box-shadow 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-btn:hover {
|
|
||||||
transform: scale(1.1);
|
|
||||||
box-shadow: 0 6px 24px rgba(102, 126, 234, 0.6);
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-btn svg {
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
fill: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-btn .badge {
|
|
||||||
position: absolute;
|
|
||||||
top: -4px;
|
|
||||||
right: -4px;
|
|
||||||
background: #ef4444;
|
|
||||||
color: white;
|
|
||||||
font-size: 10px;
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 10px;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dev Chat Panel */
|
|
||||||
#dev-chat-panel {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 90px;
|
|
||||||
right: 20px;
|
|
||||||
width: 380px;
|
|
||||||
height: 520px;
|
|
||||||
background: #0f172a;
|
|
||||||
border-radius: 16px;
|
|
||||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
|
||||||
z-index: 99998;
|
|
||||||
display: none;
|
|
||||||
flex-direction: column;
|
|
||||||
overflow: hidden;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-panel.open {
|
|
||||||
display: flex;
|
|
||||||
animation: slideUp 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slideUp {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(20px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Header */
|
|
||||||
#dev-chat-header {
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
padding: 16px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-header h3 {
|
|
||||||
margin: 0;
|
|
||||||
color: white;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-header .dev-badge {
|
|
||||||
background: rgba(255,255,255,0.2);
|
|
||||||
padding: 2px 8px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 10px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-header button {
|
|
||||||
background: rgba(255,255,255,0.2);
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-header button:hover {
|
|
||||||
background: rgba(255,255,255,0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Messages Area */
|
|
||||||
#dev-chat-messages {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 16px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dev-msg {
|
|
||||||
max-width: 85%;
|
|
||||||
padding: 10px 14px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.4;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dev-msg.user {
|
|
||||||
background: #667eea;
|
|
||||||
color: white;
|
|
||||||
align-self: flex-end;
|
|
||||||
border-bottom-right-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dev-msg.bot {
|
|
||||||
background: #1e293b;
|
|
||||||
color: #e2e8f0;
|
|
||||||
align-self: flex-start;
|
|
||||||
border-bottom-left-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dev-msg.system {
|
|
||||||
background: #064e3b;
|
|
||||||
color: #6ee7b7;
|
|
||||||
align-self: center;
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 6px 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dev-msg.error {
|
|
||||||
background: #7f1d1d;
|
|
||||||
color: #fca5a5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dev-msg pre {
|
|
||||||
background: #000;
|
|
||||||
padding: 8px;
|
|
||||||
border-radius: 6px;
|
|
||||||
overflow-x: auto;
|
|
||||||
margin: 8px 0 0 0;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dev-msg code {
|
|
||||||
font-family: 'Fira Code', monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Typing Indicator */
|
|
||||||
.typing-indicator {
|
|
||||||
display: flex;
|
|
||||||
gap: 4px;
|
|
||||||
padding: 12px 16px;
|
|
||||||
background: #1e293b;
|
|
||||||
border-radius: 12px;
|
|
||||||
align-self: flex-start;
|
|
||||||
border-bottom-left-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.typing-indicator span {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
background: #64748b;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: typing 1.4s infinite ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
|
|
||||||
.typing-indicator span:nth-child(3) { animation-delay: 0.4s; }
|
|
||||||
|
|
||||||
@keyframes typing {
|
|
||||||
0%, 60%, 100% { transform: translateY(0); }
|
|
||||||
30% { transform: translateY(-6px); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Input Area */
|
|
||||||
#dev-chat-input-area {
|
|
||||||
padding: 12px 16px;
|
|
||||||
background: #1e293b;
|
|
||||||
border-top: 1px solid #334155;
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-input {
|
|
||||||
flex: 1;
|
|
||||||
background: #0f172a;
|
|
||||||
border: 1px solid #334155;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 10px 14px;
|
|
||||||
color: #e2e8f0;
|
|
||||||
font-size: 14px;
|
|
||||||
outline: none;
|
|
||||||
resize: none;
|
|
||||||
min-height: 20px;
|
|
||||||
max-height: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-input:focus {
|
|
||||||
border-color: #667eea;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-input::placeholder {
|
|
||||||
color: #64748b;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-send {
|
|
||||||
background: #667eea;
|
|
||||||
border: none;
|
|
||||||
border-radius: 8px;
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
transition: background 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-send:hover {
|
|
||||||
background: #5a67d8;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-send:disabled {
|
|
||||||
background: #334155;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-send svg {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
fill: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Quick Actions */
|
|
||||||
#dev-chat-actions {
|
|
||||||
padding: 8px 16px;
|
|
||||||
background: #1e293b;
|
|
||||||
display: flex;
|
|
||||||
gap: 6px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dev-action-btn {
|
|
||||||
background: #334155;
|
|
||||||
border: none;
|
|
||||||
color: #94a3b8;
|
|
||||||
padding: 4px 10px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 11px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dev-action-btn:hover {
|
|
||||||
background: #475569;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* File Changes Indicator */
|
|
||||||
.file-change {
|
|
||||||
background: #1e293b;
|
|
||||||
border-left: 3px solid #22c55e;
|
|
||||||
padding: 8px 12px;
|
|
||||||
margin: 4px 0;
|
|
||||||
border-radius: 0 8px 8px 0;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-change.modified { border-color: #f59e0b; }
|
|
||||||
.file-change.deleted { border-color: #ef4444; }
|
|
||||||
.file-change.created { border-color: #22c55e; }
|
|
||||||
|
|
||||||
.file-change .path {
|
|
||||||
color: #94a3b8;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scrollbar */
|
|
||||||
#dev-chat-messages::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-messages::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-chat-messages::-webkit-scrollbar-thumb {
|
|
||||||
background: #334155;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<!-- Dev Chat Floating Button -->
|
|
||||||
<button id="dev-chat-btn" onclick="toggleDevChat()" title="Dev Chat (Ctrl+Shift+D)">
|
|
||||||
<svg viewBox="0 0 24 24">
|
|
||||||
<path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/>
|
|
||||||
<circle cx="12" cy="10" r="1.5"/>
|
|
||||||
<circle cx="8" cy="10" r="1.5"/>
|
|
||||||
<circle cx="16" cy="10" r="1.5"/>
|
|
||||||
</svg>
|
|
||||||
<span class="badge" id="dev-chat-badge">0</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Dev Chat Panel -->
|
|
||||||
<div id="dev-chat-panel">
|
|
||||||
<div id="dev-chat-header">
|
|
||||||
<h3>
|
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="white">
|
|
||||||
<path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/>
|
|
||||||
</svg>
|
|
||||||
Dev Chat
|
|
||||||
<span class="dev-badge">DEV</span>
|
|
||||||
</h3>
|
|
||||||
<button onclick="toggleDevChat()" title="Close">
|
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
|
||||||
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="dev-chat-actions">
|
|
||||||
<button class="dev-action-btn" onclick="sendQuick('show tables')">📋 Tables</button>
|
|
||||||
<button class="dev-action-btn" onclick="sendQuick('list files')">📁 Files</button>
|
|
||||||
<button class="dev-action-btn" onclick="sendQuick('reload app')">🔄 Reload</button>
|
|
||||||
<button class="dev-action-btn" onclick="sendQuick('show errors')">⚠️ Errors</button>
|
|
||||||
<button class="dev-action-btn" onclick="clearChat()">🗑️ Clear</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="dev-chat-messages">
|
|
||||||
<div class="dev-msg system">
|
|
||||||
Dev mode active. Talk to test your app or modify files.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="dev-chat-input-area">
|
|
||||||
<textarea
|
|
||||||
id="dev-chat-input"
|
|
||||||
placeholder="Ask anything or describe changes..."
|
|
||||||
rows="1"
|
|
||||||
onkeydown="handleKeyDown(event)"
|
|
||||||
></textarea>
|
|
||||||
<button id="dev-chat-send" onclick="sendMessage()">
|
|
||||||
<svg viewBox="0 0 24 24">
|
|
||||||
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const DEV_CHAT_CONFIG = {
|
|
||||||
apiEndpoint: '/api/chat/dev',
|
|
||||||
wsEndpoint: '/ws/dev',
|
|
||||||
storageKey: 'dev_chat_history',
|
|
||||||
maxHistory: 50
|
|
||||||
};
|
|
||||||
|
|
||||||
let devChatOpen = false;
|
|
||||||
let ws = null;
|
|
||||||
let isTyping = false;
|
|
||||||
|
|
||||||
// Toggle chat panel
|
|
||||||
function toggleDevChat() {
|
|
||||||
devChatOpen = !devChatOpen;
|
|
||||||
const panel = document.getElementById('dev-chat-panel');
|
|
||||||
panel.classList.toggle('open', devChatOpen);
|
|
||||||
|
|
||||||
if (devChatOpen) {
|
|
||||||
document.getElementById('dev-chat-input').focus();
|
|
||||||
connectWebSocket();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keyboard shortcut: Ctrl+Shift+D
|
|
||||||
document.addEventListener('keydown', (e) => {
|
|
||||||
if (e.ctrlKey && e.shiftKey && e.key === 'D') {
|
|
||||||
e.preventDefault();
|
|
||||||
toggleDevChat();
|
|
||||||
}
|
|
||||||
// Escape to close
|
|
||||||
if (e.key === 'Escape' && devChatOpen) {
|
|
||||||
toggleDevChat();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle input keydown
|
|
||||||
function handleKeyDown(e) {
|
|
||||||
if (e.key === 'Enter' && !e.shiftKey) {
|
|
||||||
e.preventDefault();
|
|
||||||
sendMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send message
|
|
||||||
async function sendMessage() {
|
|
||||||
const input = document.getElementById('dev-chat-input');
|
|
||||||
const text = input.value.trim();
|
|
||||||
if (!text) return;
|
|
||||||
|
|
||||||
// Add user message
|
|
||||||
addMessage(text, 'user');
|
|
||||||
input.value = '';
|
|
||||||
|
|
||||||
// Show typing
|
|
||||||
showTyping();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Send via WebSocket if connected, else HTTP
|
|
||||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
type: 'dev_message',
|
|
||||||
content: text,
|
|
||||||
context: {
|
|
||||||
url: window.location.href,
|
|
||||||
app: getAppName()
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
const response = await fetch(DEV_CHAT_CONFIG.apiEndpoint, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
message: text,
|
|
||||||
context: {
|
|
||||||
url: window.location.href,
|
|
||||||
app: getAppName()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
hideTyping();
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
handleBotResponse(data);
|
|
||||||
} else {
|
|
||||||
addMessage('Error connecting to dev server', 'error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
hideTyping();
|
|
||||||
addMessage('Connection error: ' + err.message, 'error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send quick action
|
|
||||||
function sendQuick(text) {
|
|
||||||
document.getElementById('dev-chat-input').value = text;
|
|
||||||
sendMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add message to chat
|
|
||||||
function addMessage(text, type = 'bot') {
|
|
||||||
const messages = document.getElementById('dev-chat-messages');
|
|
||||||
const msg = document.createElement('div');
|
|
||||||
msg.className = `dev-msg ${type}`;
|
|
||||||
|
|
||||||
// Parse markdown-style code blocks
|
|
||||||
if (type === 'bot' && text.includes('```')) {
|
|
||||||
msg.innerHTML = parseCodeBlocks(text);
|
|
||||||
} else {
|
|
||||||
msg.textContent = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
messages.appendChild(msg);
|
|
||||||
messages.scrollTop = messages.scrollHeight;
|
|
||||||
|
|
||||||
// Save to history
|
|
||||||
saveHistory();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse code blocks
|
|
||||||
function parseCodeBlocks(text) {
|
|
||||||
return text.replace(/```(\w*)\n?([\s\S]*?)```/g, (match, lang, code) => {
|
|
||||||
return `<pre><code class="${lang}">${escapeHtml(code.trim())}</code></pre>`;
|
|
||||||
}).replace(/\n/g, '<br>');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Escape HTML
|
|
||||||
function escapeHtml(text) {
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.textContent = text;
|
|
||||||
return div.innerHTML;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show file change
|
|
||||||
function showFileChange(path, type = 'modified') {
|
|
||||||
const messages = document.getElementById('dev-chat-messages');
|
|
||||||
const change = document.createElement('div');
|
|
||||||
change.className = `file-change ${type}`;
|
|
||||||
change.innerHTML = `
|
|
||||||
<span>${type === 'created' ? '➕' : type === 'deleted' ? '➖' : '✏️'}</span>
|
|
||||||
<span class="path">${path}</span>
|
|
||||||
`;
|
|
||||||
messages.appendChild(change);
|
|
||||||
messages.scrollTop = messages.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle bot response
|
|
||||||
function handleBotResponse(data) {
|
|
||||||
hideTyping();
|
|
||||||
|
|
||||||
if (data.message) {
|
|
||||||
addMessage(data.message, 'bot');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.files_changed) {
|
|
||||||
data.files_changed.forEach(f => {
|
|
||||||
showFileChange(f.path, f.type);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.reload) {
|
|
||||||
addMessage('Reloading app...', 'system');
|
|
||||||
setTimeout(() => location.reload(), 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Typing indicator
|
|
||||||
function showTyping() {
|
|
||||||
if (isTyping) return;
|
|
||||||
isTyping = true;
|
|
||||||
|
|
||||||
const messages = document.getElementById('dev-chat-messages');
|
|
||||||
const typing = document.createElement('div');
|
|
||||||
typing.id = 'typing-indicator';
|
|
||||||
typing.className = 'typing-indicator';
|
|
||||||
typing.innerHTML = '<span></span><span></span><span></span>';
|
|
||||||
messages.appendChild(typing);
|
|
||||||
messages.scrollTop = messages.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideTyping() {
|
|
||||||
isTyping = false;
|
|
||||||
const typing = document.getElementById('typing-indicator');
|
|
||||||
if (typing) typing.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebSocket connection
|
|
||||||
function connectWebSocket() {
|
|
||||||
if (ws && ws.readyState === WebSocket.OPEN) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
||||||
ws = new WebSocket(`${protocol}//${location.host}${DEV_CHAT_CONFIG.wsEndpoint}`);
|
|
||||||
|
|
||||||
ws.onopen = () => {
|
|
||||||
addMessage('Connected to dev server', 'system');
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onmessage = (event) => {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
|
|
||||||
switch (data.type) {
|
|
||||||
case 'message':
|
|
||||||
hideTyping();
|
|
||||||
addMessage(data.content, 'bot');
|
|
||||||
break;
|
|
||||||
case 'file_changed':
|
|
||||||
showFileChange(data.path, data.change_type);
|
|
||||||
break;
|
|
||||||
case 'reload':
|
|
||||||
addMessage('Files changed. Reloading...', 'system');
|
|
||||||
setTimeout(() => location.reload(), 500);
|
|
||||||
break;
|
|
||||||
case 'error':
|
|
||||||
hideTyping();
|
|
||||||
addMessage(data.content, 'error');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onclose = () => {
|
|
||||||
// Reconnect after 3s
|
|
||||||
setTimeout(connectWebSocket, 3000);
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onerror = (err) => {
|
|
||||||
console.error('Dev chat WS error:', err);
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
console.error('WebSocket connection failed:', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get app name from URL
|
|
||||||
function getAppName() {
|
|
||||||
const match = location.pathname.match(/\/apps\/([^\/]+)/);
|
|
||||||
return match ? match[1] : 'unknown';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear chat
|
|
||||||
function clearChat() {
|
|
||||||
const messages = document.getElementById('dev-chat-messages');
|
|
||||||
messages.innerHTML = '<div class="dev-msg system">Chat cleared. Dev mode active.</div>';
|
|
||||||
localStorage.removeItem(DEV_CHAT_CONFIG.storageKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save/load history using user_data virtual table
|
|
||||||
function saveHistory() {
|
|
||||||
const messages = document.getElementById('dev-chat-messages');
|
|
||||||
const history = Array.from(messages.children).map(m => ({
|
|
||||||
type: m.classList[1],
|
|
||||||
text: m.textContent
|
|
||||||
})).slice(-DEV_CHAT_CONFIG.maxHistory);
|
|
||||||
|
|
||||||
// Store in user_data via API
|
|
||||||
fetch('/api/db/user_data', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
key: 'dev_chat_history',
|
|
||||||
app: getAppName(),
|
|
||||||
data: history
|
|
||||||
})
|
|
||||||
}).catch(() => {
|
|
||||||
// Fallback to localStorage
|
|
||||||
localStorage.setItem(DEV_CHAT_CONFIG.storageKey, JSON.stringify(history));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadHistory() {
|
|
||||||
// Try to load from user_data
|
|
||||||
fetch(`/api/db/user_data?key=dev_chat_history&app=${getAppName()}`)
|
|
||||||
.then(r => r.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data && data.data) {
|
|
||||||
data.data.forEach(m => addMessage(m.text, m.type));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// Fallback to localStorage
|
|
||||||
const saved = localStorage.getItem(DEV_CHAT_CONFIG.storageKey);
|
|
||||||
if (saved) {
|
|
||||||
JSON.parse(saved).forEach(m => addMessage(m.text, m.type));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize on load if dev mode
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
// Only show in dev mode (check URL param or cookie)
|
|
||||||
const isDevMode = location.search.includes('dev=1') ||
|
|
||||||
document.cookie.includes('dev_mode=1') ||
|
|
||||||
location.hostname === 'localhost';
|
|
||||||
|
|
||||||
if (!isDevMode) {
|
|
||||||
document.getElementById('dev-chat-btn').style.display = 'none';
|
|
||||||
document.getElementById('dev-chat-panel').style.display = 'none';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
1043
drive.html
1043
drive.html
File diff suppressed because it is too large
Load diff
35
hr/employee-engage.gbai/config.json
Normal file
35
hr/employee-engage.gbai/config.json
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"name": "employee-engage",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Employee engagement surveys and sentiment analysis",
|
||||||
|
"author": "General Bots",
|
||||||
|
"category": "hr",
|
||||||
|
"tags": ["engagement", "surveys", "pulse", "sentiment", "feedback", "hr"],
|
||||||
|
"entry_dialog": "default.gbdialog",
|
||||||
|
"features": {
|
||||||
|
"engagement_surveys": true,
|
||||||
|
"pulse_checks": true,
|
||||||
|
"sentiment_analysis": true,
|
||||||
|
"anonymous_feedback": true,
|
||||||
|
"results_dashboard": true,
|
||||||
|
"ai_insights": true
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"default_anonymous": true,
|
||||||
|
"pulse_frequency": "weekly",
|
||||||
|
"sentiment_threshold": 0.3,
|
||||||
|
"auto_escalate_negative": true,
|
||||||
|
"survey_reminder_days": 3
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"create_survey": ["hr", "admin"],
|
||||||
|
"view_results": ["hr", "admin", "manager"],
|
||||||
|
"submit_feedback": ["all"],
|
||||||
|
"manage_settings": ["admin"]
|
||||||
|
},
|
||||||
|
"integrations": {
|
||||||
|
"calendar": true,
|
||||||
|
"mail": true,
|
||||||
|
"analytics": true
|
||||||
|
}
|
||||||
|
}
|
||||||
185
hr/employee-engage.gbai/default.gbdialog
Normal file
185
hr/employee-engage.gbai/default.gbdialog
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
PARAM action AS STRING LIKE "survey" DESCRIPTION "Action to perform: survey, pulse, results, feedback"
|
||||||
|
PARAM survey_type AS STRING LIKE "engagement" DESCRIPTION "Survey type: engagement, pulse, onboarding, exit" OPTIONAL
|
||||||
|
PARAM anonymous AS BOOLEAN LIKE TRUE DESCRIPTION "Anonymous responses" OPTIONAL
|
||||||
|
|
||||||
|
DESCRIPTION "Employee engagement surveys and sentiment analysis"
|
||||||
|
|
||||||
|
SELECT CASE UCASE(action)
|
||||||
|
|
||||||
|
CASE "SURVEY"
|
||||||
|
survey_id = GUID()
|
||||||
|
survey_name = ASK "What is the name of this survey?"
|
||||||
|
target_audience = ASK "Who should receive this survey? (all, department, team, or comma-separated emails)"
|
||||||
|
|
||||||
|
questions = NEW ARRAY
|
||||||
|
adding_questions = TRUE
|
||||||
|
|
||||||
|
WHILE adding_questions
|
||||||
|
question_text = ASK "Enter survey question (or 'done' to finish):"
|
||||||
|
IF UCASE(question_text) = "DONE" THEN
|
||||||
|
adding_questions = FALSE
|
||||||
|
ELSE
|
||||||
|
question_type = ASK "Question type? (scale, choice, text, nps)"
|
||||||
|
|
||||||
|
WITH question
|
||||||
|
id = GUID()
|
||||||
|
text = question_text
|
||||||
|
type = question_type
|
||||||
|
END WITH
|
||||||
|
|
||||||
|
IF question_type = "scale" THEN
|
||||||
|
question.min_label = "Strongly Disagree"
|
||||||
|
question.max_label = "Strongly Agree"
|
||||||
|
question.scale = 5
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF question_type = "choice" THEN
|
||||||
|
options_text = ASK "Enter options (comma-separated):"
|
||||||
|
question.options = SPLIT(options_text, ",")
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF question_type = "nps" THEN
|
||||||
|
question.min_label = "Not Likely"
|
||||||
|
question.max_label = "Very Likely"
|
||||||
|
question.scale = 10
|
||||||
|
END IF
|
||||||
|
|
||||||
|
PUSH questions, question
|
||||||
|
END IF
|
||||||
|
END WHILE
|
||||||
|
|
||||||
|
WITH survey
|
||||||
|
id = survey_id
|
||||||
|
name = survey_name
|
||||||
|
type = survey_type
|
||||||
|
questions = questions
|
||||||
|
target = target_audience
|
||||||
|
anonymous = anonymous
|
||||||
|
status = "draft"
|
||||||
|
created_at = NOW()
|
||||||
|
END WITH
|
||||||
|
|
||||||
|
SAVE survey TO "surveys"
|
||||||
|
|
||||||
|
launch_now = ASK "Launch survey now? (yes/no)"
|
||||||
|
IF UCASE(launch_now) = "YES" THEN
|
||||||
|
survey.status = "active"
|
||||||
|
survey.launched_at = NOW()
|
||||||
|
SAVE survey TO "surveys"
|
||||||
|
TALK "Survey launched! Notifications sent to " + target_audience
|
||||||
|
ELSE
|
||||||
|
TALK "Survey saved as draft. Launch when ready from the dashboard."
|
||||||
|
END IF
|
||||||
|
|
||||||
|
CASE "PULSE"
|
||||||
|
pulse_id = GUID()
|
||||||
|
pulse_question = ASK "What quick question do you want to ask?"
|
||||||
|
pulse_frequency = ASK "How often? (daily, weekly, biweekly)"
|
||||||
|
|
||||||
|
WITH pulse
|
||||||
|
id = pulse_id
|
||||||
|
question = pulse_question
|
||||||
|
frequency = pulse_frequency
|
||||||
|
anonymous = TRUE
|
||||||
|
status = "active"
|
||||||
|
created_at = NOW()
|
||||||
|
responses = NEW ARRAY
|
||||||
|
END WITH
|
||||||
|
|
||||||
|
SAVE pulse TO "pulses"
|
||||||
|
TALK "Pulse check created. First pulse will be sent based on " + pulse_frequency + " schedule."
|
||||||
|
|
||||||
|
CASE "RESULTS"
|
||||||
|
surveys = FIND "surveys" WHERE status = "completed" OR status = "active"
|
||||||
|
|
||||||
|
IF LEN(surveys) = 0 THEN
|
||||||
|
TALK "No surveys with results found."
|
||||||
|
ELSE
|
||||||
|
survey_list = ""
|
||||||
|
FOR i = 0 TO LEN(surveys) - 1
|
||||||
|
survey_list = survey_list + (i + 1) + ". " + surveys[i].name + " (" + surveys[i].status + ")\n"
|
||||||
|
END FOR
|
||||||
|
|
||||||
|
selection = ASK "Select survey to view results:\n" + survey_list
|
||||||
|
selected_survey = surveys[INT(selection) - 1]
|
||||||
|
|
||||||
|
responses = FIND "responses" WHERE survey_id = selected_survey.id
|
||||||
|
|
||||||
|
total_responses = LEN(responses)
|
||||||
|
|
||||||
|
IF total_responses = 0 THEN
|
||||||
|
TALK "No responses yet for this survey."
|
||||||
|
ELSE
|
||||||
|
TALK "📊 Results for: " + selected_survey.name
|
||||||
|
TALK "Total responses: " + total_responses
|
||||||
|
|
||||||
|
FOR q = 0 TO LEN(selected_survey.questions) - 1
|
||||||
|
question = selected_survey.questions[q]
|
||||||
|
TALK "\n📋 " + question.text
|
||||||
|
|
||||||
|
IF question.type = "scale" OR question.type = "nps" THEN
|
||||||
|
sum = 0
|
||||||
|
FOR r = 0 TO LEN(responses) - 1
|
||||||
|
answer = responses[r].answers[q]
|
||||||
|
sum = sum + answer
|
||||||
|
END FOR
|
||||||
|
avg = sum / total_responses
|
||||||
|
TALK "Average: " + FORMAT(avg, "0.0") + " / " + question.scale
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF question.type = "choice" THEN
|
||||||
|
FOR opt = 0 TO LEN(question.options) - 1
|
||||||
|
count = 0
|
||||||
|
FOR r = 0 TO LEN(responses) - 1
|
||||||
|
IF responses[r].answers[q] = question.options[opt] THEN
|
||||||
|
count = count + 1
|
||||||
|
END IF
|
||||||
|
END FOR
|
||||||
|
pct = (count / total_responses) * 100
|
||||||
|
TALK question.options[opt] + ": " + FORMAT(pct, "0") + "%"
|
||||||
|
END FOR
|
||||||
|
END IF
|
||||||
|
END FOR
|
||||||
|
|
||||||
|
sentiment = ANALYZE SENTIMENT FROM responses
|
||||||
|
TALK "\n🎯 Overall Sentiment: " + sentiment.label + " (" + FORMAT(sentiment.score * 100, "0") + "% positive)"
|
||||||
|
END IF
|
||||||
|
END IF
|
||||||
|
|
||||||
|
CASE "FEEDBACK"
|
||||||
|
feedback_type = ASK "Feedback type? (anonymous, identified, suggestion)"
|
||||||
|
feedback_text = ASK "Enter your feedback:"
|
||||||
|
|
||||||
|
WITH feedback
|
||||||
|
id = GUID()
|
||||||
|
type = feedback_type
|
||||||
|
content = feedback_text
|
||||||
|
anonymous = (feedback_type = "anonymous")
|
||||||
|
sentiment = ANALYZE SENTIMENT FROM feedback_text
|
||||||
|
created_at = NOW()
|
||||||
|
END WITH
|
||||||
|
|
||||||
|
SAVE feedback TO "feedback"
|
||||||
|
|
||||||
|
TALK "Thank you for your feedback!"
|
||||||
|
|
||||||
|
IF feedback.sentiment.score < 0.3 THEN
|
||||||
|
TALK "We noticed this might be a concern. Would you like to speak with HR?"
|
||||||
|
escalate = ASK "Request HR follow-up? (yes/no)"
|
||||||
|
IF UCASE(escalate) = "YES" THEN
|
||||||
|
feedback.escalated = TRUE
|
||||||
|
feedback.escalated_at = NOW()
|
||||||
|
SAVE feedback TO "feedback"
|
||||||
|
TALK "HR has been notified and will follow up."
|
||||||
|
END IF
|
||||||
|
END IF
|
||||||
|
|
||||||
|
CASE ELSE
|
||||||
|
TALK "Employee Engagement System"
|
||||||
|
TALK "Available actions:"
|
||||||
|
TALK "• survey - Create and launch engagement surveys"
|
||||||
|
TALK "• pulse - Quick pulse checks"
|
||||||
|
TALK "• results - View survey results and analytics"
|
||||||
|
TALK "• feedback - Submit anonymous feedback"
|
||||||
|
|
||||||
|
END SELECT
|
||||||
51
hr/team-feedback.gbai/config.json
Normal file
51
hr/team-feedback.gbai/config.json
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
{
|
||||||
|
"name": "team-feedback",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Team feedback and pulse surveys for managers",
|
||||||
|
"author": "General Bots",
|
||||||
|
"category": "hr",
|
||||||
|
"tags": ["pulse", "feedback", "team", "manager", "surveys", "hr"],
|
||||||
|
"entry_dialog": "default.gbdialog",
|
||||||
|
"features": {
|
||||||
|
"pulse_surveys": true,
|
||||||
|
"team_health": true,
|
||||||
|
"sentiment_analysis": true,
|
||||||
|
"trend_tracking": true,
|
||||||
|
"action_items": true,
|
||||||
|
"ai_insights": true
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"default_frequency": "weekly",
|
||||||
|
"anonymous_responses": true,
|
||||||
|
"alert_threshold": 3.0,
|
||||||
|
"trend_periods": 4,
|
||||||
|
"auto_remind": true,
|
||||||
|
"reminder_hours": 24
|
||||||
|
},
|
||||||
|
"templates": {
|
||||||
|
"wellbeing": {
|
||||||
|
"name": "Team Wellbeing",
|
||||||
|
"questions": 4
|
||||||
|
},
|
||||||
|
"workload": {
|
||||||
|
"name": "Workload Check",
|
||||||
|
"questions": 4
|
||||||
|
},
|
||||||
|
"collaboration": {
|
||||||
|
"name": "Collaboration Health",
|
||||||
|
"questions": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"create_pulse": ["manager", "hr", "admin"],
|
||||||
|
"view_results": ["manager", "hr", "admin"],
|
||||||
|
"respond": ["team_member"],
|
||||||
|
"manage_actions": ["manager", "hr", "admin"]
|
||||||
|
},
|
||||||
|
"integrations": {
|
||||||
|
"tasks": true,
|
||||||
|
"calendar": true,
|
||||||
|
"mail": true,
|
||||||
|
"analytics": true
|
||||||
|
}
|
||||||
|
}
|
||||||
219
hr/team-feedback.gbai/default.gbdialog
Normal file
219
hr/team-feedback.gbai/default.gbdialog
Normal file
|
|
@ -0,0 +1,219 @@
|
||||||
|
PARAM action AS STRING LIKE "pulse" DESCRIPTION "Action: pulse, review, insights, actions"
|
||||||
|
PARAM team_id AS STRING DESCRIPTION "Team identifier" OPTIONAL
|
||||||
|
PARAM period AS STRING LIKE "weekly" DESCRIPTION "Review period" OPTIONAL
|
||||||
|
|
||||||
|
DESCRIPTION "Team feedback and pulse surveys for managers"
|
||||||
|
|
||||||
|
SELECT CASE UCASE(action)
|
||||||
|
|
||||||
|
CASE "PULSE"
|
||||||
|
pulse_id = GUID()
|
||||||
|
pulse_name = ASK "Name this pulse check (e.g., 'Weekly Team Health'):"
|
||||||
|
|
||||||
|
template = ASK "Use template? (wellbeing, workload, collaboration, custom)"
|
||||||
|
|
||||||
|
questions = NEW ARRAY
|
||||||
|
|
||||||
|
IF template = "wellbeing" THEN
|
||||||
|
PUSH questions, { text: "How are you feeling about work this week?", type: "scale", scale: 5 }
|
||||||
|
PUSH questions, { text: "Do you feel supported by your team?", type: "scale", scale: 5 }
|
||||||
|
PUSH questions, { text: "Is your workload manageable?", type: "scale", scale: 5 }
|
||||||
|
PUSH questions, { text: "Any blockers or concerns?", type: "text" }
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF template = "workload" THEN
|
||||||
|
PUSH questions, { text: "How would you rate your current workload?", type: "scale", scale: 5 }
|
||||||
|
PUSH questions, { text: "Do you have clarity on priorities?", type: "scale", scale: 5 }
|
||||||
|
PUSH questions, { text: "Are deadlines realistic?", type: "scale", scale: 5 }
|
||||||
|
PUSH questions, { text: "What would help you be more productive?", type: "text" }
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF template = "collaboration" THEN
|
||||||
|
PUSH questions, { text: "How effective is team communication?", type: "scale", scale: 5 }
|
||||||
|
PUSH questions, { text: "Do you feel your input is valued?", type: "scale", scale: 5 }
|
||||||
|
PUSH questions, { text: "How well is the team working together?", type: "scale", scale: 5 }
|
||||||
|
PUSH questions, { text: "Suggestions for better collaboration?", type: "text" }
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF template = "custom" THEN
|
||||||
|
adding = TRUE
|
||||||
|
WHILE adding
|
||||||
|
q_text = ASK "Enter question (or 'done'):"
|
||||||
|
IF UCASE(q_text) = "DONE" THEN
|
||||||
|
adding = FALSE
|
||||||
|
ELSE
|
||||||
|
q_type = ASK "Type? (scale/text/choice)"
|
||||||
|
WITH q
|
||||||
|
text = q_text
|
||||||
|
type = q_type
|
||||||
|
scale = 5
|
||||||
|
END WITH
|
||||||
|
PUSH questions, q
|
||||||
|
END IF
|
||||||
|
END WHILE
|
||||||
|
END IF
|
||||||
|
|
||||||
|
frequency = ASK "Send frequency? (once, daily, weekly, biweekly, monthly)"
|
||||||
|
|
||||||
|
WITH pulse
|
||||||
|
id = pulse_id
|
||||||
|
name = pulse_name
|
||||||
|
template = template
|
||||||
|
questions = questions
|
||||||
|
frequency = frequency
|
||||||
|
team_id = team_id
|
||||||
|
anonymous = TRUE
|
||||||
|
status = "active"
|
||||||
|
created_at = NOW()
|
||||||
|
END WITH
|
||||||
|
|
||||||
|
SAVE pulse TO "team_pulses"
|
||||||
|
TALK "Pulse '" + pulse_name + "' created! First survey sends based on " + frequency + " schedule."
|
||||||
|
|
||||||
|
CASE "REVIEW"
|
||||||
|
pulses = FIND "team_pulses" WHERE team_id = team_id OR team_id = NULL
|
||||||
|
|
||||||
|
IF LEN(pulses) = 0 THEN
|
||||||
|
TALK "No pulse surveys found. Create one first with action=pulse"
|
||||||
|
ELSE
|
||||||
|
pulse_list = ""
|
||||||
|
FOR i = 0 TO LEN(pulses) - 1
|
||||||
|
pulse_list = pulse_list + (i + 1) + ". " + pulses[i].name + "\n"
|
||||||
|
END FOR
|
||||||
|
|
||||||
|
selection = ASK "Select pulse to review:\n" + pulse_list
|
||||||
|
selected = pulses[INT(selection) - 1]
|
||||||
|
|
||||||
|
responses = FIND "pulse_responses" WHERE pulse_id = selected.id
|
||||||
|
|
||||||
|
IF LEN(responses) = 0 THEN
|
||||||
|
TALK "No responses yet."
|
||||||
|
ELSE
|
||||||
|
TALK "📊 " + selected.name + " Results"
|
||||||
|
TALK "Responses: " + LEN(responses)
|
||||||
|
|
||||||
|
FOR q = 0 TO LEN(selected.questions) - 1
|
||||||
|
question = selected.questions[q]
|
||||||
|
TALK "\n" + question.text
|
||||||
|
|
||||||
|
IF question.type = "scale" THEN
|
||||||
|
sum = 0
|
||||||
|
FOR r = 0 TO LEN(responses) - 1
|
||||||
|
sum = sum + responses[r].answers[q]
|
||||||
|
END FOR
|
||||||
|
avg = sum / LEN(responses)
|
||||||
|
|
||||||
|
bar = ""
|
||||||
|
filled = INT(avg)
|
||||||
|
FOR b = 1 TO 5
|
||||||
|
IF b <= filled THEN
|
||||||
|
bar = bar + "█"
|
||||||
|
ELSE
|
||||||
|
bar = bar + "░"
|
||||||
|
END IF
|
||||||
|
END FOR
|
||||||
|
TALK bar + " " + FORMAT(avg, "0.0") + "/5"
|
||||||
|
|
||||||
|
IF avg < 3 THEN
|
||||||
|
TALK "⚠️ Area needs attention"
|
||||||
|
END IF
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF question.type = "text" THEN
|
||||||
|
TALK "Comments:"
|
||||||
|
FOR r = 0 TO LEN(responses) - 1
|
||||||
|
IF responses[r].answers[q] <> "" THEN
|
||||||
|
TALK "• " + responses[r].answers[q]
|
||||||
|
END IF
|
||||||
|
END FOR
|
||||||
|
END IF
|
||||||
|
END FOR
|
||||||
|
|
||||||
|
trend = FIND "pulse_trends" WHERE pulse_id = selected.id ORDER BY date DESC LIMIT 4
|
||||||
|
IF LEN(trend) > 1 THEN
|
||||||
|
TALK "\n📈 Trend (last 4 periods):"
|
||||||
|
FOR t = 0 TO LEN(trend) - 1
|
||||||
|
TALK trend[t].date + ": " + FORMAT(trend[t].avg_score, "0.0")
|
||||||
|
END FOR
|
||||||
|
END IF
|
||||||
|
END IF
|
||||||
|
END IF
|
||||||
|
|
||||||
|
CASE "INSIGHTS"
|
||||||
|
TALK "🔍 AI-Generated Team Insights"
|
||||||
|
|
||||||
|
responses = FIND "pulse_responses" WHERE team_id = team_id ORDER BY created_at DESC LIMIT 50
|
||||||
|
text_feedback = ""
|
||||||
|
FOR r = 0 TO LEN(responses) - 1
|
||||||
|
FOR a = 0 TO LEN(responses[r].answers) - 1
|
||||||
|
IF TYPEOF(responses[r].answers[a]) = "string" THEN
|
||||||
|
text_feedback = text_feedback + responses[r].answers[a] + " "
|
||||||
|
END IF
|
||||||
|
END FOR
|
||||||
|
END FOR
|
||||||
|
|
||||||
|
IF text_feedback <> "" THEN
|
||||||
|
themes = ANALYZE THEMES FROM text_feedback
|
||||||
|
sentiment = ANALYZE SENTIMENT FROM text_feedback
|
||||||
|
|
||||||
|
TALK "\n🎯 Key Themes:"
|
||||||
|
FOR t = 0 TO LEN(themes) - 1
|
||||||
|
TALK "• " + themes[t].topic + " (" + themes[t].count + " mentions)"
|
||||||
|
END FOR
|
||||||
|
|
||||||
|
TALK "\n😊 Team Sentiment: " + sentiment.label
|
||||||
|
TALK "Positive: " + FORMAT(sentiment.positive * 100, "0") + "%"
|
||||||
|
TALK "Neutral: " + FORMAT(sentiment.neutral * 100, "0") + "%"
|
||||||
|
TALK "Negative: " + FORMAT(sentiment.negative * 100, "0") + "%"
|
||||||
|
|
||||||
|
IF sentiment.negative > 0.3 THEN
|
||||||
|
TALK "\n⚠️ Elevated negative sentiment detected. Consider scheduling team discussion."
|
||||||
|
END IF
|
||||||
|
ELSE
|
||||||
|
TALK "Not enough feedback data for insights. Run more pulse surveys."
|
||||||
|
END IF
|
||||||
|
|
||||||
|
CASE "ACTIONS"
|
||||||
|
TALK "📋 Suggested Actions Based on Feedback"
|
||||||
|
|
||||||
|
low_scores = FIND "pulse_trends" WHERE avg_score < 3 AND team_id = team_id
|
||||||
|
|
||||||
|
IF LEN(low_scores) > 0 THEN
|
||||||
|
TALK "\nAreas needing attention:"
|
||||||
|
FOR s = 0 TO LEN(low_scores) - 1
|
||||||
|
TALK "• " + low_scores[s].question_text + " (Score: " + FORMAT(low_scores[s].avg_score, "0.0") + ")"
|
||||||
|
END FOR
|
||||||
|
|
||||||
|
create_action = ASK "Create action item? (yes/no)"
|
||||||
|
IF UCASE(create_action) = "YES" THEN
|
||||||
|
action_text = ASK "Describe the action:"
|
||||||
|
due_date = ASK "Due date (YYYY-MM-DD):"
|
||||||
|
owner = ASK "Owner (email):"
|
||||||
|
|
||||||
|
WITH action_item
|
||||||
|
id = GUID()
|
||||||
|
description = action_text
|
||||||
|
due = due_date
|
||||||
|
assigned_to = owner
|
||||||
|
team_id = team_id
|
||||||
|
source = "team_feedback"
|
||||||
|
status = "open"
|
||||||
|
created_at = NOW()
|
||||||
|
END WITH
|
||||||
|
|
||||||
|
SAVE action_item TO "action_items"
|
||||||
|
TALK "Action item created and assigned to " + owner
|
||||||
|
END IF
|
||||||
|
ELSE
|
||||||
|
TALK "No critical areas identified. Team health looks good!"
|
||||||
|
END IF
|
||||||
|
|
||||||
|
CASE ELSE
|
||||||
|
TALK "Team Feedback System"
|
||||||
|
TALK "Available actions:"
|
||||||
|
TALK "• pulse - Create quick team surveys"
|
||||||
|
TALK "• review - View pulse results"
|
||||||
|
TALK "• insights - AI-generated team insights"
|
||||||
|
TALK "• actions - Review and create action items"
|
||||||
|
|
||||||
|
END SELECT
|
||||||
37
multiagent/content-moderation.gbai/content-moderation.bas
Normal file
37
multiagent/content-moderation.gbai/content-moderation.bas
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
' Content Moderation Workflow with AI
|
||||||
|
USE KB "community-guidelines"
|
||||||
|
USE TOOL "image-analysis"
|
||||||
|
USE TOOL "text-sentiment"
|
||||||
|
|
||||||
|
ORCHESTRATE WORKFLOW "content-moderation"
|
||||||
|
|
||||||
|
STEP 1: BOT "content-analyzer" "scan content"
|
||||||
|
' Multi-modal content analysis
|
||||||
|
|
||||||
|
STEP 2: BOT "policy-checker" "verify guidelines"
|
||||||
|
' Check against community standards
|
||||||
|
|
||||||
|
IF toxicity_score > 0.7 OR contains_explicit_content = true THEN
|
||||||
|
STEP 3: BOT "auto-moderator" "remove content"
|
||||||
|
PUBLISH EVENT "content_removed"
|
||||||
|
ELSE IF toxicity_score > 0.4 THEN
|
||||||
|
STEP 4: HUMAN APPROVAL FROM "moderator@platform.com"
|
||||||
|
TIMEOUT 3600 ' 1 hour for borderline content
|
||||||
|
ON TIMEOUT: APPROVE WITH WARNING
|
||||||
|
END IF
|
||||||
|
|
||||||
|
' Enhanced LLM for context understanding
|
||||||
|
result = LLM "Analyze content context and cultural sensitivity"
|
||||||
|
WITH OPTIMIZE FOR "quality"
|
||||||
|
WITH MAX_COST 0.05
|
||||||
|
|
||||||
|
IF result.contains("cultural_sensitivity_issue") THEN
|
||||||
|
STEP 5: BOT "cultural-advisor" "review context"
|
||||||
|
END IF
|
||||||
|
|
||||||
|
' Learn from moderation decisions
|
||||||
|
BOT SHARE MEMORY "moderation_patterns" WITH "content-analyzer-v2"
|
||||||
|
|
||||||
|
PUBLISH EVENT "moderation_complete"
|
||||||
|
|
||||||
|
TALK "Content moderation completed"
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
' Example: Customer Support Workflow with Enhanced Orchestration
|
||||||
|
' This demonstrates the new ORCHESTRATE WORKFLOW, event system, and bot memory sharing
|
||||||
|
|
||||||
|
USE KB "support-policies"
|
||||||
|
USE TOOL "check-order"
|
||||||
|
USE TOOL "process-refund"
|
||||||
|
|
||||||
|
' Set up event handlers
|
||||||
|
ON EVENT "approval_received" DO
|
||||||
|
TALK "Manager approval received, processing refund..."
|
||||||
|
END ON
|
||||||
|
|
||||||
|
ON EVENT "timeout_occurred" DO
|
||||||
|
TALK "Approval timeout, escalating to director..."
|
||||||
|
END ON
|
||||||
|
|
||||||
|
' Main workflow orchestration
|
||||||
|
ORCHESTRATE WORKFLOW "customer-complaint-resolution"
|
||||||
|
|
||||||
|
STEP 1: BOT "classifier" "analyze complaint"
|
||||||
|
' Classifier bot analyzes the complaint and sets variables
|
||||||
|
|
||||||
|
STEP 2: BOT "order-checker" "validate order details"
|
||||||
|
' Order checker validates the order and warranty status
|
||||||
|
|
||||||
|
' Conditional logic based on order value
|
||||||
|
IF order_amount > 100 THEN
|
||||||
|
STEP 3: HUMAN APPROVAL FROM "manager@company.com"
|
||||||
|
TIMEOUT 1800 ' 30 minutes
|
||||||
|
ON TIMEOUT: ESCALATE TO "director@company.com"
|
||||||
|
|
||||||
|
' Wait for approval event
|
||||||
|
WAIT FOR EVENT "approval_received" TIMEOUT 3600
|
||||||
|
END IF
|
||||||
|
|
||||||
|
STEP 4: PARALLEL
|
||||||
|
BRANCH A: BOT "refund-processor" "process refund"
|
||||||
|
BRANCH B: BOT "inventory-updater" "update stock levels"
|
||||||
|
END PARALLEL
|
||||||
|
|
||||||
|
STEP 5: BOT "follow-up" "schedule customer check-in"
|
||||||
|
DELAY 86400 ' 24 hours later
|
||||||
|
|
||||||
|
' Share successful resolution patterns with other support bots
|
||||||
|
BOT SHARE MEMORY "successful_resolution_method" WITH "support-bot-2"
|
||||||
|
BOT SHARE MEMORY "customer_satisfaction_score" WITH "support-bot-3"
|
||||||
|
|
||||||
|
' Sync knowledge from master support bot
|
||||||
|
BOT SYNC MEMORY FROM "master-support-bot"
|
||||||
|
|
||||||
|
' Publish completion event for analytics
|
||||||
|
PUBLISH EVENT "workflow_completed"
|
||||||
|
|
||||||
|
TALK "Customer complaint resolved successfully!"
|
||||||
21
multiagent/customer-support-workflow.gbai/package.json
Normal file
21
multiagent/customer-support-workflow.gbai/package.json
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "Customer Support Workflow",
|
||||||
|
"description": "Advanced customer support workflow with multi-agent orchestration, event handling, and bot memory sharing",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "General Bots",
|
||||||
|
"category": "customer-support",
|
||||||
|
"keywords": ["workflow", "orchestration", "customer-support", "multi-agent"],
|
||||||
|
"features": [
|
||||||
|
"Multi-step workflow orchestration",
|
||||||
|
"Human approval integration",
|
||||||
|
"Event-driven coordination",
|
||||||
|
"Cross-bot memory sharing",
|
||||||
|
"Parallel processing",
|
||||||
|
"Automatic escalation"
|
||||||
|
],
|
||||||
|
"requirements": {
|
||||||
|
"tools": ["check-order", "process-refund"],
|
||||||
|
"knowledge_bases": ["support-policies"],
|
||||||
|
"bots": ["classifier", "order-checker", "refund-processor", "inventory-updater", "follow-up"]
|
||||||
|
}
|
||||||
|
}
|
||||||
45
multiagent/marketing-campaign.gbai/marketing-campaign.bas
Normal file
45
multiagent/marketing-campaign.gbai/marketing-campaign.bas
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
' Marketing Campaign Automation
|
||||||
|
USE KB "brand-guidelines"
|
||||||
|
USE TOOL "social-media-post"
|
||||||
|
USE TOOL "email-sender"
|
||||||
|
USE TOOL "analytics-tracker"
|
||||||
|
|
||||||
|
ORCHESTRATE WORKFLOW "marketing-campaign"
|
||||||
|
|
||||||
|
STEP 1: BOT "audience-segmenter" "analyze target demographics"
|
||||||
|
' AI-powered audience analysis
|
||||||
|
|
||||||
|
STEP 2: BOT "content-creator" "generate campaign materials"
|
||||||
|
' Multi-modal content generation
|
||||||
|
|
||||||
|
' Smart LLM routing for different content types
|
||||||
|
email_content = LLM "Create engaging email subject line"
|
||||||
|
WITH OPTIMIZE FOR "cost"
|
||||||
|
|
||||||
|
social_content = LLM "Create viral social media post"
|
||||||
|
WITH OPTIMIZE FOR "quality"
|
||||||
|
WITH MAX_LATENCY 5000
|
||||||
|
|
||||||
|
STEP 3: PARALLEL
|
||||||
|
BRANCH A: BOT "email-scheduler" "send email campaign"
|
||||||
|
BRANCH B: BOT "social-scheduler" "post to social media"
|
||||||
|
BRANCH C: BOT "ad-manager" "launch paid ads"
|
||||||
|
END PARALLEL
|
||||||
|
|
||||||
|
' Wait for initial results
|
||||||
|
WAIT FOR EVENT "campaign_metrics_ready" TIMEOUT 7200
|
||||||
|
|
||||||
|
STEP 4: BOT "performance-analyzer" "analyze results"
|
||||||
|
|
||||||
|
IF engagement_rate < 0.02 THEN
|
||||||
|
STEP 5: BOT "optimizer" "adjust campaign parameters"
|
||||||
|
PUBLISH EVENT "campaign_optimized"
|
||||||
|
END IF
|
||||||
|
|
||||||
|
' Share successful campaign patterns
|
||||||
|
BOT SHARE MEMORY "high_engagement_content" WITH "content-creator-v2"
|
||||||
|
BOT SHARE MEMORY "optimal_timing" WITH "scheduler-bots"
|
||||||
|
|
||||||
|
PUBLISH EVENT "campaign_complete"
|
||||||
|
|
||||||
|
TALK "Marketing campaign launched and optimized!"
|
||||||
40
multiagent/order-processing.gbai/order-processing.bas
Normal file
40
multiagent/order-processing.gbai/order-processing.bas
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
' E-commerce Order Processing Workflow
|
||||||
|
USE KB "order-policies"
|
||||||
|
USE TOOL "validate-payment"
|
||||||
|
USE TOOL "reserve-inventory"
|
||||||
|
USE TOOL "send-confirmation"
|
||||||
|
|
||||||
|
ORCHESTRATE WORKFLOW "order-processing"
|
||||||
|
|
||||||
|
STEP 1: BOT "fraud-detector" "analyze transaction"
|
||||||
|
' AI-powered fraud detection
|
||||||
|
|
||||||
|
STEP 2: BOT "inventory-checker" "verify availability"
|
||||||
|
' Check stock levels and reserve items
|
||||||
|
|
||||||
|
IF fraud_score > 0.8 THEN
|
||||||
|
STEP 3: HUMAN APPROVAL FROM "security@store.com"
|
||||||
|
TIMEOUT 900 ' 15 minutes for high-risk orders
|
||||||
|
ON TIMEOUT: REJECT ORDER
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF payment_method = "credit_card" THEN
|
||||||
|
STEP 4: BOT "payment-processor" "charge card"
|
||||||
|
ELSE
|
||||||
|
STEP 4: BOT "payment-processor" "process alternative"
|
||||||
|
END IF
|
||||||
|
|
||||||
|
STEP 5: PARALLEL
|
||||||
|
BRANCH A: BOT "shipping-optimizer" "select carrier"
|
||||||
|
BRANCH B: BOT "inventory-updater" "update stock"
|
||||||
|
BRANCH C: BOT "notification-sender" "send confirmation"
|
||||||
|
END PARALLEL
|
||||||
|
|
||||||
|
' Share successful processing patterns
|
||||||
|
BOT SHARE MEMORY "fraud_indicators" WITH "fraud-detector-backup"
|
||||||
|
BOT SHARE MEMORY "shipping_preferences" WITH "logistics-bot"
|
||||||
|
|
||||||
|
' Publish completion event
|
||||||
|
PUBLISH EVENT "order_processed"
|
||||||
|
|
||||||
|
TALK "Order processed successfully!"
|
||||||
52
productivity/broadcast.gbai/config.json
Normal file
52
productivity/broadcast.gbai/config.json
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
{
|
||||||
|
"name": "broadcast",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Internal communications and broadcast messaging",
|
||||||
|
"author": "General Bots",
|
||||||
|
"category": "productivity",
|
||||||
|
"tags": ["broadcast", "communications", "announcements", "campaigns", "newsletter"],
|
||||||
|
"entry_dialog": "default.gbdialog",
|
||||||
|
"features": {
|
||||||
|
"multi_channel": true,
|
||||||
|
"campaign_management": true,
|
||||||
|
"templates": true,
|
||||||
|
"scheduling": true,
|
||||||
|
"analytics": true,
|
||||||
|
"ai_content": true
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"default_channels": ["email", "chat"],
|
||||||
|
"require_approval": false,
|
||||||
|
"track_opens": true,
|
||||||
|
"track_clicks": true,
|
||||||
|
"max_recipients": 10000
|
||||||
|
},
|
||||||
|
"channels": {
|
||||||
|
"email": {
|
||||||
|
"enabled": true,
|
||||||
|
"track_opens": true
|
||||||
|
},
|
||||||
|
"chat": {
|
||||||
|
"enabled": true,
|
||||||
|
"channel_name": "announcements"
|
||||||
|
},
|
||||||
|
"social": {
|
||||||
|
"enabled": false,
|
||||||
|
"platforms": ["linkedin"]
|
||||||
|
},
|
||||||
|
"sms": {
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"create_campaign": ["communications", "hr", "admin"],
|
||||||
|
"send_campaign": ["communications", "admin"],
|
||||||
|
"view_analytics": ["communications", "hr", "admin", "manager"],
|
||||||
|
"manage_templates": ["communications", "admin"]
|
||||||
|
},
|
||||||
|
"integrations": {
|
||||||
|
"mail": true,
|
||||||
|
"social": true,
|
||||||
|
"analytics": true
|
||||||
|
}
|
||||||
|
}
|
||||||
257
productivity/broadcast.gbai/default.gbdialog
Normal file
257
productivity/broadcast.gbai/default.gbdialog
Normal file
|
|
@ -0,0 +1,257 @@
|
||||||
|
PARAM action AS STRING LIKE "campaign" DESCRIPTION "Action: campaign, send, schedule, templates, analytics"
|
||||||
|
PARAM channel AS STRING LIKE "all" DESCRIPTION "Channel: all, email, chat, social, sms" OPTIONAL
|
||||||
|
PARAM audience AS STRING DESCRIPTION "Target audience" OPTIONAL
|
||||||
|
|
||||||
|
DESCRIPTION "Internal communications and broadcast messaging"
|
||||||
|
|
||||||
|
SELECT CASE UCASE(action)
|
||||||
|
|
||||||
|
CASE "CAMPAIGN"
|
||||||
|
campaign_id = GUID()
|
||||||
|
campaign_name = ASK "Campaign name:"
|
||||||
|
campaign_goal = ASK "Campaign goal (awareness, engagement, action):"
|
||||||
|
|
||||||
|
TALK "📢 Creating broadcast campaign: " + campaign_name
|
||||||
|
|
||||||
|
message_subject = ASK "Message subject/title:"
|
||||||
|
message_body = ASK "Message content (supports markdown):"
|
||||||
|
|
||||||
|
use_ai = ASK "Use AI to improve message? (yes/no)"
|
||||||
|
IF UCASE(use_ai) = "YES" THEN
|
||||||
|
tone = ASK "Desired tone? (professional, friendly, urgent, inspirational)"
|
||||||
|
improved = IMPROVE TEXT message_body WITH TONE tone
|
||||||
|
TALK "AI-improved version:"
|
||||||
|
TALK improved
|
||||||
|
use_improved = ASK "Use improved version? (yes/no)"
|
||||||
|
IF UCASE(use_improved) = "YES" THEN
|
||||||
|
message_body = improved
|
||||||
|
END IF
|
||||||
|
END IF
|
||||||
|
|
||||||
|
channels_input = ASK "Select channels (comma-separated: email, chat, social, sms, or 'all'):"
|
||||||
|
IF UCASE(channels_input) = "ALL" THEN
|
||||||
|
selected_channels = ["email", "chat", "social", "sms"]
|
||||||
|
ELSE
|
||||||
|
selected_channels = SPLIT(channels_input, ",")
|
||||||
|
END IF
|
||||||
|
|
||||||
|
audience_type = ASK "Audience? (all, department, role, group, custom)"
|
||||||
|
IF audience_type = "custom" THEN
|
||||||
|
audience_filter = ASK "Enter audience filter (emails or group name):"
|
||||||
|
ELSE
|
||||||
|
audience_filter = audience_type
|
||||||
|
END IF
|
||||||
|
|
||||||
|
WITH campaign
|
||||||
|
id = campaign_id
|
||||||
|
name = campaign_name
|
||||||
|
goal = campaign_goal
|
||||||
|
subject = message_subject
|
||||||
|
body = message_body
|
||||||
|
channels = selected_channels
|
||||||
|
audience = audience_filter
|
||||||
|
status = "draft"
|
||||||
|
created_at = NOW()
|
||||||
|
END WITH
|
||||||
|
|
||||||
|
SAVE campaign TO "broadcast_campaigns"
|
||||||
|
|
||||||
|
preview = ASK "Preview campaign? (yes/no)"
|
||||||
|
IF UCASE(preview) = "YES" THEN
|
||||||
|
TALK "📧 Subject: " + campaign.subject
|
||||||
|
TALK "📝 Body: " + campaign.body
|
||||||
|
TALK "📡 Channels: " + JOIN(campaign.channels, ", ")
|
||||||
|
TALK "👥 Audience: " + campaign.audience
|
||||||
|
END IF
|
||||||
|
|
||||||
|
TALK "Campaign saved. Use action=send or action=schedule to distribute."
|
||||||
|
|
||||||
|
CASE "SEND"
|
||||||
|
campaigns = FIND "broadcast_campaigns" WHERE status = "draft" OR status = "scheduled"
|
||||||
|
|
||||||
|
IF LEN(campaigns) = 0 THEN
|
||||||
|
TALK "No campaigns ready to send. Create one first with action=campaign"
|
||||||
|
ELSE
|
||||||
|
campaign_list = ""
|
||||||
|
FOR i = 0 TO LEN(campaigns) - 1
|
||||||
|
campaign_list = campaign_list + (i + 1) + ". " + campaigns[i].name + " (" + campaigns[i].status + ")\n"
|
||||||
|
END FOR
|
||||||
|
|
||||||
|
selection = ASK "Select campaign to send:\n" + campaign_list
|
||||||
|
selected = campaigns[INT(selection) - 1]
|
||||||
|
|
||||||
|
confirm = ASK "Send '" + selected.name + "' now to " + selected.audience + "? (yes/no)"
|
||||||
|
IF UCASE(confirm) = "YES" THEN
|
||||||
|
recipients = GET_RECIPIENTS(selected.audience)
|
||||||
|
sent_count = 0
|
||||||
|
|
||||||
|
FOR ch = 0 TO LEN(selected.channels) - 1
|
||||||
|
channel_name = selected.channels[ch]
|
||||||
|
|
||||||
|
IF channel_name = "email" THEN
|
||||||
|
SEND MAIL TO recipients SUBJECT selected.subject BODY selected.body
|
||||||
|
sent_count = sent_count + LEN(recipients)
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF channel_name = "chat" THEN
|
||||||
|
POST TO "internal_announcements" CONTENT selected.body
|
||||||
|
sent_count = sent_count + 1
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF channel_name = "social" THEN
|
||||||
|
POST TO "linkedin" CONTENT selected.body
|
||||||
|
sent_count = sent_count + 1
|
||||||
|
END IF
|
||||||
|
END FOR
|
||||||
|
|
||||||
|
selected.status = "sent"
|
||||||
|
selected.sent_at = NOW()
|
||||||
|
selected.recipients_count = sent_count
|
||||||
|
SAVE selected TO "broadcast_campaigns"
|
||||||
|
|
||||||
|
TALK "✅ Campaign sent successfully!"
|
||||||
|
TALK "Recipients reached: " + sent_count
|
||||||
|
ELSE
|
||||||
|
TALK "Send cancelled."
|
||||||
|
END IF
|
||||||
|
END IF
|
||||||
|
|
||||||
|
CASE "SCHEDULE"
|
||||||
|
campaigns = FIND "broadcast_campaigns" WHERE status = "draft"
|
||||||
|
|
||||||
|
IF LEN(campaigns) = 0 THEN
|
||||||
|
TALK "No draft campaigns to schedule."
|
||||||
|
ELSE
|
||||||
|
campaign_list = ""
|
||||||
|
FOR i = 0 TO LEN(campaigns) - 1
|
||||||
|
campaign_list = campaign_list + (i + 1) + ". " + campaigns[i].name + "\n"
|
||||||
|
END FOR
|
||||||
|
|
||||||
|
selection = ASK "Select campaign to schedule:\n" + campaign_list
|
||||||
|
selected = campaigns[INT(selection) - 1]
|
||||||
|
|
||||||
|
schedule_date = ASK "Schedule date (YYYY-MM-DD):"
|
||||||
|
schedule_time = ASK "Schedule time (HH:MM):"
|
||||||
|
schedule_tz = ASK "Timezone (e.g., America/New_York):"
|
||||||
|
|
||||||
|
selected.status = "scheduled"
|
||||||
|
selected.scheduled_for = schedule_date + "T" + schedule_time
|
||||||
|
selected.timezone = schedule_tz
|
||||||
|
SAVE selected TO "broadcast_campaigns"
|
||||||
|
|
||||||
|
TALK "📅 Campaign scheduled for " + schedule_date + " at " + schedule_time + " " + schedule_tz
|
||||||
|
|
||||||
|
CASE "TEMPLATES"
|
||||||
|
template_action = ASK "Templates action? (list, create, use)"
|
||||||
|
|
||||||
|
IF template_action = "list" THEN
|
||||||
|
templates = FIND "broadcast_templates"
|
||||||
|
IF LEN(templates) = 0 THEN
|
||||||
|
TALK "No templates found. Create one with templates action=create"
|
||||||
|
ELSE
|
||||||
|
TALK "📋 Available templates:"
|
||||||
|
FOR t = 0 TO LEN(templates) - 1
|
||||||
|
TALK (t + 1) + ". " + templates[t].name + " - " + templates[t].category
|
||||||
|
END FOR
|
||||||
|
END IF
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF template_action = "create" THEN
|
||||||
|
template_name = ASK "Template name:"
|
||||||
|
template_category = ASK "Category (announcement, newsletter, alert, event):"
|
||||||
|
template_subject = ASK "Subject template:"
|
||||||
|
template_body = ASK "Body template (use {{variable}} for placeholders):"
|
||||||
|
|
||||||
|
WITH template
|
||||||
|
id = GUID()
|
||||||
|
name = template_name
|
||||||
|
category = template_category
|
||||||
|
subject = template_subject
|
||||||
|
body = template_body
|
||||||
|
created_at = NOW()
|
||||||
|
END WITH
|
||||||
|
|
||||||
|
SAVE template TO "broadcast_templates"
|
||||||
|
TALK "Template saved!"
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF template_action = "use" THEN
|
||||||
|
templates = FIND "broadcast_templates"
|
||||||
|
IF LEN(templates) = 0 THEN
|
||||||
|
TALK "No templates available."
|
||||||
|
ELSE
|
||||||
|
template_list = ""
|
||||||
|
FOR t = 0 TO LEN(templates) - 1
|
||||||
|
template_list = template_list + (t + 1) + ". " + templates[t].name + "\n"
|
||||||
|
END FOR
|
||||||
|
|
||||||
|
selection = ASK "Select template:\n" + template_list
|
||||||
|
selected_template = templates[INT(selection) - 1]
|
||||||
|
|
||||||
|
TALK "Using template: " + selected_template.name
|
||||||
|
TALK "Subject: " + selected_template.subject
|
||||||
|
TALK "Body: " + selected_template.body
|
||||||
|
TALK "Run action=campaign to create a campaign using this template."
|
||||||
|
END IF
|
||||||
|
END IF
|
||||||
|
|
||||||
|
CASE "ANALYTICS"
|
||||||
|
period = ASK "Analytics period? (week, month, quarter, all)"
|
||||||
|
|
||||||
|
campaigns = FIND "broadcast_campaigns" WHERE status = "sent"
|
||||||
|
|
||||||
|
IF LEN(campaigns) = 0 THEN
|
||||||
|
TALK "No sent campaigns to analyze."
|
||||||
|
ELSE
|
||||||
|
total_sent = 0
|
||||||
|
total_opened = 0
|
||||||
|
total_clicked = 0
|
||||||
|
|
||||||
|
TALK "📊 Broadcast Analytics"
|
||||||
|
TALK "═══════════════════════════════════"
|
||||||
|
|
||||||
|
FOR c = 0 TO LEN(campaigns) - 1
|
||||||
|
campaign = campaigns[c]
|
||||||
|
opens = campaign.opens_count
|
||||||
|
clicks = campaign.clicks_count
|
||||||
|
sent = campaign.recipients_count
|
||||||
|
|
||||||
|
IF opens = NULL THEN opens = 0
|
||||||
|
IF clicks = NULL THEN clicks = 0
|
||||||
|
IF sent = NULL THEN sent = 0
|
||||||
|
|
||||||
|
total_sent = total_sent + sent
|
||||||
|
total_opened = total_opened + opens
|
||||||
|
total_clicked = total_clicked + clicks
|
||||||
|
|
||||||
|
open_rate = 0
|
||||||
|
IF sent > 0 THEN
|
||||||
|
open_rate = (opens / sent) * 100
|
||||||
|
END IF
|
||||||
|
|
||||||
|
TALK "\n📢 " + campaign.name
|
||||||
|
TALK " Sent: " + sent + " | Opens: " + opens + " (" + FORMAT(open_rate, "0") + "%)"
|
||||||
|
END FOR
|
||||||
|
|
||||||
|
TALK "\n═══════════════════════════════════"
|
||||||
|
TALK "TOTALS"
|
||||||
|
TALK "Campaigns: " + LEN(campaigns)
|
||||||
|
TALK "Messages sent: " + total_sent
|
||||||
|
|
||||||
|
overall_open_rate = 0
|
||||||
|
IF total_sent > 0 THEN
|
||||||
|
overall_open_rate = (total_opened / total_sent) * 100
|
||||||
|
END IF
|
||||||
|
TALK "Overall open rate: " + FORMAT(overall_open_rate, "0") + "%"
|
||||||
|
END IF
|
||||||
|
|
||||||
|
CASE ELSE
|
||||||
|
TALK "📢 Broadcast Communication System"
|
||||||
|
TALK "Available actions:"
|
||||||
|
TALK "• campaign - Create a new broadcast campaign"
|
||||||
|
TALK "• send - Send a campaign immediately"
|
||||||
|
TALK "• schedule - Schedule a campaign for later"
|
||||||
|
TALK "• templates - Manage message templates"
|
||||||
|
TALK "• analytics - View campaign performance"
|
||||||
|
|
||||||
|
END SELECT
|
||||||
12
store-server.gbai/store-server.gbdialog/auto-register.bas
Normal file
12
store-server.gbai/store-server.gbdialog/auto-register.bas
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
PARAM query AS STRING
|
||||||
|
|
||||||
|
existing = SEARCH PRODUCTS query, 1
|
||||||
|
IF LEN(existing) > 0 THEN
|
||||||
|
RETURN existing[0]
|
||||||
|
END IF
|
||||||
|
|
||||||
|
info = LLM "Extract product info: " + query + ". Return JSON with name, category, brand, description"
|
||||||
|
|
||||||
|
SAVE "products" WITH name AS info.name, category AS info.category, brand AS info.brand, description AS info.description, is_active AS true
|
||||||
|
|
||||||
|
RETURN FIND "products" WITH "name = '" + info.name + "'"
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
PARAM product_id AS STRING
|
||||||
|
PARAM quantity AS INTEGER
|
||||||
|
|
||||||
|
product = PRODUCT product_id
|
||||||
|
IF NOT product THEN
|
||||||
|
RETURN WITH error AS "Product not found"
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF NOT quantity THEN
|
||||||
|
quantity = 1
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF NOT product.gross_weight THEN
|
||||||
|
dims = LLM "Estimate shipping dimensions for: " + product.name + ". Return JSON with length_cm, width_cm, height_cm, weight_kg"
|
||||||
|
UPDATE "products" WITH length AS dims.length_cm, width AS dims.width_cm, height AS dims.height_cm, gross_weight AS dims.weight_kg WHERE id = product_id
|
||||||
|
product = PRODUCT product_id
|
||||||
|
END IF
|
||||||
|
|
||||||
|
volume = product.length * product.width * product.height * quantity
|
||||||
|
volumetric_weight = volume / 5000
|
||||||
|
actual_weight = product.gross_weight * quantity
|
||||||
|
billable = MAX actual_weight, volumetric_weight
|
||||||
|
|
||||||
|
|
||||||
|
RETURN WITH product_id AS product_id, quantity AS quantity, length AS product.length, width AS product.width, height AS product.height, weight AS product.gross_weight, volume_cm3 AS volume, billable_weight_kg AS billable
|
||||||
13
store-server.gbai/store-server.gbdialog/classify-product.bas
Normal file
13
store-server.gbai/store-server.gbdialog/classify-product.bas
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
PARAM query AS STRING
|
||||||
|
|
||||||
|
categories = FIND "categories"
|
||||||
|
similar = SEARCH PRODUCTS query, 5
|
||||||
|
result = LLM "Classify '" + query + "' into: " + categories + ". Similar: " + similar + ". Return JSON with category_id, name, confidence, brand, type"
|
||||||
|
|
||||||
|
cached = FIND "products" WITH "name LIKE '%" + query + "%'"
|
||||||
|
IF cached THEN
|
||||||
|
RETURN cached
|
||||||
|
END IF
|
||||||
|
|
||||||
|
SAVE "products" WITH name AS query, category AS result.name, brand AS result.brand, external_metadata AS result
|
||||||
|
RETURN result
|
||||||
28
store-server.gbai/store-server.gbdialog/enrich-product.bas
Normal file
28
store-server.gbai/store-server.gbdialog/enrich-product.bas
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
PARAM product_id AS STRING
|
||||||
|
|
||||||
|
product = PRODUCT product_id
|
||||||
|
IF NOT product THEN
|
||||||
|
RETURN WITH error AS "Product not found"
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF product.description AND product.brand THEN
|
||||||
|
RETURN product
|
||||||
|
END IF
|
||||||
|
|
||||||
|
query = product.name + " " + product.category
|
||||||
|
links = SCRAPE_ALL "https://www.google.com/search?q=" + query, "a"
|
||||||
|
links = FIRST links, 10
|
||||||
|
|
||||||
|
enriched = []
|
||||||
|
FOR i = 0 TO 2
|
||||||
|
IF links[i] THEN
|
||||||
|
content = SCRAPE links[i], "body"
|
||||||
|
PUSH enriched, content
|
||||||
|
END IF
|
||||||
|
NEXT
|
||||||
|
|
||||||
|
result = LLM "Analyze these product descriptions: " + enriched + ". Create best description for: " + product.name + ". Return JSON with description, brand, material, features"
|
||||||
|
|
||||||
|
UPDATE "products" WITH description AS result.description, brand AS result.brand, material AS result.material, external_metadata AS result WHERE id = product_id
|
||||||
|
|
||||||
|
RETURN PRODUCT product_id
|
||||||
15
store-server.gbai/store-server.gbdialog/get-ncm.bas
Normal file
15
store-server.gbai/store-server.gbdialog/get-ncm.bas
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
PARAM product_id AS STRING
|
||||||
|
|
||||||
|
product = PRODUCT product_id
|
||||||
|
IF NOT product THEN
|
||||||
|
RETURN WITH error AS "Product not found"
|
||||||
|
END IF
|
||||||
|
|
||||||
|
IF product.tax_code THEN
|
||||||
|
RETURN WITH tax_code AS product.tax_code, tax_class AS product.tax_class
|
||||||
|
END IF
|
||||||
|
|
||||||
|
info = LLM "Get NCM/tax classification for: " + product.name + " " + product.category + ". Return JSON with tax_code, tax_class, description"
|
||||||
|
UPDATE "products" WITH tax_code AS info.tax_code, tax_class AS info.tax_class WHERE id = product_id
|
||||||
|
|
||||||
|
RETURN WITH tax_code AS info.tax_code, tax_class AS info.tax_class, description AS info.description
|
||||||
23
store-server.gbai/store-server.gbdialog/scan-barcode.bas
Normal file
23
store-server.gbai/store-server.gbdialog/scan-barcode.bas
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
PARAM image AS STRING
|
||||||
|
|
||||||
|
barcode = SCAN BARCODE image
|
||||||
|
IF NOT barcode THEN
|
||||||
|
RETURN WITH error AS "No barcode found"
|
||||||
|
END IF
|
||||||
|
|
||||||
|
ean = barcode.data
|
||||||
|
cached = FIND "products" WITH "global_trade_number = '" + ean + "'"
|
||||||
|
IF cached THEN
|
||||||
|
RETURN cached
|
||||||
|
END IF
|
||||||
|
|
||||||
|
data = GET "https://world.openfoodfacts.org/api/v0/product/" + ean + ".json"
|
||||||
|
IF data.product THEN
|
||||||
|
SAVE "products" WITH name AS data.product.product_name, brand AS data.product.brands, global_trade_number AS ean, description AS data.product.generic_name, category AS data.product.categories, net_weight AS data.product.quantity
|
||||||
|
RETURN FIND "products" WITH "global_trade_number = '" + ean + "'"
|
||||||
|
END IF
|
||||||
|
|
||||||
|
info = LLM "Search product info for EAN: " + ean + ". Return JSON with name, brand, description, category"
|
||||||
|
SAVE "products" WITH name AS info.name, brand AS info.brand, description AS info.description, category AS info.category, global_trade_number AS ean
|
||||||
|
|
||||||
|
RETURN FIND "products" WITH "global_trade_number = '" + ean + "'"
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
PARAM image AS STRING
|
||||||
|
|
||||||
|
description = SEE image
|
||||||
|
similar = SEARCH PRODUCTS description, 5
|
||||||
|
|
||||||
|
IF LEN(similar) > 0 THEN
|
||||||
|
RETURN similar[0]
|
||||||
|
END IF
|
||||||
|
|
||||||
|
product = LLM "Extract product info from: " + description + ". Return JSON with name, brand, category, color, material"
|
||||||
|
SAVE "products" WITH name AS product.name, brand AS product.brand, category AS product.category, color AS product.color, material AS product.material
|
||||||
|
|
||||||
|
RETURN FIND "products" WITH "name = '" + product.name + "'"
|
||||||
20
store-server.gbai/store-server.gbdialog/search-products.bas
Normal file
20
store-server.gbai/store-server.gbdialog/search-products.bas
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
PARAM query AS STRING
|
||||||
|
|
||||||
|
cached = FIND "products" WITH "name ILIKE '%" + query + "%' OR description ILIKE '%" + query + "%'"
|
||||||
|
IF cached THEN
|
||||||
|
RETURN cached
|
||||||
|
END IF
|
||||||
|
|
||||||
|
result = SEARCH PRODUCTS query, 10
|
||||||
|
IF LEN(result) = 0 THEN
|
||||||
|
web = SCRAPE_ALL "https://www.google.com/search?q=" + query + "+product", ".g"
|
||||||
|
result = LLM "Extract products from: " + web + ". Return JSON array with name, price, description"
|
||||||
|
END IF
|
||||||
|
|
||||||
|
enhanced = LLM "Add descriptions: " + result + ". Return JSON array with id, name, price, description, stock"
|
||||||
|
|
||||||
|
FOR EACH item IN enhanced
|
||||||
|
SAVE "products" WITH name AS item.name, description AS item.description, price AS item.price, external_metadata AS item
|
||||||
|
NEXT
|
||||||
|
|
||||||
|
RETURN enhanced
|
||||||
6
store-server.gbai/store-server.gbot/config.csv
Normal file
6
store-server.gbai/store-server.gbot/config.csv
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
name,value
|
||||||
|
bot-name,store-server
|
||||||
|
bot-description,Store Server API - Product search, classification, barcode scanning and enrichment
|
||||||
|
botmodels-enabled,true
|
||||||
|
llm-provider,openai
|
||||||
|
llm-model,gpt-4o
|
||||||
|
Loading…
Add table
Reference in a new issue