import { z } from 'zod' export const ROUTE_PATH = '/api/blog_search' const EMBEDDING_DIMENSION = 1024 as const const MAX_TEXT_LENGTH = 1_000 const MAX_RETRIES = 5 const WORKERS_AI_MAX_RETRIES = 3 const RequestBodySchema = z.object({ text: z.string(), }) const AIResponseSchema = z.object({ data: z.array(z.array(z.number()).length(EMBEDDING_DIMENSION)).length(1), }) const textResponse = (msg: string, status = 400) => new Response(msg, { status }) export async function handle(c: HonoContext) { let json: unknown try { json = await c.req.json() } catch { return textResponse('Bad Request: Invalid JSON', 400) } const body = RequestBodySchema.safeParse(json) if (!body.success) { return textResponse(`Bad Request: ${body.error.message}`, 400) } const text = body.data.text.trim() if (text.length > MAX_TEXT_LENGTH) { return textResponse('Bad Request: Text is too long', 400) } let embedding: number[] | null = null for (let attempt = 0; attempt < MAX_RETRIES; attempt++) { try { const raw = await c.env.AI.run( '@cf/baai/bge-m3', { text }, { gateway: { id: 'workers-embedding', retries: { maxAttempts: WORKERS_AI_MAX_RETRIES }, }, }, ) const parsed = AIResponseSchema.safeParse(raw) if (!parsed.success) { return textResponse(`Internal Server Error: ${parsed.error.message}`, 500) } embedding = parsed.data.data[0] break } catch (err) { console.error(`Embedding attempt ${attempt + 1} failed`, err) } } if (!embedding) { return textResponse('Internal Server Error: Embedding failed', 500) } // ─── Respond with binary vector ──────────────────────────────────────────── const buffer = new Float32Array(embedding).buffer const headers: Record = { 'Content-Type': 'application/octet-stream', } return new Response(buffer, { status: 200, headers }) }