"use client"; import { useState } from "react"; import { PROVIDERS, type Provider } from "../lib/askProvider"; import type { AgentMode } from "../lib/searchAgent"; const MODES: { value: AgentMode; label: string }[] = [ { value: "auto", label: "Auto" }, { value: "native", label: "Native tools" }, { value: "scripted", label: "Scripted" }, ]; type Props = { provider: Provider; setProvider: (p: Provider) => void; apiKey: string; setApiKey: (v: string) => void; model: string; setModel: (v: string) => void; remember: boolean; setRemember: (v: boolean) => void; showKey: boolean; setShowKey: (v: boolean) => void; searchMode: AgentMode; setSearchMode: (m: AgentMode) => void; persistKey: (p: Provider, key: string, mdl: string, rememberOn: boolean) => void; maxAnswerTokens: number; setMaxAnswerTokens: (n: number) => void; sweepBatch: number; setSweepBatch: (n: number) => void; // Requests-per-minute pacing for the current provider+model. rpmOverride: number | null; rpmDefault: number; effectiveRpm: number; setRpm: (n: number | null) => void; buildDebugJson: () => string; }; export function ProviderSettings(props: Props) { const { provider, setProvider, apiKey, setApiKey, model, setModel, remember, setRemember, showKey, setShowKey, searchMode, setSearchMode, persistKey, maxAnswerTokens, setMaxAnswerTokens, sweepBatch, setSweepBatch, rpmOverride, rpmDefault, effectiveRpm, setRpm, buildDebugJson, } = props; const info = PROVIDERS[provider]; // Derived spacing between requests at the effective RPM (e.g. 5 RPM → ≈12s). const spacingSec = effectiveRpm > 0 ? 60 / effectiveRpm : 0; const spacingLabel = spacingSec >= 1 ? `${Math.round(spacingSec)}s` : `${Math.max(1, Math.round(spacingSec * 1000))}ms`; // Seed the panel open when there's no key yet, then hand control to the user. // Must NOT be recomputed from `apiKey` on every render — otherwise typing the // first character of the key flips it closed and yanks the panel away mid-entry. const [open, setOpen] = useState(!apiKey); const [debugCopied, setDebugCopied] = useState(false); const downloadDebug = () => { const blob = new Blob([buildDebugJson()], { type: "application/json" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `ask-debug-${Date.now()}.json`; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); }; const copyDebug = async () => { try { if (!navigator.clipboard) throw new Error("clipboard unavailable"); await navigator.clipboard.writeText(buildDebugJson()); setDebugCopied(true); setTimeout(() => setDebugCopied(false), 1500); } catch { /* clipboard blocked — the Download button still works */ } }; return (
setOpen((e.currentTarget as HTMLDetailsElement).open)} > {apiKey ? `${info.label} · key set` : "Set up your AI provider"}
{/* Search mode: how the model drives multi-step retrieval. */}
Search mode
{MODES.map((m) => ( ))}
Auto uses your model’s native tool-calling when supported and falls back to a text protocol otherwise. Switch to Scripted if searches misbehave.
{/* Everything below is a knob, not a setup step: a first-time reader needs provider, model and key, and nothing else. */}
Advanced
{/* Answer length: raise it so long reports don't truncate at the cap. */} {/* Sweep batch size: how many result videos each corpus-sweep batch folds into the report. Smaller → more, lighter batches (kinder to rate limits + easier to pause/resume); bigger → fewer, heavier passes. */} {/* Requests per minute: paces every AI call so a free-tier key stays under its per-minute cap instead of erroring. Defaults are conservative per-model guesses (shown as the placeholder); set a higher value for a paid key. Auto-lowers itself after a rate-limit (429). */} {/* Debug export: a redacted snapshot for troubleshooting. */}
Debug
A redacted snapshot of this chat plus the most recent API calls (finish reasons + payloads) for troubleshooting. Your API key is never included.

Your key is stored only{" "} {remember ? "in this browser" : "for this page session"} and is sent directly to {info.label} — never to this site. Requests are billed to your own account.{" "} Get a key →

); }