Task #6: Cost-based work fee pricing with BTC oracle
- btc-oracle.ts: CoinGecko BTC/USD fetch (60s cache), usdToSats() helper, fallback to BTC_PRICE_USD_FALLBACK env var (default $100k), 5s abort timeout - pricing.ts: Full rewrite — per-model token rates (Haiku/Sonnet, env-var overridable), DO infra amortisation, originator margin %, estimateInputTokens(), estimateOutputTokens() by request tier, calculateActualCostUsd() for post-work ledger, async calculateWorkFeeSats() → WorkFeeBreakdown - agent.ts: WorkResult now includes inputTokens + outputTokens from Anthropic usage; workModel/evalModel exposed as readonly public; EVAL_MODEL/WORK_MODEL env var support - jobs.ts: Work invoice creation calls pricingService.calculateWorkFeeSats() async; stores estimatedCostUsd/marginPct/btcPriceUsd on job; after executeWork stores actualInputTokens/actualOutputTokens/actualCostUsd; GET response includes pricingBreakdown (awaiting_work_payment) and costLedger (complete) - lib/db/src/schema/jobs.ts: 6 new real/integer columns for cost tracking; schema pushed - openapi.yaml: PricingBreakdown + CostLedger schemas added to JobStatusResponse - replit.md: 17 new env vars documented in Cost-based work fee pricing section
This commit is contained in:
@@ -181,6 +181,36 @@ components:
|
||||
- executing
|
||||
- complete
|
||||
- failed
|
||||
PricingBreakdown:
|
||||
type: object
|
||||
description: Cost breakdown shown with the work invoice (estimations at invoice-creation time)
|
||||
properties:
|
||||
estimatedCostUsd:
|
||||
type: number
|
||||
description: Total estimated cost in USD (token cost + DO infra + margin)
|
||||
marginPct:
|
||||
type: number
|
||||
description: Originator margin percentage applied
|
||||
btcPriceUsd:
|
||||
type: number
|
||||
description: BTC/USD spot price used to convert the invoice to sats
|
||||
CostLedger:
|
||||
type: object
|
||||
description: Actual cost record stored after the job completes
|
||||
properties:
|
||||
actualInputTokens:
|
||||
type: integer
|
||||
actualOutputTokens:
|
||||
type: integer
|
||||
actualCostUsd:
|
||||
type: number
|
||||
description: Raw Anthropic token cost (no infra, no margin)
|
||||
estimatedCostUsd:
|
||||
type: number
|
||||
marginPct:
|
||||
type: number
|
||||
btcPriceUsd:
|
||||
type: number
|
||||
JobStatusResponse:
|
||||
type: object
|
||||
required:
|
||||
@@ -195,10 +225,14 @@ components:
|
||||
$ref: "#/components/schemas/InvoiceInfo"
|
||||
workInvoice:
|
||||
$ref: "#/components/schemas/InvoiceInfo"
|
||||
pricingBreakdown:
|
||||
$ref: "#/components/schemas/PricingBreakdown"
|
||||
reason:
|
||||
type: string
|
||||
result:
|
||||
type: string
|
||||
costLedger:
|
||||
$ref: "#/components/schemas/CostLedger"
|
||||
errorMessage:
|
||||
type: string
|
||||
DemoResponse:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { pgTable, text, timestamp, integer } from "drizzle-orm/pg-core";
|
||||
import { pgTable, text, timestamp, integer, real } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
@@ -25,6 +25,17 @@ export const jobs = pgTable("jobs", {
|
||||
rejectionReason: text("rejection_reason"),
|
||||
result: text("result"),
|
||||
errorMessage: text("error_message"),
|
||||
|
||||
// ── Cost-based pricing (set when work invoice is created) ───────────────
|
||||
estimatedCostUsd: real("estimated_cost_usd"),
|
||||
marginPct: real("margin_pct"),
|
||||
btcPriceUsd: real("btc_price_usd"),
|
||||
|
||||
// ── Actual token usage (set after work executes) ────────────────────────
|
||||
actualInputTokens: integer("actual_input_tokens"),
|
||||
actualOutputTokens: integer("actual_output_tokens"),
|
||||
actualCostUsd: real("actual_cost_usd"),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user