277 lines
8.9 KiB
Markdown
277 lines
8.9 KiB
Markdown
**✅ Documento Completo: Integração com Custom Endpoint do OpenCode (Provedores Custom / OpenAI-Compatible)**
|
||
|
||
OpenCode (https://opencode.ai) é um agente de IA para terminal que usa o **Vercel AI SDK** para se comunicar com provedores de LLM. Ele suporta nativamente provedores customizados via o pacote `@ai-sdk/openai-compatible`, que segue o padrão **OpenAI Chat Completions API** (`/v1/chat/completions`).
|
||
|
||
Isso significa que **sua API precisa implementar exatamente o formato OpenAI-compatible** para que o OpenCode consiga conversar com ela diretamente.
|
||
|
||
---
|
||
|
||
### 1. Como Configurar um Provedor Custom no OpenCode
|
||
|
||
Crie ou edite o arquivo de configuração (recomendado: `~/.config/opencode/opencode.json` ou `opencode.json` na raiz do projeto):
|
||
|
||
```json
|
||
{
|
||
"$schema": "https://opencode.ai/config.json",
|
||
"provider": {
|
||
"minha-ia-custom": {
|
||
"npm": "@ai-sdk/openai-compatible",
|
||
"name": "Minha IA Custom (OpenCode)",
|
||
"options": {
|
||
"baseURL": "https://sua-api.com/v1",
|
||
"apiKey": "sk-sua-chave-aqui-ou-{env:MINHA_API_KEY}",
|
||
"headers": {
|
||
"X-Custom-Header": "valor-opcional"
|
||
}
|
||
},
|
||
"models": {
|
||
"meu-modelo-principal": {
|
||
"name": "Meu Modelo Principal",
|
||
"limit": {
|
||
"context": 128000,
|
||
"output": 8192
|
||
}
|
||
},
|
||
"meu-modelo-rapido": {
|
||
"name": "Modelo Rápido",
|
||
"limit": {
|
||
"context": 32000,
|
||
"output": 4096
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"model": "minha-ia-custom/meu-modelo-principal"
|
||
}
|
||
```
|
||
|
||
**Campos importantes:**
|
||
|
||
- `npm`: Sempre use `"@ai-sdk/openai-compatible"` para APIs que expõem `/v1/chat/completions`.
|
||
- `baseURL`: URL base da sua API (deve terminar com `/v1`).
|
||
- `apiKey`: Pode ser valor direto ou `{env:NOME_VARIAVEL}`.
|
||
- `models`: Defina os modelos que sua API expõe. O `limit` ajuda o OpenCode a gerenciar contexto.
|
||
- `model`: Define o modelo padrão no formato `provider_id/model_id`.
|
||
|
||
Depois de salvar, reinicie o OpenCode ou use `/models` para selecionar.
|
||
|
||
**Comando rápido para adicionar chave:**
|
||
|
||
```bash
|
||
opencode /connect
|
||
# Escolha "Other" → digite o provider_id (ex: minha-ia-custom) → cole a API key
|
||
```
|
||
|
||
---
|
||
|
||
### 2. Como o OpenCode Envia Requisições
|
||
|
||
O OpenCode (via AI SDK) faz chamadas **POST** para:
|
||
|
||
```
|
||
POST {baseURL}/chat/completions
|
||
```
|
||
|
||
**Headers enviados:**
|
||
|
||
- `Authorization: Bearer <sua-api-key>`
|
||
- `Content-Type: application/json`
|
||
- Headers customizados que você definir em `options.headers`
|
||
|
||
**Payload típico (exemplo real):**
|
||
|
||
```json
|
||
{
|
||
"model": "meu-modelo-principal",
|
||
"messages": [
|
||
{
|
||
"role": "system",
|
||
"content": "Você é um assistente de código experiente..."
|
||
},
|
||
{
|
||
"role": "user",
|
||
"content": "Crie uma função em Python que..."
|
||
}
|
||
],
|
||
"stream": true,
|
||
"temperature": 0.7,
|
||
"max_tokens": 8192,
|
||
"tools": [
|
||
/* array de tools se o agente estiver usando ferramentas */
|
||
],
|
||
"tool_choice": "auto"
|
||
}
|
||
```
|
||
|
||
**Parâmetros comuns que o OpenCode envia:**
|
||
|
||
- `messages` (array completo do histórico)
|
||
- `stream: true` (quase sempre, para UX em tempo real)
|
||
- `temperature`, `max_tokens`, `top_p`
|
||
- `tools` + `tool_choice` (quando usa agentes com ferramentas/MCP)
|
||
- `response_format` (às vezes)
|
||
- `providerOptions` (opções específicas do provedor – raras no openai-compatible)
|
||
|
||
---
|
||
|
||
### 3. Como o OpenCode Recebe e Processa Respostas
|
||
|
||
#### A. Streaming (recomendado – usado na maioria das interações)
|
||
|
||
Sua API deve responder com `Content-Type: text/event-stream` e eventos SSE:
|
||
|
||
```
|
||
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1712345678,"model":"meu-modelo-principal","choices":[{"index":0,"delta":{"content":"Olá"},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-123","choices":[{"index":0,"delta":{"content":"! Como"},"finish_reason":null}]}
|
||
|
||
data: [DONE]
|
||
```
|
||
|
||
O OpenCode processa cada chunk em tempo real.
|
||
|
||
#### B. Resposta não-streaming (completa)
|
||
|
||
```json
|
||
{
|
||
"id": "chatcmpl-123",
|
||
"object": "chat.completion",
|
||
"created": 1712345678,
|
||
"model": "meu-modelo-principal",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "Resposta completa aqui..."
|
||
},
|
||
"finish_reason": "stop"
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 150,
|
||
"completion_tokens": 320,
|
||
"total_tokens": 470
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Suporte a Tool Calling (essencial para agentes)
|
||
|
||
Se sua API suportar `tools`, o OpenCode pode:
|
||
|
||
1. Enviar `tools` na requisição.
|
||
2. Receber `tool_calls` na resposta.
|
||
3. Executar a ferramenta (via MCP ou built-in).
|
||
4. Enviar o resultado de volta como mensagem `tool`.
|
||
|
||
**Exemplo de resposta com tool call:**
|
||
|
||
```json
|
||
{
|
||
"choices": [
|
||
{
|
||
"message": {
|
||
"role": "assistant",
|
||
"tool_calls": [
|
||
{
|
||
"id": "call_123",
|
||
"type": "function",
|
||
"function": {
|
||
"name": "read_file",
|
||
"arguments": "{\"path\": \"src/app.ts\"}"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 4. Como é Exibido no Chat do OpenCode (TUI)
|
||
|
||
- **Streaming**: Texto aparece caractere por caractere com efeito de digitação.
|
||
- **Markdown**: Suporte completo a código (syntax highlight), listas, tabelas, etc.
|
||
- **Tool Calls**: Mostra “Executando ferramenta: read_file...” + resultado.
|
||
- **Multi-turn**: Mantém histórico completo na conversa.
|
||
- **Erros**: Mostra mensagens de erro claras no terminal.
|
||
- **Contexto**: Respeita os limites de `context` e `output` que você definiu no config.
|
||
|
||
O OpenCode usa o TUI (Terminal User Interface) baseado em React/Ink para renderizar tudo de forma bonita.
|
||
|
||
---
|
||
|
||
### 5. Requisitos Mínimos da Sua API para Funcionar Perfeitamente
|
||
|
||
| Recurso | Obrigatório? | Recomendado? | Observação |
|
||
| ---------------------- | ------------ | ------------ | ---------------------------------- |
|
||
| `/v1/chat/completions` | Sim | - | Endpoint principal |
|
||
| Streaming (SSE) | Sim | - | Essencial para UX |
|
||
| Tool Calling | Não | Sim | Para agentes completos |
|
||
| System messages | Sim | - | - |
|
||
| Multi-turn history | Sim | - | - |
|
||
| Usage stats | Não | Sim | Mostra tokens gastos |
|
||
| Error handling | Sim | - | Retorne JSON de erro padrão OpenAI |
|
||
|
||
**Endpoint exato esperado:**
|
||
`POST https://sua-api.com/v1/chat/completions`
|
||
|
||
---
|
||
|
||
### 6. Exemplo Completo de Configuração + Teste
|
||
|
||
1. Configure o `opencode.json` como acima.
|
||
2. Rode:
|
||
```bash
|
||
opencode
|
||
```
|
||
3. Digite algo ou use `/models` para selecionar seu provedor.
|
||
4. Teste com:
|
||
```bash
|
||
curl -X POST https://sua-api.com/v1/chat/completions \
|
||
-H "Authorization: Bearer sk-..." \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"model":"meu-modelo-principal","messages":[{"role":"user","content":"Olá"}],"stream":true}'
|
||
```
|
||
|
||
---
|
||
|
||
### 7. Troubleshooting Comum
|
||
|
||
- **“Route not found” ou requests indo para lugar errado** → Verifique se `baseURL` termina com `/v1` e não com `/v1/chat/completions`.
|
||
- **Custom provider não aparece** → Certifique-se que o ID do provider no `/connect` bate com a chave no `opencode.json`.
|
||
- **Sem streaming / resposta lenta** → Implemente SSE corretamente.
|
||
- **Tool calls não funcionam** → Sua API precisa retornar `tool_calls` no formato OpenAI exato.
|
||
- **Erro de autenticação** → Use `apiKey` ou `headers` corretamente.
|
||
|
||
---
|
||
|
||
### Referências Oficiais
|
||
|
||
- **Docs Principais**: https://opencode.ai/docs/providers/
|
||
- **Models**: https://opencode.ai/docs/models/
|
||
- **Config**: https://opencode.ai/docs/config/
|
||
- **Schema de Config**: https://opencode.ai/config.json
|
||
- **GitHub**: https://github.com/anomalyco/opencode
|
||
- **AI SDK OpenAI-Compatible**: https://ai-sdk.dev/providers/openai-compatible-providers
|
||
|
||
---
|
||
|
||
**Resumo Final para Você (que está construindo a API):**
|
||
|
||
Sua API **precisa ser 100% compatível** com o endpoint OpenAI `/v1/chat/completions` (streaming + tool calling recomendado).
|
||
O OpenCode vai:
|
||
|
||
1. Configurar via `opencode.json` (baseURL + apiKey)
|
||
2. Enviar requisições padrão OpenAI
|
||
3. Receber streaming ou resposta completa
|
||
4. Renderizar tudo lindamente no TUI com suporte a ferramentas
|
||
|
||
Se você implementar exatamente o padrão OpenAI Chat Completions, vai funcionar perfeitamente com o OpenCode.
|
||
|
||
Quer que eu monte um **exemplo de servidor mínimo** (em Node.js/Fastify ou Python/FastAPI) que já funcione com o OpenCode? Posso criar um template completo para você.
|