feat: Gemini AI integration — conversations, messages, image gen
- Fixed YAML parse error (unquoted colon in description broke @scalar/json-magic) - Converted orval.config.ts → orval.config.cjs (fixes orval v8 TypeScript config loading) - Codegen now works: zod schemas + React Query hooks regenerated with Gemini types - Added Gemini tag, 4 path groups, 8 schemas to openapi.yaml - lib/integrations-gemini-ai wired: tsconfig refs, api-server package.json dep - Created routes/gemini.ts: CRUD conversations/messages + SSE chat stream + image gen - Mounted /gemini router in routes/index.ts
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
"@workspace/api-zod": "workspace:*",
|
||||
"@workspace/db": "workspace:*",
|
||||
"@workspace/integrations-anthropic-ai": "workspace:*",
|
||||
"@workspace/integrations-gemini-ai": "workspace:*",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2",
|
||||
"drizzle-orm": "catalog:",
|
||||
|
||||
186
artifacts/api-server/src/routes/gemini.ts
Normal file
186
artifacts/api-server/src/routes/gemini.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
import { Router, type Request, type Response } from "express";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db, conversations, messages } from "@workspace/db";
|
||||
import { ai, generateImage } from "@workspace/integrations-gemini-ai";
|
||||
import { makeLogger } from "../lib/logger.js";
|
||||
|
||||
const router = Router();
|
||||
const logger = makeLogger("gemini");
|
||||
|
||||
const DEFAULT_MODEL = "gemini-2.5-flash";
|
||||
|
||||
router.get("/conversations", async (_req: Request, res: Response) => {
|
||||
try {
|
||||
const rows = await db.select().from(conversations).orderBy(conversations.createdAt);
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
logger.error("list conversations error", { error: err });
|
||||
res.status(500).json({ error: "Failed to list conversations" });
|
||||
}
|
||||
});
|
||||
|
||||
router.post("/conversations", async (req: Request, res: Response) => {
|
||||
const { title } = req.body ?? {};
|
||||
if (typeof title !== "string" || !title.trim()) {
|
||||
res.status(400).json({ error: "title is required" });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const [row] = await db.insert(conversations).values({ title: title.trim() }).returning();
|
||||
res.status(201).json(row);
|
||||
} catch (err) {
|
||||
logger.error("create conversation error", { error: err });
|
||||
res.status(500).json({ error: "Failed to create conversation" });
|
||||
}
|
||||
});
|
||||
|
||||
router.get("/conversations/:id", async (req: Request, res: Response) => {
|
||||
const id = parseInt(req.params.id ?? "", 10);
|
||||
if (isNaN(id)) {
|
||||
res.status(400).json({ error: "Invalid id" });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const [conv] = await db.select().from(conversations).where(eq(conversations.id, id));
|
||||
if (!conv) {
|
||||
res.status(404).json({ error: "Conversation not found" });
|
||||
return;
|
||||
}
|
||||
const msgs = await db
|
||||
.select()
|
||||
.from(messages)
|
||||
.where(eq(messages.conversationId, id))
|
||||
.orderBy(messages.createdAt);
|
||||
res.json({ ...conv, messages: msgs });
|
||||
} catch (err) {
|
||||
logger.error("get conversation error", { error: err });
|
||||
res.status(500).json({ error: "Failed to get conversation" });
|
||||
}
|
||||
});
|
||||
|
||||
router.delete("/conversations/:id", async (req: Request, res: Response) => {
|
||||
const id = parseInt(req.params.id ?? "", 10);
|
||||
if (isNaN(id)) {
|
||||
res.status(400).json({ error: "Invalid id" });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const [conv] = await db.select().from(conversations).where(eq(conversations.id, id));
|
||||
if (!conv) {
|
||||
res.status(404).json({ error: "Conversation not found" });
|
||||
return;
|
||||
}
|
||||
await db.delete(conversations).where(eq(conversations.id, id));
|
||||
res.status(204).send();
|
||||
} catch (err) {
|
||||
logger.error("delete conversation error", { error: err });
|
||||
res.status(500).json({ error: "Failed to delete conversation" });
|
||||
}
|
||||
});
|
||||
|
||||
router.get("/conversations/:id/messages", async (req: Request, res: Response) => {
|
||||
const id = parseInt(req.params.id ?? "", 10);
|
||||
if (isNaN(id)) {
|
||||
res.status(400).json({ error: "Invalid id" });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const msgs = await db
|
||||
.select()
|
||||
.from(messages)
|
||||
.where(eq(messages.conversationId, id))
|
||||
.orderBy(messages.createdAt);
|
||||
res.json(msgs);
|
||||
} catch (err) {
|
||||
logger.error("list messages error", { error: err });
|
||||
res.status(500).json({ error: "Failed to list messages" });
|
||||
}
|
||||
});
|
||||
|
||||
router.post("/conversations/:id/messages", async (req: Request, res: Response) => {
|
||||
const conversationId = parseInt(req.params.id ?? "", 10);
|
||||
if (isNaN(conversationId)) {
|
||||
res.status(400).json({ error: "Invalid id" });
|
||||
return;
|
||||
}
|
||||
|
||||
const { content, model } = req.body ?? {};
|
||||
if (typeof content !== "string" || !content.trim()) {
|
||||
res.status(400).json({ error: "content is required" });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const [conv] = await db.select().from(conversations).where(eq(conversations.id, conversationId));
|
||||
if (!conv) {
|
||||
res.status(404).json({ error: "Conversation not found" });
|
||||
return;
|
||||
}
|
||||
|
||||
await db.insert(messages).values({ conversationId, role: "user", content: content.trim() });
|
||||
|
||||
const history = await db
|
||||
.select()
|
||||
.from(messages)
|
||||
.where(eq(messages.conversationId, conversationId))
|
||||
.orderBy(messages.createdAt);
|
||||
|
||||
const geminiContents = history.map((m) => ({
|
||||
role: m.role === "assistant" ? "model" : "user",
|
||||
parts: [{ text: m.content }],
|
||||
}));
|
||||
|
||||
res.setHeader("Content-Type", "text/event-stream");
|
||||
res.setHeader("Cache-Control", "no-cache");
|
||||
res.setHeader("Connection", "keep-alive");
|
||||
res.flushHeaders();
|
||||
|
||||
const sendEvent = (data: string) => {
|
||||
res.write(`data: ${data}\n\n`);
|
||||
};
|
||||
|
||||
const stream = await ai.models.generateContentStream({
|
||||
model: model ?? DEFAULT_MODEL,
|
||||
contents: geminiContents,
|
||||
});
|
||||
|
||||
let fullText = "";
|
||||
for await (const chunk of stream) {
|
||||
const text = chunk.text ?? "";
|
||||
if (text) {
|
||||
fullText += text;
|
||||
sendEvent(JSON.stringify({ text }));
|
||||
}
|
||||
}
|
||||
|
||||
sendEvent(JSON.stringify({ done: true }));
|
||||
res.end();
|
||||
|
||||
await db.insert(messages).values({ conversationId, role: "assistant", content: fullText });
|
||||
} catch (err) {
|
||||
logger.error("send message error", { error: err });
|
||||
if (!res.headersSent) {
|
||||
res.status(500).json({ error: "Failed to send message" });
|
||||
} else {
|
||||
res.write(`data: ${JSON.stringify({ error: "Stream error" })}\n\n`);
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
router.post("/generate-image", async (req: Request, res: Response) => {
|
||||
const { prompt } = req.body ?? {};
|
||||
if (typeof prompt !== "string" || !prompt.trim()) {
|
||||
res.status(400).json({ error: "prompt is required" });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const result = await generateImage(prompt.trim());
|
||||
res.json(result);
|
||||
} catch (err) {
|
||||
logger.error("generate image error", { error: err });
|
||||
res.status(500).json({ error: "Failed to generate image" });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -16,6 +16,7 @@ import estimateRouter from "./estimate.js";
|
||||
import relayRouter from "./relay.js";
|
||||
import adminRelayRouter from "./admin-relay.js";
|
||||
import adminRelayQueueRouter from "./admin-relay-queue.js";
|
||||
import geminiRouter from "./gemini.js";
|
||||
|
||||
const router: IRouter = Router();
|
||||
|
||||
@@ -30,6 +31,7 @@ router.use(relayRouter);
|
||||
router.use(adminRelayRouter);
|
||||
router.use(adminRelayQueueRouter);
|
||||
router.use(demoRouter);
|
||||
router.use("/gemini", geminiRouter);
|
||||
router.use(testkitRouter);
|
||||
router.use(uiRouter);
|
||||
router.use(nodeDiagnosticsRouter);
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
},
|
||||
{
|
||||
"path": "../../lib/integrations-anthropic-ai"
|
||||
},
|
||||
{
|
||||
"path": "../../lib/integrations-gemini-ai"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
256
lib/api-client-react/src/generated/api.schemas.ts
Normal file
256
lib/api-client-react/src/generated/api.schemas.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
export interface HealthStatus {
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface ErrorResponse {
|
||||
error: string;
|
||||
}
|
||||
|
||||
export interface InvoiceInfo {
|
||||
paymentRequest: string;
|
||||
amountSats: number;
|
||||
}
|
||||
|
||||
export interface CreateJobRequest {
|
||||
/** @minLength 1 */
|
||||
request: string;
|
||||
}
|
||||
|
||||
export interface CreateJobResponse {
|
||||
jobId: string;
|
||||
/** ISO 8601 timestamp of job creation */
|
||||
createdAt: string;
|
||||
evalInvoice: InvoiceInfo;
|
||||
}
|
||||
|
||||
export type JobState = (typeof JobState)[keyof typeof JobState];
|
||||
|
||||
export const JobState = {
|
||||
awaiting_eval_payment: "awaiting_eval_payment",
|
||||
evaluating: "evaluating",
|
||||
rejected: "rejected",
|
||||
awaiting_work_payment: "awaiting_work_payment",
|
||||
executing: "executing",
|
||||
complete: "complete",
|
||||
failed: "failed",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Cost breakdown shown with the work invoice (estimations at invoice-creation time)
|
||||
*/
|
||||
export interface PricingBreakdown {
|
||||
/** Total estimated cost in USD (token cost + DO infra + margin) */
|
||||
estimatedCostUsd?: number;
|
||||
/** Originator margin percentage applied */
|
||||
marginPct?: number;
|
||||
/** BTC/USD spot price used to convert the invoice to sats */
|
||||
btcPriceUsd?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle of the refund for this job
|
||||
*/
|
||||
export type CostLedgerRefundState =
|
||||
(typeof CostLedgerRefundState)[keyof typeof CostLedgerRefundState];
|
||||
|
||||
export const CostLedgerRefundState = {
|
||||
not_applicable: "not_applicable",
|
||||
pending: "pending",
|
||||
paid: "paid",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Honest post-work accounting stored after the job completes
|
||||
*/
|
||||
export interface CostLedger {
|
||||
actualInputTokens?: number;
|
||||
actualOutputTokens?: number;
|
||||
/** Sum of actualInputTokens + actualOutputTokens */
|
||||
totalTokens?: number;
|
||||
/** Raw Anthropic token cost (no infra, no margin) */
|
||||
actualCostUsd?: number;
|
||||
/** What we honestly charged in USD (actual token cost + DO infra + margin) */
|
||||
actualChargeUsd?: number;
|
||||
/** Original estimate used to create the work invoice */
|
||||
estimatedCostUsd?: number;
|
||||
/** Honest sats charge (actual cost converted at the locked BTC price) */
|
||||
actualAmountSats?: number;
|
||||
/** Amount the user originally paid in sats */
|
||||
workAmountSats?: number;
|
||||
/** Sats owed back to the user (workAmountSats - actualAmountSats, >= 0) */
|
||||
refundAmountSats?: number;
|
||||
/** Lifecycle of the refund for this job */
|
||||
refundState?: CostLedgerRefundState;
|
||||
marginPct?: number;
|
||||
/** BTC/USD price locked at invoice creation time */
|
||||
btcPriceUsd?: number;
|
||||
}
|
||||
|
||||
export interface JobStatusResponse {
|
||||
jobId: string;
|
||||
state: JobState;
|
||||
/** ISO 8601 timestamp of job creation (always present) */
|
||||
createdAt: string;
|
||||
/** ISO 8601 timestamp of job completion; null when not yet complete */
|
||||
completedAt?: string | null;
|
||||
evalInvoice?: InvoiceInfo;
|
||||
workInvoice?: InvoiceInfo;
|
||||
pricingBreakdown?: PricingBreakdown;
|
||||
reason?: string;
|
||||
result?: string;
|
||||
costLedger?: CostLedger;
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
export type SessionState = (typeof SessionState)[keyof typeof SessionState];
|
||||
|
||||
export const SessionState = {
|
||||
awaiting_payment: "awaiting_payment",
|
||||
active: "active",
|
||||
paused: "paused",
|
||||
expired: "expired",
|
||||
} as const;
|
||||
|
||||
export interface SessionInvoiceInfo {
|
||||
paymentRequest?: string;
|
||||
amountSats?: number;
|
||||
/** Only present in stub/dev mode */
|
||||
paymentHash?: string;
|
||||
}
|
||||
|
||||
export interface CreateSessionRequest {
|
||||
/**
|
||||
* Deposit amount (100–10,000 sats)
|
||||
* @minimum 100
|
||||
* @maximum 10000
|
||||
*/
|
||||
amount_sats: number;
|
||||
}
|
||||
|
||||
export interface CreateSessionResponse {
|
||||
sessionId: string;
|
||||
state: SessionState;
|
||||
invoice: SessionInvoiceInfo;
|
||||
}
|
||||
|
||||
export interface SessionStatusResponse {
|
||||
sessionId: string;
|
||||
state: SessionState;
|
||||
balanceSats: number;
|
||||
minimumBalanceSats?: number;
|
||||
/** Bearer token for authenticating requests; present when active or paused */
|
||||
macaroon?: string;
|
||||
expiresAt?: string;
|
||||
/** Present when state is awaiting_payment */
|
||||
invoice?: SessionInvoiceInfo;
|
||||
/** Present when a topup invoice is outstanding */
|
||||
pendingTopup?: SessionInvoiceInfo;
|
||||
}
|
||||
|
||||
export interface SessionRequestBody {
|
||||
/** @minLength 1 */
|
||||
request: string;
|
||||
}
|
||||
|
||||
export interface SessionCostBreakdown {
|
||||
evalSats?: number;
|
||||
workSats?: number;
|
||||
totalSats?: number;
|
||||
btcPriceUsd?: number;
|
||||
}
|
||||
|
||||
export type SessionRequestResponseState =
|
||||
(typeof SessionRequestResponseState)[keyof typeof SessionRequestResponseState];
|
||||
|
||||
export const SessionRequestResponseState = {
|
||||
complete: "complete",
|
||||
rejected: "rejected",
|
||||
failed: "failed",
|
||||
} as const;
|
||||
|
||||
export interface SessionRequestResponse {
|
||||
requestId: string;
|
||||
state: SessionRequestResponseState;
|
||||
result?: string;
|
||||
reason?: string;
|
||||
errorMessage?: string;
|
||||
debitedSats: number;
|
||||
balanceRemaining: number;
|
||||
cost?: SessionCostBreakdown;
|
||||
}
|
||||
|
||||
export interface TopupSessionResponse {
|
||||
sessionId: string;
|
||||
topup: SessionInvoiceInfo;
|
||||
}
|
||||
|
||||
export interface ClaimRefundRequest {
|
||||
/** BOLT11 invoice for exactly refundAmountSats */
|
||||
invoice: string;
|
||||
}
|
||||
|
||||
export interface ClaimRefundResponse {
|
||||
ok: boolean;
|
||||
refundAmountSats: number;
|
||||
paymentHash: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface DemoResponse {
|
||||
result: string;
|
||||
}
|
||||
|
||||
export interface GeminiConversation {
|
||||
id: number;
|
||||
title: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface GeminiMessage {
|
||||
id: number;
|
||||
conversationId: number;
|
||||
role: string;
|
||||
content: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface GeminiConversationWithMessages {
|
||||
id: number;
|
||||
title: string;
|
||||
createdAt: string;
|
||||
messages: GeminiMessage[];
|
||||
}
|
||||
|
||||
export interface CreateGeminiConversationBody {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface SendGeminiMessageBody {
|
||||
content: string;
|
||||
/** Gemini model override (default: gemini-3-flash-preview) */
|
||||
model?: string;
|
||||
}
|
||||
|
||||
export interface GenerateGeminiImageBody {
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
export interface GenerateGeminiImageResponse {
|
||||
b64_json: string;
|
||||
mimeType: string;
|
||||
}
|
||||
|
||||
export interface GeminiError {
|
||||
error: string;
|
||||
}
|
||||
|
||||
export type RunDemoParams = {
|
||||
request: string;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,8 @@ tags:
|
||||
description: Pre-funded session balance mode (Mode 2 -- pay once, run many)
|
||||
- name: demo
|
||||
description: Free demo endpoint (rate-limited)
|
||||
- name: gemini
|
||||
description: Gemini AI chat and image operations
|
||||
paths:
|
||||
/healthz:
|
||||
get:
|
||||
@@ -385,6 +387,139 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
/gemini/conversations:
|
||||
get:
|
||||
operationId: listGeminiConversations
|
||||
tags: [gemini]
|
||||
summary: List all conversations
|
||||
responses:
|
||||
"200":
|
||||
description: List of conversations
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/GeminiConversation"
|
||||
post:
|
||||
operationId: createGeminiConversation
|
||||
tags: [gemini]
|
||||
summary: Create a new conversation
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/CreateGeminiConversationBody"
|
||||
responses:
|
||||
"201":
|
||||
description: Created conversation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/GeminiConversation"
|
||||
/gemini/conversations/{id}:
|
||||
get:
|
||||
operationId: getGeminiConversation
|
||||
tags: [gemini]
|
||||
summary: Get conversation with messages
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
description: Conversation with messages
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/GeminiConversationWithMessages"
|
||||
"404":
|
||||
description: Not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/GeminiError"
|
||||
delete:
|
||||
operationId: deleteGeminiConversation
|
||||
tags: [gemini]
|
||||
summary: Delete a conversation
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
"204":
|
||||
description: Deleted
|
||||
"404":
|
||||
description: Not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/GeminiError"
|
||||
/gemini/conversations/{id}/messages:
|
||||
get:
|
||||
operationId: listGeminiMessages
|
||||
tags: [gemini]
|
||||
summary: List messages in a conversation
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
description: List of messages
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/GeminiMessage"
|
||||
post:
|
||||
operationId: sendGeminiMessage
|
||||
tags: [gemini]
|
||||
summary: Send a message and receive an AI response (SSE stream)
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/SendGeminiMessageBody"
|
||||
responses:
|
||||
"200":
|
||||
description: SSE stream of assistant response chunks
|
||||
content:
|
||||
text/event-stream: {}
|
||||
/gemini/generate-image:
|
||||
post:
|
||||
operationId: generateGeminiImage
|
||||
tags: [gemini]
|
||||
summary: Generate an image from a text prompt
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/GenerateGeminiImageBody"
|
||||
responses:
|
||||
"200":
|
||||
description: Generated image
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/GenerateGeminiImageResponse"
|
||||
components:
|
||||
schemas:
|
||||
HealthStatus:
|
||||
@@ -663,8 +798,84 @@ components:
|
||||
properties:
|
||||
result:
|
||||
type: string
|
||||
GeminiConversation:
|
||||
type: object
|
||||
required: [id, title, createdAt]
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
title:
|
||||
type: string
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
GeminiMessage:
|
||||
type: object
|
||||
required: [id, conversationId, role, content, createdAt]
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
conversationId:
|
||||
type: integer
|
||||
role:
|
||||
type: string
|
||||
content:
|
||||
type: string
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
GeminiConversationWithMessages:
|
||||
type: object
|
||||
required: [id, title, createdAt, messages]
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
title:
|
||||
type: string
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/GeminiMessage"
|
||||
CreateGeminiConversationBody:
|
||||
type: object
|
||||
required: [title]
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
SendGeminiMessageBody:
|
||||
type: object
|
||||
required: [content]
|
||||
properties:
|
||||
content:
|
||||
type: string
|
||||
model:
|
||||
type: string
|
||||
description: "Gemini model override (default: gemini-3-flash-preview)"
|
||||
GenerateGeminiImageBody:
|
||||
type: object
|
||||
required: [prompt]
|
||||
properties:
|
||||
prompt:
|
||||
type: string
|
||||
GenerateGeminiImageResponse:
|
||||
type: object
|
||||
required: [b64_json, mimeType]
|
||||
properties:
|
||||
b64_json:
|
||||
type: string
|
||||
mimeType:
|
||||
type: string
|
||||
GeminiError:
|
||||
type: object
|
||||
required: [error]
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
securitySchemes:
|
||||
sessionMacaroon:
|
||||
type: http
|
||||
scheme: bearer
|
||||
description: Session macaroon issued when a session activates. Pass as `Authorization: Bearer <macaroon>`.
|
||||
description: "Session macaroon issued when a session activates. Pass as `Authorization: Bearer <macaroon>`."
|
||||
|
||||
66
lib/api-spec/orval.config.cjs
Normal file
66
lib/api-spec/orval.config.cjs
Normal file
@@ -0,0 +1,66 @@
|
||||
const path = require("path");
|
||||
|
||||
const root = path.resolve(__dirname, "..", "..");
|
||||
const apiClientReactSrc = path.resolve(root, "lib", "api-client-react", "src");
|
||||
const apiZodSrc = path.resolve(root, "lib", "api-zod", "src");
|
||||
|
||||
const titleTransformer = (config) => {
|
||||
config.info ??= {};
|
||||
config.info.title = "Api";
|
||||
return config;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
"api-client-react": {
|
||||
input: {
|
||||
target: path.resolve(__dirname, "./openapi.yaml"),
|
||||
override: {
|
||||
transformer: titleTransformer,
|
||||
},
|
||||
},
|
||||
output: {
|
||||
workspace: apiClientReactSrc,
|
||||
target: "generated",
|
||||
client: "react-query",
|
||||
mode: "split",
|
||||
baseUrl: "/api",
|
||||
clean: true,
|
||||
prettier: true,
|
||||
override: {
|
||||
fetch: {
|
||||
includeHttpResponseReturnType: false,
|
||||
},
|
||||
mutator: {
|
||||
path: path.resolve(apiClientReactSrc, "custom-fetch.ts"),
|
||||
name: "customFetch",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
zod: {
|
||||
input: {
|
||||
target: path.resolve(__dirname, "./openapi.yaml"),
|
||||
override: {
|
||||
transformer: titleTransformer,
|
||||
},
|
||||
},
|
||||
output: {
|
||||
workspace: apiZodSrc,
|
||||
client: "zod",
|
||||
target: "generated",
|
||||
schemas: { path: "generated/types", type: "typescript" },
|
||||
mode: "split",
|
||||
clean: true,
|
||||
prettier: true,
|
||||
override: {
|
||||
zod: {
|
||||
coerce: {
|
||||
query: ["boolean", "number", "string"],
|
||||
param: ["boolean", "number", "string"],
|
||||
},
|
||||
},
|
||||
useDates: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"codegen": "orval --config ./orval.config.ts"
|
||||
"codegen": "orval --config ./orval.config.cjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"orval": "^8.5.2"
|
||||
|
||||
@@ -1,45 +1,396 @@
|
||||
import { z } from "zod";
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
import * as zod from "zod";
|
||||
|
||||
export const HealthCheckResponse = z.object({
|
||||
status: z.string(),
|
||||
/**
|
||||
* Returns server health status
|
||||
* @summary Health check
|
||||
*/
|
||||
export const HealthCheckResponse = zod.object({
|
||||
status: zod.string(),
|
||||
});
|
||||
|
||||
export const ErrorResponse = z.object({
|
||||
error: z.string(),
|
||||
/**
|
||||
* Accepts a request, creates a job row, and issues an eval fee Lightning invoice.
|
||||
* @summary Create a new agent job
|
||||
*/
|
||||
|
||||
export const CreateJobBody = zod.object({
|
||||
request: zod.string().min(1),
|
||||
});
|
||||
|
||||
export const CreateJobBody = z.object({
|
||||
request: z.string().min(1).max(500),
|
||||
/**
|
||||
* Returns current job state. Automatically advances the state machine when a pending invoice is found to be paid.
|
||||
* @summary Get job status
|
||||
*/
|
||||
export const GetJobParams = zod.object({
|
||||
id: zod.coerce.string(),
|
||||
});
|
||||
|
||||
export const GetJobParams = z.object({
|
||||
id: z.string(),
|
||||
export const GetJobResponse = zod.object({
|
||||
jobId: zod.string(),
|
||||
state: zod.enum([
|
||||
"awaiting_eval_payment",
|
||||
"evaluating",
|
||||
"rejected",
|
||||
"awaiting_work_payment",
|
||||
"executing",
|
||||
"complete",
|
||||
"failed",
|
||||
]),
|
||||
createdAt: zod
|
||||
.date()
|
||||
.describe("ISO 8601 timestamp of job creation (always present)"),
|
||||
completedAt: zod
|
||||
.date()
|
||||
.nullish()
|
||||
.describe(
|
||||
"ISO 8601 timestamp of job completion; null when not yet complete",
|
||||
),
|
||||
evalInvoice: zod
|
||||
.object({
|
||||
paymentRequest: zod.string(),
|
||||
amountSats: zod.number(),
|
||||
})
|
||||
.optional(),
|
||||
workInvoice: zod
|
||||
.object({
|
||||
paymentRequest: zod.string(),
|
||||
amountSats: zod.number(),
|
||||
})
|
||||
.optional(),
|
||||
pricingBreakdown: zod
|
||||
.object({
|
||||
estimatedCostUsd: zod
|
||||
.number()
|
||||
.optional()
|
||||
.describe(
|
||||
"Total estimated cost in USD (token cost + DO infra + margin)",
|
||||
),
|
||||
marginPct: zod
|
||||
.number()
|
||||
.optional()
|
||||
.describe("Originator margin percentage applied"),
|
||||
btcPriceUsd: zod
|
||||
.number()
|
||||
.optional()
|
||||
.describe("BTC\/USD spot price used to convert the invoice to sats"),
|
||||
})
|
||||
.optional()
|
||||
.describe(
|
||||
"Cost breakdown shown with the work invoice (estimations at invoice-creation time)",
|
||||
),
|
||||
reason: zod.string().optional(),
|
||||
result: zod.string().optional(),
|
||||
costLedger: zod
|
||||
.object({
|
||||
actualInputTokens: zod.number().optional(),
|
||||
actualOutputTokens: zod.number().optional(),
|
||||
totalTokens: zod
|
||||
.number()
|
||||
.optional()
|
||||
.describe("Sum of actualInputTokens + actualOutputTokens"),
|
||||
actualCostUsd: zod
|
||||
.number()
|
||||
.optional()
|
||||
.describe("Raw Anthropic token cost (no infra, no margin)"),
|
||||
actualChargeUsd: zod
|
||||
.number()
|
||||
.optional()
|
||||
.describe(
|
||||
"What we honestly charged in USD (actual token cost + DO infra + margin)",
|
||||
),
|
||||
estimatedCostUsd: zod
|
||||
.number()
|
||||
.optional()
|
||||
.describe("Original estimate used to create the work invoice"),
|
||||
actualAmountSats: zod
|
||||
.number()
|
||||
.optional()
|
||||
.describe(
|
||||
"Honest sats charge (actual cost converted at the locked BTC price)",
|
||||
),
|
||||
workAmountSats: zod
|
||||
.number()
|
||||
.optional()
|
||||
.describe("Amount the user originally paid in sats"),
|
||||
refundAmountSats: zod
|
||||
.number()
|
||||
.optional()
|
||||
.describe(
|
||||
"Sats owed back to the user (workAmountSats - actualAmountSats, >= 0)",
|
||||
),
|
||||
refundState: zod
|
||||
.enum(["not_applicable", "pending", "paid"])
|
||||
.optional()
|
||||
.describe("Lifecycle of the refund for this job"),
|
||||
marginPct: zod.number().optional(),
|
||||
btcPriceUsd: zod
|
||||
.number()
|
||||
.optional()
|
||||
.describe("BTC\/USD price locked at invoice creation time"),
|
||||
})
|
||||
.optional()
|
||||
.describe("Honest post-work accounting stored after the job completes"),
|
||||
errorMessage: zod.string().optional(),
|
||||
});
|
||||
|
||||
export const GetJobRefundParams = z.object({
|
||||
id: z.string(),
|
||||
/**
|
||||
* After a job completes, if the actual cost (tokens used + infra + margin) was
|
||||
less than the work invoice amount, the difference is owed back to the user.
|
||||
Submit a BOLT11 invoice for exactly `refundAmountSats` to receive the payment.
|
||||
Idempotent: returns 409 if already paid or if no refund is owed.
|
||||
|
||||
* @summary Claim a refund for overpayment
|
||||
*/
|
||||
export const ClaimRefundParams = zod.object({
|
||||
id: zod.coerce.string(),
|
||||
});
|
||||
|
||||
export const ClaimRefundBody = z.object({
|
||||
paymentRequest: z.string(),
|
||||
export const ClaimRefundBody = zod.object({
|
||||
invoice: zod.string().describe("BOLT11 invoice for exactly refundAmountSats"),
|
||||
});
|
||||
|
||||
export const RunDemoQueryParams = z.object({
|
||||
request: z.string().min(1),
|
||||
export const ClaimRefundResponse = zod.object({
|
||||
ok: zod.boolean(),
|
||||
refundAmountSats: zod.number(),
|
||||
paymentHash: zod.string(),
|
||||
message: zod.string(),
|
||||
});
|
||||
|
||||
export const CreateSessionBody = z.object({
|
||||
amount_sats: z.number().int().min(100).max(10000),
|
||||
/**
|
||||
* Opens a new session. Pay the returned Lightning invoice to activate it.
|
||||
Once active, use the `macaroon` from GET /sessions/:id to authenticate requests.
|
||||
Deposits: 100–10,000 sats. Sessions expire after 24 h of inactivity.
|
||||
|
||||
* @summary Create a pre-funded session
|
||||
*/
|
||||
export const createSessionBodyAmountSatsMin = 100;
|
||||
export const createSessionBodyAmountSatsMax = 10000;
|
||||
|
||||
export const CreateSessionBody = zod.object({
|
||||
amount_sats: zod
|
||||
.number()
|
||||
.min(createSessionBodyAmountSatsMin)
|
||||
.max(createSessionBodyAmountSatsMax)
|
||||
.describe("Deposit amount (100–10,000 sats)"),
|
||||
});
|
||||
|
||||
export const GetSessionParams = z.object({
|
||||
id: z.string(),
|
||||
/**
|
||||
* Returns current state, balance, and pending invoice info. Auto-advances on payment.
|
||||
* @summary Get session status
|
||||
*/
|
||||
export const GetSessionParams = zod.object({
|
||||
id: zod.coerce.string(),
|
||||
});
|
||||
|
||||
export const SubmitSessionRequestBody = z.object({
|
||||
request: z.string().min(1),
|
||||
export const GetSessionResponse = zod.object({
|
||||
sessionId: zod.string(),
|
||||
state: zod.enum(["awaiting_payment", "active", "paused", "expired"]),
|
||||
balanceSats: zod.number(),
|
||||
minimumBalanceSats: zod.number().optional(),
|
||||
macaroon: zod
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
"Bearer token for authenticating requests; present when active or paused",
|
||||
),
|
||||
expiresAt: zod.date().optional(),
|
||||
invoice: zod
|
||||
.object({
|
||||
paymentRequest: zod.string().optional(),
|
||||
amountSats: zod.number().optional(),
|
||||
paymentHash: zod
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Only present in stub\/dev mode"),
|
||||
})
|
||||
.optional()
|
||||
.describe("Present when state is awaiting_payment"),
|
||||
pendingTopup: zod
|
||||
.object({
|
||||
paymentRequest: zod.string().optional(),
|
||||
amountSats: zod.number().optional(),
|
||||
paymentHash: zod
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Only present in stub\/dev mode"),
|
||||
})
|
||||
.optional()
|
||||
.describe("Present when a topup invoice is outstanding"),
|
||||
});
|
||||
|
||||
export const TopupSessionBody = z.object({
|
||||
amount_sats: z.number().int().min(100).max(10000),
|
||||
/**
|
||||
* Runs eval + work and debits the actual compute cost from the session balance.
|
||||
Rejected requests still incur a small eval fee. Requires `Authorization: Bearer <macaroon>`.
|
||||
|
||||
* @summary Submit a request against a session balance
|
||||
*/
|
||||
export const SubmitSessionRequestParams = zod.object({
|
||||
id: zod.coerce.string(),
|
||||
});
|
||||
|
||||
export const SubmitSessionRequestBody = zod.object({
|
||||
request: zod.string().min(1),
|
||||
});
|
||||
|
||||
export const SubmitSessionRequestResponse = zod.object({
|
||||
requestId: zod.string(),
|
||||
state: zod.enum(["complete", "rejected", "failed"]),
|
||||
result: zod.string().optional(),
|
||||
reason: zod.string().optional(),
|
||||
errorMessage: zod.string().optional(),
|
||||
debitedSats: zod.number(),
|
||||
balanceRemaining: zod.number(),
|
||||
cost: zod
|
||||
.object({
|
||||
evalSats: zod.number().optional(),
|
||||
workSats: zod.number().optional(),
|
||||
totalSats: zod.number().optional(),
|
||||
btcPriceUsd: zod.number().optional(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a new Lightning invoice to top up the session balance.
|
||||
Only one pending topup at a time. Paying it resumes a paused session.
|
||||
|
||||
* @summary Add sats to a session
|
||||
*/
|
||||
export const TopupSessionParams = zod.object({
|
||||
id: zod.coerce.string(),
|
||||
});
|
||||
|
||||
export const topupSessionBodyAmountSatsMin = 100;
|
||||
export const topupSessionBodyAmountSatsMax = 10000;
|
||||
|
||||
export const TopupSessionBody = zod.object({
|
||||
amount_sats: zod
|
||||
.number()
|
||||
.min(topupSessionBodyAmountSatsMin)
|
||||
.max(topupSessionBodyAmountSatsMax)
|
||||
.describe("Deposit amount (100–10,000 sats)"),
|
||||
});
|
||||
|
||||
export const TopupSessionResponse = zod.object({
|
||||
sessionId: zod.string(),
|
||||
topup: zod.object({
|
||||
paymentRequest: zod.string().optional(),
|
||||
amountSats: zod.number().optional(),
|
||||
paymentHash: zod
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Only present in stub\/dev mode"),
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
* Runs the agent without payment. Limited to 5 requests per IP per hour.
|
||||
* @summary Free demo (rate-limited)
|
||||
*/
|
||||
export const RunDemoQueryParams = zod.object({
|
||||
request: zod.coerce.string(),
|
||||
});
|
||||
|
||||
export const RunDemoResponse = zod.object({
|
||||
result: zod.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* @summary List all conversations
|
||||
*/
|
||||
export const ListGeminiConversationsResponseItem = zod.object({
|
||||
id: zod.number(),
|
||||
title: zod.string(),
|
||||
createdAt: zod.date(),
|
||||
});
|
||||
export const ListGeminiConversationsResponse = zod.array(
|
||||
ListGeminiConversationsResponseItem,
|
||||
);
|
||||
|
||||
/**
|
||||
* @summary Create a new conversation
|
||||
*/
|
||||
export const CreateGeminiConversationBody = zod.object({
|
||||
title: zod.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* @summary Get conversation with messages
|
||||
*/
|
||||
export const GetGeminiConversationParams = zod.object({
|
||||
id: zod.coerce.number(),
|
||||
});
|
||||
|
||||
export const GetGeminiConversationResponse = zod.object({
|
||||
id: zod.number(),
|
||||
title: zod.string(),
|
||||
createdAt: zod.date(),
|
||||
messages: zod.array(
|
||||
zod.object({
|
||||
id: zod.number(),
|
||||
conversationId: zod.number(),
|
||||
role: zod.string(),
|
||||
content: zod.string(),
|
||||
createdAt: zod.date(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
/**
|
||||
* @summary Delete a conversation
|
||||
*/
|
||||
export const DeleteGeminiConversationParams = zod.object({
|
||||
id: zod.coerce.number(),
|
||||
});
|
||||
|
||||
/**
|
||||
* @summary List messages in a conversation
|
||||
*/
|
||||
export const ListGeminiMessagesParams = zod.object({
|
||||
id: zod.coerce.number(),
|
||||
});
|
||||
|
||||
export const ListGeminiMessagesResponseItem = zod.object({
|
||||
id: zod.number(),
|
||||
conversationId: zod.number(),
|
||||
role: zod.string(),
|
||||
content: zod.string(),
|
||||
createdAt: zod.date(),
|
||||
});
|
||||
export const ListGeminiMessagesResponse = zod.array(
|
||||
ListGeminiMessagesResponseItem,
|
||||
);
|
||||
|
||||
/**
|
||||
* @summary Send a message and receive an AI response (SSE stream)
|
||||
*/
|
||||
export const SendGeminiMessageParams = zod.object({
|
||||
id: zod.coerce.number(),
|
||||
});
|
||||
|
||||
export const SendGeminiMessageBody = zod.object({
|
||||
content: zod.string(),
|
||||
model: zod
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Gemini model override (default: gemini-3-flash-preview)"),
|
||||
});
|
||||
|
||||
/**
|
||||
* @summary Generate an image from a text prompt
|
||||
*/
|
||||
export const GenerateGeminiImageBody = zod.object({
|
||||
prompt: zod.string(),
|
||||
});
|
||||
|
||||
export const GenerateGeminiImageResponse = zod.object({
|
||||
b64_json: zod.string(),
|
||||
mimeType: zod.string(),
|
||||
});
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
// TypeScript types derived from Zod schemas in ./api
|
||||
// All schemas and their inferred types are exported from ./api via src/index.ts
|
||||
export {};
|
||||
12
lib/api-zod/src/generated/types/claimRefundRequest.ts
Normal file
12
lib/api-zod/src/generated/types/claimRefundRequest.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface ClaimRefundRequest {
|
||||
/** BOLT11 invoice for exactly refundAmountSats */
|
||||
invoice: string;
|
||||
}
|
||||
14
lib/api-zod/src/generated/types/claimRefundResponse.ts
Normal file
14
lib/api-zod/src/generated/types/claimRefundResponse.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface ClaimRefundResponse {
|
||||
ok: boolean;
|
||||
refundAmountSats: number;
|
||||
paymentHash: string;
|
||||
message: string;
|
||||
}
|
||||
35
lib/api-zod/src/generated/types/costLedger.ts
Normal file
35
lib/api-zod/src/generated/types/costLedger.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
import type { CostLedgerRefundState } from "./costLedgerRefundState";
|
||||
|
||||
/**
|
||||
* Honest post-work accounting stored after the job completes
|
||||
*/
|
||||
export interface CostLedger {
|
||||
actualInputTokens?: number;
|
||||
actualOutputTokens?: number;
|
||||
/** Sum of actualInputTokens + actualOutputTokens */
|
||||
totalTokens?: number;
|
||||
/** Raw Anthropic token cost (no infra, no margin) */
|
||||
actualCostUsd?: number;
|
||||
/** What we honestly charged in USD (actual token cost + DO infra + margin) */
|
||||
actualChargeUsd?: number;
|
||||
/** Original estimate used to create the work invoice */
|
||||
estimatedCostUsd?: number;
|
||||
/** Honest sats charge (actual cost converted at the locked BTC price) */
|
||||
actualAmountSats?: number;
|
||||
/** Amount the user originally paid in sats */
|
||||
workAmountSats?: number;
|
||||
/** Sats owed back to the user (workAmountSats - actualAmountSats, >= 0) */
|
||||
refundAmountSats?: number;
|
||||
/** Lifecycle of the refund for this job */
|
||||
refundState?: CostLedgerRefundState;
|
||||
marginPct?: number;
|
||||
/** BTC/USD price locked at invoice creation time */
|
||||
btcPriceUsd?: number;
|
||||
}
|
||||
19
lib/api-zod/src/generated/types/costLedgerRefundState.ts
Normal file
19
lib/api-zod/src/generated/types/costLedgerRefundState.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Lifecycle of the refund for this job
|
||||
*/
|
||||
export type CostLedgerRefundState =
|
||||
(typeof CostLedgerRefundState)[keyof typeof CostLedgerRefundState];
|
||||
|
||||
export const CostLedgerRefundState = {
|
||||
not_applicable: "not_applicable",
|
||||
pending: "pending",
|
||||
paid: "paid",
|
||||
} as const;
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface CreateGeminiConversationBody {
|
||||
title: string;
|
||||
}
|
||||
12
lib/api-zod/src/generated/types/createJobRequest.ts
Normal file
12
lib/api-zod/src/generated/types/createJobRequest.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface CreateJobRequest {
|
||||
/** @minLength 1 */
|
||||
request: string;
|
||||
}
|
||||
15
lib/api-zod/src/generated/types/createJobResponse.ts
Normal file
15
lib/api-zod/src/generated/types/createJobResponse.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
import type { InvoiceInfo } from "./invoiceInfo";
|
||||
|
||||
export interface CreateJobResponse {
|
||||
jobId: string;
|
||||
/** ISO 8601 timestamp of job creation */
|
||||
createdAt: Date;
|
||||
evalInvoice: InvoiceInfo;
|
||||
}
|
||||
16
lib/api-zod/src/generated/types/createSessionRequest.ts
Normal file
16
lib/api-zod/src/generated/types/createSessionRequest.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface CreateSessionRequest {
|
||||
/**
|
||||
* Deposit amount (100–10,000 sats)
|
||||
* @minimum 100
|
||||
* @maximum 10000
|
||||
*/
|
||||
amount_sats: number;
|
||||
}
|
||||
15
lib/api-zod/src/generated/types/createSessionResponse.ts
Normal file
15
lib/api-zod/src/generated/types/createSessionResponse.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
import type { SessionInvoiceInfo } from "./sessionInvoiceInfo";
|
||||
import type { SessionState } from "./sessionState";
|
||||
|
||||
export interface CreateSessionResponse {
|
||||
sessionId: string;
|
||||
state: SessionState;
|
||||
invoice: SessionInvoiceInfo;
|
||||
}
|
||||
11
lib/api-zod/src/generated/types/demoResponse.ts
Normal file
11
lib/api-zod/src/generated/types/demoResponse.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface DemoResponse {
|
||||
result: string;
|
||||
}
|
||||
11
lib/api-zod/src/generated/types/errorResponse.ts
Normal file
11
lib/api-zod/src/generated/types/errorResponse.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface ErrorResponse {
|
||||
error: string;
|
||||
}
|
||||
13
lib/api-zod/src/generated/types/geminiConversation.ts
Normal file
13
lib/api-zod/src/generated/types/geminiConversation.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface GeminiConversation {
|
||||
id: number;
|
||||
title: string;
|
||||
createdAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
import type { GeminiMessage } from "./geminiMessage";
|
||||
|
||||
export interface GeminiConversationWithMessages {
|
||||
id: number;
|
||||
title: string;
|
||||
createdAt: Date;
|
||||
messages: GeminiMessage[];
|
||||
}
|
||||
11
lib/api-zod/src/generated/types/geminiError.ts
Normal file
11
lib/api-zod/src/generated/types/geminiError.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface GeminiError {
|
||||
error: string;
|
||||
}
|
||||
15
lib/api-zod/src/generated/types/geminiMessage.ts
Normal file
15
lib/api-zod/src/generated/types/geminiMessage.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface GeminiMessage {
|
||||
id: number;
|
||||
conversationId: number;
|
||||
role: string;
|
||||
content: string;
|
||||
createdAt: Date;
|
||||
}
|
||||
11
lib/api-zod/src/generated/types/generateGeminiImageBody.ts
Normal file
11
lib/api-zod/src/generated/types/generateGeminiImageBody.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface GenerateGeminiImageBody {
|
||||
prompt: string;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface GenerateGeminiImageResponse {
|
||||
b64_json: string;
|
||||
mimeType: string;
|
||||
}
|
||||
11
lib/api-zod/src/generated/types/healthStatus.ts
Normal file
11
lib/api-zod/src/generated/types/healthStatus.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface HealthStatus {
|
||||
status: string;
|
||||
}
|
||||
40
lib/api-zod/src/generated/types/index.ts
Normal file
40
lib/api-zod/src/generated/types/index.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export * from "./claimRefundRequest";
|
||||
export * from "./claimRefundResponse";
|
||||
export * from "./costLedger";
|
||||
export * from "./costLedgerRefundState";
|
||||
export * from "./createGeminiConversationBody";
|
||||
export * from "./createJobRequest";
|
||||
export * from "./createJobResponse";
|
||||
export * from "./createSessionRequest";
|
||||
export * from "./createSessionResponse";
|
||||
export * from "./demoResponse";
|
||||
export * from "./errorResponse";
|
||||
export * from "./geminiConversation";
|
||||
export * from "./geminiConversationWithMessages";
|
||||
export * from "./geminiError";
|
||||
export * from "./geminiMessage";
|
||||
export * from "./generateGeminiImageBody";
|
||||
export * from "./generateGeminiImageResponse";
|
||||
export * from "./healthStatus";
|
||||
export * from "./invoiceInfo";
|
||||
export * from "./jobState";
|
||||
export * from "./jobStatusResponse";
|
||||
export * from "./pricingBreakdown";
|
||||
export * from "./runDemoParams";
|
||||
export * from "./sendGeminiMessageBody";
|
||||
export * from "./sessionCostBreakdown";
|
||||
export * from "./sessionInvoiceInfo";
|
||||
export * from "./sessionRequestBody";
|
||||
export * from "./sessionRequestResponse";
|
||||
export * from "./sessionRequestResponseState";
|
||||
export * from "./sessionState";
|
||||
export * from "./sessionStatusResponse";
|
||||
export * from "./topupSessionResponse";
|
||||
12
lib/api-zod/src/generated/types/invoiceInfo.ts
Normal file
12
lib/api-zod/src/generated/types/invoiceInfo.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface InvoiceInfo {
|
||||
paymentRequest: string;
|
||||
amountSats: number;
|
||||
}
|
||||
19
lib/api-zod/src/generated/types/jobState.ts
Normal file
19
lib/api-zod/src/generated/types/jobState.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export type JobState = (typeof JobState)[keyof typeof JobState];
|
||||
|
||||
export const JobState = {
|
||||
awaiting_eval_payment: "awaiting_eval_payment",
|
||||
evaluating: "evaluating",
|
||||
rejected: "rejected",
|
||||
awaiting_work_payment: "awaiting_work_payment",
|
||||
executing: "executing",
|
||||
complete: "complete",
|
||||
failed: "failed",
|
||||
} as const;
|
||||
27
lib/api-zod/src/generated/types/jobStatusResponse.ts
Normal file
27
lib/api-zod/src/generated/types/jobStatusResponse.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
import type { CostLedger } from "./costLedger";
|
||||
import type { InvoiceInfo } from "./invoiceInfo";
|
||||
import type { JobState } from "./jobState";
|
||||
import type { PricingBreakdown } from "./pricingBreakdown";
|
||||
|
||||
export interface JobStatusResponse {
|
||||
jobId: string;
|
||||
state: JobState;
|
||||
/** ISO 8601 timestamp of job creation (always present) */
|
||||
createdAt: Date;
|
||||
/** ISO 8601 timestamp of job completion; null when not yet complete */
|
||||
completedAt?: Date | null;
|
||||
evalInvoice?: InvoiceInfo;
|
||||
workInvoice?: InvoiceInfo;
|
||||
pricingBreakdown?: PricingBreakdown;
|
||||
reason?: string;
|
||||
result?: string;
|
||||
costLedger?: CostLedger;
|
||||
errorMessage?: string;
|
||||
}
|
||||
19
lib/api-zod/src/generated/types/pricingBreakdown.ts
Normal file
19
lib/api-zod/src/generated/types/pricingBreakdown.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cost breakdown shown with the work invoice (estimations at invoice-creation time)
|
||||
*/
|
||||
export interface PricingBreakdown {
|
||||
/** Total estimated cost in USD (token cost + DO infra + margin) */
|
||||
estimatedCostUsd?: number;
|
||||
/** Originator margin percentage applied */
|
||||
marginPct?: number;
|
||||
/** BTC/USD spot price used to convert the invoice to sats */
|
||||
btcPriceUsd?: number;
|
||||
}
|
||||
11
lib/api-zod/src/generated/types/runDemoParams.ts
Normal file
11
lib/api-zod/src/generated/types/runDemoParams.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export type RunDemoParams = {
|
||||
request: string;
|
||||
};
|
||||
13
lib/api-zod/src/generated/types/sendGeminiMessageBody.ts
Normal file
13
lib/api-zod/src/generated/types/sendGeminiMessageBody.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface SendGeminiMessageBody {
|
||||
content: string;
|
||||
/** Gemini model override (default: gemini-3-flash-preview) */
|
||||
model?: string;
|
||||
}
|
||||
14
lib/api-zod/src/generated/types/sessionCostBreakdown.ts
Normal file
14
lib/api-zod/src/generated/types/sessionCostBreakdown.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface SessionCostBreakdown {
|
||||
evalSats?: number;
|
||||
workSats?: number;
|
||||
totalSats?: number;
|
||||
btcPriceUsd?: number;
|
||||
}
|
||||
14
lib/api-zod/src/generated/types/sessionInvoiceInfo.ts
Normal file
14
lib/api-zod/src/generated/types/sessionInvoiceInfo.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface SessionInvoiceInfo {
|
||||
paymentRequest?: string;
|
||||
amountSats?: number;
|
||||
/** Only present in stub/dev mode */
|
||||
paymentHash?: string;
|
||||
}
|
||||
12
lib/api-zod/src/generated/types/sessionRequestBody.ts
Normal file
12
lib/api-zod/src/generated/types/sessionRequestBody.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export interface SessionRequestBody {
|
||||
/** @minLength 1 */
|
||||
request: string;
|
||||
}
|
||||
20
lib/api-zod/src/generated/types/sessionRequestResponse.ts
Normal file
20
lib/api-zod/src/generated/types/sessionRequestResponse.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
import type { SessionCostBreakdown } from "./sessionCostBreakdown";
|
||||
import type { SessionRequestResponseState } from "./sessionRequestResponseState";
|
||||
|
||||
export interface SessionRequestResponse {
|
||||
requestId: string;
|
||||
state: SessionRequestResponseState;
|
||||
result?: string;
|
||||
reason?: string;
|
||||
errorMessage?: string;
|
||||
debitedSats: number;
|
||||
balanceRemaining: number;
|
||||
cost?: SessionCostBreakdown;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export type SessionRequestResponseState =
|
||||
(typeof SessionRequestResponseState)[keyof typeof SessionRequestResponseState];
|
||||
|
||||
export const SessionRequestResponseState = {
|
||||
complete: "complete",
|
||||
rejected: "rejected",
|
||||
failed: "failed",
|
||||
} as const;
|
||||
16
lib/api-zod/src/generated/types/sessionState.ts
Normal file
16
lib/api-zod/src/generated/types/sessionState.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
|
||||
export type SessionState = (typeof SessionState)[keyof typeof SessionState];
|
||||
|
||||
export const SessionState = {
|
||||
awaiting_payment: "awaiting_payment",
|
||||
active: "active",
|
||||
paused: "paused",
|
||||
expired: "expired",
|
||||
} as const;
|
||||
23
lib/api-zod/src/generated/types/sessionStatusResponse.ts
Normal file
23
lib/api-zod/src/generated/types/sessionStatusResponse.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
import type { SessionInvoiceInfo } from "./sessionInvoiceInfo";
|
||||
import type { SessionState } from "./sessionState";
|
||||
|
||||
export interface SessionStatusResponse {
|
||||
sessionId: string;
|
||||
state: SessionState;
|
||||
balanceSats: number;
|
||||
minimumBalanceSats?: number;
|
||||
/** Bearer token for authenticating requests; present when active or paused */
|
||||
macaroon?: string;
|
||||
expiresAt?: Date;
|
||||
/** Present when state is awaiting_payment */
|
||||
invoice?: SessionInvoiceInfo;
|
||||
/** Present when a topup invoice is outstanding */
|
||||
pendingTopup?: SessionInvoiceInfo;
|
||||
}
|
||||
13
lib/api-zod/src/generated/types/topupSessionResponse.ts
Normal file
13
lib/api-zod/src/generated/types/topupSessionResponse.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Generated by orval v8.5.3 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* API specification
|
||||
* OpenAPI spec version: 0.1.0
|
||||
*/
|
||||
import type { SessionInvoiceInfo } from "./sessionInvoiceInfo";
|
||||
|
||||
export interface TopupSessionResponse {
|
||||
sessionId: string;
|
||||
topup: SessionInvoiceInfo;
|
||||
}
|
||||
16
lib/integrations-gemini-ai/package.json
Normal file
16
lib/integrations-gemini-ai/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "@workspace/integrations-gemini-ai",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./batch": "./src/batch/index.ts",
|
||||
"./image": "./src/image/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@google/genai": "^1.44.0",
|
||||
"p-limit": "^7.3.0",
|
||||
"p-retry": "^7.1.1"
|
||||
}
|
||||
}
|
||||
6
lib/integrations-gemini-ai/src/batch/index.ts
Normal file
6
lib/integrations-gemini-ai/src/batch/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export {
|
||||
batchProcess,
|
||||
batchProcessWithSSE,
|
||||
isRateLimitError,
|
||||
type BatchOptions,
|
||||
} from "./utils";
|
||||
139
lib/integrations-gemini-ai/src/batch/utils.ts
Normal file
139
lib/integrations-gemini-ai/src/batch/utils.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import pLimit from "p-limit";
|
||||
import pRetry from "p-retry";
|
||||
|
||||
/**
|
||||
* Batch Processing Utilities
|
||||
*
|
||||
* Generic batch processing with built-in rate limiting and automatic retries.
|
||||
* Use for any task that requires processing multiple items through an LLM or external API.
|
||||
*
|
||||
* USAGE:
|
||||
* ```typescript
|
||||
* import { batchProcess } from "@workspace/integrations-gemini-ai/batch";
|
||||
* import { ai } from "@workspace/integrations-gemini-ai";
|
||||
*
|
||||
* const results = await batchProcess(
|
||||
* artworks,
|
||||
* async (artwork) => {
|
||||
* const response = await ai.models.generateContent({
|
||||
* model: "gemini-2.5-flash",
|
||||
* contents: [{ role: "user", parts: [{ text: `Categorize: ${artwork.name}` }] }],
|
||||
* config: { responseMimeType: "application/json" },
|
||||
* });
|
||||
* return JSON.parse(response.text ?? "{}");
|
||||
* },
|
||||
* { concurrency: 2, retries: 5 }
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
|
||||
export interface BatchOptions {
|
||||
concurrency?: number;
|
||||
retries?: number;
|
||||
minTimeout?: number;
|
||||
maxTimeout?: number;
|
||||
onProgress?: (completed: number, total: number, item: unknown) => void;
|
||||
}
|
||||
|
||||
export function isRateLimitError(error: unknown): boolean {
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
return (
|
||||
errorMsg.includes("429") ||
|
||||
errorMsg.includes("RATELIMIT_EXCEEDED") ||
|
||||
errorMsg.toLowerCase().includes("quota") ||
|
||||
errorMsg.toLowerCase().includes("rate limit")
|
||||
);
|
||||
}
|
||||
|
||||
export async function batchProcess<T, R>(
|
||||
items: T[],
|
||||
processor: (item: T, index: number) => Promise<R>,
|
||||
options: BatchOptions = {}
|
||||
): Promise<R[]> {
|
||||
const {
|
||||
concurrency = 2,
|
||||
retries = 7,
|
||||
minTimeout = 2000,
|
||||
maxTimeout = 128000,
|
||||
onProgress,
|
||||
} = options;
|
||||
|
||||
const limit = pLimit(concurrency);
|
||||
let completed = 0;
|
||||
|
||||
const promises = items.map((item, index) =>
|
||||
limit(() =>
|
||||
pRetry(
|
||||
async () => {
|
||||
try {
|
||||
const result = await processor(item, index);
|
||||
completed++;
|
||||
onProgress?.(completed, items.length, item);
|
||||
return result;
|
||||
} catch (error: unknown) {
|
||||
if (isRateLimitError(error)) {
|
||||
throw error;
|
||||
}
|
||||
throw new pRetry.AbortError(
|
||||
error instanceof Error ? error : new Error(String(error))
|
||||
);
|
||||
}
|
||||
},
|
||||
{ retries, minTimeout, maxTimeout, factor: 2 }
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
export async function batchProcessWithSSE<T, R>(
|
||||
items: T[],
|
||||
processor: (item: T, index: number) => Promise<R>,
|
||||
sendEvent: (event: { type: string; [key: string]: unknown }) => void,
|
||||
options: Omit<BatchOptions, "concurrency" | "onProgress"> = {}
|
||||
): Promise<R[]> {
|
||||
const { retries = 5, minTimeout = 1000, maxTimeout = 15000 } = options;
|
||||
|
||||
sendEvent({ type: "started", total: items.length });
|
||||
|
||||
const results: R[] = [];
|
||||
let errors = 0;
|
||||
|
||||
for (let index = 0; index < items.length; index++) {
|
||||
const item = items[index];
|
||||
sendEvent({ type: "processing", index, item });
|
||||
|
||||
try {
|
||||
const result = await pRetry(
|
||||
() => processor(item, index),
|
||||
{
|
||||
retries,
|
||||
minTimeout,
|
||||
maxTimeout,
|
||||
factor: 2,
|
||||
onFailedAttempt: (error) => {
|
||||
if (!isRateLimitError(error)) {
|
||||
throw new pRetry.AbortError(
|
||||
error instanceof Error ? error : new Error(String(error))
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
results.push(result);
|
||||
sendEvent({ type: "progress", index, result });
|
||||
} catch (error) {
|
||||
errors++;
|
||||
results.push(undefined as R);
|
||||
sendEvent({
|
||||
type: "progress",
|
||||
index,
|
||||
error: error instanceof Error ? error.message : "Processing failed",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
sendEvent({ type: "complete", processed: items.length, errors });
|
||||
return results;
|
||||
}
|
||||
21
lib/integrations-gemini-ai/src/client.ts
Normal file
21
lib/integrations-gemini-ai/src/client.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { GoogleGenAI } from "@google/genai";
|
||||
|
||||
if (!process.env.AI_INTEGRATIONS_GEMINI_BASE_URL) {
|
||||
throw new Error(
|
||||
"AI_INTEGRATIONS_GEMINI_BASE_URL must be set. Did you forget to provision the Gemini AI integration?",
|
||||
);
|
||||
}
|
||||
|
||||
if (!process.env.AI_INTEGRATIONS_GEMINI_API_KEY) {
|
||||
throw new Error(
|
||||
"AI_INTEGRATIONS_GEMINI_API_KEY must be set. Did you forget to provision the Gemini AI integration?",
|
||||
);
|
||||
}
|
||||
|
||||
export const ai = new GoogleGenAI({
|
||||
apiKey: process.env.AI_INTEGRATIONS_GEMINI_API_KEY,
|
||||
httpOptions: {
|
||||
apiVersion: "",
|
||||
baseUrl: process.env.AI_INTEGRATIONS_GEMINI_BASE_URL,
|
||||
},
|
||||
});
|
||||
47
lib/integrations-gemini-ai/src/image/client.ts
Normal file
47
lib/integrations-gemini-ai/src/image/client.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { GoogleGenAI, Modality } from "@google/genai";
|
||||
|
||||
if (!process.env.AI_INTEGRATIONS_GEMINI_BASE_URL) {
|
||||
throw new Error(
|
||||
"AI_INTEGRATIONS_GEMINI_BASE_URL must be set. Did you forget to provision the Gemini AI integration?",
|
||||
);
|
||||
}
|
||||
|
||||
if (!process.env.AI_INTEGRATIONS_GEMINI_API_KEY) {
|
||||
throw new Error(
|
||||
"AI_INTEGRATIONS_GEMINI_API_KEY must be set. Did you forget to provision the Gemini AI integration?",
|
||||
);
|
||||
}
|
||||
|
||||
export const ai = new GoogleGenAI({
|
||||
apiKey: process.env.AI_INTEGRATIONS_GEMINI_API_KEY,
|
||||
httpOptions: {
|
||||
apiVersion: "",
|
||||
baseUrl: process.env.AI_INTEGRATIONS_GEMINI_BASE_URL,
|
||||
},
|
||||
});
|
||||
|
||||
export async function generateImage(
|
||||
prompt: string
|
||||
): Promise<{ b64_json: string; mimeType: string }> {
|
||||
const response = await ai.models.generateContent({
|
||||
model: "gemini-2.5-flash-image",
|
||||
contents: [{ role: "user", parts: [{ text: prompt }] }],
|
||||
config: {
|
||||
responseModalities: [Modality.TEXT, Modality.IMAGE],
|
||||
},
|
||||
});
|
||||
|
||||
const candidate = response.candidates?.[0];
|
||||
const imagePart = candidate?.content?.parts?.find(
|
||||
(part: { inlineData?: { data?: string; mimeType?: string } }) => part.inlineData
|
||||
);
|
||||
|
||||
if (!imagePart?.inlineData?.data) {
|
||||
throw new Error("No image data in response");
|
||||
}
|
||||
|
||||
return {
|
||||
b64_json: imagePart.inlineData.data,
|
||||
mimeType: imagePart.inlineData.mimeType || "image/png",
|
||||
};
|
||||
}
|
||||
1
lib/integrations-gemini-ai/src/image/index.ts
Normal file
1
lib/integrations-gemini-ai/src/image/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { ai, generateImage } from "./client";
|
||||
3
lib/integrations-gemini-ai/src/index.ts
Normal file
3
lib/integrations-gemini-ai/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { ai } from "./client";
|
||||
export { generateImage } from "./image";
|
||||
export { batchProcess, batchProcessWithSSE, isRateLimitError, type BatchOptions } from "./batch";
|
||||
12
lib/integrations-gemini-ai/tsconfig.json
Normal file
12
lib/integrations-gemini-ai/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"declarationMap": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
317
pnpm-lock.yaml
generated
317
pnpm-lock.yaml
generated
@@ -6,15 +6,63 @@ settings:
|
||||
|
||||
catalogs:
|
||||
default:
|
||||
'@replit/vite-plugin-cartographer':
|
||||
specifier: ^0.5.0
|
||||
version: 0.5.0
|
||||
'@replit/vite-plugin-runtime-error-modal':
|
||||
specifier: ^0.0.6
|
||||
version: 0.0.6
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.1.14
|
||||
version: 4.2.1
|
||||
'@tanstack/react-query':
|
||||
specifier: ^5.90.21
|
||||
version: 5.90.21
|
||||
'@types/node':
|
||||
specifier: ^25.3.3
|
||||
version: 25.3.5
|
||||
'@types/react':
|
||||
specifier: ^19.2.0
|
||||
version: 19.2.14
|
||||
'@types/react-dom':
|
||||
specifier: ^19.2.0
|
||||
version: 19.2.3
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^5.0.4
|
||||
version: 5.1.4
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1
|
||||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
drizzle-orm:
|
||||
specifier: ^0.45.1
|
||||
version: 0.45.1
|
||||
framer-motion:
|
||||
specifier: ^12.23.24
|
||||
version: 12.35.1
|
||||
lucide-react:
|
||||
specifier: ^0.545.0
|
||||
version: 0.545.0
|
||||
react:
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0
|
||||
react-dom:
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0
|
||||
tailwind-merge:
|
||||
specifier: ^3.3.1
|
||||
version: 3.5.0
|
||||
tailwindcss:
|
||||
specifier: ^4.1.14
|
||||
version: 4.2.1
|
||||
tsx:
|
||||
specifier: ^4.21.0
|
||||
version: 4.21.0
|
||||
vite:
|
||||
specifier: ^7.3.0
|
||||
version: 7.3.1
|
||||
zod:
|
||||
specifier: ^3.25.76
|
||||
version: 3.25.76
|
||||
@@ -130,6 +178,9 @@ importers:
|
||||
'@workspace/integrations-anthropic-ai':
|
||||
specifier: workspace:*
|
||||
version: link:../../lib/integrations-anthropic-ai
|
||||
'@workspace/integrations-gemini-ai':
|
||||
specifier: workspace:*
|
||||
version: link:../../lib/integrations-gemini-ai
|
||||
cookie-parser:
|
||||
specifier: ^1.4.7
|
||||
version: 1.4.7
|
||||
@@ -561,6 +612,18 @@ importers:
|
||||
specifier: 'catalog:'
|
||||
version: 25.3.5
|
||||
|
||||
lib/integrations-gemini-ai:
|
||||
dependencies:
|
||||
'@google/genai':
|
||||
specifier: ^1.44.0
|
||||
version: 1.46.0
|
||||
p-limit:
|
||||
specifier: ^7.3.0
|
||||
version: 7.3.0
|
||||
p-retry:
|
||||
specifier: ^7.1.1
|
||||
version: 7.1.1
|
||||
|
||||
scripts:
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
@@ -1305,6 +1368,15 @@ packages:
|
||||
'@gerrit0/mini-shiki@3.23.0':
|
||||
resolution: {integrity: sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==}
|
||||
|
||||
'@google/genai@1.46.0':
|
||||
resolution: {integrity: sha512-ewPMN5JkKfgU5/kdco9ZhXBHDPhVqZpMQqIFQhwsHLf8kyZfx1cNpw1pHo1eV6PGEW7EhIBFi3aYZraFndAXqg==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
peerDependencies:
|
||||
'@modelcontextprotocol/sdk': ^1.25.2
|
||||
peerDependenciesMeta:
|
||||
'@modelcontextprotocol/sdk':
|
||||
optional: true
|
||||
|
||||
'@hookform/resolvers@3.10.0':
|
||||
resolution: {integrity: sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==}
|
||||
peerDependencies:
|
||||
@@ -1447,6 +1519,36 @@ packages:
|
||||
'@orval/zod@8.5.3':
|
||||
resolution: {integrity: sha512-qcbnpGE0VrgCDm0hNWQSOmzbfgdnr1xo+PYQ3PJjxfLuk3kGdJmFANTr53/1lI3sZUvWZwX5nKJCLWVxvwJEgg==}
|
||||
|
||||
'@protobufjs/aspromise@1.1.2':
|
||||
resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
|
||||
|
||||
'@protobufjs/base64@1.1.2':
|
||||
resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==}
|
||||
|
||||
'@protobufjs/codegen@2.0.4':
|
||||
resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==}
|
||||
|
||||
'@protobufjs/eventemitter@1.1.0':
|
||||
resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==}
|
||||
|
||||
'@protobufjs/fetch@1.1.0':
|
||||
resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==}
|
||||
|
||||
'@protobufjs/float@1.0.2':
|
||||
resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==}
|
||||
|
||||
'@protobufjs/inquire@1.1.0':
|
||||
resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==}
|
||||
|
||||
'@protobufjs/path@1.1.2':
|
||||
resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==}
|
||||
|
||||
'@protobufjs/pool@1.1.0':
|
||||
resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==}
|
||||
|
||||
'@protobufjs/utf8@1.1.0':
|
||||
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
|
||||
|
||||
'@radix-ui/number@1.1.1':
|
||||
resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
|
||||
|
||||
@@ -2494,6 +2596,9 @@ packages:
|
||||
'@types/responselike@1.0.3':
|
||||
resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==}
|
||||
|
||||
'@types/retry@0.12.0':
|
||||
resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==}
|
||||
|
||||
'@types/send@1.2.1':
|
||||
resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==}
|
||||
|
||||
@@ -2787,6 +2892,9 @@ packages:
|
||||
resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
bignumber.js@9.3.1:
|
||||
resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==}
|
||||
|
||||
body-parser@2.2.2:
|
||||
resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -2827,6 +2935,9 @@ packages:
|
||||
bser@2.1.1:
|
||||
resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
||||
|
||||
buffer-from@1.1.2:
|
||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||
|
||||
@@ -3092,6 +3203,10 @@ packages:
|
||||
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
data-uri-to-buffer@4.0.1:
|
||||
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
date-fns-jalali@4.1.0-0:
|
||||
resolution: {integrity: sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==}
|
||||
|
||||
@@ -3304,6 +3419,9 @@ packages:
|
||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
ecdsa-sig-formatter@1.0.11:
|
||||
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
|
||||
|
||||
ee-first@1.1.1:
|
||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||
|
||||
@@ -3664,6 +3782,9 @@ packages:
|
||||
resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
extend@3.0.2:
|
||||
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
|
||||
|
||||
fast-deep-equal@3.1.3:
|
||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||
|
||||
@@ -3705,6 +3826,10 @@ packages:
|
||||
picomatch:
|
||||
optional: true
|
||||
|
||||
fetch-blob@3.2.0:
|
||||
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
|
||||
engines: {node: ^12.20 || >= 14.13}
|
||||
|
||||
figures@6.1.0:
|
||||
resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -3754,6 +3879,10 @@ packages:
|
||||
fontfaceobserver@2.3.0:
|
||||
resolution: {integrity: sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==}
|
||||
|
||||
formdata-polyfill@4.0.10:
|
||||
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
|
||||
forwarded@0.2.0:
|
||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -3803,6 +3932,14 @@ packages:
|
||||
function-bind@1.1.2:
|
||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||
|
||||
gaxios@7.1.4:
|
||||
resolution: {integrity: sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
gcp-metadata@8.1.2:
|
||||
resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
gensync@1.0.0-beta.2:
|
||||
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -3870,6 +4007,14 @@ packages:
|
||||
resolution: {integrity: sha512-+A4Hq7m7Ze592k9gZRy4gJ27DrXRNnC1vPjxTt1qQxEY8RxagBkBxivkCwg7FxSTG0iLLEMaUx13oOr0R2/qcQ==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
google-auth-library@10.6.2:
|
||||
resolution: {integrity: sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
google-logging-utils@1.1.3:
|
||||
resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
gopd@1.2.0:
|
||||
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -4134,6 +4279,9 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
|
||||
json-bigint@1.0.0:
|
||||
resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==}
|
||||
|
||||
json-buffer@3.0.1:
|
||||
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
|
||||
|
||||
@@ -4166,6 +4314,12 @@ packages:
|
||||
resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
jwa@2.0.1:
|
||||
resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==}
|
||||
|
||||
jws@4.0.1:
|
||||
resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==}
|
||||
|
||||
keyv@4.5.4:
|
||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||
|
||||
@@ -4234,6 +4388,9 @@ packages:
|
||||
resolution: {integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
long@5.3.2:
|
||||
resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==}
|
||||
|
||||
loose-envify@1.4.0:
|
||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||
hasBin: true
|
||||
@@ -4535,6 +4692,11 @@ packages:
|
||||
react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
|
||||
|
||||
node-domexception@1.0.0:
|
||||
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||
engines: {node: '>=10.5.0'}
|
||||
deprecated: Use your platform's native DOMException instead
|
||||
|
||||
node-fetch@2.7.0:
|
||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
@@ -4544,6 +4706,10 @@ packages:
|
||||
encoding:
|
||||
optional: true
|
||||
|
||||
node-fetch@3.3.2:
|
||||
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
node-forge@1.3.3:
|
||||
resolution: {integrity: sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==}
|
||||
engines: {node: '>= 6.13.0'}
|
||||
@@ -4680,6 +4846,10 @@ packages:
|
||||
resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
p-retry@4.6.2:
|
||||
resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
p-retry@7.1.1:
|
||||
resolution: {integrity: sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w==}
|
||||
engines: {node: '>=20'}
|
||||
@@ -4859,6 +5029,10 @@ packages:
|
||||
prop-types@15.8.1:
|
||||
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
|
||||
|
||||
protobufjs@7.5.4:
|
||||
resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
proxy-addr@2.0.7:
|
||||
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
||||
engines: {node: '>= 0.10'}
|
||||
@@ -5167,6 +5341,10 @@ packages:
|
||||
resolution: {integrity: sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
retry@0.13.1:
|
||||
resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
reusify@1.1.0:
|
||||
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
@@ -5744,6 +5922,10 @@ packages:
|
||||
wcwidth@1.0.1:
|
||||
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
|
||||
|
||||
web-streams-polyfill@3.3.3:
|
||||
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
|
||||
@@ -6939,6 +7121,17 @@ snapshots:
|
||||
'@shikijs/types': 3.23.0
|
||||
'@shikijs/vscode-textmate': 10.0.2
|
||||
|
||||
'@google/genai@1.46.0':
|
||||
dependencies:
|
||||
google-auth-library: 10.6.2
|
||||
p-retry: 4.6.2
|
||||
protobufjs: 7.5.4
|
||||
ws: 8.19.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
'@hookform/resolvers@3.10.0(react-hook-form@7.71.2(react@19.1.0))':
|
||||
dependencies:
|
||||
react-hook-form: 7.71.2(react@19.1.0)
|
||||
@@ -7175,6 +7368,29 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@protobufjs/aspromise@1.1.2': {}
|
||||
|
||||
'@protobufjs/base64@1.1.2': {}
|
||||
|
||||
'@protobufjs/codegen@2.0.4': {}
|
||||
|
||||
'@protobufjs/eventemitter@1.1.0': {}
|
||||
|
||||
'@protobufjs/fetch@1.1.0':
|
||||
dependencies:
|
||||
'@protobufjs/aspromise': 1.1.2
|
||||
'@protobufjs/inquire': 1.1.0
|
||||
|
||||
'@protobufjs/float@1.0.2': {}
|
||||
|
||||
'@protobufjs/inquire@1.1.0': {}
|
||||
|
||||
'@protobufjs/path@1.1.2': {}
|
||||
|
||||
'@protobufjs/pool@1.1.0': {}
|
||||
|
||||
'@protobufjs/utf8@1.1.0': {}
|
||||
|
||||
'@radix-ui/number@1.1.1': {}
|
||||
|
||||
'@radix-ui/primitive@1.1.3': {}
|
||||
@@ -8560,6 +8776,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 25.3.5
|
||||
|
||||
'@types/retry@0.12.0': {}
|
||||
|
||||
'@types/send@1.2.1':
|
||||
dependencies:
|
||||
'@types/node': 25.3.5
|
||||
@@ -8940,6 +9158,8 @@ snapshots:
|
||||
|
||||
big-integer@1.6.52: {}
|
||||
|
||||
bignumber.js@9.3.1: {}
|
||||
|
||||
body-parser@2.2.2:
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
@@ -8997,6 +9217,8 @@ snapshots:
|
||||
dependencies:
|
||||
node-int64: 0.4.0
|
||||
|
||||
buffer-equal-constant-time@1.0.1: {}
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
|
||||
buffer@5.7.1:
|
||||
@@ -9275,6 +9497,8 @@ snapshots:
|
||||
|
||||
d3-timer@3.0.1: {}
|
||||
|
||||
data-uri-to-buffer@4.0.1: {}
|
||||
|
||||
date-fns-jalali@4.1.0-0: {}
|
||||
|
||||
date-fns@3.6.0: {}
|
||||
@@ -9377,6 +9601,10 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
gopd: 1.2.0
|
||||
|
||||
ecdsa-sig-formatter@1.0.11:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
ee-first@1.1.1: {}
|
||||
|
||||
electron-to-chromium@1.5.307: {}
|
||||
@@ -9802,6 +10030,8 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
extend@3.0.2: {}
|
||||
|
||||
fast-deep-equal@3.1.3: {}
|
||||
|
||||
fast-equals@5.4.0: {}
|
||||
@@ -9846,6 +10076,11 @@ snapshots:
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.3
|
||||
|
||||
fetch-blob@3.2.0:
|
||||
dependencies:
|
||||
node-domexception: 1.0.0
|
||||
web-streams-polyfill: 3.3.3
|
||||
|
||||
figures@6.1.0:
|
||||
dependencies:
|
||||
is-unicode-supported: 2.1.0
|
||||
@@ -9909,6 +10144,10 @@ snapshots:
|
||||
|
||||
fontfaceobserver@2.3.0: {}
|
||||
|
||||
formdata-polyfill@4.0.10:
|
||||
dependencies:
|
||||
fetch-blob: 3.2.0
|
||||
|
||||
forwarded@0.2.0: {}
|
||||
|
||||
framer-motion@12.35.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||
@@ -9946,6 +10185,22 @@ snapshots:
|
||||
|
||||
function-bind@1.1.2: {}
|
||||
|
||||
gaxios@7.1.4:
|
||||
dependencies:
|
||||
extend: 3.0.2
|
||||
https-proxy-agent: 7.0.6
|
||||
node-fetch: 3.3.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
gcp-metadata@8.1.2:
|
||||
dependencies:
|
||||
gaxios: 7.1.4
|
||||
google-logging-utils: 1.1.3
|
||||
json-bigint: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
gensync@1.0.0-beta.2: {}
|
||||
|
||||
get-caller-file@2.0.5: {}
|
||||
@@ -10030,6 +10285,19 @@ snapshots:
|
||||
slash: 5.1.0
|
||||
unicorn-magic: 0.4.0
|
||||
|
||||
google-auth-library@10.6.2:
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
ecdsa-sig-formatter: 1.0.11
|
||||
gaxios: 7.1.4
|
||||
gcp-metadata: 8.1.2
|
||||
google-logging-utils: 1.1.3
|
||||
jws: 4.0.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
google-logging-utils@1.1.3: {}
|
||||
|
||||
gopd@1.2.0: {}
|
||||
|
||||
got@11.8.6:
|
||||
@@ -10295,6 +10563,10 @@ snapshots:
|
||||
|
||||
jsesc@3.1.0: {}
|
||||
|
||||
json-bigint@1.0.0:
|
||||
dependencies:
|
||||
bignumber.js: 9.3.1
|
||||
|
||||
json-buffer@3.0.1: {}
|
||||
|
||||
json-schema-to-ts@3.1.1:
|
||||
@@ -10322,6 +10594,17 @@ snapshots:
|
||||
|
||||
jsonpointer@5.0.1: {}
|
||||
|
||||
jwa@2.0.1:
|
||||
dependencies:
|
||||
buffer-equal-constant-time: 1.0.1
|
||||
ecdsa-sig-formatter: 1.0.11
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
jws@4.0.1:
|
||||
dependencies:
|
||||
jwa: 2.0.1
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
keyv@4.5.4:
|
||||
dependencies:
|
||||
json-buffer: 3.0.1
|
||||
@@ -10383,6 +10666,8 @@ snapshots:
|
||||
dependencies:
|
||||
chalk: 2.4.2
|
||||
|
||||
long@5.3.2: {}
|
||||
|
||||
loose-envify@1.4.0:
|
||||
dependencies:
|
||||
js-tokens: 4.0.0
|
||||
@@ -10875,10 +11160,18 @@ snapshots:
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
|
||||
node-domexception@1.0.0: {}
|
||||
|
||||
node-fetch@2.7.0:
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
|
||||
node-fetch@3.3.2:
|
||||
dependencies:
|
||||
data-uri-to-buffer: 4.0.1
|
||||
fetch-blob: 3.2.0
|
||||
formdata-polyfill: 4.0.10
|
||||
|
||||
node-forge@1.3.3: {}
|
||||
|
||||
node-int64@0.4.0: {}
|
||||
@@ -11048,6 +11341,11 @@ snapshots:
|
||||
dependencies:
|
||||
p-limit: 4.0.0
|
||||
|
||||
p-retry@4.6.2:
|
||||
dependencies:
|
||||
'@types/retry': 0.12.0
|
||||
retry: 0.13.1
|
||||
|
||||
p-retry@7.1.1:
|
||||
dependencies:
|
||||
is-network-error: 1.3.1
|
||||
@@ -11197,6 +11495,21 @@ snapshots:
|
||||
object-assign: 4.1.1
|
||||
react-is: 16.13.1
|
||||
|
||||
protobufjs@7.5.4:
|
||||
dependencies:
|
||||
'@protobufjs/aspromise': 1.1.2
|
||||
'@protobufjs/base64': 1.1.2
|
||||
'@protobufjs/codegen': 2.0.4
|
||||
'@protobufjs/eventemitter': 1.1.0
|
||||
'@protobufjs/fetch': 1.1.0
|
||||
'@protobufjs/float': 1.0.2
|
||||
'@protobufjs/inquire': 1.1.0
|
||||
'@protobufjs/path': 1.1.2
|
||||
'@protobufjs/pool': 1.1.0
|
||||
'@protobufjs/utf8': 1.1.0
|
||||
'@types/node': 25.3.5
|
||||
long: 5.3.2
|
||||
|
||||
proxy-addr@2.0.7:
|
||||
dependencies:
|
||||
forwarded: 0.2.0
|
||||
@@ -11590,6 +11903,8 @@ snapshots:
|
||||
onetime: 2.0.1
|
||||
signal-exit: 3.0.7
|
||||
|
||||
retry@0.13.1: {}
|
||||
|
||||
reusify@1.1.0: {}
|
||||
|
||||
rimraf@3.0.2:
|
||||
@@ -12131,6 +12446,8 @@ snapshots:
|
||||
dependencies:
|
||||
defaults: 1.0.4
|
||||
|
||||
web-streams-polyfill@3.3.3: {}
|
||||
|
||||
webidl-conversions@3.0.1: {}
|
||||
|
||||
webidl-conversions@5.0.0: {}
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
{
|
||||
"path": "./lib/integrations-anthropic-ai"
|
||||
},
|
||||
{
|
||||
"path": "./lib/integrations-gemini-ai"
|
||||
},
|
||||
{
|
||||
"path": "./artifacts/api-server"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user