DB schema
- jobs and invoices tables in lib/db/src/schema/
- schema barrel exports jobs, invoices, conversations, messages
- Schema pushed successfully
LNbitsService (artifacts/api-server/src/lib/lnbits.ts)
- Injectable class: constructor accepts optional { url, apiKey } config
- Falls back to LNBITS_URL / LNBITS_API_KEY env vars
- Auto-detects stub mode when credentials absent; logs clear warning
- createInvoice(amountSats, memo) -> { paymentHash, paymentRequest }
- checkInvoicePaid(paymentHash) -> boolean
- stubMarkPaid(hash) helper for dev flows (guarded to stub mode only)
- Real LNbits REST v1 calls wired behind stub guard
AgentService (artifacts/api-server/src/lib/agent.ts)
- Injectable class with configurable evalModel/workModel
- evaluateRequest(text) -> { accepted: boolean, reason: string }
uses claude-haiku-4-5; strips markdown fences before JSON parse
- executeWork(text) -> { result: string } uses claude-sonnet-4-6
- No @anthropic-ai/sdk import; types inferred from SDK response union
- Wired via Replit Anthropic AI Integration
PricingService (artifacts/api-server/src/lib/pricing.ts)
- Injectable class with configurable fee/bucket thresholds
- calculateEvalFeeSats() -> 10 sats (fixed)
- calculateWorkFeeSats(text) -> 50/100/250 by char-length bucket
- Fully deterministic, no LLM
Smoke test (artifacts/api-server/src/smoke.ts)
- pnpm --filter @workspace/api-server run smoke
- LNbits: create invoice, check unpaid, mark paid, check paid — all pass
- Anthropic: evaluateRequest round-trip — passes
replit.md: documented LNBITS_URL, LNBITS_API_KEY and auto-provisioned secrets
72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
import { anthropic } from "@workspace/integrations-anthropic-ai";
|
|
|
|
export interface EvalResult {
|
|
accepted: boolean;
|
|
reason: string;
|
|
}
|
|
|
|
export interface WorkResult {
|
|
result: string;
|
|
}
|
|
|
|
export interface AgentConfig {
|
|
evalModel?: string;
|
|
workModel?: string;
|
|
}
|
|
|
|
export class AgentService {
|
|
private readonly evalModel: string;
|
|
private readonly workModel: string;
|
|
|
|
constructor(config?: AgentConfig) {
|
|
this.evalModel = config?.evalModel ?? "claude-haiku-4-5";
|
|
this.workModel = config?.workModel ?? "claude-sonnet-4-6";
|
|
}
|
|
|
|
async evaluateRequest(requestText: string): Promise<EvalResult> {
|
|
const message = await anthropic.messages.create({
|
|
model: this.evalModel,
|
|
max_tokens: 8192,
|
|
system: `You are Timmy, an AI agent gatekeeper. Evaluate whether a request is acceptable to act on.
|
|
ACCEPT if the request is: clear enough to act on, ethical, lawful, and within the capability of a general-purpose AI.
|
|
REJECT if the request is: harmful, illegal, unethical, incoherent, or spam.
|
|
Respond ONLY with valid JSON: {"accepted": true, "reason": "..."} or {"accepted": false, "reason": "..."}`,
|
|
messages: [{ role: "user", content: `Evaluate this request: ${requestText}` }],
|
|
});
|
|
|
|
const block = message.content[0];
|
|
if (block.type !== "text") {
|
|
throw new Error("Unexpected non-text response from eval model");
|
|
}
|
|
|
|
let parsed: { accepted: boolean; reason: string };
|
|
try {
|
|
const raw = block.text.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
parsed = JSON.parse(raw) as { accepted: boolean; reason: string };
|
|
} catch {
|
|
throw new Error(`Failed to parse eval JSON: ${block.text}`);
|
|
}
|
|
|
|
return { accepted: Boolean(parsed.accepted), reason: parsed.reason ?? "" };
|
|
}
|
|
|
|
async executeWork(requestText: string): Promise<WorkResult> {
|
|
const message = await anthropic.messages.create({
|
|
model: this.workModel,
|
|
max_tokens: 8192,
|
|
system: `You are Timmy, a capable AI agent. A user has paid for you to handle their request.
|
|
Fulfill it thoroughly and helpfully. Be concise yet complete.`,
|
|
messages: [{ role: "user", content: requestText }],
|
|
});
|
|
|
|
const block = message.content[0];
|
|
if (block.type !== "text") {
|
|
throw new Error("Unexpected non-text response from work model");
|
|
}
|
|
|
|
return { result: block.text };
|
|
}
|
|
}
|
|
|
|
export const agentService = new AgentService();
|