import { randomUUID } from 'node:crypto'; import type { NextRequest } from 'next/server'; import type { IService } from 'soma-core'; import { AiApiRuntime } from '../../../domain/ai-api/AiApiRuntime'; import { aiErrorResponse, aiSuccessResponse } from '../../../domain/ai-shared/AiResponse'; import { readBodyParams, runService } from '../../common/ServiceRuntime'; const REQUEST_ID_HEADER = 'X-Request-Id'; const REQUEST_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/; export default class InvokeService implements IService { async post(req: NextRequest): Promise { const requestId = resolveRequestId(req.headers.get('x-request-id')); return runService(async () => { try { const body = await readBodyParams(req); const result = await AiApiRuntime.getInstance().invoke( body.func as string, body.input as Record, req.signal, ); return withRequestId(aiSuccessResponse({ func: body.func, output: result.output, meta: { ...result.meta, requestId, }, }), requestId); } catch (error) { return withRequestId(aiErrorResponse(error), requestId); } }); } } export function resolveRequestId(candidate: string | null): string { return candidate !== null && REQUEST_ID_PATTERN.test(candidate) ? candidate : randomUUID(); } function withRequestId(response: Response, requestId: string): Response { response.headers.set(REQUEST_ID_HEADER, requestId); return response; }