diff --git a/app/actions/runAudit.ts b/app/actions/runAudit.ts index 87b04136..aa4174e4 100644 --- a/app/actions/runAudit.ts +++ b/app/actions/runAudit.ts @@ -47,7 +47,7 @@ async function notifyWorker(auditId: string, pdfEmail?: string) { } } -const ALL_ENGINES: Engine[] = ["rule", "spec", "dns", "links", "vu1nz", "claude", "openai", "gemini", "qwen", "kimi", "deepseek", "perplexity"]; +const ALL_ENGINES: Engine[] = ["rule", "spec", "dns", "links", "vu1nz", "claude", "openai", "gemini", "qwen", "kimi", "deepseek", "perplexity", "fugu"]; function normalizeEngines(input: unknown, signedIn: boolean): Engine[] { if (!signedIn) return ["rule"]; diff --git a/components/charts/engine-trend.tsx b/components/charts/engine-trend.tsx index 744da6df..3508fee6 100644 --- a/components/charts/engine-trend.tsx +++ b/components/charts/engine-trend.tsx @@ -33,6 +33,7 @@ const ENGINE_COLORS: Record = { kimi: "#ec4899", deepseek: "#ef4444", perplexity: "#06b6d4", + fugu: "#eab308", }; export function EngineTrend({ diff --git a/components/engines-panel.tsx b/components/engines-panel.tsx index 09c55ba1..74512a0d 100644 --- a/components/engines-panel.tsx +++ b/components/engines-panel.tsx @@ -6,7 +6,7 @@ import { DEFAULT_PROJECT_ENGINES, ENGINES, selectionCost, type Engine } from "@/ import { runScanForProject } from "@/app/actions/runAudit"; import { updateProjectEngines } from "@/app/actions/projects"; -const ENGINE_ORDER: Engine[] = ["rule", "spec", "dns", "links", "vu1nz", "claude", "openai", "gemini", "perplexity", "qwen", "kimi", "deepseek"]; +const ENGINE_ORDER: Engine[] = ["rule", "spec", "dns", "links", "vu1nz", "claude", "openai", "gemini", "perplexity", "qwen", "kimi", "deepseek", "fugu"]; export function EnginesPanel({ projectId, diff --git a/lib/audit/fugu-engine.ts b/lib/audit/fugu-engine.ts new file mode 100644 index 00000000..0f0c3760 --- /dev/null +++ b/lib/audit/fugu-engine.ts @@ -0,0 +1,18 @@ +import { env } from "../env"; +import { oaCompatAudit } from "./oa-compat-engine"; +import type { ClaudeAuditResult } from "./claude-engine"; + +// Sakana Fugu — an orchestration model that routes each request across a +// swappable pool of frontier LLMs. Speaks the OpenAI-compatible Chat +// Completions API (Sakana also offers a Responses API, but oa-compat is +// chat-completions + streaming JSON, same as every other engine here). +// Base URL + model are env-overridable; defaults match Sakana's console. +export async function fuguAudit(targetUrl: string): Promise { + return oaCompatAudit(targetUrl, { + apiKey: env.fuguApiKey, + baseURL: env.fuguBaseUrl, + model: env.fuguModel, + providerLabel: "Sakana Fugu", + maxOutputTokens: 32_000, + }); +} diff --git a/lib/credits.ts b/lib/credits.ts index 8bc88b4d..790af4de 100644 --- a/lib/credits.ts +++ b/lib/credits.ts @@ -69,7 +69,8 @@ export type Engine = | "kimi" | "gemini" | "deepseek" - | "perplexity"; + | "perplexity" + | "fugu"; export const DEFAULT_PROJECT_ENGINES: Engine[] = ["rule", "dns"]; @@ -166,6 +167,13 @@ export const ENGINES: Record = { blurb: "Web-grounded with live citations. Frames your site the way Perplexity's answer engine would surface it to users.", }, + fugu: { + label: "Sakana Fugu", + cost: SCAN_CREDITS, + available: true, + blurb: + "Sakana AI's orchestration model — routes each request across a swappable pool of frontier LLMs for a frontier-level, vendor-neutral second opinion.", + }, }; export function engineCost(engine: Engine): number { diff --git a/lib/env.ts b/lib/env.ts index bba31717..01ae0494 100644 --- a/lib/env.ts +++ b/lib/env.ts @@ -63,6 +63,12 @@ export const env = { moonshotApiKey: process.env.MOONSHOT_API_KEY ?? "", // Kimi deepseekApiKey: process.env.DEEPSEEK_API_KEY ?? "", perplexityApiKey: process.env.PERPLEXITY_API_KEY ?? "", // Sonar API + // Sakana Fugu — orchestration model, OpenAI-compatible Chat Completions. + // Base URL + model are env-overridable; defaults match Sakana's console + // (https://api.sakana.ai/v1, model "fugu"; "fugu-ultra" is the higher tier). + fuguApiKey: process.env.FUGU_API_KEY ?? "", + fuguBaseUrl: process.env.FUGU_BASE_URL ?? "https://api.sakana.ai/v1", + fuguModel: process.env.FUGU_MODEL ?? "fugu", // DataForSEO — keyword research for Autoblog. Basic-auth credentials. dataforseoLogin: process.env.DATAFORSEO_LOGIN ?? "", dataforseoPassword: process.env.DATAFORSEO_PASSWORD ?? "", diff --git a/worker/index.ts b/worker/index.ts index c09c717f..82c3dd11 100644 --- a/worker/index.ts +++ b/worker/index.ts @@ -13,6 +13,7 @@ import { qwenAudit } from "../lib/audit/qwen-engine"; import { kimiAudit } from "../lib/audit/kimi-engine"; import { deepseekAudit } from "../lib/audit/deepseek-engine"; import { perplexityAudit } from "../lib/audit/perplexity-engine"; +import { fuguAudit } from "../lib/audit/fugu-engine"; import { toMarkdown } from "../lib/audit/markdown"; import { Resend } from "resend"; import { renderPdf, renderPdfFromHtml } from "./pdf"; @@ -346,7 +347,8 @@ async function processJob(job: Job) { | "qwen" | "kimi" | "deepseek" - | "perplexity") ?? "rule"; + | "perplexity" + | "fugu") ?? "rule"; console.log(`[worker] audit ${auditId} engine=${engine}`); let score: number; @@ -373,6 +375,7 @@ async function processJob(job: Job) { kimi: kimiAudit, deepseek: deepseekAudit, perplexity: perplexityAudit, + fugu: fuguAudit, } as const; if (engine === "spec") {