Resolve duas camadas de problema identificadas em teste end-to-end:
1. Embeddings falhavam com HTTP 404 (/codex/v1/embeddings não existe).
Solução: Captain::Llm::EmbeddingService sempre usa OpenAI tradicional
via Llm::Config.with_api_key(legacy_settings). ProviderConfig expõe
legacy_openai_settings pra isso.
2. Servidor Codex ocasionalmente responde com response.failed +
code=server_error (instabilidade transitória). Client agora retenta
até 2x com backoff exponencial (0.5s, 1.5s) em erros retryable:
HTTP 5xx, server_error no response.failed, ou stream inacabado.
Outras correções nesta etapa:
- Scenario#agent_model: em modo Codex, ignora CAPTAIN_OPEN_AI_MODEL_SCENARIO
(que pode ter gpt-4o legado) e usa ProviderConfig.model.
- ExtractionService/ContradictionCheckerService/TranslateQueryService:
trocam constantes hardcoded gpt-4o-mini/gpt-4.1-nano por
ProviderConfig.light_model (respeitando o provider ativo).
- ProviderConfig.DEFAULT_CODEX_MODEL agora é gpt-5.2 (reconhecido pelo
RubyLLM; gpt-5.4 não está no catalog do gem).
Validado ponta-a-ponta: WhatsApp → Chatwoot → Jasmine → handoff Daniela
→ faq_lookup com embedding OK → resposta com preços corretos.
Docs em docs/captain-codex-oauth.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lays the data + job foundation for tracking customer interactions,
recurrence, and Pix conversion on Contact. Design decisions negotiated
with Rodrigo (see docs to come):
Rules:
- Gap of 30h from last message defines separate interactions
- Qualified interaction = >=2 customer msgs + >=2 attendant msgs,
both with textual content (>= 2 letters)
- One-shot consultation = >=1+1 but below the qualified threshold
(tracked as secondary KPI)
- Excludes contacts labeled `equipe_interna`
- is_recurring = interactions_count >= 2
- pix_generated_count counts all PixCharges; reservations_paid_count
only counts those with status = paid
Surface area:
- Migration adds denormalized stats to contacts + indexes for fast filtering
- Captain::ContactStats::InteractionCalculatorService computes the stats
for a single contact (pure, no persistence)
- Captain::Retention::RecalculateContactStatsJob persists them for one
contact (idempotent)
- Captain::Retention::RecalculateAllContactStatsJob runs daily at 3am BRT,
enqueues per-contact jobs for everyone active in the last 120 days
- Event-driven refresh: CaptainListener#conversation_resolved enqueues
recalc; Captain::PixCharge after_create/after_update enqueues recalc
on status change
No UI yet — that's the next layer.
Consolida o trabalho desta branch de abril/2026 em um bloco pronto pra
testar em staging antes do merge pra main.
## Correções de memória semântica
- ExtractionService: Princípio Zero + Regra de Ouro (ação consumada vs intenção).
- Cenário Daniela_Reservas: Passo 0 de classificação (consulta/intenção/fora).
## Roleta da Sorte (end-to-end)
- Schema Supabase + 7 RPCs atômicas (server-side, idempotentes).
- Services: Offer, Redeem, WeeklyReport.
- Jobs: OfferRouletteJob (hook em ConfirmationService após Pix pago),
NotifyRevealed + Scheduler de fallback.
- Tool manual GenerateRoletaLinkTool + endpoint público /roleta/notify.
- Dashboard /captain/roleta com Resgate + Relatório + anomaly detection.
## Cenário Reclamacoes_Ouvidoria
- Triagem P1-P4, framework LAST, Three-level listening, Self-check.
- Sem compensação material, detecção de cliente frustrado eleva prioridade.
## Analytics
- Funil de conversão /captain/funnel: 5 etapas via regex, zero LLM.
- Detector de churn via ChurnOutreach* (cron dias úteis 10h-17h BRT).
## Trabalho pré-existente incluído
- Captain Executive Reports (ceo_digest, mattermost_delivery).
- get_reserva_preco_tool, Lifecycle ajustes, Reservations UI polimentos.
## Outros
- .gitignore: patterns pra credenciais.
- Migrations de scenarios idempotentes.
- i18n completa pt_BR+en pra roleta/funnel.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two orthogonal cost optimizations to the Captain agent pipeline:
1. Hierarchical model routing (optimization A)
Captain::Scenario now overrides agent_model to read a dedicated
InstallationConfig CAPTAIN_OPEN_AI_MODEL_SCENARIO, falling back to the
global CAPTAIN_OPEN_AI_MODEL used by the orchestrator (Assistant).
Rationale: the orchestrator (Jasmine) does cheap triage (is this a
reservation intent? a greeting? escalate to human?) — a smaller model
handles this well. Scenarios (Daniela — reserva) run complex flows with
tool calling, strict taxonomies, and JSON schema output — they benefit
from a stronger model.
Config in this install: CAPTAIN_OPEN_AI_MODEL=gpt-4o-mini (orchestrator)
and CAPTAIN_OPEN_AI_MODEL_SCENARIO=gpt-4o (scenarios). Estimated ~60%
cost reduction vs everything on gpt-4o, preserving quality where it
matters for the business flow.
2. Conversation-level memory cache (optimization B)
MemoryPromptInjector now persists the computed memory block on
conversation.custom_attributes[captain_cached_memory_block]. First turn
computes once (embedding + pgvector query + XML formatting); subsequent
turns reuse. The customer's profile does not change during an open
conversation, so re-running the pipeline on every turn was pure waste.
Graceful fallbacks:
- Cache write failure → per-service-instance in-memory fallback still
applies.
- Cache read failure → fresh recall runs (no regression).
- Contact mismatch → invalidates cache, fresh recall runs.
When a new conversation starts, custom_attributes is empty → fresh
recall populates the cache for that conversation's lifetime.
Estimated ~80% reduction in embedding + pgvector calls during
multi-turn conversations.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implement guards following the same pass/reschedule/too_stale pattern as QuietHours.
Also fix belongs_to :conversation on Delivery to use class_name: '::Conversation' to avoid namespace resolution failure inside Captain::Lifecycle module.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add after_commit callbacks to call Captain::Lifecycle::Scheduler on
create, status change (cancelled/no_show), and check_in_at change.
Each handler wraps in rescue StandardError to preserve existing behavior.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TDD: 16 examples passing. Adds EVENTS constant, active/for_event scopes,
and matches_reservation? with unit_ids/categorias/permanencias filters.
Also adds captain_reservation factory used by the spec.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cobre ambos os caminhos (generate_pix_tool e PublicReservationsController):
toda reserva criada recebe um after_create_commit que posta uma mensagem
privada na conversa com os detalhes (suite, check-in, valores, ID).
Remove a criacao duplicada do PublicReservationsController.
- GeneratePixTool: envia payment_link como mensagem outgoing direta (bypassa
hallucination de [Link do Pix] placeholder pela LLM)
- GeneratePixTool: extrai email das mensagens recentes via regex e persiste
em contact.email
- GenerateReservationLinkTool: mesmo padrao de envio direto do link
- Captain::Reservation: after_create_commit callback atualiza
ultima_suite/permanencia/reserva_em/total_reservas em contact.custom_attributes
(aparece no painel lateral)
- Arquitetura corrigida: templates agora pertencem à inbox (WhatsApp),
não à unidade PIX (que é uma config financeira, não de mensagens)
- Migration: troca FK captain_unit_id -> inbox_id (up/down explícito)
- Model: belongs_to :inbox; scope for_inbox
- Controller: escopo via account.inboxes.find(inbox_id)
- Rotas: move de captain/units/:id → inboxes/:id/notification_templates
- Scanner job: joins(:conversation).where(conversations: {inbox_id:})
- UI: página /captain/notifications com seletor de inbox no topo
(chips clicáveis, templates carregam por watch no selectedInboxId)
- i18n PT/EN: novas keys INBOX_LABEL, SELECT_INBOX_HINT, EMPTY
- Adiciona check_in_at/duration_hours ao schema do tool CreateReservationIntent
para que a IA capture o horário EXATO de chegada informado pelo cliente
- Cria captain_notification_templates: label, content, timing_minutes,
timing_direction (before/after), active, position
- Implementa SendNotificationService com interpolação de variáveis
(guest_name, check_in_time, check_out_time, suite_name, unit_name)
- Implementa NotificationScannerJob (Sidekiq-cron a cada 5min) com
janela de tolerância de ±5min e idempotência via metadata JSONB
- API REST: /captain/units/:unit_id/notification_templates (CRUD)
- Store Vuex captainNotificationTemplates + API client
- UI: página de gestão de templates com editor inline e botão '+'
- Configura rota captain_settings_notifications
- i18n PT/EN para todas as strings novas
- Rubocop e ESLint: zero offenses
Implementa a página Relatórios IA com geração de análises semanais
por IA baseadas nas conversas de cada unidade/caixa de entrada.
Funcionalidades:
- Página /settings/captain/reports com dois tabs (Insights IA / Operacional)
- Botão "Gerar Análise" que enfileira job Sidekiq
- Filtro por unidade ou caixa de entrada
- Exibe insights com status (pendente/processando/concluído/falhou)
- Mostra top_topics, ai_failures e period_summary
- Estado vazio com CTA para gerar primeiro relatório
Backend:
- InsightsController com endpoints index/show/generate
- GenerateInsightsJob que processa conversas com LLM
- ConversationInsightService com chunking e merge inteligente
- Migração para adicionar inbox_id à tabela captain_conversation_insights
- Link sidebar "Relatórios IA" em /settings/captain/reports
Frontend:
- Vuex store captainReports com actions/mutations/getters
- API client CaptainReportsAPI (getInsights, generateInsight)
- i18n en e pt_BR para CAPTAIN_REPORTS.*
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Melhorias na ferramenta send_suite_images para resolver confusão entre
categoria e número de suíte:
1. **Descrições de parâmetros mais claras**
- suite_category: exemplos específicos (Hidromassagem, ALEXA, STILO)
- suite_number: apenas números (101, 102, 103) - remove exemplos confusos
2. **Instruções explícitas no system prompt**
- Seção [Galeria de Fotos] com regras claras
- Prioriza suite_category quando ambíguo
- Evita confirmações desnecessárias com cliente
3. **Mensagens de erro melhoradas**
- Sugere buscar por categoria quando busca por número falha
- Feedback mais útil para a IA
Resultado esperado:
- Cliente: "Me manda foto da suite Alexa"
- IA: busca por suite_category="Alexa" ✓ (sem pedir confirmação)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
## Summary
- Fix captain response builder not getting triggered for cases where
responses are created as completed.
## Testing Instructions
- Test articles with firecrawl
- Test articles without firecrawl
- Test PDF documents
---------
Co-authored-by: Pranav <pranav@chatwoot.com>
- Add API support for creating a thread
- Add API support for creating a message
- Remove uuid from thread (no longer required, we will use existing
websocket connection to send messages)
- Update message_type to a column (user, assistant, assistant_thinking)
Show captain messages under the name of the assistant which generated
the message.
- Add support for `Captain::Assistant` sender type
- Add push_event_data for captain_assistants
- Add activity message handler for captain_assistants
- Update UI to show captain messages under the name of the assistant
- Fix the issue where openAI errors when image is sent
- Add support for custom name of the assistant
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
- Fixed Firecrawl webhook payloads to ensure proper data handling and
delivery.
- Removed unused Robin AI code to improve codebase cleanliness and
maintainability.
- Implement authentication for the Firecrawl endpoint to improve
security. A key is generated to secure the webhook URLs from FireCrawl.
---------
Co-authored-by: Pranav <pranavrajs@gmail.com>
This pull request introduces several changes to implement and manage
usage limits for the Captain AI service. The key changes include adding
configuration for plan limits, updating error messages, modifying
controllers and models to handle usage limits, and updating tests to
ensure the new functionality works correctly.
## Implementation Checklist
- [x] Ability to configure captain limits per check
- [x] Update response for `usage_limits` to include captain limits
- [x] Methods to increment or reset captain responses limits in the
`limits` column for the `Account` model
- [x] Check documents limit using a count query
- [x] Ensure Captain hand-off if a limit is reached
- [x] Ensure limits are enforced for Copilot Chat
- [x] Ensure limits are reset when stripe webhook comes in
- [x] Increment usage for FAQ generation and Contact notes
- [x] Ensure documents limit is enforced
These changes ensure that the Captain AI service operates within the defined usage limits for different subscription plans, providing appropriate error messages and handling when limits are exceeded.
Currently, it’s unclear whether an FAQ item is generated from a
document, derived from a conversation, or added manually.
This PR resolves the issue by providing visibility into the source of
each FAQ. Users can now see whether an FAQ was generated or manually
added and, if applicable, by whom.
- Move the document_id to a polymorphic relation (documentable).
- Updated the APIs to accommodate the change.
- Update the service to add corresponding references.
- Updated the specs.
<img width="1007" alt="Screenshot 2025-01-15 at 11 27 56 PM"
src="https://github.com/user-attachments/assets/7d58f798-19c0-4407-b3e2-748a919d14af"
/>
---------
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This PR introduces a review step for generated FAQs, allowing a human to
validate and approve them before use in customer interactions. While
hallucinations are minimal, this step ensures accurate and reliable FAQs
for Captain to use during LLM calls when responding to customers.
- Added a status field for the FAQ
- Allow the filter on the UI.
<img width="1072" alt="Screenshot 2025-01-15 at 6 39 26 PM"
src="https://github.com/user-attachments/assets/81dfc038-31e9-40e6-8a09-586ebc4e8384"
/>
Migration Guide: https://chwt.app/v4/migration
This PR imports all the work related to Captain into the EE codebase. Captain represents the AI-based features in Chatwoot and includes the following key components:
- Assistant: An assistant has a persona, the product it would be trained on. At the moment, the data at which it is trained is from websites. Future integrations on Notion documents, PDF etc. This PR enables connecting an assistant to an inbox. The assistant would run the conversation every time before transferring it to an agent.
- Copilot for Agents: When an agent is supporting a customer, we will be able to offer additional help to lookup some data or fetch information from integrations etc via copilot.
- Conversation FAQ generator: When a conversation is resolved, the Captain integration would identify questions which were not in the knowledge base.
- CRM memory: Learns from the conversations and identifies important information about the contact.
---------
Co-authored-by: Vishnu Narayanan <vishnu@chatwoot.com>
Co-authored-by: Sojan <sojan@pepalo.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>