Compare commits
1 Commits
feat/ai-ap
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3be72d9c7f |
@@ -12,8 +12,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bigtux/ophion/internal/aiapm"
|
|
||||||
aiapmapi "github.com/bigtux/ophion/internal/api"
|
|
||||||
"github.com/bigtux/ophion/internal/auth"
|
"github.com/bigtux/ophion/internal/auth"
|
||||||
"github.com/bigtux/ophion/internal/otel"
|
"github.com/bigtux/ophion/internal/otel"
|
||||||
"github.com/bigtux/ophion/internal/security"
|
"github.com/bigtux/ophion/internal/security"
|
||||||
@@ -109,12 +107,6 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
log.Println("✓ Connected to PostgreSQL")
|
log.Println("✓ Connected to PostgreSQL")
|
||||||
initSchema(db)
|
initSchema(db)
|
||||||
// Initialize AI APM table
|
|
||||||
if err := aiapm.CreateTable(db); err != nil {
|
|
||||||
log.Printf("⚠ Failed to create AI APM table: %v", err)
|
|
||||||
} else {
|
|
||||||
log.Println("✓ AI APM table initialized")
|
|
||||||
}
|
|
||||||
// Create default admin user
|
// Create default admin user
|
||||||
if err := auth.CreateDefaultAdmin(db); err != nil {
|
if err := auth.CreateDefaultAdmin(db); err != nil {
|
||||||
log.Printf("⚠ Failed to create default admin: %v", err)
|
log.Printf("⚠ Failed to create default admin: %v", err)
|
||||||
@@ -343,9 +335,6 @@ func (s *Server) setupRoutes() {
|
|||||||
// Dashboard
|
// Dashboard
|
||||||
protected.Get("/dashboard/overview", s.getDashboardOverview)
|
protected.Get("/dashboard/overview", s.getDashboardOverview)
|
||||||
|
|
||||||
// AI APM routes
|
|
||||||
aiapmapi.RegisterAIAPMRoutes(protected, s.db)
|
|
||||||
|
|
||||||
// User info
|
// User info
|
||||||
protected.Get("/me", s.authHandler.Me)
|
protected.Get("/me", s.authHandler.Me)
|
||||||
|
|
||||||
|
|||||||
565
docs/manual_tecnico.html
Normal file
565
docs/manual_tecnico.html
Normal file
@@ -0,0 +1,565 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="pt-BR">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>OPHION - Manual Técnico</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
color: #2D3142;
|
||||||
|
background: white;
|
||||||
|
line-height: 1.6;
|
||||||
|
padding: 40px 60px;
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #1A7A4C;
|
||||||
|
font-size: 28px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 3px solid #1A7A4C;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
color: #1A7A4C;
|
||||||
|
font-size: 22px;
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
color: #1A7A4C;
|
||||||
|
font-size: 18px;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #E2E4E8;
|
||||||
|
padding: 12px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background: #1A7A4C;
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background: #f9f9f9;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background: #f4f4f4;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background: #1e1e1e;
|
||||||
|
color: #d4d4d4;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
ul, ol {
|
||||||
|
margin: 15px 0;
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
blockquote {
|
||||||
|
border-left: 4px solid #1A7A4C;
|
||||||
|
padding-left: 20px;
|
||||||
|
margin: 20px 0;
|
||||||
|
font-style: italic;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
border-top: 2px solid #E2E4E8;
|
||||||
|
margin: 30px 0;
|
||||||
|
}
|
||||||
|
strong {
|
||||||
|
color: #1A7A4C;
|
||||||
|
}
|
||||||
|
.page-break {
|
||||||
|
page-break-after: always;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>OPHION - Manual Técnico</h1>
|
||||||
|
<h2>Plataforma de Observabilidade com IA</h2>
|
||||||
|
<hr />
|
||||||
|
<h2>1. Visão Geral</h2>
|
||||||
|
<p><strong>OPHION</strong> é uma plataforma de observabilidade open source que combina <strong>métricas, logs e traces</strong> em uma única solução, potencializada por <strong>inteligência artificial</strong> para monitoramento proativo e auto-healing.</p>
|
||||||
|
<hr />
|
||||||
|
<h2>2. Stack Tecnológico</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Camada</th>
|
||||||
|
<th>Tecnologia</th>
|
||||||
|
<th>Função</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>API Server</td>
|
||||||
|
<td>Go 1.22</td>
|
||||||
|
<td>Backend principal</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Dashboard</td>
|
||||||
|
<td>Next.js + TypeScript</td>
|
||||||
|
<td>Interface web</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Collector</td>
|
||||||
|
<td>OpenTelemetry Collector</td>
|
||||||
|
<td>Ingestão de telemetria</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Database</td>
|
||||||
|
<td>PostgreSQL</td>
|
||||||
|
<td>Dados estruturados</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Time Series</td>
|
||||||
|
<td>ClickHouse</td>
|
||||||
|
<td>Métricas de alta performance</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Cache</td>
|
||||||
|
<td>Redis</td>
|
||||||
|
<td>Cache e filas</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Container</td>
|
||||||
|
<td>Docker Compose</td>
|
||||||
|
<td>Orquestração</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<hr />
|
||||||
|
<h2>3. Arquitetura do Sistema</h2>
|
||||||
|
<pre><code>┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ APLICAÇÕES INSTRUMENTADAS │
|
||||||
|
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||||||
|
│ │ Node.js │ │ Python │ │ Java │ │ .NET │ │ Go │ │
|
||||||
|
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
|
||||||
|
│ └───────────┴───────────┼───────────┴───────────┘ │
|
||||||
|
│ │ OTLP (4317/4318) │
|
||||||
|
└───────────────────────────────┼─────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌───────────────────────────────┼─────────────────────────────────┐
|
||||||
|
│ OPHION STACK ▼ │
|
||||||
|
│ ┌─────────────────────────────────────────────────┐ │
|
||||||
|
│ │ OpenTelemetry Collector │ │
|
||||||
|
│ │ (receivers → processors → exporters) │ │
|
||||||
|
│ └─────────────────────────┬───────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌────────────────┐ ▼ ┌────────────────┐ │
|
||||||
|
│ │ Dashboard │◄───► Server ◄──►│ PostgreSQL │ │
|
||||||
|
│ │ (Next.js) │ (Go API) │ ClickHouse │ │
|
||||||
|
│ │ :3000 │ :8080 │ Redis │ │
|
||||||
|
│ └────────────────┘ └────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌────────────────────────────────────────────────┐ │
|
||||||
|
│ │ AI Engine (Copilot) │ │
|
||||||
|
│ │ - Correlação de alertas │ │
|
||||||
|
│ │ - Previsão de capacidade │ │
|
||||||
|
│ │ - Auto-healing │ │
|
||||||
|
│ └────────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
</code></pre>
|
||||||
|
<hr />
|
||||||
|
<h2>4. Estrutura de Diretórios</h2>
|
||||||
|
<pre><code>ophion/
|
||||||
|
├── cmd/
|
||||||
|
│ ├── server/ # API Server (Go)
|
||||||
|
│ └── agent/ # Agent de coleta
|
||||||
|
├── internal/ # Código interno Go
|
||||||
|
│ ├── api/ # Handlers HTTP
|
||||||
|
│ ├── db/ # Repositórios
|
||||||
|
│ ├── ai/ # Engine de IA
|
||||||
|
│ └── telemetry/ # Processamento OTLP
|
||||||
|
├── dashboard/ # Frontend Next.js
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── app/ # App Router
|
||||||
|
│ │ ├── components/ # UI Components
|
||||||
|
│ │ └── lib/ # Utilitários
|
||||||
|
│ └── package.json
|
||||||
|
├── deploy/
|
||||||
|
│ ├── docker/ # Docker configs
|
||||||
|
│ │ └── otel-collector-config.yaml
|
||||||
|
│ ├── remote-agent/ # Agent remoto
|
||||||
|
│ └── instrumentation/ # Scripts de instrumentação
|
||||||
|
├── examples/ # Exemplos por linguagem
|
||||||
|
│ ├── otel-nodejs/
|
||||||
|
│ ├── otel-python/
|
||||||
|
│ └── docker/
|
||||||
|
├── configs/ # Configurações
|
||||||
|
├── docs/ # Documentação
|
||||||
|
├── instrument.sh # Script de auto-instrumentação
|
||||||
|
├── install.sh # Instalador
|
||||||
|
├── docker-compose.yml
|
||||||
|
├── go.mod / go.sum
|
||||||
|
└── server # Binário compilado
|
||||||
|
</code></pre>
|
||||||
|
<hr />
|
||||||
|
<h2>5. Componentes Principais</h2>
|
||||||
|
<h3>5.1 OpenTelemetry Collector</h3>
|
||||||
|
<p><strong>Portas:</strong>
|
||||||
|
- <code>4317</code> - OTLP gRPC
|
||||||
|
- <code>4318</code> - OTLP HTTP</p>
|
||||||
|
<p><strong>Pipeline:</strong></p>
|
||||||
|
<pre><code class="language-yaml">receivers:
|
||||||
|
otlp:
|
||||||
|
protocols:
|
||||||
|
grpc:
|
||||||
|
endpoint: 0.0.0.0:4317
|
||||||
|
http:
|
||||||
|
endpoint: 0.0.0.0:4318
|
||||||
|
|
||||||
|
processors:
|
||||||
|
batch:
|
||||||
|
timeout: 1s
|
||||||
|
memory_limiter:
|
||||||
|
limit_mib: 512
|
||||||
|
|
||||||
|
exporters:
|
||||||
|
ophion:
|
||||||
|
endpoint: http://server:8080
|
||||||
|
</code></pre>
|
||||||
|
<h3>5.2 Server (Go API)</h3>
|
||||||
|
<p><strong>Endpoints principais:</strong></p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Endpoint</th>
|
||||||
|
<th>Método</th>
|
||||||
|
<th>Descrição</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>/api/v1/traces</code></td>
|
||||||
|
<td>POST</td>
|
||||||
|
<td>Ingestão de traces</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>/api/v1/metrics</code></td>
|
||||||
|
<td>POST</td>
|
||||||
|
<td>Ingestão de métricas</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>/api/v1/logs</code></td>
|
||||||
|
<td>POST</td>
|
||||||
|
<td>Ingestão de logs</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>/api/v1/services</code></td>
|
||||||
|
<td>GET</td>
|
||||||
|
<td>Lista serviços</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>/api/v1/alerts</code></td>
|
||||||
|
<td>GET/POST</td>
|
||||||
|
<td>Gerenciamento de alertas</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>/api/v1/ai/analyze</code></td>
|
||||||
|
<td>POST</td>
|
||||||
|
<td>Análise por IA</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<h3>5.3 Dashboard (Next.js)</h3>
|
||||||
|
<p><strong>Recursos:</strong>
|
||||||
|
- Service Map visual
|
||||||
|
- Trace waterfall
|
||||||
|
- Métricas em tempo real
|
||||||
|
- Logs agregados
|
||||||
|
- Alertas e notificações
|
||||||
|
- AI Copilot chat</p>
|
||||||
|
<hr />
|
||||||
|
<h2>6. Auto-Instrumentação</h2>
|
||||||
|
<h3>6.1 Script Universal</h3>
|
||||||
|
<pre><code class="language-bash"># Auto-detecta linguagem
|
||||||
|
./instrument.sh <container-name>
|
||||||
|
|
||||||
|
# Especifica linguagem
|
||||||
|
./instrument.sh my-app nodejs
|
||||||
|
./instrument.sh my-app python
|
||||||
|
./instrument.sh my-app java
|
||||||
|
./instrument.sh my-app dotnet
|
||||||
|
</code></pre>
|
||||||
|
<h3>6.2 Suporte por Linguagem</h3>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Linguagem</th>
|
||||||
|
<th>Método</th>
|
||||||
|
<th>Complexidade</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>.NET</td>
|
||||||
|
<td>Auto-instrumentation</td>
|
||||||
|
<td>Zero code</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Node.js</td>
|
||||||
|
<td>Auto-instrumentation</td>
|
||||||
|
<td>Zero code</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Python</td>
|
||||||
|
<td>Auto-instrumentation</td>
|
||||||
|
<td>Zero code</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Java</td>
|
||||||
|
<td>Java Agent</td>
|
||||||
|
<td>Zero code</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Go</td>
|
||||||
|
<td>SDK (compile-time)</td>
|
||||||
|
<td>Pequenas mudanças</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>PHP</td>
|
||||||
|
<td>SDK</td>
|
||||||
|
<td>Pequenas mudanças</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<h3>6.3 Variáveis de Ambiente</h3>
|
||||||
|
<pre><code class="language-env">OTEL_EXPORTER_OTLP_ENDPOINT=http://ophion:4318
|
||||||
|
OTEL_SERVICE_NAME=my-service
|
||||||
|
OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production
|
||||||
|
</code></pre>
|
||||||
|
<hr />
|
||||||
|
<h2>7. AI Engine</h2>
|
||||||
|
<h3>7.1 Funcionalidades</h3>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Correlação de Alertas</strong>: Agrupa alertas relacionados</li>
|
||||||
|
<li><strong>Root Cause Analysis</strong>: Identifica causa raiz</li>
|
||||||
|
<li><strong>Previsão de Capacidade</strong>: Prevê saturação de recursos</li>
|
||||||
|
<li><strong>Auto-Healing</strong>: Executa ações corretivas automáticas</li>
|
||||||
|
<li><strong>Copilot</strong>: Chat para consultas em linguagem natural</li>
|
||||||
|
</ul>
|
||||||
|
<h3>7.2 Integração OpenAI</h3>
|
||||||
|
<pre><code class="language-env">OPENAI_API_KEY=sk-...
|
||||||
|
AI_MODEL=gpt-4
|
||||||
|
</code></pre>
|
||||||
|
<hr />
|
||||||
|
<h2>8. Deploy</h2>
|
||||||
|
<h3>8.1 Quick Start (Docker)</h3>
|
||||||
|
<pre><code class="language-bash">git clone https://github.com/bigtux/ophion.git
|
||||||
|
cd ophion
|
||||||
|
docker compose up -d
|
||||||
|
</code></pre>
|
||||||
|
<h3>8.2 Acessos</h3>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Serviço</th>
|
||||||
|
<th>URL</th>
|
||||||
|
<th>Porta</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Dashboard</td>
|
||||||
|
<td>http://localhost:3000</td>
|
||||||
|
<td>3000</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>API</td>
|
||||||
|
<td>http://localhost:8080</td>
|
||||||
|
<td>8080</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>OTLP gRPC</td>
|
||||||
|
<td>localhost:4317</td>
|
||||||
|
<td>4317</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>OTLP HTTP</td>
|
||||||
|
<td>localhost:4318</td>
|
||||||
|
<td>4318</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<h3>8.3 Produção</h3>
|
||||||
|
<pre><code class="language-bash"># Com install.sh
|
||||||
|
./install.sh --production
|
||||||
|
|
||||||
|
# Ou manualmente
|
||||||
|
docker compose -f docker-compose.prod.yml up -d
|
||||||
|
</code></pre>
|
||||||
|
<hr />
|
||||||
|
<h2>9. Requisitos do Sistema</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Recurso</th>
|
||||||
|
<th>Mínimo</th>
|
||||||
|
<th>Recomendado</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>CPU</td>
|
||||||
|
<td>2 cores</td>
|
||||||
|
<td>4+ cores</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>RAM</td>
|
||||||
|
<td>4 GB</td>
|
||||||
|
<td>8+ GB</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Disco</td>
|
||||||
|
<td>20 GB SSD</td>
|
||||||
|
<td>100+ GB SSD</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Docker</td>
|
||||||
|
<td>20.10+</td>
|
||||||
|
<td>Latest</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Docker Compose</td>
|
||||||
|
<td>v2+</td>
|
||||||
|
<td>Latest</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<hr />
|
||||||
|
<h2>10. Configuração</h2>
|
||||||
|
<h3>10.1 Variáveis de Ambiente</h3>
|
||||||
|
<pre><code class="language-env"># Server
|
||||||
|
PORT=8080
|
||||||
|
DATABASE_URL=postgres://user:pass@localhost:5432/ophion
|
||||||
|
REDIS_URL=redis://localhost:6379
|
||||||
|
CLICKHOUSE_URL=clickhouse://localhost:9000
|
||||||
|
|
||||||
|
# Auth
|
||||||
|
JWT_SECRET=your-secret
|
||||||
|
AGENT_KEY=agent-secret-key
|
||||||
|
|
||||||
|
# AI
|
||||||
|
OPENAI_API_KEY=sk-...
|
||||||
|
AI_ENABLED=true
|
||||||
|
|
||||||
|
# Notifications
|
||||||
|
TELEGRAM_BOT_TOKEN=...
|
||||||
|
TELEGRAM_CHAT_ID=...
|
||||||
|
</code></pre>
|
||||||
|
<h3>10.2 Configuração do Collector</h3>
|
||||||
|
<pre><code class="language-yaml"># otel-collector-config.yaml
|
||||||
|
receivers:
|
||||||
|
otlp:
|
||||||
|
protocols:
|
||||||
|
grpc:
|
||||||
|
endpoint: 0.0.0.0:4317
|
||||||
|
http:
|
||||||
|
endpoint: 0.0.0.0:4318
|
||||||
|
|
||||||
|
processors:
|
||||||
|
batch:
|
||||||
|
timeout: 1s
|
||||||
|
send_batch_size: 1024
|
||||||
|
|
||||||
|
exporters:
|
||||||
|
otlphttp:
|
||||||
|
endpoint: http://server:8080/api/v1
|
||||||
|
|
||||||
|
service:
|
||||||
|
pipelines:
|
||||||
|
traces:
|
||||||
|
receivers: [otlp]
|
||||||
|
processors: [batch]
|
||||||
|
exporters: [otlphttp]
|
||||||
|
</code></pre>
|
||||||
|
<hr />
|
||||||
|
<h2>11. Segurança</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Autenticação JWT para dashboard</li>
|
||||||
|
<li>API Key para agents (<code>AGENT_KEY</code>)</li>
|
||||||
|
<li>TLS opcional para OTLP</li>
|
||||||
|
<li>Isolamento de rede via Docker</li>
|
||||||
|
<li>Logs de auditoria</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<h2>12. Desenvolvimento</h2>
|
||||||
|
<pre><code class="language-bash"># Backend (Go)
|
||||||
|
go run ./cmd/server
|
||||||
|
|
||||||
|
# Frontend (Next.js)
|
||||||
|
cd dashboard
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Testes
|
||||||
|
go test ./...
|
||||||
|
</code></pre>
|
||||||
|
<hr />
|
||||||
|
<h2>13. Troubleshooting</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Problema</th>
|
||||||
|
<th>Solução</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Traces não aparecem</td>
|
||||||
|
<td>Verificar OTEL_EXPORTER_OTLP_ENDPOINT</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Dashboard lento</td>
|
||||||
|
<td>Aumentar RAM do ClickHouse</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Collector crash</td>
|
||||||
|
<td>Verificar memory_limiter no config</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Auth falhando</td>
|
||||||
|
<td>Verificar JWT_SECRET</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<hr />
|
||||||
|
<h2>14. Licença</h2>
|
||||||
|
<p>AGPL-3.0 (Community Edition)</p>
|
||||||
|
<hr />
|
||||||
|
<p><em>Documento gerado automaticamente - OPHION</em></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
352
docs/manual_tecnico.md
Normal file
352
docs/manual_tecnico.md
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
# OPHION - Manual Técnico
|
||||||
|
|
||||||
|
## Plataforma de Observabilidade com IA
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Visão Geral
|
||||||
|
|
||||||
|
**OPHION** é uma plataforma de observabilidade open source que combina **métricas, logs e traces** em uma única solução, potencializada por **inteligência artificial** para monitoramento proativo e auto-healing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Stack Tecnológico
|
||||||
|
|
||||||
|
| Camada | Tecnologia | Função |
|
||||||
|
|--------|------------|--------|
|
||||||
|
| API Server | Go 1.22 | Backend principal |
|
||||||
|
| Dashboard | Next.js + TypeScript | Interface web |
|
||||||
|
| Collector | OpenTelemetry Collector | Ingestão de telemetria |
|
||||||
|
| Database | PostgreSQL | Dados estruturados |
|
||||||
|
| Time Series | ClickHouse | Métricas de alta performance |
|
||||||
|
| Cache | Redis | Cache e filas |
|
||||||
|
| Container | Docker Compose | Orquestração |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Arquitetura do Sistema
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ APLICAÇÕES INSTRUMENTADAS │
|
||||||
|
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||||||
|
│ │ Node.js │ │ Python │ │ Java │ │ .NET │ │ Go │ │
|
||||||
|
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
|
||||||
|
│ └───────────┴───────────┼───────────┴───────────┘ │
|
||||||
|
│ │ OTLP (4317/4318) │
|
||||||
|
└───────────────────────────────┼─────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌───────────────────────────────┼─────────────────────────────────┐
|
||||||
|
│ OPHION STACK ▼ │
|
||||||
|
│ ┌─────────────────────────────────────────────────┐ │
|
||||||
|
│ │ OpenTelemetry Collector │ │
|
||||||
|
│ │ (receivers → processors → exporters) │ │
|
||||||
|
│ └─────────────────────────┬───────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌────────────────┐ ▼ ┌────────────────┐ │
|
||||||
|
│ │ Dashboard │◄───► Server ◄──►│ PostgreSQL │ │
|
||||||
|
│ │ (Next.js) │ (Go API) │ ClickHouse │ │
|
||||||
|
│ │ :3000 │ :8080 │ Redis │ │
|
||||||
|
│ └────────────────┘ └────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌────────────────────────────────────────────────┐ │
|
||||||
|
│ │ AI Engine (Copilot) │ │
|
||||||
|
│ │ - Correlação de alertas │ │
|
||||||
|
│ │ - Previsão de capacidade │ │
|
||||||
|
│ │ - Auto-healing │ │
|
||||||
|
│ └────────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Estrutura de Diretórios
|
||||||
|
|
||||||
|
```
|
||||||
|
ophion/
|
||||||
|
├── cmd/
|
||||||
|
│ ├── server/ # API Server (Go)
|
||||||
|
│ └── agent/ # Agent de coleta
|
||||||
|
├── internal/ # Código interno Go
|
||||||
|
│ ├── api/ # Handlers HTTP
|
||||||
|
│ ├── db/ # Repositórios
|
||||||
|
│ ├── ai/ # Engine de IA
|
||||||
|
│ └── telemetry/ # Processamento OTLP
|
||||||
|
├── dashboard/ # Frontend Next.js
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── app/ # App Router
|
||||||
|
│ │ ├── components/ # UI Components
|
||||||
|
│ │ └── lib/ # Utilitários
|
||||||
|
│ └── package.json
|
||||||
|
├── deploy/
|
||||||
|
│ ├── docker/ # Docker configs
|
||||||
|
│ │ └── otel-collector-config.yaml
|
||||||
|
│ ├── remote-agent/ # Agent remoto
|
||||||
|
│ └── instrumentation/ # Scripts de instrumentação
|
||||||
|
├── examples/ # Exemplos por linguagem
|
||||||
|
│ ├── otel-nodejs/
|
||||||
|
│ ├── otel-python/
|
||||||
|
│ └── docker/
|
||||||
|
├── configs/ # Configurações
|
||||||
|
├── docs/ # Documentação
|
||||||
|
├── instrument.sh # Script de auto-instrumentação
|
||||||
|
├── install.sh # Instalador
|
||||||
|
├── docker-compose.yml
|
||||||
|
├── go.mod / go.sum
|
||||||
|
└── server # Binário compilado
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Componentes Principais
|
||||||
|
|
||||||
|
### 5.1 OpenTelemetry Collector
|
||||||
|
|
||||||
|
**Portas:**
|
||||||
|
- `4317` - OTLP gRPC
|
||||||
|
- `4318` - OTLP HTTP
|
||||||
|
|
||||||
|
**Pipeline:**
|
||||||
|
```yaml
|
||||||
|
receivers:
|
||||||
|
otlp:
|
||||||
|
protocols:
|
||||||
|
grpc:
|
||||||
|
endpoint: 0.0.0.0:4317
|
||||||
|
http:
|
||||||
|
endpoint: 0.0.0.0:4318
|
||||||
|
|
||||||
|
processors:
|
||||||
|
batch:
|
||||||
|
timeout: 1s
|
||||||
|
memory_limiter:
|
||||||
|
limit_mib: 512
|
||||||
|
|
||||||
|
exporters:
|
||||||
|
ophion:
|
||||||
|
endpoint: http://server:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 Server (Go API)
|
||||||
|
|
||||||
|
**Endpoints principais:**
|
||||||
|
|
||||||
|
| Endpoint | Método | Descrição |
|
||||||
|
|----------|--------|-----------|
|
||||||
|
| `/api/v1/traces` | POST | Ingestão de traces |
|
||||||
|
| `/api/v1/metrics` | POST | Ingestão de métricas |
|
||||||
|
| `/api/v1/logs` | POST | Ingestão de logs |
|
||||||
|
| `/api/v1/services` | GET | Lista serviços |
|
||||||
|
| `/api/v1/alerts` | GET/POST | Gerenciamento de alertas |
|
||||||
|
| `/api/v1/ai/analyze` | POST | Análise por IA |
|
||||||
|
|
||||||
|
### 5.3 Dashboard (Next.js)
|
||||||
|
|
||||||
|
**Recursos:**
|
||||||
|
- Service Map visual
|
||||||
|
- Trace waterfall
|
||||||
|
- Métricas em tempo real
|
||||||
|
- Logs agregados
|
||||||
|
- Alertas e notificações
|
||||||
|
- AI Copilot chat
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Auto-Instrumentação
|
||||||
|
|
||||||
|
### 6.1 Script Universal
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Auto-detecta linguagem
|
||||||
|
./instrument.sh <container-name>
|
||||||
|
|
||||||
|
# Especifica linguagem
|
||||||
|
./instrument.sh my-app nodejs
|
||||||
|
./instrument.sh my-app python
|
||||||
|
./instrument.sh my-app java
|
||||||
|
./instrument.sh my-app dotnet
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 Suporte por Linguagem
|
||||||
|
|
||||||
|
| Linguagem | Método | Complexidade |
|
||||||
|
|-----------|--------|--------------|
|
||||||
|
| .NET | Auto-instrumentation | Zero code |
|
||||||
|
| Node.js | Auto-instrumentation | Zero code |
|
||||||
|
| Python | Auto-instrumentation | Zero code |
|
||||||
|
| Java | Java Agent | Zero code |
|
||||||
|
| Go | SDK (compile-time) | Pequenas mudanças |
|
||||||
|
| PHP | SDK | Pequenas mudanças |
|
||||||
|
|
||||||
|
### 6.3 Variáveis de Ambiente
|
||||||
|
|
||||||
|
```env
|
||||||
|
OTEL_EXPORTER_OTLP_ENDPOINT=http://ophion:4318
|
||||||
|
OTEL_SERVICE_NAME=my-service
|
||||||
|
OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. AI Engine
|
||||||
|
|
||||||
|
### 7.1 Funcionalidades
|
||||||
|
|
||||||
|
- **Correlação de Alertas**: Agrupa alertas relacionados
|
||||||
|
- **Root Cause Analysis**: Identifica causa raiz
|
||||||
|
- **Previsão de Capacidade**: Prevê saturação de recursos
|
||||||
|
- **Auto-Healing**: Executa ações corretivas automáticas
|
||||||
|
- **Copilot**: Chat para consultas em linguagem natural
|
||||||
|
|
||||||
|
### 7.2 Integração OpenAI
|
||||||
|
|
||||||
|
```env
|
||||||
|
OPENAI_API_KEY=sk-...
|
||||||
|
AI_MODEL=gpt-4
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Deploy
|
||||||
|
|
||||||
|
### 8.1 Quick Start (Docker)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/bigtux/ophion.git
|
||||||
|
cd ophion
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 Acessos
|
||||||
|
|
||||||
|
| Serviço | URL | Porta |
|
||||||
|
|---------|-----|-------|
|
||||||
|
| Dashboard | http://localhost:3000 | 3000 |
|
||||||
|
| API | http://localhost:8080 | 8080 |
|
||||||
|
| OTLP gRPC | localhost:4317 | 4317 |
|
||||||
|
| OTLP HTTP | localhost:4318 | 4318 |
|
||||||
|
|
||||||
|
### 8.3 Produção
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Com install.sh
|
||||||
|
./install.sh --production
|
||||||
|
|
||||||
|
# Ou manualmente
|
||||||
|
docker compose -f docker-compose.prod.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Requisitos do Sistema
|
||||||
|
|
||||||
|
| Recurso | Mínimo | Recomendado |
|
||||||
|
|---------|--------|-------------|
|
||||||
|
| CPU | 2 cores | 4+ cores |
|
||||||
|
| RAM | 4 GB | 8+ GB |
|
||||||
|
| Disco | 20 GB SSD | 100+ GB SSD |
|
||||||
|
| Docker | 20.10+ | Latest |
|
||||||
|
| Docker Compose | v2+ | Latest |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Configuração
|
||||||
|
|
||||||
|
### 10.1 Variáveis de Ambiente
|
||||||
|
|
||||||
|
```env
|
||||||
|
# Server
|
||||||
|
PORT=8080
|
||||||
|
DATABASE_URL=postgres://user:pass@localhost:5432/ophion
|
||||||
|
REDIS_URL=redis://localhost:6379
|
||||||
|
CLICKHOUSE_URL=clickhouse://localhost:9000
|
||||||
|
|
||||||
|
# Auth
|
||||||
|
JWT_SECRET=your-secret
|
||||||
|
AGENT_KEY=agent-secret-key
|
||||||
|
|
||||||
|
# AI
|
||||||
|
OPENAI_API_KEY=sk-...
|
||||||
|
AI_ENABLED=true
|
||||||
|
|
||||||
|
# Notifications
|
||||||
|
TELEGRAM_BOT_TOKEN=...
|
||||||
|
TELEGRAM_CHAT_ID=...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.2 Configuração do Collector
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# otel-collector-config.yaml
|
||||||
|
receivers:
|
||||||
|
otlp:
|
||||||
|
protocols:
|
||||||
|
grpc:
|
||||||
|
endpoint: 0.0.0.0:4317
|
||||||
|
http:
|
||||||
|
endpoint: 0.0.0.0:4318
|
||||||
|
|
||||||
|
processors:
|
||||||
|
batch:
|
||||||
|
timeout: 1s
|
||||||
|
send_batch_size: 1024
|
||||||
|
|
||||||
|
exporters:
|
||||||
|
otlphttp:
|
||||||
|
endpoint: http://server:8080/api/v1
|
||||||
|
|
||||||
|
service:
|
||||||
|
pipelines:
|
||||||
|
traces:
|
||||||
|
receivers: [otlp]
|
||||||
|
processors: [batch]
|
||||||
|
exporters: [otlphttp]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Segurança
|
||||||
|
|
||||||
|
- Autenticação JWT para dashboard
|
||||||
|
- API Key para agents (`AGENT_KEY`)
|
||||||
|
- TLS opcional para OTLP
|
||||||
|
- Isolamento de rede via Docker
|
||||||
|
- Logs de auditoria
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Desenvolvimento
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backend (Go)
|
||||||
|
go run ./cmd/server
|
||||||
|
|
||||||
|
# Frontend (Next.js)
|
||||||
|
cd dashboard
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Testes
|
||||||
|
go test ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Troubleshooting
|
||||||
|
|
||||||
|
| Problema | Solução |
|
||||||
|
|----------|---------|
|
||||||
|
| Traces não aparecem | Verificar OTEL_EXPORTER_OTLP_ENDPOINT |
|
||||||
|
| Dashboard lento | Aumentar RAM do ClickHouse |
|
||||||
|
| Collector crash | Verificar memory_limiter no config |
|
||||||
|
| Auth falhando | Verificar JWT_SECRET |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Licença
|
||||||
|
|
||||||
|
AGPL-3.0 (Community Edition)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Documento gerado automaticamente - OPHION*
|
||||||
BIN
docs/manual_tecnico.pdf
Normal file
BIN
docs/manual_tecnico.pdf
Normal file
Binary file not shown.
453
docs/manual_vendas.html
Normal file
453
docs/manual_vendas.html
Normal file
@@ -0,0 +1,453 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="pt-BR">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>OPHION - Manual de Vendas</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
color: #2D3142;
|
||||||
|
background: white;
|
||||||
|
line-height: 1.6;
|
||||||
|
padding: 40px 60px;
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #1A7A4C;
|
||||||
|
font-size: 28px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 3px solid #1A7A4C;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
color: #1A7A4C;
|
||||||
|
font-size: 22px;
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
color: #1A7A4C;
|
||||||
|
font-size: 18px;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #E2E4E8;
|
||||||
|
padding: 12px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background: #1A7A4C;
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background: #f9f9f9;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background: #f4f4f4;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background: #1e1e1e;
|
||||||
|
color: #d4d4d4;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
ul, ol {
|
||||||
|
margin: 15px 0;
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
blockquote {
|
||||||
|
border-left: 4px solid #1A7A4C;
|
||||||
|
padding-left: 20px;
|
||||||
|
margin: 20px 0;
|
||||||
|
font-style: italic;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
border-top: 2px solid #E2E4E8;
|
||||||
|
margin: 30px 0;
|
||||||
|
}
|
||||||
|
strong {
|
||||||
|
color: #1A7A4C;
|
||||||
|
}
|
||||||
|
.page-break {
|
||||||
|
page-break-after: always;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>OPHION - Manual de Vendas</h1>
|
||||||
|
<h2>Observabilidade Inteligente para o Mundo Real</h2>
|
||||||
|
<hr />
|
||||||
|
<h2>1. Proposta de Valor</h2>
|
||||||
|
<p><strong>OPHION</strong> é a primeira plataforma de observabilidade open source que combina <strong>métricas, logs e traces</strong> com <strong>inteligência artificial</strong> para não apenas monitorar, mas <strong>prever e resolver problemas automaticamente</strong>.</p>
|
||||||
|
<blockquote>
|
||||||
|
<p><em>"Não apenas veja seus sistemas. Entenda-os."</em></p>
|
||||||
|
</blockquote>
|
||||||
|
<h3>O Problema de Observabilidade Hoje</h3>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Desafio</th>
|
||||||
|
<th>Impacto no Negócio</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Ferramentas fragmentadas</td>
|
||||||
|
<td>Tempo perdido alternando dashboards</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Alertas sem contexto</td>
|
||||||
|
<td>Alert fatigue, problemas ignorados</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Diagnóstico manual</td>
|
||||||
|
<td>Horas para encontrar root cause</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Dashboards estáticos</td>
|
||||||
|
<td>Não preveem problemas</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Custos por volume</td>
|
||||||
|
<td>Contas astronômicas (Datadog, New Relic)</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<h3>A Diferença OPHION</h3>
|
||||||
|
<p>✅ <strong>Uma plataforma, toda telemetria</strong> (métricas, logs, traces)<br />
|
||||||
|
✅ <strong>IA que correlaciona</strong> alertas e identifica root cause<br />
|
||||||
|
✅ <strong>Previsão de capacidade</strong> antes do problema acontecer<br />
|
||||||
|
✅ <strong>Auto-healing</strong> para ações corretivas automáticas<br />
|
||||||
|
✅ <strong>Custo previsível</strong> — não cobra por volume</p>
|
||||||
|
<hr />
|
||||||
|
<h2>2. Público-Alvo</h2>
|
||||||
|
<h3>2.1 Equipes de SRE / Platform Engineering</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Responsáveis por uptime e performance</li>
|
||||||
|
<li>Precisam de visibilidade cross-service</li>
|
||||||
|
<li>Buscam reduzir MTTR</li>
|
||||||
|
</ul>
|
||||||
|
<h3>2.2 DevOps / Infrastructure Teams</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Gerenciam dezenas/centenas de serviços</li>
|
||||||
|
<li>Precisam de troubleshooting rápido</li>
|
||||||
|
<li>Querem automatizar operações</li>
|
||||||
|
</ul>
|
||||||
|
<h3>2.3 CTOs e VPs de Engineering</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Buscam reduzir custos de observabilidade</li>
|
||||||
|
<li>Precisam de métricas de SLA/SLO</li>
|
||||||
|
<li>Querem visão executiva de saúde</li>
|
||||||
|
</ul>
|
||||||
|
<h3>2.4 Startups em Crescimento</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Infraestrutura ficando complexa</li>
|
||||||
|
<li>Budget limitado para ferramentas enterprise</li>
|
||||||
|
<li>Precisam escalar observabilidade</li>
|
||||||
|
</ul>
|
||||||
|
<h3>2.5 Empresas com Compliance</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Precisam manter dados on-premise</li>
|
||||||
|
<li>Regulamentações de soberania de dados</li>
|
||||||
|
<li>Auditoria e rastreabilidade</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<h2>3. Funcionalidades</h2>
|
||||||
|
<h3>📊 Unified Telemetry</h3>
|
||||||
|
<p>Uma única interface para tudo:
|
||||||
|
- <strong>Métricas</strong>: CPU, memória, latência, custom metrics
|
||||||
|
- <strong>Logs</strong>: Agregados, pesquisáveis, correlacionados
|
||||||
|
- <strong>Traces</strong>: Distributed tracing com waterfall</p>
|
||||||
|
<h3>🗺️ Service Map</h3>
|
||||||
|
<p>Visualize sua arquitetura em tempo real:
|
||||||
|
- Dependências entre serviços
|
||||||
|
- Latência por salto
|
||||||
|
- Detecção de gargalos
|
||||||
|
- Health status por serviço</p>
|
||||||
|
<h3>🤖 AI Copilot</h3>
|
||||||
|
<p>Inteligência artificial integrada:
|
||||||
|
- <strong>Chat em linguagem natural</strong>: "Por que o checkout está lento?"
|
||||||
|
- <strong>Correlação de alertas</strong>: Agrupa relacionados automaticamente
|
||||||
|
- <strong>Root Cause Analysis</strong>: Identifica a origem do problema
|
||||||
|
- <strong>Sugestões de correção</strong>: O que fazer para resolver</p>
|
||||||
|
<h3>🔮 Predictive Analytics</h3>
|
||||||
|
<p>Antecipe problemas:
|
||||||
|
- Previsão de saturação de recursos
|
||||||
|
- Detecção de anomalias
|
||||||
|
- Tendências de degradação
|
||||||
|
- Alertas proativos</p>
|
||||||
|
<h3>⚡ Auto-Healing</h3>
|
||||||
|
<p>Ações automáticas:
|
||||||
|
- Restart de containers
|
||||||
|
- Scale-up/down automático
|
||||||
|
- Rollback de deployments
|
||||||
|
- Notificações inteligentes</p>
|
||||||
|
<h3>🔧 Auto-Instrumentação</h3>
|
||||||
|
<p>Zero code para começar:
|
||||||
|
- Node.js, Python, Java, .NET
|
||||||
|
- Script único: <code>./instrument.sh my-app</code>
|
||||||
|
- OpenTelemetry nativo
|
||||||
|
- Suporte a todas as linguagens populares</p>
|
||||||
|
<h3>📈 SLO/SLI Management</h3>
|
||||||
|
<p>Gerencie objetivos de serviço:
|
||||||
|
- Definição de SLOs
|
||||||
|
- Error budgets
|
||||||
|
- Burn rate alerts
|
||||||
|
- Relatórios de compliance</p>
|
||||||
|
<h3>🔔 Alerting Inteligente</h3>
|
||||||
|
<p>Alertas que fazem sentido:
|
||||||
|
- Correlação para reduzir ruído
|
||||||
|
- Escalation automático
|
||||||
|
- Integração: Telegram, Slack, PagerDuty
|
||||||
|
- Silenciamento inteligente</p>
|
||||||
|
<hr />
|
||||||
|
<h2>4. Benefícios</h2>
|
||||||
|
<h3>Para SRE/DevOps</h3>
|
||||||
|
<p>✅ <strong>MTTR 10x menor</strong>: Root cause em segundos, não horas<br />
|
||||||
|
✅ <strong>Alert fatigue eliminado</strong>: IA agrupa e prioriza<br />
|
||||||
|
✅ <strong>Troubleshooting visual</strong>: Service map + traces<br />
|
||||||
|
✅ <strong>Menos toil</strong>: Auto-healing reduz intervenções manuais</p>
|
||||||
|
<h3>Para a Empresa</h3>
|
||||||
|
<p>✅ <strong>Custo previsível</strong>: Sem surpresas na fatura<br />
|
||||||
|
✅ <strong>Uptime melhorado</strong>: Previsão de problemas<br />
|
||||||
|
✅ <strong>Compliance</strong>: Dados on-premise, auditáveis<br />
|
||||||
|
✅ <strong>ROI rápido</strong>: Implementação em horas, não semanas</p>
|
||||||
|
<h3>Para Desenvolvedores</h3>
|
||||||
|
<p>✅ <strong>Debug em produção</strong>: Traces detalhados<br />
|
||||||
|
✅ <strong>Contexto completo</strong>: Logs + traces correlacionados<br />
|
||||||
|
✅ <strong>Zero config</strong>: Auto-instrumentação<br />
|
||||||
|
✅ <strong>Ownership</strong>: Veja o impacto do seu código</p>
|
||||||
|
<hr />
|
||||||
|
<h2>5. Casos de Uso</h2>
|
||||||
|
<h3>Caso 1: E-commerce em Black Friday</h3>
|
||||||
|
<blockquote>
|
||||||
|
<p>Um e-commerce com 500k requests/min usa OPHION para monitorar a Black Friday. O AI Copilot detecta degradação no serviço de pagamentos 15 minutos antes de virar problema. Auto-healing escala o serviço automaticamente. <strong>Zero downtime, R$ 2M em vendas salvas</strong>.</p>
|
||||||
|
</blockquote>
|
||||||
|
<h3>Caso 2: Fintech com Compliance</h3>
|
||||||
|
<blockquote>
|
||||||
|
<p>Uma fintech precisa de observabilidade mas não pode enviar dados para cloud pública. OPHION roda on-premise, com logs criptografados e trilha de auditoria completa. <strong>Compliance SOC 2 atendido</strong>.</p>
|
||||||
|
</blockquote>
|
||||||
|
<h3>Caso 3: Startup Escalonando</h3>
|
||||||
|
<blockquote>
|
||||||
|
<p>Uma startup cresceu de 5 para 50 microserviços em 1 ano. O service map do OPHION mostra dependências que ninguém documentou. Root cause analysis reduz MTTR de 4h para 10 minutos. <strong>Equipe de 3 pessoas gerencia tudo</strong>.</p>
|
||||||
|
</blockquote>
|
||||||
|
<h3>Caso 4: Migração para Kubernetes</h3>
|
||||||
|
<blockquote>
|
||||||
|
<p>Uma empresa migra para K8s e perde visibilidade. OPHION com auto-instrumentação cobre todos os pods sem mudar código. <strong>Observabilidade completa em 1 dia</strong>.</p>
|
||||||
|
</blockquote>
|
||||||
|
<hr />
|
||||||
|
<h2>6. Comparativo de Mercado</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Feature</th>
|
||||||
|
<th>OPHION</th>
|
||||||
|
<th>Datadog</th>
|
||||||
|
<th>Grafana Cloud</th>
|
||||||
|
<th>New Relic</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Métricas + Logs + Traces</td>
|
||||||
|
<td>✅</td>
|
||||||
|
<td>✅</td>
|
||||||
|
<td>✅</td>
|
||||||
|
<td>✅</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>AI Copilot</strong></td>
|
||||||
|
<td>✅</td>
|
||||||
|
<td>❌</td>
|
||||||
|
<td>❌</td>
|
||||||
|
<td>Parcial</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Auto-Healing</strong></td>
|
||||||
|
<td>✅</td>
|
||||||
|
<td>❌</td>
|
||||||
|
<td>❌</td>
|
||||||
|
<td>❌</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Correlação IA</strong></td>
|
||||||
|
<td>✅</td>
|
||||||
|
<td>💰💰</td>
|
||||||
|
<td>❌</td>
|
||||||
|
<td>💰💰</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Previsão de Capacidade</strong></td>
|
||||||
|
<td>✅</td>
|
||||||
|
<td>💰💰</td>
|
||||||
|
<td>❌</td>
|
||||||
|
<td>💰</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Open Source</td>
|
||||||
|
<td>✅</td>
|
||||||
|
<td>❌</td>
|
||||||
|
<td>Parcial</td>
|
||||||
|
<td>❌</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>On-Premise</td>
|
||||||
|
<td>✅</td>
|
||||||
|
<td>❌</td>
|
||||||
|
<td>❌</td>
|
||||||
|
<td>❌</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Auto-instrumentação</td>
|
||||||
|
<td>✅</td>
|
||||||
|
<td>💰</td>
|
||||||
|
<td>❌</td>
|
||||||
|
<td>💰</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Custo por volume</strong></td>
|
||||||
|
<td>❌ Fixo</td>
|
||||||
|
<td>💰💰💰</td>
|
||||||
|
<td>💰💰</td>
|
||||||
|
<td>💰💰💰</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<hr />
|
||||||
|
<h2>7. Modelo de Implantação</h2>
|
||||||
|
<h3>Community Edition (Open Source)</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Todas as funcionalidades core</li>
|
||||||
|
<li>Self-hosted</li>
|
||||||
|
<li>Suporte via comunidade</li>
|
||||||
|
<li>Licença AGPL-3.0</li>
|
||||||
|
</ul>
|
||||||
|
<h3>Enterprise Edition</h3>
|
||||||
|
<ul>
|
||||||
|
<li>SLA garantido</li>
|
||||||
|
<li>Suporte dedicado</li>
|
||||||
|
<li>Funcionalidades avançadas de compliance</li>
|
||||||
|
<li>Multi-tenancy</li>
|
||||||
|
</ul>
|
||||||
|
<h3>Managed (Cloud)</h3>
|
||||||
|
<ul>
|
||||||
|
<li>OPHION gerenciado</li>
|
||||||
|
<li>SLA 99.9%</li>
|
||||||
|
<li>Backups automáticos</li>
|
||||||
|
<li>Updates gerenciados</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<h2>8. Integrações</h2>
|
||||||
|
<h3>Plataformas</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Kubernetes, Docker, AWS, GCP, Azure</li>
|
||||||
|
</ul>
|
||||||
|
<h3>Linguagens (Auto-instrumentação)</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Node.js, Python, Java, .NET, Go, PHP</li>
|
||||||
|
</ul>
|
||||||
|
<h3>Alerting</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Telegram, Slack, PagerDuty, OpsGenie, Email</li>
|
||||||
|
</ul>
|
||||||
|
<h3>CI/CD</h3>
|
||||||
|
<ul>
|
||||||
|
<li>GitHub Actions, GitLab CI, Jenkins</li>
|
||||||
|
</ul>
|
||||||
|
<h3>Standards</h3>
|
||||||
|
<ul>
|
||||||
|
<li>OpenTelemetry, Prometheus, Jaeger</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<h2>9. Implementação</h2>
|
||||||
|
<h3>Timeline Típico</h3>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Fase</th>
|
||||||
|
<th>Duração</th>
|
||||||
|
<th>Atividade</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Setup</td>
|
||||||
|
<td>1h</td>
|
||||||
|
<td>docker compose up</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Instrumentação</td>
|
||||||
|
<td>1-2h</td>
|
||||||
|
<td>Auto-instrument serviços críticos</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Configuração</td>
|
||||||
|
<td>2-4h</td>
|
||||||
|
<td>Alertas, dashboards, SLOs</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Produção</td>
|
||||||
|
<td>1 semana</td>
|
||||||
|
<td>Refinamento e ajustes</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<h3>Pré-requisitos</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Docker 20.10+</li>
|
||||||
|
<li>4GB RAM mínimo</li>
|
||||||
|
<li>Acesso aos serviços a monitorar</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<h2>10. Próximos Passos</h2>
|
||||||
|
<ol>
|
||||||
|
<li><strong>Demo</strong>: Veja OPHION em ação (15 min)</li>
|
||||||
|
<li><strong>POC</strong>: Instale em ambiente de teste</li>
|
||||||
|
<li><strong>Pilot</strong>: Instrumente 3-5 serviços críticos</li>
|
||||||
|
<li><strong>Rollout</strong>: Expanda para toda infraestrutura</li>
|
||||||
|
<li><strong>Optimize</strong>: Configure AI e auto-healing</li>
|
||||||
|
</ol>
|
||||||
|
<hr />
|
||||||
|
<h2>11. Suporte</h2>
|
||||||
|
<ul>
|
||||||
|
<li>📚 Documentação: docs.ophion.io</li>
|
||||||
|
<li>💬 Comunidade: Discord</li>
|
||||||
|
<li>📧 Enterprise: contato@ophion.io</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<p><em>OPHION - Observabilidade que pensa por você</em></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
256
docs/manual_vendas.md
Normal file
256
docs/manual_vendas.md
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
# OPHION - Manual de Vendas
|
||||||
|
|
||||||
|
## Observabilidade Inteligente para o Mundo Real
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Proposta de Valor
|
||||||
|
|
||||||
|
**OPHION** é a primeira plataforma de observabilidade open source que combina **métricas, logs e traces** com **inteligência artificial** para não apenas monitorar, mas **prever e resolver problemas automaticamente**.
|
||||||
|
|
||||||
|
> *"Não apenas veja seus sistemas. Entenda-os."*
|
||||||
|
|
||||||
|
### O Problema de Observabilidade Hoje
|
||||||
|
|
||||||
|
| Desafio | Impacto no Negócio |
|
||||||
|
|---------|-------------------|
|
||||||
|
| Ferramentas fragmentadas | Tempo perdido alternando dashboards |
|
||||||
|
| Alertas sem contexto | Alert fatigue, problemas ignorados |
|
||||||
|
| Diagnóstico manual | Horas para encontrar root cause |
|
||||||
|
| Dashboards estáticos | Não preveem problemas |
|
||||||
|
| Custos por volume | Contas astronômicas (Datadog, New Relic) |
|
||||||
|
|
||||||
|
### A Diferença OPHION
|
||||||
|
|
||||||
|
✅ **Uma plataforma, toda telemetria** (métricas, logs, traces)
|
||||||
|
✅ **IA que correlaciona** alertas e identifica root cause
|
||||||
|
✅ **Previsão de capacidade** antes do problema acontecer
|
||||||
|
✅ **Auto-healing** para ações corretivas automáticas
|
||||||
|
✅ **Custo previsível** — não cobra por volume
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Público-Alvo
|
||||||
|
|
||||||
|
### 2.1 Equipes de SRE / Platform Engineering
|
||||||
|
- Responsáveis por uptime e performance
|
||||||
|
- Precisam de visibilidade cross-service
|
||||||
|
- Buscam reduzir MTTR
|
||||||
|
|
||||||
|
### 2.2 DevOps / Infrastructure Teams
|
||||||
|
- Gerenciam dezenas/centenas de serviços
|
||||||
|
- Precisam de troubleshooting rápido
|
||||||
|
- Querem automatizar operações
|
||||||
|
|
||||||
|
### 2.3 CTOs e VPs de Engineering
|
||||||
|
- Buscam reduzir custos de observabilidade
|
||||||
|
- Precisam de métricas de SLA/SLO
|
||||||
|
- Querem visão executiva de saúde
|
||||||
|
|
||||||
|
### 2.4 Startups em Crescimento
|
||||||
|
- Infraestrutura ficando complexa
|
||||||
|
- Budget limitado para ferramentas enterprise
|
||||||
|
- Precisam escalar observabilidade
|
||||||
|
|
||||||
|
### 2.5 Empresas com Compliance
|
||||||
|
- Precisam manter dados on-premise
|
||||||
|
- Regulamentações de soberania de dados
|
||||||
|
- Auditoria e rastreabilidade
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Funcionalidades
|
||||||
|
|
||||||
|
### 📊 Unified Telemetry
|
||||||
|
Uma única interface para tudo:
|
||||||
|
- **Métricas**: CPU, memória, latência, custom metrics
|
||||||
|
- **Logs**: Agregados, pesquisáveis, correlacionados
|
||||||
|
- **Traces**: Distributed tracing com waterfall
|
||||||
|
|
||||||
|
### 🗺️ Service Map
|
||||||
|
Visualize sua arquitetura em tempo real:
|
||||||
|
- Dependências entre serviços
|
||||||
|
- Latência por salto
|
||||||
|
- Detecção de gargalos
|
||||||
|
- Health status por serviço
|
||||||
|
|
||||||
|
### 🤖 AI Copilot
|
||||||
|
Inteligência artificial integrada:
|
||||||
|
- **Chat em linguagem natural**: "Por que o checkout está lento?"
|
||||||
|
- **Correlação de alertas**: Agrupa relacionados automaticamente
|
||||||
|
- **Root Cause Analysis**: Identifica a origem do problema
|
||||||
|
- **Sugestões de correção**: O que fazer para resolver
|
||||||
|
|
||||||
|
### 🔮 Predictive Analytics
|
||||||
|
Antecipe problemas:
|
||||||
|
- Previsão de saturação de recursos
|
||||||
|
- Detecção de anomalias
|
||||||
|
- Tendências de degradação
|
||||||
|
- Alertas proativos
|
||||||
|
|
||||||
|
### ⚡ Auto-Healing
|
||||||
|
Ações automáticas:
|
||||||
|
- Restart de containers
|
||||||
|
- Scale-up/down automático
|
||||||
|
- Rollback de deployments
|
||||||
|
- Notificações inteligentes
|
||||||
|
|
||||||
|
### 🔧 Auto-Instrumentação
|
||||||
|
Zero code para começar:
|
||||||
|
- Node.js, Python, Java, .NET
|
||||||
|
- Script único: `./instrument.sh my-app`
|
||||||
|
- OpenTelemetry nativo
|
||||||
|
- Suporte a todas as linguagens populares
|
||||||
|
|
||||||
|
### 📈 SLO/SLI Management
|
||||||
|
Gerencie objetivos de serviço:
|
||||||
|
- Definição de SLOs
|
||||||
|
- Error budgets
|
||||||
|
- Burn rate alerts
|
||||||
|
- Relatórios de compliance
|
||||||
|
|
||||||
|
### 🔔 Alerting Inteligente
|
||||||
|
Alertas que fazem sentido:
|
||||||
|
- Correlação para reduzir ruído
|
||||||
|
- Escalation automático
|
||||||
|
- Integração: Telegram, Slack, PagerDuty
|
||||||
|
- Silenciamento inteligente
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Benefícios
|
||||||
|
|
||||||
|
### Para SRE/DevOps
|
||||||
|
|
||||||
|
✅ **MTTR 10x menor**: Root cause em segundos, não horas
|
||||||
|
✅ **Alert fatigue eliminado**: IA agrupa e prioriza
|
||||||
|
✅ **Troubleshooting visual**: Service map + traces
|
||||||
|
✅ **Menos toil**: Auto-healing reduz intervenções manuais
|
||||||
|
|
||||||
|
### Para a Empresa
|
||||||
|
|
||||||
|
✅ **Custo previsível**: Sem surpresas na fatura
|
||||||
|
✅ **Uptime melhorado**: Previsão de problemas
|
||||||
|
✅ **Compliance**: Dados on-premise, auditáveis
|
||||||
|
✅ **ROI rápido**: Implementação em horas, não semanas
|
||||||
|
|
||||||
|
### Para Desenvolvedores
|
||||||
|
|
||||||
|
✅ **Debug em produção**: Traces detalhados
|
||||||
|
✅ **Contexto completo**: Logs + traces correlacionados
|
||||||
|
✅ **Zero config**: Auto-instrumentação
|
||||||
|
✅ **Ownership**: Veja o impacto do seu código
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Casos de Uso
|
||||||
|
|
||||||
|
### Caso 1: E-commerce em Black Friday
|
||||||
|
> Um e-commerce com 500k requests/min usa OPHION para monitorar a Black Friday. O AI Copilot detecta degradação no serviço de pagamentos 15 minutos antes de virar problema. Auto-healing escala o serviço automaticamente. **Zero downtime, R$ 2M em vendas salvas**.
|
||||||
|
|
||||||
|
### Caso 2: Fintech com Compliance
|
||||||
|
> Uma fintech precisa de observabilidade mas não pode enviar dados para cloud pública. OPHION roda on-premise, com logs criptografados e trilha de auditoria completa. **Compliance SOC 2 atendido**.
|
||||||
|
|
||||||
|
### Caso 3: Startup Escalonando
|
||||||
|
> Uma startup cresceu de 5 para 50 microserviços em 1 ano. O service map do OPHION mostra dependências que ninguém documentou. Root cause analysis reduz MTTR de 4h para 10 minutos. **Equipe de 3 pessoas gerencia tudo**.
|
||||||
|
|
||||||
|
### Caso 4: Migração para Kubernetes
|
||||||
|
> Uma empresa migra para K8s e perde visibilidade. OPHION com auto-instrumentação cobre todos os pods sem mudar código. **Observabilidade completa em 1 dia**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Comparativo de Mercado
|
||||||
|
|
||||||
|
| Feature | OPHION | Datadog | Grafana Cloud | New Relic |
|
||||||
|
|---------|--------|---------|---------------|-----------|
|
||||||
|
| Métricas + Logs + Traces | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| **AI Copilot** | ✅ | ❌ | ❌ | Parcial |
|
||||||
|
| **Auto-Healing** | ✅ | ❌ | ❌ | ❌ |
|
||||||
|
| **Correlação IA** | ✅ | 💰💰 | ❌ | 💰💰 |
|
||||||
|
| **Previsão de Capacidade** | ✅ | 💰💰 | ❌ | 💰 |
|
||||||
|
| Open Source | ✅ | ❌ | Parcial | ❌ |
|
||||||
|
| On-Premise | ✅ | ❌ | ❌ | ❌ |
|
||||||
|
| Auto-instrumentação | ✅ | 💰 | ❌ | 💰 |
|
||||||
|
| **Custo por volume** | ❌ Fixo | 💰💰💰 | 💰💰 | 💰💰💰 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Modelo de Implantação
|
||||||
|
|
||||||
|
### Community Edition (Open Source)
|
||||||
|
- Todas as funcionalidades core
|
||||||
|
- Self-hosted
|
||||||
|
- Suporte via comunidade
|
||||||
|
- Licença AGPL-3.0
|
||||||
|
|
||||||
|
### Enterprise Edition
|
||||||
|
- SLA garantido
|
||||||
|
- Suporte dedicado
|
||||||
|
- Funcionalidades avançadas de compliance
|
||||||
|
- Multi-tenancy
|
||||||
|
|
||||||
|
### Managed (Cloud)
|
||||||
|
- OPHION gerenciado
|
||||||
|
- SLA 99.9%
|
||||||
|
- Backups automáticos
|
||||||
|
- Updates gerenciados
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Integrações
|
||||||
|
|
||||||
|
### Plataformas
|
||||||
|
- Kubernetes, Docker, AWS, GCP, Azure
|
||||||
|
|
||||||
|
### Linguagens (Auto-instrumentação)
|
||||||
|
- Node.js, Python, Java, .NET, Go, PHP
|
||||||
|
|
||||||
|
### Alerting
|
||||||
|
- Telegram, Slack, PagerDuty, OpsGenie, Email
|
||||||
|
|
||||||
|
### CI/CD
|
||||||
|
- GitHub Actions, GitLab CI, Jenkins
|
||||||
|
|
||||||
|
### Standards
|
||||||
|
- OpenTelemetry, Prometheus, Jaeger
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Implementação
|
||||||
|
|
||||||
|
### Timeline Típico
|
||||||
|
|
||||||
|
| Fase | Duração | Atividade |
|
||||||
|
|------|---------|-----------|
|
||||||
|
| Setup | 1h | docker compose up |
|
||||||
|
| Instrumentação | 1-2h | Auto-instrument serviços críticos |
|
||||||
|
| Configuração | 2-4h | Alertas, dashboards, SLOs |
|
||||||
|
| Produção | 1 semana | Refinamento e ajustes |
|
||||||
|
|
||||||
|
### Pré-requisitos
|
||||||
|
- Docker 20.10+
|
||||||
|
- 4GB RAM mínimo
|
||||||
|
- Acesso aos serviços a monitorar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Próximos Passos
|
||||||
|
|
||||||
|
1. **Demo**: Veja OPHION em ação (15 min)
|
||||||
|
2. **POC**: Instale em ambiente de teste
|
||||||
|
3. **Pilot**: Instrumente 3-5 serviços críticos
|
||||||
|
4. **Rollout**: Expanda para toda infraestrutura
|
||||||
|
5. **Optimize**: Configure AI e auto-healing
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Suporte
|
||||||
|
|
||||||
|
- 📚 Documentação: docs.ophion.io
|
||||||
|
- 💬 Comunidade: Discord
|
||||||
|
- 📧 Enterprise: contato@ophion.io
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*OPHION - Observabilidade que pensa por você*
|
||||||
|
|
||||||
BIN
docs/manual_vendas.pdf
Normal file
BIN
docs/manual_vendas.pdf
Normal file
Binary file not shown.
@@ -1,109 +0,0 @@
|
|||||||
package aiapm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"log"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Collector receives AI call records and writes them to the database asynchronously
|
|
||||||
type Collector struct {
|
|
||||||
db *sql.DB
|
|
||||||
ch chan AICallRecord
|
|
||||||
done chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCollector creates a new Collector with a buffered channel and background writer
|
|
||||||
func NewCollector(db *sql.DB, bufferSize int) *Collector {
|
|
||||||
if bufferSize <= 0 {
|
|
||||||
bufferSize = 1000
|
|
||||||
}
|
|
||||||
c := &Collector{
|
|
||||||
db: db,
|
|
||||||
ch: make(chan AICallRecord, bufferSize),
|
|
||||||
done: make(chan struct{}),
|
|
||||||
}
|
|
||||||
go c.backgroundWriter()
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect validates and enqueues a record for async storage
|
|
||||||
func (c *Collector) Collect(r AICallRecord) {
|
|
||||||
if r.ID == "" {
|
|
||||||
r.ID = uuid.New().String()
|
|
||||||
}
|
|
||||||
if r.Timestamp.IsZero() {
|
|
||||||
r.Timestamp = time.Now()
|
|
||||||
}
|
|
||||||
if r.Status == "" {
|
|
||||||
r.Status = "success"
|
|
||||||
}
|
|
||||||
// Estimate cost if not provided
|
|
||||||
if r.EstimatedCost == 0 && (r.TokensIn > 0 || r.TokensOut > 0) {
|
|
||||||
r.EstimatedCost = EstimateCost(r.Vendor, r.Model, r.TokensIn, r.TokensOut, r.TokensCacheRead, r.TokensCacheWrite)
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case c.ch <- r:
|
|
||||||
default:
|
|
||||||
// Channel full — write synchronously to avoid data loss
|
|
||||||
if err := InsertCall(c.db, r); err != nil {
|
|
||||||
log.Printf("ai-apm: sync insert error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CollectBatch validates and enqueues multiple records
|
|
||||||
func (c *Collector) CollectBatch(records []AICallRecord) {
|
|
||||||
for i := range records {
|
|
||||||
c.Collect(records[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop gracefully stops the background writer
|
|
||||||
func (c *Collector) Stop() {
|
|
||||||
close(c.ch)
|
|
||||||
<-c.done
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Collector) backgroundWriter() {
|
|
||||||
defer close(c.done)
|
|
||||||
|
|
||||||
batch := make([]AICallRecord, 0, 100)
|
|
||||||
ticker := time.NewTicker(500 * time.Millisecond)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
flush := func() {
|
|
||||||
if len(batch) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := InsertCallBatch(c.db, batch); err != nil {
|
|
||||||
log.Printf("ai-apm: batch insert error (%d records): %v", len(batch), err)
|
|
||||||
// Fallback: insert one by one
|
|
||||||
for _, r := range batch {
|
|
||||||
if err := InsertCall(c.db, r); err != nil {
|
|
||||||
log.Printf("ai-apm: single insert error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
batch = batch[:0]
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case r, ok := <-c.ch:
|
|
||||||
if !ok {
|
|
||||||
flush()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
batch = append(batch, r)
|
|
||||||
if len(batch) >= 100 {
|
|
||||||
flush()
|
|
||||||
}
|
|
||||||
case <-ticker.C:
|
|
||||||
flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package aiapm
|
|
||||||
|
|
||||||
// ModelPricing holds per-1M-token pricing for a model
|
|
||||||
type ModelPricing struct {
|
|
||||||
InputPer1M float64 `json:"input_per_1m"`
|
|
||||||
OutputPer1M float64 `json:"output_per_1m"`
|
|
||||||
CacheReadPer1M float64 `json:"cache_read_per_1m"`
|
|
||||||
CacheWritePer1M float64 `json:"cache_write_per_1m"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PricingTable maps "vendor/model" to pricing. Prices in USD per 1M tokens.
|
|
||||||
var PricingTable = map[string]ModelPricing{
|
|
||||||
// Anthropic
|
|
||||||
"anthropic/claude-opus-4": {InputPer1M: 15.0, OutputPer1M: 75.0, CacheReadPer1M: 1.5, CacheWritePer1M: 18.75},
|
|
||||||
"anthropic/claude-sonnet-4": {InputPer1M: 3.0, OutputPer1M: 15.0, CacheReadPer1M: 0.3, CacheWritePer1M: 3.75},
|
|
||||||
"anthropic/claude-3.5-sonnet": {InputPer1M: 3.0, OutputPer1M: 15.0, CacheReadPer1M: 0.3, CacheWritePer1M: 3.75},
|
|
||||||
"anthropic/claude-3.5-haiku": {InputPer1M: 0.8, OutputPer1M: 4.0, CacheReadPer1M: 0.08, CacheWritePer1M: 1.0},
|
|
||||||
"anthropic/claude-3-haiku": {InputPer1M: 0.25, OutputPer1M: 1.25, CacheReadPer1M: 0.03, CacheWritePer1M: 0.3},
|
|
||||||
|
|
||||||
// OpenAI
|
|
||||||
"openai/gpt-4o": {InputPer1M: 2.5, OutputPer1M: 10.0, CacheReadPer1M: 1.25, CacheWritePer1M: 2.5},
|
|
||||||
"openai/gpt-4o-mini": {InputPer1M: 0.15, OutputPer1M: 0.6, CacheReadPer1M: 0.075, CacheWritePer1M: 0.15},
|
|
||||||
"openai/o1": {InputPer1M: 15.0, OutputPer1M: 60.0, CacheReadPer1M: 7.5, CacheWritePer1M: 15.0},
|
|
||||||
"openai/o1-mini": {InputPer1M: 3.0, OutputPer1M: 12.0, CacheReadPer1M: 1.5, CacheWritePer1M: 3.0},
|
|
||||||
"openai/o3": {InputPer1M: 10.0, OutputPer1M: 40.0, CacheReadPer1M: 5.0, CacheWritePer1M: 10.0},
|
|
||||||
"openai/o3-mini": {InputPer1M: 1.1, OutputPer1M: 4.4, CacheReadPer1M: 0.55, CacheWritePer1M: 1.1},
|
|
||||||
|
|
||||||
// Google
|
|
||||||
"google/gemini-2.5-pro": {InputPer1M: 1.25, OutputPer1M: 10.0, CacheReadPer1M: 0.315, CacheWritePer1M: 1.25},
|
|
||||||
"google/gemini-2.5-flash": {InputPer1M: 0.15, OutputPer1M: 0.6, CacheReadPer1M: 0.0375, CacheWritePer1M: 0.15},
|
|
||||||
"google/gemini-2.0-flash": {InputPer1M: 0.1, OutputPer1M: 0.4, CacheReadPer1M: 0.025, CacheWritePer1M: 0.1},
|
|
||||||
|
|
||||||
// Mistral
|
|
||||||
"mistral/mistral-large": {InputPer1M: 2.0, OutputPer1M: 6.0, CacheReadPer1M: 2.0, CacheWritePer1M: 2.0},
|
|
||||||
"mistral/mistral-small": {InputPer1M: 0.1, OutputPer1M: 0.3, CacheReadPer1M: 0.1, CacheWritePer1M: 0.1},
|
|
||||||
"mistral/codestral": {InputPer1M: 0.3, OutputPer1M: 0.9, CacheReadPer1M: 0.3, CacheWritePer1M: 0.3},
|
|
||||||
|
|
||||||
// DeepSeek
|
|
||||||
"deepseek/deepseek-chat": {InputPer1M: 0.14, OutputPer1M: 0.28, CacheReadPer1M: 0.014, CacheWritePer1M: 0.14},
|
|
||||||
"deepseek/deepseek-reasoner": {InputPer1M: 0.55, OutputPer1M: 2.19, CacheReadPer1M: 0.055, CacheWritePer1M: 0.55},
|
|
||||||
|
|
||||||
// Groq (hosted models — pricing approximate)
|
|
||||||
"groq/llama-3.3-70b": {InputPer1M: 0.59, OutputPer1M: 0.79, CacheReadPer1M: 0.59, CacheWritePer1M: 0.59},
|
|
||||||
"groq/llama-3.1-8b": {InputPer1M: 0.05, OutputPer1M: 0.08, CacheReadPer1M: 0.05, CacheWritePer1M: 0.05},
|
|
||||||
"groq/gemma2-9b": {InputPer1M: 0.2, OutputPer1M: 0.2, CacheReadPer1M: 0.2, CacheWritePer1M: 0.2},
|
|
||||||
}
|
|
||||||
|
|
||||||
// EstimateCost calculates the estimated cost in USD for an AI call
|
|
||||||
func EstimateCost(vendor, model string, tokensIn, tokensOut, cacheRead, cacheWrite int) float64 {
|
|
||||||
key := vendor + "/" + model
|
|
||||||
pricing, ok := PricingTable[key]
|
|
||||||
if !ok {
|
|
||||||
// Fallback: try just the model name with vendor prefix variations
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
cost := float64(tokensIn) * pricing.InputPer1M / 1_000_000
|
|
||||||
cost += float64(tokensOut) * pricing.OutputPer1M / 1_000_000
|
|
||||||
cost += float64(cacheRead) * pricing.CacheReadPer1M / 1_000_000
|
|
||||||
cost += float64(cacheWrite) * pricing.CacheWritePer1M / 1_000_000
|
|
||||||
|
|
||||||
return cost
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPricingTable returns the full pricing table (for the API endpoint)
|
|
||||||
func GetPricingTable() map[string]ModelPricing {
|
|
||||||
return PricingTable
|
|
||||||
}
|
|
||||||
@@ -1,349 +0,0 @@
|
|||||||
package aiapm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CreateTable creates the ai_calls table and indexes
|
|
||||||
func CreateTable(db *sql.DB) error {
|
|
||||||
schema := `
|
|
||||||
CREATE TABLE IF NOT EXISTS ai_calls (
|
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
||||||
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
service_name VARCHAR(255) NOT NULL,
|
|
||||||
project_id VARCHAR(255) NOT NULL DEFAULT '',
|
|
||||||
vendor VARCHAR(100) NOT NULL,
|
|
||||||
model VARCHAR(255) NOT NULL,
|
|
||||||
tokens_in INT NOT NULL DEFAULT 0,
|
|
||||||
tokens_out INT NOT NULL DEFAULT 0,
|
|
||||||
tokens_cache_read INT NOT NULL DEFAULT 0,
|
|
||||||
tokens_cache_write INT NOT NULL DEFAULT 0,
|
|
||||||
estimated_cost DOUBLE PRECISION NOT NULL DEFAULT 0,
|
|
||||||
latency_ms INT NOT NULL DEFAULT 0,
|
|
||||||
ttfb_ms INT NOT NULL DEFAULT 0,
|
|
||||||
status VARCHAR(20) NOT NULL DEFAULT 'success',
|
|
||||||
error_message TEXT,
|
|
||||||
stream BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
cached BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
tags JSONB
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_ai_calls_timestamp ON ai_calls(timestamp DESC);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_ai_calls_service ON ai_calls(service_name);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_ai_calls_vendor ON ai_calls(vendor);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_ai_calls_model ON ai_calls(model);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_ai_calls_project ON ai_calls(project_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_ai_calls_status ON ai_calls(status);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_ai_calls_vendor_model ON ai_calls(vendor, model);
|
|
||||||
`
|
|
||||||
_, err := db.Exec(schema)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// InsertCall inserts a single AI call record
|
|
||||||
func InsertCall(db *sql.DB, r AICallRecord) error {
|
|
||||||
if r.ID == "" {
|
|
||||||
r.ID = uuid.New().String()
|
|
||||||
}
|
|
||||||
if r.Timestamp.IsZero() {
|
|
||||||
r.Timestamp = time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
tags, _ := json.Marshal(r.Tags)
|
|
||||||
|
|
||||||
_, err := db.Exec(`
|
|
||||||
INSERT INTO ai_calls (id, timestamp, service_name, project_id, vendor, model,
|
|
||||||
tokens_in, tokens_out, tokens_cache_read, tokens_cache_write,
|
|
||||||
estimated_cost, latency_ms, ttfb_ms, status, error_message, stream, cached, tags)
|
|
||||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18)`,
|
|
||||||
r.ID, r.Timestamp, r.ServiceName, r.ProjectID, r.Vendor, r.Model,
|
|
||||||
r.TokensIn, r.TokensOut, r.TokensCacheRead, r.TokensCacheWrite,
|
|
||||||
r.EstimatedCost, r.LatencyMs, r.TTFBMs, r.Status, r.ErrorMessage,
|
|
||||||
r.Stream, r.Cached, tags)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// InsertCallBatch inserts multiple AI call records in a single transaction
|
|
||||||
func InsertCallBatch(db *sql.DB, records []AICallRecord) error {
|
|
||||||
if len(records) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tx, err := db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer tx.Rollback()
|
|
||||||
|
|
||||||
stmt, err := tx.Prepare(`
|
|
||||||
INSERT INTO ai_calls (id, timestamp, service_name, project_id, vendor, model,
|
|
||||||
tokens_in, tokens_out, tokens_cache_read, tokens_cache_write,
|
|
||||||
estimated_cost, latency_ms, ttfb_ms, status, error_message, stream, cached, tags)
|
|
||||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18)`)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
for _, r := range records {
|
|
||||||
if r.ID == "" {
|
|
||||||
r.ID = uuid.New().String()
|
|
||||||
}
|
|
||||||
if r.Timestamp.IsZero() {
|
|
||||||
r.Timestamp = time.Now()
|
|
||||||
}
|
|
||||||
tags, _ := json.Marshal(r.Tags)
|
|
||||||
|
|
||||||
_, err := stmt.Exec(
|
|
||||||
r.ID, r.Timestamp, r.ServiceName, r.ProjectID, r.Vendor, r.Model,
|
|
||||||
r.TokensIn, r.TokensOut, r.TokensCacheRead, r.TokensCacheWrite,
|
|
||||||
r.EstimatedCost, r.LatencyMs, r.TTFBMs, r.Status, r.ErrorMessage,
|
|
||||||
r.Stream, r.Cached, tags)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tx.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildWhereClause constructs WHERE clause from filter
|
|
||||||
func buildWhereClause(f AICallFilter, startArg int) (string, []any) {
|
|
||||||
var conditions []string
|
|
||||||
var args []any
|
|
||||||
n := startArg
|
|
||||||
|
|
||||||
if !f.From.IsZero() {
|
|
||||||
conditions = append(conditions, "timestamp >= $"+strconv.Itoa(n))
|
|
||||||
args = append(args, f.From)
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
if !f.To.IsZero() {
|
|
||||||
conditions = append(conditions, "timestamp <= $"+strconv.Itoa(n))
|
|
||||||
args = append(args, f.To)
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
if f.ServiceName != "" {
|
|
||||||
conditions = append(conditions, "service_name = $"+strconv.Itoa(n))
|
|
||||||
args = append(args, f.ServiceName)
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
if f.ProjectID != "" {
|
|
||||||
conditions = append(conditions, "project_id = $"+strconv.Itoa(n))
|
|
||||||
args = append(args, f.ProjectID)
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
if f.Vendor != "" {
|
|
||||||
conditions = append(conditions, "vendor = $"+strconv.Itoa(n))
|
|
||||||
args = append(args, f.Vendor)
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
if f.Model != "" {
|
|
||||||
conditions = append(conditions, "model = $"+strconv.Itoa(n))
|
|
||||||
args = append(args, f.Model)
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
if f.Status != "" {
|
|
||||||
conditions = append(conditions, "status = $"+strconv.Itoa(n))
|
|
||||||
args = append(args, f.Status)
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(conditions) == 0 {
|
|
||||||
return "", args
|
|
||||||
}
|
|
||||||
return " WHERE " + strings.Join(conditions, " AND "), args
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryCalls queries AI call records with filters
|
|
||||||
func QueryCalls(db *sql.DB, filter AICallFilter) ([]AICallRecord, error) {
|
|
||||||
where, args := buildWhereClause(filter, 1)
|
|
||||||
|
|
||||||
limit := filter.Limit
|
|
||||||
if limit <= 0 {
|
|
||||||
limit = 100
|
|
||||||
}
|
|
||||||
offset := filter.Offset
|
|
||||||
|
|
||||||
q := `SELECT id, timestamp, service_name, project_id, vendor, model,
|
|
||||||
tokens_in, tokens_out, tokens_cache_read, tokens_cache_write,
|
|
||||||
estimated_cost, latency_ms, ttfb_ms, status, COALESCE(error_message,''),
|
|
||||||
stream, cached, COALESCE(tags::text,'{}')
|
|
||||||
FROM ai_calls` + where + ` ORDER BY timestamp DESC LIMIT ` +
|
|
||||||
strconv.Itoa(limit) + ` OFFSET ` + strconv.Itoa(offset)
|
|
||||||
|
|
||||||
rows, err := db.Query(q, args...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var records []AICallRecord
|
|
||||||
for rows.Next() {
|
|
||||||
var r AICallRecord
|
|
||||||
var tagsJSON string
|
|
||||||
if err := rows.Scan(&r.ID, &r.Timestamp, &r.ServiceName, &r.ProjectID,
|
|
||||||
&r.Vendor, &r.Model, &r.TokensIn, &r.TokensOut,
|
|
||||||
&r.TokensCacheRead, &r.TokensCacheWrite, &r.EstimatedCost,
|
|
||||||
&r.LatencyMs, &r.TTFBMs, &r.Status, &r.ErrorMessage,
|
|
||||||
&r.Stream, &r.Cached, &tagsJSON); err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_ = json.Unmarshal([]byte(tagsJSON), &r.Tags)
|
|
||||||
records = append(records, r)
|
|
||||||
}
|
|
||||||
return records, rows.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUsageSummary returns aggregated usage statistics
|
|
||||||
func GetUsageSummary(db *sql.DB, filter AICallFilter) (*AIUsageSummary, error) {
|
|
||||||
where, args := buildWhereClause(filter, 1)
|
|
||||||
|
|
||||||
q := `SELECT
|
|
||||||
COUNT(*),
|
|
||||||
COALESCE(SUM(tokens_in),0),
|
|
||||||
COALESCE(SUM(tokens_out),0),
|
|
||||||
COALESCE(SUM(tokens_cache_read),0),
|
|
||||||
COALESCE(SUM(tokens_cache_write),0),
|
|
||||||
COALESCE(SUM(estimated_cost),0),
|
|
||||||
COALESCE(AVG(latency_ms),0),
|
|
||||||
COALESCE(AVG(ttfb_ms),0),
|
|
||||||
COUNT(*) FILTER (WHERE status = 'error'),
|
|
||||||
COUNT(DISTINCT model),
|
|
||||||
COUNT(DISTINCT vendor),
|
|
||||||
COUNT(DISTINCT service_name)
|
|
||||||
FROM ai_calls` + where
|
|
||||||
|
|
||||||
s := &AIUsageSummary{}
|
|
||||||
err := db.QueryRow(q, args...).Scan(
|
|
||||||
&s.TotalCalls, &s.TotalTokensIn, &s.TotalTokensOut,
|
|
||||||
&s.TotalCacheRead, &s.TotalCacheWrite, &s.TotalCost,
|
|
||||||
&s.AvgLatencyMs, &s.AvgTTFBMs, &s.ErrorCount,
|
|
||||||
&s.UniqueModels, &s.UniqueVendors, &s.UniqueServices)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if s.TotalCalls > 0 {
|
|
||||||
s.ErrorRate = float64(s.ErrorCount) / float64(s.TotalCalls)
|
|
||||||
}
|
|
||||||
// Cache hit rate
|
|
||||||
var cachedCount int
|
|
||||||
cq := `SELECT COUNT(*) FILTER (WHERE cached = true) FROM ai_calls` + where
|
|
||||||
if err := db.QueryRow(cq, args...).Scan(&cachedCount); err == nil && s.TotalCalls > 0 {
|
|
||||||
s.CacheHitRate = float64(cachedCount) / float64(s.TotalCalls)
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetModelStats returns per-model statistics
|
|
||||||
func GetModelStats(db *sql.DB, filter AICallFilter) ([]AIModelStats, error) {
|
|
||||||
where, args := buildWhereClause(filter, 1)
|
|
||||||
|
|
||||||
q := `SELECT vendor, model, COUNT(*),
|
|
||||||
COALESCE(SUM(tokens_in + tokens_out),0),
|
|
||||||
COALESCE(SUM(estimated_cost),0),
|
|
||||||
COALESCE(AVG(latency_ms),0),
|
|
||||||
COUNT(*) FILTER (WHERE status = 'error')
|
|
||||||
FROM ai_calls` + where + `
|
|
||||||
GROUP BY vendor, model ORDER BY SUM(estimated_cost) DESC`
|
|
||||||
|
|
||||||
rows, err := db.Query(q, args...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var stats []AIModelStats
|
|
||||||
for rows.Next() {
|
|
||||||
var s AIModelStats
|
|
||||||
if err := rows.Scan(&s.Vendor, &s.Model, &s.TotalCalls,
|
|
||||||
&s.TotalTokens, &s.TotalCost, &s.AvgLatencyMs, &s.ErrorCount); err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if s.TotalCalls > 0 {
|
|
||||||
s.ErrorRate = float64(s.ErrorCount) / float64(s.TotalCalls)
|
|
||||||
}
|
|
||||||
stats = append(stats, s)
|
|
||||||
}
|
|
||||||
return stats, rows.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetVendorStats returns per-vendor statistics
|
|
||||||
func GetVendorStats(db *sql.DB, filter AICallFilter) ([]AIVendorStats, error) {
|
|
||||||
where, args := buildWhereClause(filter, 1)
|
|
||||||
|
|
||||||
q := `SELECT vendor, COUNT(*),
|
|
||||||
COALESCE(SUM(tokens_in + tokens_out),0),
|
|
||||||
COALESCE(SUM(estimated_cost),0),
|
|
||||||
COALESCE(AVG(latency_ms),0),
|
|
||||||
COUNT(DISTINCT model),
|
|
||||||
COUNT(*) FILTER (WHERE status = 'error')
|
|
||||||
FROM ai_calls` + where + `
|
|
||||||
GROUP BY vendor ORDER BY SUM(estimated_cost) DESC`
|
|
||||||
|
|
||||||
rows, err := db.Query(q, args...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var stats []AIVendorStats
|
|
||||||
for rows.Next() {
|
|
||||||
var s AIVendorStats
|
|
||||||
if err := rows.Scan(&s.Vendor, &s.TotalCalls, &s.TotalTokens,
|
|
||||||
&s.TotalCost, &s.AvgLatencyMs, &s.ModelCount, &s.ErrorCount); err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if s.TotalCalls > 0 {
|
|
||||||
s.ErrorRate = float64(s.ErrorCount) / float64(s.TotalCalls)
|
|
||||||
}
|
|
||||||
stats = append(stats, s)
|
|
||||||
}
|
|
||||||
return stats, rows.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCostTimeseries returns cost aggregated over time intervals
|
|
||||||
func GetCostTimeseries(db *sql.DB, filter AICallFilter, interval string) ([]TimeseriesPoint, error) {
|
|
||||||
// Validate interval
|
|
||||||
validIntervals := map[string]bool{"1h": true, "6h": true, "1d": true, "7d": true, "1m": true}
|
|
||||||
if !validIntervals[interval] {
|
|
||||||
interval = "1d"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map to PostgreSQL interval
|
|
||||||
pgInterval := map[string]string{
|
|
||||||
"1h": "1 hour", "6h": "6 hours", "1d": "1 day", "7d": "7 days", "1m": "1 month",
|
|
||||||
}[interval]
|
|
||||||
|
|
||||||
where, args := buildWhereClause(filter, 1)
|
|
||||||
|
|
||||||
q := fmt.Sprintf(`SELECT date_trunc('hour', timestamp) -
|
|
||||||
(EXTRACT(EPOCH FROM date_trunc('hour', timestamp))::int %%%% EXTRACT(EPOCH FROM interval '%s')::int) * interval '1 second' AS bucket,
|
|
||||||
COALESCE(SUM(estimated_cost),0),
|
|
||||||
COUNT(*)
|
|
||||||
FROM ai_calls%s
|
|
||||||
GROUP BY bucket ORDER BY bucket ASC`, pgInterval, where)
|
|
||||||
|
|
||||||
rows, err := db.Query(q, args...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var points []TimeseriesPoint
|
|
||||||
for rows.Next() {
|
|
||||||
var p TimeseriesPoint
|
|
||||||
if err := rows.Scan(&p.Timestamp, &p.Value, &p.Count); err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
points = append(points, p)
|
|
||||||
}
|
|
||||||
return points, rows.Err()
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
package aiapm
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// AICallRecord represents a single AI/LLM API call
|
|
||||||
type AICallRecord struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Timestamp time.Time `json:"timestamp"`
|
|
||||||
ServiceName string `json:"service_name"`
|
|
||||||
ProjectID string `json:"project_id"`
|
|
||||||
Vendor string `json:"vendor"`
|
|
||||||
Model string `json:"model"`
|
|
||||||
TokensIn int `json:"tokens_in"`
|
|
||||||
TokensOut int `json:"tokens_out"`
|
|
||||||
TokensCacheRead int `json:"tokens_cache_read"`
|
|
||||||
TokensCacheWrite int `json:"tokens_cache_write"`
|
|
||||||
EstimatedCost float64 `json:"estimated_cost"`
|
|
||||||
LatencyMs int `json:"latency_ms"`
|
|
||||||
TTFBMs int `json:"ttfb_ms"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
ErrorMessage string `json:"error_message,omitempty"`
|
|
||||||
Stream bool `json:"stream"`
|
|
||||||
Cached bool `json:"cached"`
|
|
||||||
Tags map[string]string `json:"tags,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AICallFilter defines query filters for AI call records
|
|
||||||
type AICallFilter struct {
|
|
||||||
ServiceName string `json:"service_name"`
|
|
||||||
ProjectID string `json:"project_id"`
|
|
||||||
Vendor string `json:"vendor"`
|
|
||||||
Model string `json:"model"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
From time.Time `json:"from"`
|
|
||||||
To time.Time `json:"to"`
|
|
||||||
Limit int `json:"limit"`
|
|
||||||
Offset int `json:"offset"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AIUsageSummary aggregated usage statistics
|
|
||||||
type AIUsageSummary struct {
|
|
||||||
TotalCalls int `json:"total_calls"`
|
|
||||||
TotalTokensIn int64 `json:"total_tokens_in"`
|
|
||||||
TotalTokensOut int64 `json:"total_tokens_out"`
|
|
||||||
TotalCacheRead int64 `json:"total_cache_read"`
|
|
||||||
TotalCacheWrite int64 `json:"total_cache_write"`
|
|
||||||
TotalCost float64 `json:"total_cost"`
|
|
||||||
AvgLatencyMs float64 `json:"avg_latency_ms"`
|
|
||||||
AvgTTFBMs float64 `json:"avg_ttfb_ms"`
|
|
||||||
ErrorCount int `json:"error_count"`
|
|
||||||
ErrorRate float64 `json:"error_rate"`
|
|
||||||
CacheHitRate float64 `json:"cache_hit_rate"`
|
|
||||||
UniqueModels int `json:"unique_models"`
|
|
||||||
UniqueVendors int `json:"unique_vendors"`
|
|
||||||
UniqueServices int `json:"unique_services"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AIModelStats per-model breakdown
|
|
||||||
type AIModelStats struct {
|
|
||||||
Vendor string `json:"vendor"`
|
|
||||||
Model string `json:"model"`
|
|
||||||
TotalCalls int `json:"total_calls"`
|
|
||||||
TotalTokens int64 `json:"total_tokens"`
|
|
||||||
TotalCost float64 `json:"total_cost"`
|
|
||||||
AvgLatencyMs float64 `json:"avg_latency_ms"`
|
|
||||||
ErrorCount int `json:"error_count"`
|
|
||||||
ErrorRate float64 `json:"error_rate"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AIVendorStats per-vendor breakdown
|
|
||||||
type AIVendorStats struct {
|
|
||||||
Vendor string `json:"vendor"`
|
|
||||||
TotalCalls int `json:"total_calls"`
|
|
||||||
TotalTokens int64 `json:"total_tokens"`
|
|
||||||
TotalCost float64 `json:"total_cost"`
|
|
||||||
AvgLatencyMs float64 `json:"avg_latency_ms"`
|
|
||||||
ModelCount int `json:"model_count"`
|
|
||||||
ErrorCount int `json:"error_count"`
|
|
||||||
ErrorRate float64 `json:"error_rate"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AICostBreakdown cost breakdown by dimension
|
|
||||||
type AICostBreakdown struct {
|
|
||||||
Dimension string `json:"dimension"` // vendor, model, service, project
|
|
||||||
Key string `json:"key"`
|
|
||||||
Cost float64 `json:"cost"`
|
|
||||||
Calls int `json:"calls"`
|
|
||||||
Tokens int64 `json:"tokens"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TimeseriesPoint a single point in a time series
|
|
||||||
type TimeseriesPoint struct {
|
|
||||||
Timestamp time.Time `json:"timestamp"`
|
|
||||||
Value float64 `json:"value"`
|
|
||||||
Count int `json:"count"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// IngestRequest payload for the ingest endpoint
|
|
||||||
type IngestRequest struct {
|
|
||||||
Call *AICallRecord `json:"call,omitempty"`
|
|
||||||
Calls []AICallRecord `json:"calls,omitempty"`
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bigtux/ophion/internal/aiapm"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AIAPMHandlers holds dependencies for AI APM route handlers
|
|
||||||
type AIAPMHandlers struct {
|
|
||||||
db *sql.DB
|
|
||||||
collector *aiapm.Collector
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterAIAPMRoutes registers all AI APM routes on the given router
|
|
||||||
func RegisterAIAPMRoutes(api fiber.Router, db *sql.DB) *aiapm.Collector {
|
|
||||||
collector := aiapm.NewCollector(db, 5000)
|
|
||||||
h := &AIAPMHandlers{db: db, collector: collector}
|
|
||||||
|
|
||||||
g := api.Group("/ai-apm")
|
|
||||||
g.Post("/ingest", h.Ingest)
|
|
||||||
g.Get("/summary", h.Summary)
|
|
||||||
g.Get("/models", h.Models)
|
|
||||||
g.Get("/vendors", h.Vendors)
|
|
||||||
g.Get("/costs", h.Costs)
|
|
||||||
g.Get("/calls", h.Calls)
|
|
||||||
g.Get("/pricing", h.Pricing)
|
|
||||||
|
|
||||||
return collector
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ingest receives AI call records (single or batch)
|
|
||||||
func (h *AIAPMHandlers) Ingest(c *fiber.Ctx) error {
|
|
||||||
var req aiapm.IngestRequest
|
|
||||||
if err := c.BodyParser(&req); err != nil {
|
|
||||||
return c.Status(400).JSON(fiber.Map{"error": "invalid request body: " + err.Error()})
|
|
||||||
}
|
|
||||||
|
|
||||||
count := 0
|
|
||||||
if req.Call != nil {
|
|
||||||
h.collector.Collect(*req.Call)
|
|
||||||
count = 1
|
|
||||||
}
|
|
||||||
if len(req.Calls) > 0 {
|
|
||||||
h.collector.CollectBatch(req.Calls)
|
|
||||||
count += len(req.Calls)
|
|
||||||
}
|
|
||||||
|
|
||||||
if count == 0 {
|
|
||||||
return c.Status(400).JSON(fiber.Map{"error": "no call records provided; use 'call' or 'calls' field"})
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(fiber.Map{"status": "accepted", "count": count})
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseFilter extracts common filter parameters from query string
|
|
||||||
func parseFilter(c *fiber.Ctx) aiapm.AICallFilter {
|
|
||||||
f := aiapm.AICallFilter{
|
|
||||||
ServiceName: c.Query("service"),
|
|
||||||
ProjectID: c.Query("project"),
|
|
||||||
Vendor: c.Query("vendor"),
|
|
||||||
Model: c.Query("model"),
|
|
||||||
Status: c.Query("status"),
|
|
||||||
}
|
|
||||||
if from := c.Query("from"); from != "" {
|
|
||||||
if t, err := time.Parse(time.RFC3339, from); err == nil {
|
|
||||||
f.From = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if to := c.Query("to"); to != "" {
|
|
||||||
if t, err := time.Parse(time.RFC3339, to); err == nil {
|
|
||||||
f.To = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if f.From.IsZero() {
|
|
||||||
f.From = time.Now().Add(-24 * time.Hour)
|
|
||||||
}
|
|
||||||
if f.To.IsZero() {
|
|
||||||
f.To = time.Now()
|
|
||||||
}
|
|
||||||
f.Limit = c.QueryInt("limit", 100)
|
|
||||||
f.Offset = c.QueryInt("offset", 0)
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Summary returns aggregated usage statistics
|
|
||||||
func (h *AIAPMHandlers) Summary(c *fiber.Ctx) error {
|
|
||||||
filter := parseFilter(c)
|
|
||||||
summary, err := aiapm.GetUsageSummary(h.db, filter)
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
|
||||||
}
|
|
||||||
return c.JSON(summary)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Models returns per-model statistics
|
|
||||||
func (h *AIAPMHandlers) Models(c *fiber.Ctx) error {
|
|
||||||
filter := parseFilter(c)
|
|
||||||
stats, err := aiapm.GetModelStats(h.db, filter)
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
|
||||||
}
|
|
||||||
return c.JSON(fiber.Map{"models": stats})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vendors returns per-vendor statistics
|
|
||||||
func (h *AIAPMHandlers) Vendors(c *fiber.Ctx) error {
|
|
||||||
filter := parseFilter(c)
|
|
||||||
stats, err := aiapm.GetVendorStats(h.db, filter)
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
|
||||||
}
|
|
||||||
return c.JSON(fiber.Map{"vendors": stats})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Costs returns cost timeseries data
|
|
||||||
func (h *AIAPMHandlers) Costs(c *fiber.Ctx) error {
|
|
||||||
filter := parseFilter(c)
|
|
||||||
interval := c.Query("interval", "1d")
|
|
||||||
points, err := aiapm.GetCostTimeseries(h.db, filter, interval)
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
|
||||||
}
|
|
||||||
return c.JSON(fiber.Map{"timeseries": points, "interval": interval})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calls returns recent AI call records (paginated)
|
|
||||||
func (h *AIAPMHandlers) Calls(c *fiber.Ctx) error {
|
|
||||||
filter := parseFilter(c)
|
|
||||||
calls, err := aiapm.QueryCalls(h.db, filter)
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
|
||||||
}
|
|
||||||
return c.JSON(fiber.Map{"calls": calls, "count": len(calls)})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pricing returns the current pricing table
|
|
||||||
func (h *AIAPMHandlers) Pricing(c *fiber.Ctx) error {
|
|
||||||
return c.JSON(fiber.Map{"pricing": aiapm.GetPricingTable()})
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user