Update .gitignore, remove old prompt docs, update botserver submodule
This commit is contained in:
parent
468a4fdbb7
commit
ce4c676501
8 changed files with 33 additions and 1908 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -84,3 +84,11 @@ errors_utf8.txt
|
|||
vault-unseal-keysdefault-vault.tar
|
||||
prompts/sec-bots.md
|
||||
AGENTS-PROD.md
|
||||
|
||||
# Backup files
|
||||
*.head
|
||||
*.bad2
|
||||
*.bad
|
||||
*.check
|
||||
*.fix
|
||||
*.bak
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 7673eccc5963ec73b9f83b61888cb36670d60802
|
||||
Subproject commit e8e1b7d65b8d1c37e2ea73cdcfec9a0e1f040027
|
||||
File diff suppressed because it is too large
Load diff
146
prompts/crmex.md
146
prompts/crmex.md
|
|
@ -1,146 +0,0 @@
|
|||
# Email Campaigns — Feature Plan
|
||||
|
||||
## Existing Foundation (botserver/src/marketing/)
|
||||
- `campaigns.rs` — CrmCampaign model, CRUD handlers
|
||||
- `metrics.rs` — CampaignMetrics, ChannelBreakdown, open/click/conversion rates
|
||||
- `lists.rs` — recipient lists
|
||||
- `templates.rs` — content templates
|
||||
- `triggers.rs` — event-based sending
|
||||
- `email/tracking.rs` — open/click tracking pixels
|
||||
|
||||
---
|
||||
|
||||
## Features to Build
|
||||
|
||||
### 1. Insights Dashboard
|
||||
**What:** Time series views of delivery + engagement metrics per campaign.
|
||||
|
||||
**Data points per time bucket (hourly/daily):**
|
||||
- Sent, delivered, bounced, failed
|
||||
- Opens (unique + total), clicks, replies, unsubscribes
|
||||
- Delivery rate, open rate, click-to-open rate (CTOR)
|
||||
|
||||
**Filters/pivots:**
|
||||
- By mailbox provider (Gmail, Outlook, Yahoo, etc. — parsed from MX/SMTP response)
|
||||
- By sender identity (from address / domain)
|
||||
- By campaign or list
|
||||
- Message search → show exact SMTP response from provider
|
||||
|
||||
**Implementation:**
|
||||
- Add `email_delivery_events` table: `(id, campaign_id, recipient_id, event_type, provider, smtp_response, ts)`
|
||||
- API: `GET /api/campaigns/:id/insights?from=&to=&group_by=provider|identity|day`
|
||||
- UI: HTMX + chart.js time series (local vendor)
|
||||
|
||||
---
|
||||
|
||||
### 2. Advisor Recommendations
|
||||
**What:** Analyze sending config + results and surface actionable fixes.
|
||||
|
||||
**Checks to run:**
|
||||
| Check | Signal | Recommendation |
|
||||
|---|---|---|
|
||||
| SPF/DKIM/DMARC | DNS lookup | "Add missing record" |
|
||||
| Bounce rate > 5% | delivery_events | "Clean list — remove hard bounces" |
|
||||
| Open rate < 15% | metrics | "Improve subject line / send time" |
|
||||
| Spam complaints > 0.1% | FBL data | "Remove complainers immediately" |
|
||||
| Sending from new IP | warmup_schedule | "Follow warmup plan" |
|
||||
| List age > 6 months | list.last_sent | "Re-engagement campaign before bulk send" |
|
||||
|
||||
**Implementation:**
|
||||
- `marketing/advisor.rs` — `AdvisorEngine::analyze(campaign_id) -> Vec<Recommendation>`
|
||||
- API: `GET /api/campaigns/:id/advisor`
|
||||
- Runs automatically after each campaign completes
|
||||
|
||||
---
|
||||
|
||||
### 3. IP Warmup (like OneSignal / Mailchimp)
|
||||
**What:** Gradually increase daily send volume over 4–6 weeks to build sender reputation.
|
||||
|
||||
**Warmup schedule (standard):**
|
||||
| Day | Max emails/day |
|
||||
|---|---|
|
||||
| 1–2 | 50 |
|
||||
| 3–4 | 100 |
|
||||
| 5–7 | 500 |
|
||||
| 8–10 | 1,000 |
|
||||
| 11–14 | 5,000 |
|
||||
| 15–21 | 10,000 |
|
||||
| 22–28 | 50,000 |
|
||||
| 29+ | unlimited |
|
||||
|
||||
**Rules:**
|
||||
- Only send to most engaged subscribers first (opened in last 90 days)
|
||||
- Stop warmup if bounce rate > 3% or complaint rate > 0.1%
|
||||
- Resume next day at same volume if paused
|
||||
|
||||
**Implementation:**
|
||||
- `marketing/warmup.rs` — `WarmupSchedule`, `WarmupEngine::get_daily_limit(ip, day) -> u32`
|
||||
- `warmup_schedules` table: `(id, ip, started_at, current_day, status, paused_reason)`
|
||||
- Scheduler checks warmup limit before each send batch
|
||||
- API: `GET /api/warmup/status`, `POST /api/warmup/start`
|
||||
|
||||
---
|
||||
|
||||
### 4. Optimized Shared Delivery
|
||||
**What:** Auto-select best sending IP based on real-time reputation signals.
|
||||
|
||||
**Logic:**
|
||||
- Track per-IP: bounce rate, complaint rate, delivery rate (last 24h)
|
||||
- Score each IP: `score = delivery_rate - (bounce_rate * 10) - (complaint_rate * 100)`
|
||||
- Route each send to highest-scored IP for that destination provider
|
||||
- Rotate IPs to spread load and preserve reputation
|
||||
|
||||
**Implementation:**
|
||||
- `marketing/ip_router.rs` — `IpRouter::select(destination_domain) -> IpAddr`
|
||||
- `ip_reputation` table: `(ip, provider, bounces, complaints, delivered, window_start)`
|
||||
- Plugs into Stalwart send path via botserver API
|
||||
|
||||
---
|
||||
|
||||
### 5. Modern Email Marketing Features
|
||||
|
||||
| Feature | Description |
|
||||
|---|---|
|
||||
| **Send time optimization** | ML-based per-contact best send time (based on past open history) |
|
||||
| **A/B testing** | Split subject/content, auto-pick winner after N hours |
|
||||
| **Suppression list** | Global unsubscribe/bounce/complaint list, auto-applied to all sends |
|
||||
| **Re-engagement flows** | Auto-trigger "we miss you" to contacts inactive > 90 days |
|
||||
| **Transactional + marketing separation** | Separate IPs/domains for transactional vs bulk |
|
||||
| **One-click unsubscribe** | RFC 8058 `List-Unsubscribe-Post` header on all bulk sends |
|
||||
| **Preview & spam score** | Pre-send SpamAssassin score check |
|
||||
| **Link tracking** | Redirect all links through tracker, record clicks per contact |
|
||||
| **Webhook events** | Push delivery events to external URLs (Stalwart webhook → botserver) |
|
||||
|
||||
---
|
||||
|
||||
## DB Tables to Add
|
||||
|
||||
```sql
|
||||
email_delivery_events (id, campaign_id, recipient_id, event_type, provider, smtp_code, smtp_response, ts)
|
||||
warmup_schedules (id, ip, started_at, current_day, daily_limit, status, paused_reason)
|
||||
ip_reputation (id, ip, provider, delivered, bounced, complained, window_start)
|
||||
advisor_recommendations (id, campaign_id, check_name, severity, message, created_at, dismissed)
|
||||
ab_tests (id, campaign_id, variant_a, variant_b, split_pct, winner, decided_at)
|
||||
suppression_list (id, org_id, email, reason, added_at)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Files to Create
|
||||
```
|
||||
botserver/src/marketing/
|
||||
├── warmup.rs — IP warmup engine + schedule
|
||||
├── advisor.rs — recommendation engine
|
||||
├── ip_router.rs — optimized IP selection
|
||||
├── ab_test.rs — A/B test logic
|
||||
├── suppression.rs — global suppression list
|
||||
└── send_time.rs — send time optimization
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Existing Code to Extend
|
||||
- `marketing/metrics.rs` → add time-series queries + provider breakdown
|
||||
- `marketing/campaigns.rs` → add warmup_enabled, ab_test_id fields
|
||||
- `email/tracking.rs` → already has open/click tracking, extend with provider parsing
|
||||
- `core/shared/schema/` → add new tables above
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
# detector - Detecção de Desvios na Folha
|
||||
|
||||
## Objetivo
|
||||
- Bot detector deve usar start.bas para inserir dados via init_folha.bas
|
||||
- detecta.bas deve detectar anomalias nos dados inseridos
|
||||
|
||||
## ✅ Status Atual
|
||||
|
||||
### Correção REM em mod.rs (FEITA)
|
||||
**Arquivo:** `botserver/src/basic/mod.rs` linha ~588-594
|
||||
|
||||
Filtro adicionado para `REM ` e `REM\t` no `compile_tool_script`:
|
||||
```rust
|
||||
!(trimmed.starts_with("PARAM ") ||
|
||||
trimmed.starts_with("PARAM\t") ||
|
||||
trimmed.starts_with("DESCRIPTION ") ||
|
||||
trimmed.starts_with("DESCRIPTION\t") ||
|
||||
trimmed.starts_with("REM ") || // <-- ADICIONADO
|
||||
trimmed.starts_with("REM\t") || // <-- ADICIONADO
|
||||
trimmed.starts_with('\'') ||
|
||||
trimmed.starts_with('#') ||
|
||||
trimmed.is_empty())
|
||||
```
|
||||
|
||||
### Arquivos Envolvidos (VERIFICADOS)
|
||||
- `/opt/gbo/data/detector.gbai/detector.gbdialog/start.bas` ✅ OK
|
||||
- Contém botões de sugestão: detecta e init_folha
|
||||
- `/opt/gbo/data/detector.gbai/detector.gbdialog/init_folha.bas` ✅ OK
|
||||
- 4 INSERT statements para dados de exemplo
|
||||
- `/opt/gbo/data/detector.gbai/detector.gbdialog/detecta.bas` ✅ OK
|
||||
- Usa DETECT keyword
|
||||
- `/opt/gbo/data/detector.gbai/detector.gbdialog/tables.bas` ✅ OK
|
||||
- TABLE folha_salarios definida
|
||||
|
||||
### Botserver (RODANDO)
|
||||
- ✅ Botserver compilado com sucesso
|
||||
- ✅ Botserver rodando em http://localhost:8080
|
||||
- ✅ Health check OK
|
||||
|
||||
## Próximos Passos (Pendentes)
|
||||
|
||||
1. **Testar via navegador** - Necessário instalar Playwright browsers
|
||||
- Navegar para http://localhost:3000/detector
|
||||
- Clicar em "⚙️ Inicializar Dados de Teste"
|
||||
- Verificar se INSERT funciona
|
||||
- Clicar em "🔍 Detectar Desvios na Folha"
|
||||
- Verificar se DETECT funciona
|
||||
|
||||
2. **Verificar se há warnings relevantes**
|
||||
- Alguns warnings de código podem precisar ser corrigidos
|
||||
|
||||
## Cache
|
||||
- AST limpo: `rm ./botserver-stack/data/system/work/detector.gbai/detector.gbdialog/*.ast`
|
||||
- Reiniciado: `./restart.sh`
|
||||
- Botserver: ✅ Rodando
|
||||
|
||||
## Arquivos de Trabalho
|
||||
- Work directory: `./botserver-stack/data/system/work/detector.gbai/detector.gbdialog/`
|
||||
- Todos os arquivos BASIC estão presentes e parecem válidos
|
||||
|
|
@ -270,3 +270,27 @@ botui/ui/suite/
|
|||
```
|
||||
|
||||
File uploads go to `POST /api/suite/upload` → stored in Drive → media_id passed to tool.
|
||||
|
||||
---
|
||||
|
||||
## Compilation & Validation Log
|
||||
|
||||
**Last Validated:** 2026-04-17
|
||||
|
||||
**Status:** ✅ Design Document - No Code to Compile
|
||||
|
||||
**Validation Checklist:**
|
||||
- [x] Architecture diagram is clear
|
||||
- [x] App pattern is defined
|
||||
- [x] WhatsApp campaign flow is documented
|
||||
- [x] Tool definitions are complete
|
||||
- [x] HTML structure is valid
|
||||
- [x] Rust code examples are syntactically correct
|
||||
- [x] Implementation phases are planned
|
||||
- [x] File structure is organized
|
||||
|
||||
**Notes:**
|
||||
- This is a design document for integrated suite conversational interface
|
||||
- No compilation required - it's planning documentation
|
||||
- Ready for implementation when developer is available
|
||||
- References existing code in botserver/src/marketing/ and botserver/src/core/bot/
|
||||
|
|
|
|||
|
|
@ -1,435 +0,0 @@
|
|||
# SWITCHER Feature - Response Format Modifiers
|
||||
|
||||
## Overview
|
||||
Add a switcher interface that allows users to toggle response modifiers that influence how the AI generates responses. Unlike suggestions (which are one-time actions), switchers are persistent toggles that remain active until deactivated.
|
||||
|
||||
## Location
|
||||
`botui/ui/suite/chat/` - alongside existing suggestion buttons
|
||||
|
||||
## Syntax
|
||||
|
||||
### Standard Switcher (predefined prompt)
|
||||
```
|
||||
ADD SWITCHER "tables" AS "Tabelas"
|
||||
```
|
||||
|
||||
### Custom Switcher (with custom prompt)
|
||||
```
|
||||
ADD SWITCHER "sempre mostrar 10 perguntas" AS "Mostrar Perguntas"
|
||||
```
|
||||
|
||||
## What Switcher Does
|
||||
|
||||
The switcher:
|
||||
1. **Injects the prompt** into every LLM request
|
||||
2. **The prompt** can be:
|
||||
- **Standard**: References a predefined prompt by ID (`"tables"`, `"cards"`, etc.)
|
||||
- **Custom**: Any custom instruction string (`"sempre mostrar 10 perguntas"`)
|
||||
3. **Influences** the AI response format
|
||||
4. **Persists** until toggled OFF
|
||||
|
||||
## Available Standard Switchers
|
||||
|
||||
| ID | Label | Color | Description |
|
||||
|----|--------|--------|-------------|
|
||||
| tables | Tabelas | #4CAF50 | Format responses as tables |
|
||||
| infographic | Infográfico | #2196F3 | Visual, graphical representations |
|
||||
| cards | Cards | #FF9800 | Card-based layout |
|
||||
| list | Lista | #9C27B0 | Bulleted lists |
|
||||
| comparison | Comparação | #E91E63 | Side-by-side comparisons |
|
||||
| timeline | Timeline | #00BCD4 | Chronological ordering |
|
||||
| markdown | Markdown | #607D8B | Standard markdown |
|
||||
| chart | Gráfico | #F44336 | Charts and diagrams |
|
||||
|
||||
## Predefined Prompts (Backend)
|
||||
|
||||
Each standard ID maps to a predefined prompt in the backend:
|
||||
|
||||
```
|
||||
ID: tables
|
||||
Prompt: "REGRAS DE FORMATO: SEMPRE retorne suas respostas em formato de tabela HTML usando <table>, <thead>, <tbody>, <tr>, <th>, <td>. Cada dado deve ser uma célula. Use cabeçalhos claros na primeira linha. Se houver dados numéricos, alinhe à direita. Se houver texto, alinhe à esquerda. Use cores sutis em linhas alternadas (nth-child). NÃO use markdown tables, use HTML puro."
|
||||
|
||||
ID: infographic
|
||||
Prompt: "REGRAS DE FORMATO: Crie representações visuais HTML usando SVG, progress bars, stat cards, e elementos gráficos. Use elementos como: <svg> para gráficos, <div style="width:X%;background:color"> para barras de progresso, ícones emoji, badges coloridos. Organize informações visualmente com grids, flexbox, e espaçamento. Inclua legendas e rótulos visuais claros."
|
||||
|
||||
ID: cards
|
||||
Prompt: "REGRAS DE FORMATO: Retorne informações em formato de cards HTML. Cada card deve ter: <div class="card" style="border:1px solid #ddd;border-radius:8px;padding:16px;margin:8px;box-shadow:0 2px 4px rgba(0,0,0,0.1)">. Dentro do card use: título em <h3> ou <strong>, subtítulo em <p> style="color:#666", ícone emoji ou ícone SVG no topo, badges de status. Organize cards em grid usando display:grid ou flex-wrap."
|
||||
|
||||
ID: list
|
||||
Prompt: "REGRAS DE FORMATO: Use apenas listas HTML: <ul> para bullets e <ol> para números numerados. Cada item em <li>. Use sublistas aninhadas quando apropriado. NÃO use parágrafos de texto, converta tudo em itens de lista. Adicione ícones emoji no início de cada <li> quando possível. Use classes CSS para estilização: .list-item, .sub-list."
|
||||
|
||||
ID: comparison
|
||||
Prompt: "REGRAS DE FORMATO: Crie comparações lado a lado em HTML. Use grid de 2 colunas: <div style="display:grid;grid-template-columns:1fr 1fr;gap:20px">. Cada lado em uma <div class="comparison-side"> com borda colorida distinta. Use headers claros para cada lado. Adicione seção de "Diferenças Chave" com bullet points. Use cores contrastantes para cada lado (ex: azul vs laranja). Inclua tabela de comparação resumida no final."
|
||||
|
||||
ID: timeline
|
||||
Prompt: "REGRAS DE FORMATO: Organize eventos cronologicamente em formato de timeline HTML. Use <div class="timeline"> com border-left vertical. Cada evento em <div class="timeline-item"> com: data em <span class="timeline-date" style="font-weight:bold;color:#666">, título em <h3>, descrição em <p>. Adicione círculo indicador na timeline line. Ordene do mais antigo para o mais recente. Use espaçamento claro entre eventos."
|
||||
|
||||
ID: markdown
|
||||
Prompt: "REGRAS DE FORMATO: Use exclusivamente formato Markdown padrão. Sintaxe permitida: **negrito**, *itálico*, `inline code`, ```bloco de código```, # cabeçalhos, - bullets, 1. números, [links](url), , | tabela | markdown |. NÃO use HTML tags exceto para blocos de código. Siga estritamente a sintaxe CommonMark."
|
||||
|
||||
ID: chart
|
||||
Prompt: "REGRAS DE FORMATO: Crie gráficos e diagramas em HTML SVG. Use elementos SVG: <svg width="X" height="Y">, <line> para gráficos de linha, <rect> para gráficos de barra, <circle> para gráficos de pizza, <path> para gráficos de área. Inclua eixos com labels, grid lines, legendas. Use cores distintas para cada série de dados (ex: vermelho, azul, verde). Adicione tooltips com valores ao hover. Se o usuário pedir gráfico de pizza com "pizza vermelha", use fill="#FF0000" no SVG."
|
||||
```
|
||||
|
||||
## UI Design
|
||||
|
||||
### HTML Structure
|
||||
```html
|
||||
<div class="switchers-container" id="switchers">
|
||||
<div class="switchers-label">Formato:</div>
|
||||
<div class="switchers-chips" id="switchersChips">
|
||||
<!-- Switcher chips will be rendered here -->
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Placement
|
||||
Position the switchers container **above** the suggestions container:
|
||||
```html
|
||||
<footer>
|
||||
<div class="switchers-container" id="switchers"></div>
|
||||
<div class="suggestions-container" id="suggestions"></div>
|
||||
<!-- ... existing form ... -->
|
||||
</footer>
|
||||
```
|
||||
|
||||
### CSS Styling
|
||||
|
||||
#### Container
|
||||
```css
|
||||
.switchers-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 8px 16px;
|
||||
flex-wrap: wrap;
|
||||
background: rgba(0, 0, 0, 0.02);
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.switchers-label {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #666;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
```
|
||||
|
||||
#### Switcher Chips (Toggle Buttons)
|
||||
```css
|
||||
.switchers-chips {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.switcher-chip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 20px;
|
||||
border: 2px solid transparent;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
color: #666;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.switcher-chip:hover {
|
||||
background: rgba(0, 0, 0, 0.08);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.switcher-chip.active {
|
||||
border-color: currentColor;
|
||||
background: currentColor;
|
||||
color: white;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.switcher-chip-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
```
|
||||
|
||||
## JavaScript Implementation
|
||||
|
||||
### State Management
|
||||
```javascript
|
||||
// Track active switchers
|
||||
var activeSwitchers = new Set();
|
||||
|
||||
// Switcher definitions (from ADD SWITCHER commands in start.bas)
|
||||
var switcherDefinitions = [
|
||||
{
|
||||
id: 'tables',
|
||||
label: 'Tabelas',
|
||||
icon: '📊',
|
||||
color: '#4CAF50'
|
||||
},
|
||||
{
|
||||
id: 'infographic',
|
||||
label: 'Infográfico',
|
||||
icon: '📈',
|
||||
color: '#2196F3'
|
||||
},
|
||||
{
|
||||
id: 'cards',
|
||||
label: 'Cards',
|
||||
icon: '🃏',
|
||||
color: '#FF9800'
|
||||
},
|
||||
{
|
||||
id: 'list',
|
||||
label: 'Lista',
|
||||
icon: '📋',
|
||||
color: '#9C27B0'
|
||||
},
|
||||
{
|
||||
id: 'comparison',
|
||||
label: 'Comparação',
|
||||
icon: '⚖️',
|
||||
color: '#E91E63'
|
||||
},
|
||||
{
|
||||
id: 'timeline',
|
||||
label: 'Timeline',
|
||||
icon: '📅',
|
||||
color: '#00BCD4'
|
||||
},
|
||||
{
|
||||
id: 'markdown',
|
||||
label: 'Markdown',
|
||||
icon: '📝',
|
||||
color: '#607D8B'
|
||||
},
|
||||
{
|
||||
id: 'chart',
|
||||
label: 'Gráfico',
|
||||
icon: '📉',
|
||||
color: '#F44336'
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||
### Render Switchers
|
||||
```javascript
|
||||
function renderSwitchers() {
|
||||
var container = document.getElementById("switcherChips");
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = switcherDefinitions.map(function(sw) {
|
||||
var isActive = activeSwitchers.has(sw.id);
|
||||
return (
|
||||
'<div class="switcher-chip' + (isActive ? ' active' : '') + '" ' +
|
||||
'data-switch-id="' + sw.id + '" ' +
|
||||
'style="--switcher-color: ' + sw.color + '; ' +
|
||||
(isActive ? 'color: ' + sw.color + ' background: ' + sw.color + '; ' : '') +
|
||||
'">' +
|
||||
'<span class="switcher-chip-icon">' + sw.icon + '</span>' +
|
||||
'<span>' + sw.label + '</span>' +
|
||||
'</div>'
|
||||
);
|
||||
}).join('');
|
||||
|
||||
// Add click handlers
|
||||
container.querySelectorAll('.switcher-chip').forEach(function(chip) {
|
||||
chip.addEventListener('click', function() {
|
||||
toggleSwitcher(this.getAttribute('data-switch-id'));
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Toggle Handler
|
||||
```javascript
|
||||
function toggleSwitcher(switcherId) {
|
||||
if (activeSwitchers.has(switcherId)) {
|
||||
activeSwitchers.delete(switcherId);
|
||||
} else {
|
||||
activeSwitchers.add(switcherId);
|
||||
}
|
||||
renderSwitchers();
|
||||
}
|
||||
```
|
||||
|
||||
### Message Enhancement
|
||||
When sending a user message, prepend active switcher prompts:
|
||||
|
||||
```javascript
|
||||
function sendMessage(messageContent) {
|
||||
// ... existing code ...
|
||||
|
||||
var content = messageContent || input.value.trim();
|
||||
if (!content) return;
|
||||
|
||||
// Prepend active switcher prompts
|
||||
var enhancedContent = content;
|
||||
if (activeSwitchers.size > 0) {
|
||||
// Get prompts for active switchers from backend
|
||||
var activePrompts = [];
|
||||
activeSwitchers.forEach(function(id) {
|
||||
// Backend has predefined prompts for each ID
|
||||
activePrompts.push(getSwitcherPrompt(id));
|
||||
});
|
||||
|
||||
// Inject prompts before user message
|
||||
if (activePrompts.length > 0) {
|
||||
enhancedContent = activePrompts.join('\n\n') + '\n\n---\n\n' + content;
|
||||
}
|
||||
}
|
||||
|
||||
// Send enhanced content
|
||||
addMessage("user", content);
|
||||
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({
|
||||
bot_id: currentBotId,
|
||||
user_id: currentUserId,
|
||||
session_id: currentSessionId,
|
||||
channel: "web",
|
||||
content: enhancedContent,
|
||||
message_type: MessageType.USER,
|
||||
timestamp: new Date().toISOString(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function getSwitcherPrompt(switcherId) {
|
||||
// Get predefined prompt from backend or API
|
||||
// For example, tables ID maps to:
|
||||
// "REGRAS DE FORMATO: SEMPRE retorne suas respostas em formato de tabela HTML..."
|
||||
var switcher = switcherDefinitions.find(function(s) { return s.id === switcherId; });
|
||||
if (!switcher) return "";
|
||||
|
||||
// This could be fetched from backend or stored locally
|
||||
return SWITCHER_PROMPTS[switcherId] || "";
|
||||
}
|
||||
```
|
||||
|
||||
## Bot Integration (start.bas)
|
||||
|
||||
The bot receives the switcher prompt injected into the user message and simply passes it to the LLM.
|
||||
|
||||
### Example in start.bas
|
||||
|
||||
```basic
|
||||
REM Switcher prompts are automatically injected by frontend
|
||||
REM Just pass user_input to LLM - no parsing needed!
|
||||
|
||||
REM If user types: "mostra os cursos"
|
||||
REM And "Tabelas" switcher is active
|
||||
REM Frontend sends: "REGRAS DE FORMATO: SEMPRE retorne suas respostas em formato de tabela HTML... --- mostra os cursos"
|
||||
|
||||
REM Bot passes directly to LLM:
|
||||
response$ = CALL_LLM(user_input)
|
||||
|
||||
REM The LLM will follow the REGRAS DE FORMATO instructions
|
||||
```
|
||||
|
||||
### Multiple Active Switchers
|
||||
|
||||
When multiple switchers are active, all prompts are injected:
|
||||
|
||||
```basic
|
||||
REM Frontend injects multiple REGRAS DE FORMATO blocks
|
||||
REM Example with "Tabelas" and "Gráfico" active:
|
||||
REM
|
||||
REM "REGRAS DE FORMATO: SEMPRE retorne suas respostas em formato de tabela HTML...
|
||||
REM REGRAS DE FORMATO: Crie gráficos e diagramas em HTML SVG...
|
||||
REM ---
|
||||
REM mostra os dados de vendas"
|
||||
|
||||
REM Bot passes to LLM:
|
||||
response$ = CALL_LLM(user_input)
|
||||
```
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
1. ✅ Create prompts/switcher.md (this file)
|
||||
2. ⬜ Define predefined prompts in backend (map IDs to prompt strings)
|
||||
3. ⬜ Add HTML structure to chat.html (switchers container)
|
||||
4. ⬜ Add CSS styles to chat.css (switcher chip styles)
|
||||
5. ⬜ Add switcher definitions to chat.js
|
||||
6. ⬜ Implement renderSwitchers() function
|
||||
7. ⬜ Implement toggleSwitcher() function
|
||||
8. ⬜ Modify sendMessage() to prepend switcher prompts
|
||||
9. ⬜ Update salesianos bot start.bas to use ADD SWITCHER commands
|
||||
10. ⬜ Test locally with all switcher options
|
||||
11. ⬜ Verify multiple switchers can be active simultaneously
|
||||
12. ⬜ Test persistence across page refreshes (optional - localStorage)
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] Switchers appear above suggestions
|
||||
- [ ] Switchers are colorful and match their defined colors
|
||||
- [ ] Clicking a switcher toggles it on/off
|
||||
- [ ] Multiple switchers can be active simultaneously
|
||||
- [ ] Active switchers have distinct visual state (border, background, shadow)
|
||||
- [ ] Formatted responses match the selected format
|
||||
- [ ] Toggling off removes the format modifier
|
||||
- [ ] Works with empty active switchers (normal response)
|
||||
- [ ] Works in combination with suggestions
|
||||
- [ ] Responsive design on mobile devices
|
||||
|
||||
## Files to Modify
|
||||
|
||||
1. `botui/ui/suite/chat/chat.html` - Add switcher container HTML
|
||||
2. `botui/ui/suite/chat/chat.css` - Add switcher styles
|
||||
3. `botui/ui/suite/chat/chat.js` - Add switcher logic
|
||||
4. `botserver/bots/salesianos/start.bas` - Add ADD SWITCHER commands
|
||||
|
||||
## Example start.bas
|
||||
|
||||
```basic
|
||||
USE_WEBSITE("https://salesianos.br", "30d")
|
||||
|
||||
USE KB "carta"
|
||||
USE KB "proc"
|
||||
|
||||
USE TOOL "inscricao"
|
||||
USE TOOL "consultar_inscricao"
|
||||
USE TOOL "agendamento_visita"
|
||||
USE TOOL "informacoes_curso"
|
||||
USE TOOL "documentos_necessarios"
|
||||
USE TOOL "contato_secretaria"
|
||||
USE TOOL "calendario_letivo"
|
||||
|
||||
ADD_SUGGESTION_TOOL "inscricao" AS "Fazer Inscrição"
|
||||
ADD_SUGGESTION_TOOL "consultar_inscricao" AS "Consultar Inscrição"
|
||||
ADD_SUGGESTION_TOOL "agendamento_visita" AS "Agendar Visita"
|
||||
ADD_SUGGESTION_TOOL "informacoes_curso" AS "Informações de Cursos"
|
||||
ADD_SUGGESTION_TOOL "documentos_necessarios" AS "Documentos Necessários"
|
||||
ADD_SUGGESTION_TOOL "contato_secretaria" AS "Falar com Secretaria"
|
||||
ADD_SUGGESTION_TOOL "segunda_via" AS "Segunda Via de Boleto"
|
||||
ADD_SUGGESTION_TOOL "calendario_letivo" AS "Calendário Letivo"
|
||||
ADD_SUGGESTION_TOOL "outros" AS "Outros"
|
||||
|
||||
ADD SWITCHER "tables" AS "Tabelas"
|
||||
ADD SWITCHER "infographic" AS "Infográfico"
|
||||
ADD SWITCHER "cards" AS "Cards"
|
||||
ADD SWITCHER "list" AS "Lista"
|
||||
ADD SWITCHER "comparison" AS "Comparação"
|
||||
ADD SWITCHER "timeline" AS "Timeline"
|
||||
ADD SWITCHER "markdown" AS "Markdown"
|
||||
ADD SWITCHER "chart" AS "Gráfico"
|
||||
|
||||
TALK "Olá! Sou o assistente virtual da Escola Salesiana. Como posso ajudá-lo hoje com inscrições, visitas, informações sobre cursos, documentos ou calendário letivo? Você pode também escolher formatos de resposta acima da caixa de mensagem."
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Switchers are **persistent** until deactivated
|
||||
- Multiple switchers can be active at once
|
||||
- Switcher prompts are prepended to user messages with "---" separator
|
||||
- The backend (LLM) should follow these format instructions
|
||||
- UI should provide clear visual feedback for active switchers
|
||||
- Color coding helps users quickly identify active formats
|
||||
- Standard switchers use predefined prompts in backend
|
||||
- Custom switchers allow any prompt string to be injected
|
||||
- render each switch with a random glow color at html
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
# USE KB 2.0: Group-Based Knowledge Base Access
|
||||
|
||||
## Overview
|
||||
Modify the USE KB keyword to respect user group permissions, ensuring that THINK KB queries only return answers from knowledge base folders that belong to groups the logged-in user is a member of.
|
||||
|
||||
## Current Architecture
|
||||
|
||||
### USE KB Flow
|
||||
1. User executes `USE KB "kb_name"` in BASIC script
|
||||
2. `use_kb.rs:add_kb_to_session()` checks if KB exists in `kb_collections`
|
||||
3. Creates default KB entry if not found
|
||||
4. Adds association to `session_kb_associations` table
|
||||
5. KB becomes active for the session
|
||||
|
||||
### THINK KB Flow
|
||||
1. User executes `THINK KB "query"`
|
||||
2. `think_kb.rs:think_kb_search()` gets all active KBs from `session_kb_associations`
|
||||
3. For each active KB, calls `KnowledgeBaseManager.search()` on its Qdrant collection
|
||||
4. Returns combined results from all active KBs
|
||||
|
||||
### Group System
|
||||
- Groups stored in `rbac_groups` table
|
||||
- User membership in `rbac_user_groups` table
|
||||
- Group permissions via `rbac_group_roles` table
|
||||
|
||||
## Proposed Changes
|
||||
|
||||
### 1. Database Schema Changes
|
||||
|
||||
Add new table `kb_group_associations`:
|
||||
|
||||
```sql
|
||||
CREATE TABLE kb_group_associations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
kb_id UUID NOT NULL REFERENCES kb_collections(id) ON DELETE CASCADE,
|
||||
group_id UUID NOT NULL REFERENCES rbac_groups(id) ON DELETE CASCADE,
|
||||
granted_by UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
granted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(kb_id, group_id)
|
||||
);
|
||||
```
|
||||
|
||||
Migration file: `botserver/migrations/6.2.0-01-kb-groups/up.sql`
|
||||
|
||||
### 2. Backend Logic Changes
|
||||
|
||||
#### Modify `think_kb_search()` in `think_kb.rs`
|
||||
- Add user group lookup before searching
|
||||
- Filter active KBs to only those accessible by user's groups
|
||||
- Allow access if KB has no group associations (public KBs) OR user is in associated groups
|
||||
|
||||
```rust
|
||||
async fn think_kb_search(
|
||||
kb_manager: Arc<KnowledgeBaseManager>,
|
||||
db_pool: DbPool,
|
||||
session_id: Uuid,
|
||||
bot_id: Uuid,
|
||||
user_id: Uuid, // Add user_id parameter
|
||||
query: &str,
|
||||
) -> Result<serde_json::Value, String> {
|
||||
// Get user's groups
|
||||
let user_groups = get_user_groups(&db_pool, user_id)?;
|
||||
|
||||
// Get active KBs filtered by groups
|
||||
let accessible_kbs = get_accessible_kbs_for_session(&db_pool, session_id, &user_groups)?;
|
||||
|
||||
// Search only accessible KBs
|
||||
// ... rest of search logic
|
||||
}
|
||||
```
|
||||
|
||||
#### Add `get_accessible_kbs_for_session()` function
|
||||
```rust
|
||||
fn get_accessible_kbs_for_session(
|
||||
conn_pool: &DbPool,
|
||||
session_id: Uuid,
|
||||
user_groups: &[String],
|
||||
) -> Result<Vec<(String, String, String)>, String> {
|
||||
// Query that joins session_kb_associations with kb_group_associations
|
||||
// Returns KBs where group_id IS NULL (public) OR group_id IN user_groups
|
||||
}
|
||||
```
|
||||
|
||||
#### Modify `add_kb_to_session()` in `use_kb.rs`
|
||||
- Add optional group access check
|
||||
- Allow USE KB if user has access to the KB's groups
|
||||
|
||||
### 3. API Changes
|
||||
|
||||
Add new endpoints in `rbac.rs` for KB-group management:
|
||||
|
||||
```rust
|
||||
// Assign KB to group
|
||||
POST /api/rbac/kbs/{kb_id}/groups/{group_id}
|
||||
|
||||
// Remove KB from group
|
||||
DELETE /api/rbac/kbs/{kb_id}/groups/{group_id}
|
||||
|
||||
// Get groups for KB
|
||||
GET /api/rbac/kbs/{kb_id}/groups
|
||||
|
||||
// Get KBs accessible by user
|
||||
GET /api/rbac/users/{user_id}/accessible-kbs
|
||||
```
|
||||
|
||||
### 4. Frontend Changes
|
||||
|
||||
#### Update `botui/ui/suite/admin/groups.html`
|
||||
- Add "Knowledge Bases" tab to group detail panel
|
||||
- Show list of KBs assigned to the group
|
||||
- Allow adding/removing KB assignments
|
||||
|
||||
#### Update `botui/ui/suite/drive/drive.html`
|
||||
- Add group visibility indicators for KB folders
|
||||
- Show which groups have access to each KB
|
||||
|
||||
### 5. Migration Strategy
|
||||
|
||||
1. Create new migration for `kb_group_associations` table
|
||||
2. Run migration to create table
|
||||
3. Assign existing KBs to default groups (e.g., "all_users" group)
|
||||
4. Update application code
|
||||
5. Deploy and test
|
||||
|
||||
### 6. Backward Compatibility
|
||||
|
||||
- Existing KBs without group associations remain public
|
||||
- Existing USE KB calls continue to work
|
||||
- THINK KB will filter results based on new permissions
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
1. ✅ Database migration for kb_group_associations
|
||||
2. ✅ Modify think_kb_search to accept user_id and filter by groups
|
||||
3. ✅ Update THINK KB keyword registration to pass user_id
|
||||
4. ✅ Add group access check to USE KB
|
||||
5. ✅ Add API endpoints for KB-group management
|
||||
6. ✅ Update admin UI for group-KB assignment
|
||||
7. ✅ Update drive UI to show group access
|
||||
8. ✅ Add tests for group-based access control
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- All KB access checks must happen at the database level
|
||||
- No client-side filtering of search results
|
||||
- Group membership verified on each request
|
||||
- Audit logging for KB access attempts
|
||||
|
||||
## Testing
|
||||
|
||||
- Unit tests for group access functions
|
||||
- Integration tests for THINK KB with group filtering
|
||||
- UI tests for admin group-KB management
|
||||
- End-to-end tests with different user group scenarios
|
||||
Loading…
Add table
Reference in a new issue