diff --git a/.env.example b/.env.example index ee78a22a..f16a2ea4 100644 --- a/.env.example +++ b/.env.example @@ -36,6 +36,11 @@ CAMPAIGN_EMAIL_FROM_NAME=Campanha Jorge Solla # Note: the automated test suite ignores this file and reads .env.test instead, # and only ever runs against a database whose name ends with "_test". -# Deep Infra API key for the AI chat feature (Sollinha) — same provider/key the -# agents use (one provider, one key, one invoice). Get one at https://deepinfra.com +# DeepSeek API key for the AI chat feature (Sollinha) — the LLM talks directly +# to api.deepseek.com (cache-hit input is ~6x cheaper than via Deep Infra). +# Get one at https://platform.deepseek.com/api_keys +DEEPSEEK_API_KEY=YOUR_DEEPSEEK_API_KEY_HERE + +# Deep Infra API key for the Sollinha voice transcription (STT, Whisper large-v3) +# — cheapest speech-to-text around ($0.00045/audio min). Get one at https://deepinfra.com DEEPINFRA_API_KEY=YOUR_DEEPINFRA_API_KEY_HERE diff --git a/docs/CHANGELOG-AGENTS.md b/docs/CHANGELOG-AGENTS.md index 4df6990e..f07b8b48 100644 --- a/docs/CHANGELOG-AGENTS.md +++ b/docs/CHANGELOG-AGENTS.md @@ -4,6 +4,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente --- +**Recently resolved (2026-08-09):** **B174 reverso** (sem Issue — pedido direto) — a Sollinha (`/campanha/api/ai-chat`) voltou do `@ai-sdk/deepinfra` para o **`@ai-sdk/deepseek`** (API oficial `api.deepseek.com`): o cache-hit do V4-Flash direto custa **$0.0028/M** contra **$0.018/M** na Deep Infra (~6,4× mais barato), e a Sollinha tem prefixo estável (system prompt + tools + histórico) — o hit ratio alto derruba o custo efetivo mesmo com miss ($0.14 vs $0.09) e output ($0.28 vs $0.18) mais caros. Mesmo modelo (`deepseek-v4-flash`), mesmas tools; `@ai-sdk/deepseek` **pinado em `3.0.19`** (não `^3.0.19`, que flutuaria a 3.0.26 e puxaria a ferroviária `provider@4.0.7` duplicada — o lock ficou na `4.0.4/5.0.18` do `ai@7.0.47`, verificado no lockfile). **STT fica na Deep Infra** (B173): `whisper-large-v3` a $0.00045/min de áudio é o mais barato verificado (Groq $0.0019, OpenAI $0.006); `.env.example` agora documenta **as duas chaves** — `DEEPSEEK_API_KEY` (LLM) e `DEEPINFRA_API_KEY` (STT). Aviso da DeepSeek: aumento de preço anunciado ("em breve"). Sem migration, access, UI ou teste novo; smoke de deploy = 2–3 perguntas forçando tool calling real antes de confiar. + **Recently resolved (2026-08-09):** **OPS20** (Issue #464) — o e2e de cada worktree agora usa a porta do slot próprio (`3100+slot`), nunca o fallback `3000`: o `.env.test.local` (que o `playwright.config.ts` lê em `playwright.config.ts:14`) passou a carregar `PLAYWRIGHT_BASE_URL` — antes ele só ia para o `.env.local` (que o config **não** lê), então dois `pnpm test:e2e` em worktrees paralelos subiam dois dev servers na porta `3000`. Causa-raiz tratada além do sintoma: os dois blocos env inline do `provision` (que já tinham derivado uma vez) viraram um builder puro único `worktreeEnvFileContents` em `scripts/lib/worktree-env.mjs` que deriva `NEXT_PUBLIC_SITE_URL` + `PLAYWRIGHT_BASE_URL` do mesmo `env.devPort` para **ambos** `.env.local`/`.env.test.local` (paridade estrutural, drift impossível) + unit tests de paridade em `tests/unit/worktree.unit.spec.ts` (falham se o drift voltar). `playwright.config.ts` intocado (a variável certa já existia; só faltava chegar). Sem Docker (fallback) continua degradando ao compartilhado — fora de escopo. Sem migration. Intenção [`porta-e2e-isolada-por-worktree.md`](plans/porta-e2e-isolada-por-worktree.md); impl [`porta-e2e-isolada-por-worktree-impl.md`](plans/porta-e2e-isolada-por-worktree-impl.md). **Recently resolved (2026-08-08):** **B174** (Issue #449) — a Sollinha (`/campanha/api/ai-chat`) trocou o provider de `@ai-sdk/deepseek` (API oficial `api.deepseek.com`) pelo **`@ai-sdk/deepinfra`** no caminho OpenAI-compatível da Deep Infra — **mesmo modelo** (`deepseek-ai/DeepSeek-V4-Flash`), **mesmas tools** e **mesma chave** que os agentes do time usam (um provedor, uma chave, uma fatura). Swap de 2 linhas no `route.ts`; dep `@ai-sdk/deepinfra@3.0.20` pinada na mesma ferroviária do `ai@7.0.47` (provider 4.0.4 / provider-utils 5.0.18, `pnpm why` confirma cópia única — nada de 4.0.7 duplicado), `@ai-sdk/deepseek` removido (dead dep, knip); `.env.example` `DEEPSEEK_API_KEY` → `DEEPINFRA_API_KEY` (B173 STT/Whisper reusa a mesma chave). Sem migration, access, UI ou teste novo — e2e já mocka o SSE (sem chave real no CI); rollback documentado = `git revert` (restaura provider/baseURL antiga); smoke de deploy = 2–3 perguntas forçando tool calling real antes de confiar o flip. Intenção [`sollinha-no-deepinfra.md`](plans/sollinha-no-deepinfra.md); impl [`sollinha-no-deepinfra-impl.md`](plans/sollinha-no-deepinfra-impl.md). diff --git a/package.json b/package.json index 58c31813..86e060ad 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "issue": "node scripts/issue.mjs" }, "dependencies": { - "@ai-sdk/deepinfra": "3.0.20", + "@ai-sdk/deepseek": "3.0.19", "@ai-sdk/react": "^4.0.50", "@base-ui/react": "^1.6.0", "@fullcalendar/react": "7.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 59aad99d..88b22131 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,9 @@ importers: .: dependencies: - '@ai-sdk/deepinfra': - specifier: 3.0.20 - version: 3.0.20(zod@4.3.6) + '@ai-sdk/deepseek': + specifier: 3.0.19 + version: 3.0.19(zod@4.3.6) '@ai-sdk/react': specifier: ^4.0.50 version: 4.0.50(react@19.2.4)(zod@4.3.6) @@ -288,8 +288,8 @@ packages: '@acemir/cssom@0.9.31': resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} - '@ai-sdk/deepinfra@3.0.20': - resolution: {integrity: sha512-LqBoRPuSiU9R8hJF/q3/ujDnK5niDcQfbbSHoUrJNmSKOLT0Gwi6sL6l2XxxoJXLlt+1MtxDuZvO9GVbvmODLA==} + '@ai-sdk/deepseek@3.0.19': + resolution: {integrity: sha512-APzL/IpDSLhVlKQXo1gKCvTDLeli98ITzKnMq/eUNVWZ/UzWKYYG/0mCsmJRwTC37YE0ZzpxwDBw3J8xQT3lxg==} engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -306,12 +306,6 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/openai-compatible@3.0.20': - resolution: {integrity: sha512-RsPg+HilsKc/d+jy4zItG7v7RJQm+caUu+iU0a5bx3eyyX5tkayMGDRklXMMMAPa4l34luzQwT3t4cmeHfdiWw==} - engines: {node: '>=22'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider-utils@5.0.18': resolution: {integrity: sha512-UBNCrkxS5llgN2/RXLBRkjCFTIuL6YB1Goq5c+yRecnznhtX4XPjTwLHz2hrsTltTVZ0rqVegtnwFXdPBkjDHA==} engines: {node: '>=22'} @@ -7783,9 +7777,8 @@ snapshots: '@acemir/cssom@0.9.31': {} - '@ai-sdk/deepinfra@3.0.20(zod@4.3.6)': + '@ai-sdk/deepseek@3.0.19(zod@4.3.6)': dependencies: - '@ai-sdk/openai-compatible': 3.0.20(zod@4.3.6) '@ai-sdk/provider': 4.0.4 '@ai-sdk/provider-utils': 5.0.18(zod@4.3.6) zod: 4.3.6 @@ -7804,12 +7797,6 @@ snapshots: pkce-challenge: 5.0.1 zod: 4.3.6 - '@ai-sdk/openai-compatible@3.0.20(zod@4.3.6)': - dependencies: - '@ai-sdk/provider': 4.0.4 - '@ai-sdk/provider-utils': 5.0.18(zod@4.3.6) - zod: 4.3.6 - '@ai-sdk/provider-utils@5.0.18(zod@4.3.6)': dependencies: '@ai-sdk/provider': 4.0.4 diff --git a/src/app/(campaign)/campanha/api/ai-chat/route.ts b/src/app/(campaign)/campanha/api/ai-chat/route.ts index 8513ec55..ccb88f52 100644 --- a/src/app/(campaign)/campanha/api/ai-chat/route.ts +++ b/src/app/(campaign)/campanha/api/ai-chat/route.ts @@ -1,4 +1,4 @@ -import { deepInfra } from '@ai-sdk/deepinfra' +import { deepSeek } from '@ai-sdk/deepseek' import configPromise from '@payload-config' import { convertToModelMessages, @@ -38,7 +38,7 @@ export async function POST(req: Request) { const { messages } = await req.json() const result = streamText({ - model: deepInfra('deepseek-ai/DeepSeek-V4-Flash'), + model: deepSeek('deepseek-v4-flash'), system: AI_SYSTEM_PROMPT, messages: await convertToModelMessages(messages), tools: buildAITools({ user, payload }), diff --git a/src/utilities/ai/deepInfraTranscribe.ts b/src/utilities/ai/deepInfraTranscribe.ts index f2126d6b..6946b3b8 100644 --- a/src/utilities/ai/deepInfraTranscribe.ts +++ b/src/utilities/ai/deepInfraTranscribe.ts @@ -2,8 +2,10 @@ import 'server-only' /** * Transcribes a recorded voice clip to text via Deep Infra's OpenAI-compatible - * transcription endpoint (B173). Same provider/key as the Sollinha LLM - * (`DEEPINFRA_API_KEY`), so one account, one key, one invoice. + * transcription endpoint (B173). Deep Infra serves Whisper large-v3 at + * $0.00045/audio minute — the cheapest speech-to-text around — so it stays + * here even though the Sollinha LLM talks to api.deepseek.com directly + * (`DEEPSEEK_API_KEY`); this path uses its own `DEEPINFRA_API_KEY`. * * The audio is forwarded as a multipart `file` and never persisted anywhere — * it lives only in this function's memory for the duration of the provider