@genkit-ai/next
Version:
Next.js plugin for Genkit
1 lines • 13.7 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { randomUUID } from 'crypto';\nimport {\n Action,\n AsyncTaskQueue,\n StreamNotFoundError,\n type ActionContext,\n type ActionStreamInput,\n type StreamManager,\n type z,\n} from 'genkit/beta';\nimport {\n getCallableJSON,\n getHttpStatus,\n type ContextProvider,\n type RequestData,\n} from 'genkit/context';\nimport { NextRequest, NextResponse } from 'next/server.js';\nexport { NextRequest, NextResponse, z, type Action, type ActionContext };\n\nconst delimiter = '\\n\\n';\n\nasync function subscribeToStream<S, O>(\n streamManager: StreamManager,\n streamId: string\n): Promise<NextResponse | null> {\n try {\n const encoder = new TextEncoder();\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n await streamManager.subscribe(streamId, {\n onChunk: (chunk) => {\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ message: chunk }) + delimiter\n )\n );\n },\n onDone: (output) => {\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ result: output }) + delimiter\n )\n );\n writer.write(encoder.encode('END'));\n writer.close();\n },\n onError: (err) => {\n console.error(\n `Streaming request failed with error: ${(err as Error).message}\\n${\n (err as Error).stack\n }`\n );\n writer.write(\n encoder.encode(\n `error: ${JSON.stringify({\n error: getCallableJSON(err),\n })}${delimiter}`\n )\n );\n writer.write(encoder.encode('END'));\n writer.close();\n },\n });\n return new NextResponse(readable, {\n status: 200,\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'Transfer-Encoding': 'chunked',\n 'x-genkit-stream-id': streamId,\n },\n });\n } catch (e: any) {\n if (e instanceof StreamNotFoundError) {\n return new NextResponse(null, { status: 204 });\n }\n if (e.status === 'DEADLINE_EXCEEDED') {\n const encoder = new TextEncoder();\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n writer.write(\n encoder.encode(\n `error: ${JSON.stringify({\n error: getCallableJSON(e),\n })}${delimiter}`\n )\n );\n writer.write(encoder.encode('END'));\n writer.close();\n return new NextResponse(readable, {\n status: 200,\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'Transfer-Encoding': 'chunked',\n },\n });\n }\n throw e;\n }\n}\n\nasync function getContext<C extends ActionContext, T>(\n request: NextRequest,\n input: T,\n provider: ContextProvider<C, T> | undefined\n): Promise<C> {\n // Type cast is necessary because there is no runtime way to generate a context if C is provided to appRoute\n // but contextProvider is missing. When I'm less sleepy/busy I'll see if I can make this a type error.\n const context = {} as C;\n if (!provider) {\n return context;\n }\n\n const r: RequestData = {\n method: request.method as RequestData['method'],\n headers: {},\n input,\n };\n request.headers.forEach((val, key) => {\n r.headers[key.toLowerCase()] = val;\n });\n return await provider(r);\n}\n\nfunction appRoute<\n C extends ActionContext = ActionContext,\n I extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n S extends z.ZodTypeAny = z.ZodTypeAny,\n>(\n action: Action<I, O, S>,\n opts?: {\n contextProvider?: ContextProvider<C, I>;\n streamManager?: StreamManager;\n }\n) {\n return async (req: NextRequest): Promise<NextResponse> => {\n let context: C = {} as C;\n const { data: input } = await req.json();\n const streamId = req.headers.get('x-genkit-stream-id');\n if (req.headers.get('accept') !== 'text/event-stream') {\n try {\n context = await getContext(req, input, opts?.contextProvider);\n } catch (e) {\n console.error('Error gathering context for running action:', e);\n return NextResponse.json(\n { error: getCallableJSON(e) },\n { status: getHttpStatus(e) }\n );\n }\n try {\n const resp = await action.run(input, {\n context,\n abortSignal: req.signal,\n });\n const response = NextResponse.json({ result: resp.result });\n if (opts?.streamManager && streamId) {\n response.headers.set('x-genkit-stream-id', streamId);\n }\n return response;\n } catch (e) {\n // For security reasons, log the error rather than responding with it.\n console.error('Error calling action:', e);\n return NextResponse.json(\n { error: getCallableJSON(e) },\n { status: getHttpStatus(e) }\n );\n }\n }\n\n try {\n context = await getContext(req, input, opts?.contextProvider);\n } catch (e) {\n console.error('Error gathering context for streaming action:', e);\n return new NextResponse(\n `error: ${JSON.stringify(getCallableJSON(e))}${delimiter}END`,\n { status: getHttpStatus(e) }\n );\n }\n const streamManager = opts?.streamManager;\n if (streamManager && streamId) {\n const response = await subscribeToStream(streamManager, streamId);\n if (response) {\n return response;\n }\n }\n\n const streamIdToUse = randomUUID();\n const encoder = new TextEncoder();\n const { readable, writable } = new TransformStream();\n\n // Not using a dangling promise causes this closure to block on the stream being drained,\n // which doesn't happen until the NextResponse is consumed later in the cosure.\n // TODO: Add ping comments at regular intervals between streaming responses to mitigate\n // timeouts.\n (async (): Promise<void> => {\n const writer = writable.getWriter();\n const taskQueue = new AsyncTaskQueue();\n let durableStream: ActionStreamInput<S, O> | undefined = undefined;\n if (streamManager) {\n durableStream = await streamManager.open(streamIdToUse);\n }\n try {\n const output = action.run(input, {\n context,\n abortSignal: req.signal,\n onChunk: (chunk) => {\n if (durableStream) {\n taskQueue.enqueue(() => durableStream!.write(chunk));\n }\n taskQueue.enqueue(() =>\n writer.write(\n encoder.encode(\n `data: ${JSON.stringify({ message: chunk })}${delimiter}`\n )\n )\n );\n },\n });\n const finalOutput = await output;\n if (durableStream) {\n taskQueue.enqueue(() => durableStream!.done(finalOutput.result));\n }\n taskQueue.enqueue(() =>\n writer.write(\n encoder.encode(\n `data: ${JSON.stringify({ result: finalOutput.result })}${delimiter}`\n )\n )\n );\n taskQueue.enqueue(() => writer.write(encoder.encode('END')));\n } catch (err) {\n if (durableStream) {\n taskQueue.enqueue(() => durableStream!.error(err));\n }\n console.error('Error streaming action:', err);\n taskQueue.enqueue(() =>\n writer.write(\n encoder.encode(\n `error: ${JSON.stringify(getCallableJSON(err))}` + '\\n\\n'\n )\n )\n );\n taskQueue.enqueue(() => writer.write(encoder.encode('END')));\n } finally {\n await taskQueue.merge();\n await writer.close();\n }\n })();\n\n const headers = {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'Transfer-Encoding': 'chunked',\n };\n if (streamManager) {\n headers['x-genkit-stream-id'] = streamIdToUse;\n }\n\n return new NextResponse(readable, {\n status: 200,\n headers,\n });\n };\n}\n\nexport default appRoute;\nexport { appRoute };\n"],"mappings":"AAgBA,SAAS,kBAAkB;AAC3B;AAAA,EAEE;AAAA,EACA;AAAA,OAKK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,SAAS,aAAa,oBAAoB;AAG1C,MAAM,YAAY;AAElB,eAAe,kBACb,eACA,UAC8B;AAC9B,MAAI;AACF,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,EAAE,UAAU,SAAS,IAAI,IAAI,gBAAgB;AACnD,UAAM,SAAS,SAAS,UAAU;AAClC,UAAM,cAAc,UAAU,UAAU;AAAA,MACtC,SAAS,CAAC,UAAU;AAClB,eAAO;AAAA,UACL,QAAQ;AAAA,YACN,WAAW,KAAK,UAAU,EAAE,SAAS,MAAM,CAAC,IAAI;AAAA,UAClD;AAAA,QACF;AAAA,MACF;AAAA,MACA,QAAQ,CAAC,WAAW;AAClB,eAAO;AAAA,UACL,QAAQ;AAAA,YACN,WAAW,KAAK,UAAU,EAAE,QAAQ,OAAO,CAAC,IAAI;AAAA,UAClD;AAAA,QACF;AACA,eAAO,MAAM,QAAQ,OAAO,KAAK,CAAC;AAClC,eAAO,MAAM;AAAA,MACf;AAAA,MACA,SAAS,CAAC,QAAQ;AAChB,gBAAQ;AAAA,UACN,wCAAyC,IAAc,OAAO;AAAA,EAC3D,IAAc,KACjB;AAAA,QACF;AACA,eAAO;AAAA,UACL,QAAQ;AAAA,YACN,UAAU,KAAK,UAAU;AAAA,cACvB,OAAO,gBAAgB,GAAG;AAAA,YAC5B,CAAC,CAAC,GAAG,SAAS;AAAA,UAChB;AAAA,QACF;AACA,eAAO,MAAM,QAAQ,OAAO,KAAK,CAAC;AAClC,eAAO,MAAM;AAAA,MACf;AAAA,IACF,CAAC;AACD,WAAO,IAAI,aAAa,UAAU;AAAA,MAChC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,QACjB,YAAY;AAAA,QACZ,qBAAqB;AAAA,QACrB,sBAAsB;AAAA,MACxB;AAAA,IACF,CAAC;AAAA,EACH,SAAS,GAAQ;AACf,QAAI,aAAa,qBAAqB;AACpC,aAAO,IAAI,aAAa,MAAM,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC/C;AACA,QAAI,EAAE,WAAW,qBAAqB;AACpC,YAAM,UAAU,IAAI,YAAY;AAChC,YAAM,EAAE,UAAU,SAAS,IAAI,IAAI,gBAAgB;AACnD,YAAM,SAAS,SAAS,UAAU;AAClC,aAAO;AAAA,QACL,QAAQ;AAAA,UACN,UAAU,KAAK,UAAU;AAAA,YACvB,OAAO,gBAAgB,CAAC;AAAA,UAC1B,CAAC,CAAC,GAAG,SAAS;AAAA,QAChB;AAAA,MACF;AACA,aAAO,MAAM,QAAQ,OAAO,KAAK,CAAC;AAClC,aAAO,MAAM;AACb,aAAO,IAAI,aAAa,UAAU;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,iBAAiB;AAAA,UACjB,YAAY;AAAA,UACZ,qBAAqB;AAAA,QACvB;AAAA,MACF,CAAC;AAAA,IACH;AACA,UAAM;AAAA,EACR;AACF;AAEA,eAAe,WACb,SACA,OACA,UACY;AAGZ,QAAM,UAAU,CAAC;AACjB,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,QAAM,IAAiB;AAAA,IACrB,QAAQ,QAAQ;AAAA,IAChB,SAAS,CAAC;AAAA,IACV;AAAA,EACF;AACA,UAAQ,QAAQ,QAAQ,CAAC,KAAK,QAAQ;AACpC,MAAE,QAAQ,IAAI,YAAY,CAAC,IAAI;AAAA,EACjC,CAAC;AACD,SAAO,MAAM,SAAS,CAAC;AACzB;AAEA,SAAS,SAMP,QACA,MAIA;AACA,SAAO,OAAO,QAA4C;AACxD,QAAI,UAAa,CAAC;AAClB,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,IAAI,KAAK;AACvC,UAAM,WAAW,IAAI,QAAQ,IAAI,oBAAoB;AACrD,QAAI,IAAI,QAAQ,IAAI,QAAQ,MAAM,qBAAqB;AACrD,UAAI;AACF,kBAAU,MAAM,WAAW,KAAK,OAAO,MAAM,eAAe;AAAA,MAC9D,SAAS,GAAG;AACV,gBAAQ,MAAM,+CAA+C,CAAC;AAC9D,eAAO,aAAa;AAAA,UAClB,EAAE,OAAO,gBAAgB,CAAC,EAAE;AAAA,UAC5B,EAAE,QAAQ,cAAc,CAAC,EAAE;AAAA,QAC7B;AAAA,MACF;AACA,UAAI;AACF,cAAM,OAAO,MAAM,OAAO,IAAI,OAAO;AAAA,UACnC;AAAA,UACA,aAAa,IAAI;AAAA,QACnB,CAAC;AACD,cAAM,WAAW,aAAa,KAAK,EAAE,QAAQ,KAAK,OAAO,CAAC;AAC1D,YAAI,MAAM,iBAAiB,UAAU;AACnC,mBAAS,QAAQ,IAAI,sBAAsB,QAAQ;AAAA,QACrD;AACA,eAAO;AAAA,MACT,SAAS,GAAG;AAEV,gBAAQ,MAAM,yBAAyB,CAAC;AACxC,eAAO,aAAa;AAAA,UAClB,EAAE,OAAO,gBAAgB,CAAC,EAAE;AAAA,UAC5B,EAAE,QAAQ,cAAc,CAAC,EAAE;AAAA,QAC7B;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACF,gBAAU,MAAM,WAAW,KAAK,OAAO,MAAM,eAAe;AAAA,IAC9D,SAAS,GAAG;AACV,cAAQ,MAAM,iDAAiD,CAAC;AAChE,aAAO,IAAI;AAAA,QACT,UAAU,KAAK,UAAU,gBAAgB,CAAC,CAAC,CAAC,GAAG,SAAS;AAAA,QACxD,EAAE,QAAQ,cAAc,CAAC,EAAE;AAAA,MAC7B;AAAA,IACF;AACA,UAAM,gBAAgB,MAAM;AAC5B,QAAI,iBAAiB,UAAU;AAC7B,YAAM,WAAW,MAAM,kBAAkB,eAAe,QAAQ;AAChE,UAAI,UAAU;AACZ,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,gBAAgB,WAAW;AACjC,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,EAAE,UAAU,SAAS,IAAI,IAAI,gBAAgB;AAMnD,KAAC,YAA2B;AAC1B,YAAM,SAAS,SAAS,UAAU;AAClC,YAAM,YAAY,IAAI,eAAe;AACrC,UAAI,gBAAqD;AACzD,UAAI,eAAe;AACjB,wBAAgB,MAAM,cAAc,KAAK,aAAa;AAAA,MACxD;AACA,UAAI;AACF,cAAM,SAAS,OAAO,IAAI,OAAO;AAAA,UAC/B;AAAA,UACA,aAAa,IAAI;AAAA,UACjB,SAAS,CAAC,UAAU;AAClB,gBAAI,eAAe;AACjB,wBAAU,QAAQ,MAAM,cAAe,MAAM,KAAK,CAAC;AAAA,YACrD;AACA,sBAAU;AAAA,cAAQ,MAChB,OAAO;AAAA,gBACL,QAAQ;AAAA,kBACN,SAAS,KAAK,UAAU,EAAE,SAAS,MAAM,CAAC,CAAC,GAAG,SAAS;AAAA,gBACzD;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AACD,cAAM,cAAc,MAAM;AAC1B,YAAI,eAAe;AACjB,oBAAU,QAAQ,MAAM,cAAe,KAAK,YAAY,MAAM,CAAC;AAAA,QACjE;AACA,kBAAU;AAAA,UAAQ,MAChB,OAAO;AAAA,YACL,QAAQ;AAAA,cACN,SAAS,KAAK,UAAU,EAAE,QAAQ,YAAY,OAAO,CAAC,CAAC,GAAG,SAAS;AAAA,YACrE;AAAA,UACF;AAAA,QACF;AACA,kBAAU,QAAQ,MAAM,OAAO,MAAM,QAAQ,OAAO,KAAK,CAAC,CAAC;AAAA,MAC7D,SAAS,KAAK;AACZ,YAAI,eAAe;AACjB,oBAAU,QAAQ,MAAM,cAAe,MAAM,GAAG,CAAC;AAAA,QACnD;AACA,gBAAQ,MAAM,2BAA2B,GAAG;AAC5C,kBAAU;AAAA,UAAQ,MAChB,OAAO;AAAA,YACL,QAAQ;AAAA,cACN,UAAU,KAAK,UAAU,gBAAgB,GAAG,CAAC,CAAC;AAAA;AAAA;AAAA,YAChD;AAAA,UACF;AAAA,QACF;AACA,kBAAU,QAAQ,MAAM,OAAO,MAAM,QAAQ,OAAO,KAAK,CAAC,CAAC;AAAA,MAC7D,UAAE;AACA,cAAM,UAAU,MAAM;AACtB,cAAM,OAAO,MAAM;AAAA,MACrB;AAAA,IACF,GAAG;AAEH,UAAM,UAAU;AAAA,MACd,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,YAAY;AAAA,MACZ,qBAAqB;AAAA,IACvB;AACA,QAAI,eAAe;AACjB,cAAQ,oBAAoB,IAAI;AAAA,IAClC;AAEA,WAAO,IAAI,aAAa,UAAU;AAAA,MAChC,QAAQ;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,IAAO,gBAAQ;","names":[]}