import { Router, type Request, type Response } from "express"; import { pricingService } from "../lib/pricing.js"; import { agentService } from "../lib/agent.js"; import { getBtcPriceUsd, usdToSats } from "../lib/btc-oracle.js"; import { freeTierService } from "../lib/free-tier.js"; import { trustService } from "../lib/trust.js"; const router = Router(); /** * GET /api/estimate?request=[&nostr_token=] * * Returns a pre-flight cost estimate for a request, including free-tier * status for the authenticated identity (if a valid nostr_token is supplied). * * No payment required. Does not create a job. */ router.get("/estimate", async (req: Request, res: Response) => { const requestText = typeof req.query.request === "string" ? req.query.request.trim() : ""; if (!requestText) { res.status(400).json({ error: "Query param 'request' is required" }); return; } try { const { estimatedInputTokens: inputTokens, estimatedOutputTokens: outputTokens, estimatedCostUsd: costUsd } = pricingService.estimateRequestCost(requestText, agentService.workModel); const btcPriceUsd = await getBtcPriceUsd(); const estimatedSats = usdToSats(costUsd, btcPriceUsd); // Optionally resolve Nostr identity from query param or header for free-tier preview const rawToken = (req.headers["x-nostr-token"] as string | undefined) ?? (typeof req.query.nostr_token === "string" ? req.query.nostr_token : undefined); let pubkey: string | null = null; let trustTier = "anonymous"; let freeTierDecision: { serve: string; absorbSats: number; chargeSats: number } | null = null; if (rawToken) { const parsed = trustService.verifyToken(rawToken.trim()); if (parsed) { pubkey = parsed.pubkey; trustTier = await trustService.getTier(pubkey); const decision = await freeTierService.decideDryRun(pubkey, estimatedSats); freeTierDecision = { serve: decision.serve, absorbSats: decision.absorbSats, chargeSats: decision.chargeSats, }; } } const poolStatus = await freeTierService.poolStatus(); res.json({ estimatedSats, estimatedCostUsd: costUsd, btcPriceUsd, tokenEstimate: { inputTokens, outputTokens, model: agentService.workModel, }, identity: { trust_tier: trustTier, ...(pubkey ? { pubkey } : {}), ...(freeTierDecision ? { free_tier: freeTierDecision } : {}), }, pool: { balanceSats: poolStatus.balanceSats, dailyBudgets: poolStatus.budgets, }, }); } catch (err) { res.status(500).json({ error: err instanceof Error ? err.message : "Estimate failed" }); } }); export default router;