@genkit-ai/fetch 0.1.3 → 0.2.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -60,7 +60,11 @@ Use a context provider (e.g. for auth) and attach it to an action with `withActi
60
60
  ```ts
61
61
  import { UserFacingError } from 'genkit';
62
62
  import type { ContextProvider, RequestData } from 'genkit/context';
63
- import { fetchHandler, fetchHandlers, withActionOptions } from '@genkit-ai/fetch';
63
+ import {
64
+ fetchHandler,
65
+ fetchHandlers,
66
+ withActionOptions,
67
+ } from '@genkit-ai/fetch';
64
68
 
65
69
  const authContext: ContextProvider<{ userId: string }> = (req: RequestData) => {
66
70
  if (req.headers['authorization'] !== 'Bearer open-sesame') {
@@ -90,7 +94,11 @@ Provide a `streamManager` in the options. For development, use `InMemoryStreamMa
90
94
 
91
95
  ```ts
92
96
  import { InMemoryStreamManager } from 'genkit/beta';
93
- import { fetchHandler, fetchHandlers, withActionOptions } from '@genkit-ai/fetch';
97
+ import {
98
+ fetchHandler,
99
+ fetchHandlers,
100
+ withActionOptions,
101
+ } from '@genkit-ai/fetch';
94
102
 
95
103
  app.all('/myDurableFlow', (c) =>
96
104
  fetchHandler(myFlow, {
@@ -159,15 +167,40 @@ for await (const chunk of result.stream) {
159
167
  console.log(await result.output);
160
168
  ```
161
169
 
170
+ ### Initialization Data
171
+
172
+ If your flow or action accepts initialization data (defined via `initSchema`), you can pass it using the `init` option in the client:
173
+
174
+ ```ts
175
+ const result = await runFlow({
176
+ url: 'http://localhost:3780/api/myFlow',
177
+ input: 'say hello',
178
+ init: { sessionId: 'abc123', config: { temperature: 0.7 } },
179
+ });
180
+
181
+ // Also works with streaming
182
+ const streamed = streamFlow({
183
+ url: 'http://localhost:3780/api/myFlow',
184
+ input: 'say hello',
185
+ init: { sessionId: 'abc123' },
186
+ });
187
+ for await (const chunk of streamed.stream) {
188
+ console.log(chunk);
189
+ }
190
+ console.log(await streamed.output);
191
+ ```
192
+
193
+ The `init` data is sent in the request body alongside `data` and is validated against the action's `initSchema` on the server side. If the `init` data does not conform to the `initSchema`, the request fails with a `400 INVALID_ARGUMENT` error before the flow runs.
194
+
162
195
  ## API summary
163
196
 
164
- | Export | Description |
165
- |---------------------|-----------------------------------------------------------------------------|
166
- | `fetchHandler(action, options?)` | Returns a handler `(request) => Promise<Response>` for a single action (flow, model, etc.). |
167
- | `fetchHandlers(actions, pathPrefix?)` | Returns a handler that dispatches by path to one of the given actions. |
168
- | `withActionOptions(action, options)` | Wraps an action with `contextProvider`, `streamManager`, or custom `path`. |
169
- | `ActionWithOptions` | Type for an action plus options. |
170
- | `FetchHandlerOptions` | Options for `fetchHandler`: `contextProvider`, `streamManager`. |
197
+ | Export | Description |
198
+ | ------------------------------------- | ------------------------------------------------------------------------------------------- |
199
+ | `fetchHandler(action, options?)` | Returns a handler `(request) => Promise<Response>` for a single action (flow, model, etc.). |
200
+ | `fetchHandlers(actions, pathPrefix?)` | Returns a handler that dispatches by path to one of the given actions. |
201
+ | `withActionOptions(action, options)` | Wraps an action with `contextProvider`, `streamManager`, or custom `path`. |
202
+ | `ActionWithOptions` | Type for an action plus options. |
203
+ | `FetchHandlerOptions` | Options for `fetchHandler`: `contextProvider`, `streamManager`. |
171
204
 
172
205
  Request body must be JSON with a `data` field: `{ "data": <input> }`. For streaming, use `Accept: text/event-stream` or query `?stream=true`.
173
206
 
package/lib/index.d.mts CHANGED
@@ -43,7 +43,7 @@ declare function withActionOptions<I extends z.ZodTypeAny, O extends z.ZodTypeAn
43
43
  streamManager?: StreamManager;
44
44
  path?: string;
45
45
  }): ActionWithOptions<I, O, S>;
46
- declare function fetchHandler<C extends ActionContext = ActionContext, I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny>(action: Action<I, O, S>, options?: FetchHandlerOptions<C, I>): (request: Request) => Promise<Response>;
46
+ declare function fetchHandler<C extends ActionContext = ActionContext, I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny, Init extends z.ZodTypeAny = z.ZodTypeAny>(action: Action<I, O, S, any, Init>, options?: FetchHandlerOptions<C, I>): (request: Request) => Promise<Response>;
47
47
  /**
48
48
  * Returns a Fetch handler for multiple actions with path-based routing.
49
49
  * Express-like API: pass actions and optional path prefix, then call the returned handler with the request.
package/lib/index.d.ts CHANGED
@@ -43,7 +43,7 @@ declare function withActionOptions<I extends z.ZodTypeAny, O extends z.ZodTypeAn
43
43
  streamManager?: StreamManager;
44
44
  path?: string;
45
45
  }): ActionWithOptions<I, O, S>;
46
- declare function fetchHandler<C extends ActionContext = ActionContext, I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny>(action: Action<I, O, S>, options?: FetchHandlerOptions<C, I>): (request: Request) => Promise<Response>;
46
+ declare function fetchHandler<C extends ActionContext = ActionContext, I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny, Init extends z.ZodTypeAny = z.ZodTypeAny>(action: Action<I, O, S, any, Init>, options?: FetchHandlerOptions<C, I>): (request: Request) => Promise<Response>;
47
47
  /**
48
48
  * Returns a Fetch handler for multiple actions with path-based routing.
49
49
  * Express-like API: pass actions and optional path prefix, then call the returned handler with the request.
package/lib/index.js CHANGED
@@ -129,7 +129,7 @@ ${(0, import_utils.getErrorStack)(err)}`
129
129
  throw e;
130
130
  }
131
131
  }
132
- async function runActionWithDurableStreaming(action, streamManager, streamId, input, context, writer, abortSignal) {
132
+ async function runActionWithDurableStreaming(action, streamManager, streamId, input, init, context, writer, abortSignal) {
133
133
  const encoder = new TextEncoder();
134
134
  let taskQueue;
135
135
  let durableStream;
@@ -155,7 +155,8 @@ async function runActionWithDurableStreaming(action, streamManager, streamId, in
155
155
  const result = await action.run(input, {
156
156
  onChunk,
157
157
  context,
158
- abortSignal
158
+ abortSignal,
159
+ init
159
160
  });
160
161
  if (streamManager && durableStream) {
161
162
  taskQueue.enqueue(() => durableStream.done(result.result));
@@ -212,6 +213,7 @@ async function handleActionRequest(request, action, options) {
212
213
  );
213
214
  }
214
215
  const input = body.data;
216
+ const init = body.init;
215
217
  let context;
216
218
  try {
217
219
  context = await getContext(request, input, options?.contextProvider);
@@ -243,6 +245,7 @@ ${e.stack}`
243
245
  options?.streamManager,
244
246
  streamIdToUse,
245
247
  input,
248
+ init,
246
249
  context,
247
250
  writer,
248
251
  request.signal
@@ -266,7 +269,8 @@ ${e.stack}`
266
269
  try {
267
270
  const result = await action.run(input, {
268
271
  context,
269
- abortSignal: request.signal
272
+ abortSignal: request.signal,
273
+ init
270
274
  });
271
275
  const headers = {
272
276
  "x-genkit-trace-id": result.telemetry.traceId,
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"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 {\n Action,\n ActionStreamInput,\n AsyncTaskQueue,\n StreamNotFoundError,\n type ActionContext,\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 { logger } from 'genkit/logging';\nimport { getErrorMessage, getErrorStack } from './utils.js';\n\nconst streamDelimiter = '\\n\\n';\n\n// Let the runtime provide the randomUUID function.\nconst randomUUID = () => globalThis.crypto.randomUUID();\n\n/**\n * A wrapper object containing an action with its associated options.\n */\nexport type ActionWithOptions<\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 options: {\n contextProvider?: ContextProvider<any, I>;\n streamManager?: StreamManager;\n path?: string;\n };\n};\n\n/**\n * Options for a fetch handler (context provider, stream manager).\n */\nexport interface FetchHandlerOptions<\n C extends ActionContext = ActionContext,\n I extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n contextProvider?: ContextProvider<C, I>;\n streamManager?: StreamManager;\n}\n\n/**\n * Wraps an action (flow, model, etc.) with options for use with {@link fetchHandlers}.\n */\nexport function withActionOptions<\n I extends z.ZodTypeAny,\n O extends z.ZodTypeAny,\n S extends z.ZodTypeAny,\n>(\n action: Action<I, O, S>,\n options: {\n contextProvider?: ContextProvider<any, I>;\n streamManager?: StreamManager;\n path?: string;\n }\n): ActionWithOptions<I, O, S> {\n return {\n action,\n options,\n };\n}\n\n/**\n * Converts Headers object to a plain object with lowercase keys.\n */\nfunction headersToObject(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n result[key.toLowerCase()] = value;\n });\n return result;\n}\n\n/**\n * Gets context from the request using the context provider if available.\n */\nasync function getContext<C extends ActionContext, I extends z.ZodTypeAny>(\n request: Request,\n input: z.infer<I>,\n provider?: ContextProvider<C, I>\n): Promise<C> {\n const context = {} as C;\n if (!provider) {\n return context;\n }\n\n const requestData: RequestData = {\n method: request.method as RequestData['method'],\n headers: headersToObject(request.headers),\n input,\n };\n\n return await provider(requestData);\n}\n\n/**\n * Subscribes to an existing stream using StreamManager.\n */\nasync function subscribeToStream(\n streamManager: StreamManager,\n streamId: string\n): Promise<Response | null> {\n try {\n const encoder = new TextEncoder();\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n\n await streamManager.subscribe(streamId, {\n onChunk: (chunk) => {\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter\n )\n );\n },\n onDone: (output) => {\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ result: output }) + streamDelimiter\n )\n );\n writer.close();\n },\n onError: (err) => {\n logger.error(\n `Streaming request failed with error: ${getErrorMessage(err)}\\n${getErrorStack(err)}`\n );\n writer.write(\n encoder.encode(\n `error: ${JSON.stringify({\n error: getCallableJSON(err),\n })}${streamDelimiter}`\n )\n );\n writer.close();\n },\n });\n\n return new Response(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 Response(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 })}${streamDelimiter}`\n )\n );\n writer.close();\n return new Response(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\n/**\n * Runs an action with durable streaming support.\n */\nasync function runActionWithDurableStreaming<\n I extends z.ZodTypeAny,\n O extends z.ZodTypeAny,\n S extends z.ZodTypeAny,\n>(\n action: Action<I, O, S>,\n streamManager: StreamManager | undefined,\n streamId: string,\n input: z.infer<I>,\n context: ActionContext,\n writer: WritableStreamDefaultWriter<Uint8Array>,\n abortSignal: AbortSignal\n): Promise<void> {\n const encoder = new TextEncoder();\n let taskQueue: AsyncTaskQueue | undefined;\n let durableStream: ActionStreamInput<any, any> | undefined;\n\n if (streamManager) {\n taskQueue = new AsyncTaskQueue();\n durableStream = await streamManager.open(streamId);\n }\n\n try {\n let onChunk = (chunk: z.infer<S>) => {\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter\n )\n );\n };\n\n if (streamManager && durableStream) {\n const originalOnChunk = onChunk;\n onChunk = (chunk: z.infer<S>) => {\n originalOnChunk(chunk);\n taskQueue!.enqueue(() => durableStream!.write(chunk));\n };\n }\n\n const result = await action.run(input, {\n onChunk,\n context,\n abortSignal,\n });\n\n if (streamManager && durableStream) {\n taskQueue!.enqueue(() => durableStream!.done(result.result));\n await taskQueue!.merge();\n }\n\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ result: result.result }) + streamDelimiter\n )\n );\n writer.close();\n } catch (e) {\n if (durableStream) {\n taskQueue!.enqueue(() => durableStream!.error(e));\n await taskQueue!.merge();\n }\n logger.error(\n `Streaming request failed with error: ${(e as Error).message}\\n${\n (e as Error).stack\n }`\n );\n writer.write(\n encoder.encode(\n `error: ${JSON.stringify({\n error: getCallableJSON(e),\n })}${streamDelimiter}`\n )\n );\n writer.close();\n }\n}\n\n/**\n * Returns a Fetch handler for a single action (flow, model, or any Genkit action).\n * Express-like API: pass the action first, then call the returned handler with the request.\n *\n * @param action - The Genkit action to execute (flow, model, etc.)\n * @param options - Optional configuration including contextProvider and streamManager\n * @returns A handler function that takes a Request and returns a Promise<Response>\n *\n * @example\n * ```typescript\n * import { fetchHandler } from '@genkit-ai/fetch';\n *\n * // Flow or model\n * app.post('/myFlow', (c) => fetchHandler(myFlow)(c.req.raw));\n * app.post('/models/gpt5', (c) => fetchHandler(gpt5)(c.req.raw));\n *\n * // With options\n * app.post('/secure', (c) => fetchHandler(secureFlow, { contextProvider })(c.req.raw));\n * ```\n */\nasync function handleActionRequest<\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 request: Request,\n action: Action<I, O, S>,\n options?: FetchHandlerOptions<C, I>\n): Promise<Response> {\n const url = new URL(request.url);\n const streamParam = url.searchParams.get('stream');\n const shouldStream = streamParam === 'true';\n\n const streamIdHeader = request.headers.get('x-genkit-stream-id');\n const streamId = streamIdHeader || undefined;\n\n let body: any;\n try {\n body = await request.json();\n } catch (e) {\n const errMsg =\n `Error: Failed to parse request body as JSON. ` +\n `Make sure the request has 'Content-Type: application/json' header.`;\n logger.error(errMsg);\n return new Response(\n JSON.stringify({ message: errMsg, status: 'INVALID_ARGUMENT' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n if (!body || typeof body !== 'object' || !('data' in body)) {\n const errMsg =\n `Error: Request body must be a JSON object with a 'data' field. ` +\n `Expected format: {\"data\": ...}`;\n logger.error(errMsg);\n return new Response(\n JSON.stringify({ message: errMsg, status: 'INVALID_ARGUMENT' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const input = body.data as z.infer<I>;\n\n let context: C;\n try {\n context = await getContext(request, input, options?.contextProvider);\n } catch (e: any) {\n logger.error(\n `Context provider failed with error: ${(e as Error).message}\\n${\n (e as Error).stack\n }`\n );\n return new Response(JSON.stringify(getCallableJSON(e)), {\n status: getHttpStatus(e),\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n const acceptHeader = request.headers.get('Accept') || '';\n const isStreaming =\n acceptHeader.toLowerCase().includes('text/event-stream') || shouldStream;\n\n if (isStreaming) {\n const streamManager = options?.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 { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n\n runActionWithDurableStreaming(\n action,\n options?.streamManager,\n streamIdToUse,\n input,\n context,\n writer,\n request.signal\n ).catch((err) => {\n logger.error(`Error in streaming handler: ${err}`);\n });\n\n const headers: Record<string, string> = {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'Transfer-Encoding': 'chunked',\n };\n\n if (options?.streamManager) {\n headers['x-genkit-stream-id'] = streamIdToUse;\n }\n\n return new Response(readable, {\n status: 200,\n headers,\n });\n }\n\n try {\n const result = await action.run(input, {\n context,\n abortSignal: request.signal,\n });\n\n const headers: Record<string, string> = {\n 'x-genkit-trace-id': result.telemetry.traceId,\n 'x-genkit-span-id': result.telemetry.spanId,\n };\n\n return new Response(JSON.stringify({ result: result.result }), {\n status: 200,\n headers: { 'Content-Type': 'application/json', ...headers },\n });\n } catch (e) {\n logger.error(\n `Non-streaming request failed with error: ${(e as Error).message}\\n${\n (e as Error).stack\n }`\n );\n return new Response(JSON.stringify(getCallableJSON(e)), {\n status: getHttpStatus(e),\n headers: { 'Content-Type': 'application/json' },\n });\n }\n}\n\nexport function fetchHandler<\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 options?: FetchHandlerOptions<C, I>\n): (request: Request) => Promise<Response> {\n return (request: Request) => handleActionRequest(request, action, options);\n}\n\n/**\n * Returns a Fetch handler for multiple actions with path-based routing.\n * Express-like API: pass actions and optional path prefix, then call the returned handler with the request.\n *\n * @param actions - Array of actions (or ActionWithOptions) to expose; each is routed by path (action name or options.path)\n * @param pathPrefix - Optional path prefix to strip from the URL (e.g., '/api')\n * @returns A handler function that takes a Request and returns a Promise<Response>\n *\n * @example\n * ```typescript\n * import { fetchHandlers } from '@genkit-ai/fetch';\n *\n * // Mount all actions under /api - routes by path (e.g. POST /api/hello, POST /api/models/gpt5)\n * app.all('/api/*', (c) => fetchHandlers(actions, '/api')(c.req.raw));\n * ```\n */\nexport function fetchHandlers(\n actions: (Action<any, any, any> | ActionWithOptions<any, any, any>)[],\n pathPrefix?: string\n): (request: Request) => Promise<Response> {\n return async (request: Request): Promise<Response> => {\n const url = new URL(request.url);\n let pathname = url.pathname;\n\n if (pathPrefix) {\n const prefix = pathPrefix.endsWith('/') ? pathPrefix : pathPrefix + '/';\n if (pathname === pathPrefix) {\n pathname = '';\n } else if (pathname.startsWith(prefix)) {\n pathname = pathname.slice(prefix.length);\n } else {\n return new Response(\n JSON.stringify({\n status: 'NOT_FOUND',\n message: 'No action matched the request path.',\n }),\n { status: 404, headers: { 'Content-Type': 'application/json' } }\n );\n }\n }\n\n pathname = pathname.replace(/^\\//, '');\n\n let matchedAction: Action<any, any, any> | null = null;\n let handlerOptions: FetchHandlerOptions<any, any> | undefined = undefined;\n\n for (const item of actions) {\n if ('action' in item) {\n const options = item.options;\n const actionPath = options.path ?? item.action.__action.name;\n if (pathname === actionPath) {\n matchedAction = item.action;\n handlerOptions = {\n contextProvider: options.contextProvider,\n streamManager: options.streamManager,\n };\n break;\n }\n } else {\n if (pathname === item.__action.name) {\n matchedAction = item;\n break;\n }\n }\n }\n\n if (!matchedAction) {\n return new Response(\n JSON.stringify({\n status: 'NOT_FOUND',\n message: 'No action matched the request path.',\n }),\n { status: 404, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n return handleActionRequest(request, matchedAction, handlerOptions);\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"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 {\n Action,\n ActionStreamInput,\n AsyncTaskQueue,\n StreamNotFoundError,\n type ActionContext,\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 { logger } from 'genkit/logging';\nimport { getErrorMessage, getErrorStack } from './utils.js';\n\nconst streamDelimiter = '\\n\\n';\n\n// Let the runtime provide the randomUUID function.\nconst randomUUID = () => globalThis.crypto.randomUUID();\n\n/**\n * A wrapper object containing an action with its associated options.\n */\nexport type ActionWithOptions<\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 options: {\n contextProvider?: ContextProvider<any, I>;\n streamManager?: StreamManager;\n path?: string;\n };\n};\n\n/**\n * Options for a fetch handler (context provider, stream manager).\n */\nexport interface FetchHandlerOptions<\n C extends ActionContext = ActionContext,\n I extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n contextProvider?: ContextProvider<C, I>;\n streamManager?: StreamManager;\n}\n\n/**\n * Wraps an action (flow, model, etc.) with options for use with {@link fetchHandlers}.\n */\nexport function withActionOptions<\n I extends z.ZodTypeAny,\n O extends z.ZodTypeAny,\n S extends z.ZodTypeAny,\n>(\n action: Action<I, O, S>,\n options: {\n contextProvider?: ContextProvider<any, I>;\n streamManager?: StreamManager;\n path?: string;\n }\n): ActionWithOptions<I, O, S> {\n return {\n action,\n options,\n };\n}\n\n/**\n * Converts Headers object to a plain object with lowercase keys.\n */\nfunction headersToObject(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n result[key.toLowerCase()] = value;\n });\n return result;\n}\n\n/**\n * Gets context from the request using the context provider if available.\n */\nasync function getContext<C extends ActionContext, I extends z.ZodTypeAny>(\n request: Request,\n input: z.infer<I>,\n provider?: ContextProvider<C, I>\n): Promise<C> {\n const context = {} as C;\n if (!provider) {\n return context;\n }\n\n const requestData: RequestData = {\n method: request.method as RequestData['method'],\n headers: headersToObject(request.headers),\n input,\n };\n\n return await provider(requestData);\n}\n\n/**\n * Subscribes to an existing stream using StreamManager.\n */\nasync function subscribeToStream(\n streamManager: StreamManager,\n streamId: string\n): Promise<Response | null> {\n try {\n const encoder = new TextEncoder();\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n\n await streamManager.subscribe(streamId, {\n onChunk: (chunk) => {\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter\n )\n );\n },\n onDone: (output) => {\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ result: output }) + streamDelimiter\n )\n );\n writer.close();\n },\n onError: (err) => {\n logger.error(\n `Streaming request failed with error: ${getErrorMessage(err)}\\n${getErrorStack(err)}`\n );\n writer.write(\n encoder.encode(\n `error: ${JSON.stringify({\n error: getCallableJSON(err),\n })}${streamDelimiter}`\n )\n );\n writer.close();\n },\n });\n\n return new Response(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 Response(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 })}${streamDelimiter}`\n )\n );\n writer.close();\n return new Response(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\n/**\n * Runs an action with durable streaming support.\n */\nasync function runActionWithDurableStreaming<\n I extends z.ZodTypeAny,\n O extends z.ZodTypeAny,\n S extends z.ZodTypeAny,\n Init extends z.ZodTypeAny = z.ZodTypeAny,\n>(\n action: Action<I, O, S, any, Init>,\n streamManager: StreamManager | undefined,\n streamId: string,\n input: z.infer<I>,\n init: z.infer<Init> | undefined,\n context: ActionContext,\n writer: WritableStreamDefaultWriter<Uint8Array>,\n abortSignal: AbortSignal\n): Promise<void> {\n const encoder = new TextEncoder();\n let taskQueue: AsyncTaskQueue | undefined;\n let durableStream: ActionStreamInput<any, any> | undefined;\n\n if (streamManager) {\n taskQueue = new AsyncTaskQueue();\n durableStream = await streamManager.open(streamId);\n }\n\n try {\n let onChunk = (chunk: z.infer<S>) => {\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter\n )\n );\n };\n\n if (streamManager && durableStream) {\n const originalOnChunk = onChunk;\n onChunk = (chunk: z.infer<S>) => {\n originalOnChunk(chunk);\n taskQueue!.enqueue(() => durableStream!.write(chunk));\n };\n }\n\n const result = await action.run(input, {\n onChunk,\n context,\n abortSignal,\n init,\n });\n\n if (streamManager && durableStream) {\n taskQueue!.enqueue(() => durableStream!.done(result.result));\n await taskQueue!.merge();\n }\n\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ result: result.result }) + streamDelimiter\n )\n );\n writer.close();\n } catch (e) {\n if (durableStream) {\n taskQueue!.enqueue(() => durableStream!.error(e));\n await taskQueue!.merge();\n }\n logger.error(\n `Streaming request failed with error: ${(e as Error).message}\\n${\n (e as Error).stack\n }`\n );\n writer.write(\n encoder.encode(\n `error: ${JSON.stringify({\n error: getCallableJSON(e),\n })}${streamDelimiter}`\n )\n );\n writer.close();\n }\n}\n\n/**\n * Returns a Fetch handler for a single action (flow, model, or any Genkit action).\n * Express-like API: pass the action first, then call the returned handler with the request.\n *\n * @param action - The Genkit action to execute (flow, model, etc.)\n * @param options - Optional configuration including contextProvider and streamManager\n * @returns A handler function that takes a Request and returns a Promise<Response>\n *\n * @example\n * ```typescript\n * import { fetchHandler } from '@genkit-ai/fetch';\n *\n * // Flow or model\n * app.post('/myFlow', (c) => fetchHandler(myFlow)(c.req.raw));\n * app.post('/models/gpt5', (c) => fetchHandler(gpt5)(c.req.raw));\n *\n * // With options\n * app.post('/secure', (c) => fetchHandler(secureFlow, { contextProvider })(c.req.raw));\n * ```\n */\nasync function handleActionRequest<\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 Init extends z.ZodTypeAny = z.ZodTypeAny,\n>(\n request: Request,\n action: Action<I, O, S, any, Init>,\n options?: FetchHandlerOptions<C, I>\n): Promise<Response> {\n const url = new URL(request.url);\n const streamParam = url.searchParams.get('stream');\n const shouldStream = streamParam === 'true';\n\n const streamIdHeader = request.headers.get('x-genkit-stream-id');\n const streamId = streamIdHeader || undefined;\n\n let body: any;\n try {\n body = await request.json();\n } catch (e) {\n const errMsg =\n `Error: Failed to parse request body as JSON. ` +\n `Make sure the request has 'Content-Type: application/json' header.`;\n logger.error(errMsg);\n return new Response(\n JSON.stringify({ message: errMsg, status: 'INVALID_ARGUMENT' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n if (!body || typeof body !== 'object' || !('data' in body)) {\n const errMsg =\n `Error: Request body must be a JSON object with a 'data' field. ` +\n `Expected format: {\"data\": ...}`;\n logger.error(errMsg);\n return new Response(\n JSON.stringify({ message: errMsg, status: 'INVALID_ARGUMENT' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const input = body.data as z.infer<I>;\n const init = body.init as z.infer<Init> | undefined;\n\n let context: C;\n try {\n context = await getContext(request, input, options?.contextProvider);\n } catch (e: any) {\n logger.error(\n `Context provider failed with error: ${(e as Error).message}\\n${\n (e as Error).stack\n }`\n );\n return new Response(JSON.stringify(getCallableJSON(e)), {\n status: getHttpStatus(e),\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n const acceptHeader = request.headers.get('Accept') || '';\n const isStreaming =\n acceptHeader.toLowerCase().includes('text/event-stream') || shouldStream;\n\n if (isStreaming) {\n const streamManager = options?.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 { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n\n runActionWithDurableStreaming(\n action,\n options?.streamManager,\n streamIdToUse,\n input,\n init,\n context,\n writer,\n request.signal\n ).catch((err) => {\n logger.error(`Error in streaming handler: ${err}`);\n });\n\n const headers: Record<string, string> = {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'Transfer-Encoding': 'chunked',\n };\n\n if (options?.streamManager) {\n headers['x-genkit-stream-id'] = streamIdToUse;\n }\n\n return new Response(readable, {\n status: 200,\n headers,\n });\n }\n\n try {\n const result = await action.run(input, {\n context,\n abortSignal: request.signal,\n init,\n });\n\n const headers: Record<string, string> = {\n 'x-genkit-trace-id': result.telemetry.traceId,\n 'x-genkit-span-id': result.telemetry.spanId,\n };\n\n return new Response(JSON.stringify({ result: result.result }), {\n status: 200,\n headers: { 'Content-Type': 'application/json', ...headers },\n });\n } catch (e) {\n logger.error(\n `Non-streaming request failed with error: ${(e as Error).message}\\n${\n (e as Error).stack\n }`\n );\n return new Response(JSON.stringify(getCallableJSON(e)), {\n status: getHttpStatus(e),\n headers: { 'Content-Type': 'application/json' },\n });\n }\n}\n\nexport function fetchHandler<\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 Init extends z.ZodTypeAny = z.ZodTypeAny,\n>(\n action: Action<I, O, S, any, Init>,\n options?: FetchHandlerOptions<C, I>\n): (request: Request) => Promise<Response> {\n return (request: Request) => handleActionRequest(request, action, options);\n}\n\n/**\n * Returns a Fetch handler for multiple actions with path-based routing.\n * Express-like API: pass actions and optional path prefix, then call the returned handler with the request.\n *\n * @param actions - Array of actions (or ActionWithOptions) to expose; each is routed by path (action name or options.path)\n * @param pathPrefix - Optional path prefix to strip from the URL (e.g., '/api')\n * @returns A handler function that takes a Request and returns a Promise<Response>\n *\n * @example\n * ```typescript\n * import { fetchHandlers } from '@genkit-ai/fetch';\n *\n * // Mount all actions under /api - routes by path (e.g. POST /api/hello, POST /api/models/gpt5)\n * app.all('/api/*', (c) => fetchHandlers(actions, '/api')(c.req.raw));\n * ```\n */\nexport function fetchHandlers(\n actions: (Action<any, any, any> | ActionWithOptions<any, any, any>)[],\n pathPrefix?: string\n): (request: Request) => Promise<Response> {\n return async (request: Request): Promise<Response> => {\n const url = new URL(request.url);\n let pathname = url.pathname;\n\n if (pathPrefix) {\n const prefix = pathPrefix.endsWith('/') ? pathPrefix : pathPrefix + '/';\n if (pathname === pathPrefix) {\n pathname = '';\n } else if (pathname.startsWith(prefix)) {\n pathname = pathname.slice(prefix.length);\n } else {\n return new Response(\n JSON.stringify({\n status: 'NOT_FOUND',\n message: 'No action matched the request path.',\n }),\n { status: 404, headers: { 'Content-Type': 'application/json' } }\n );\n }\n }\n\n pathname = pathname.replace(/^\\//, '');\n\n let matchedAction: Action<any, any, any> | null = null;\n let handlerOptions: FetchHandlerOptions<any, any> | undefined = undefined;\n\n for (const item of actions) {\n if ('action' in item) {\n const options = item.options;\n const actionPath = options.path ?? item.action.__action.name;\n if (pathname === actionPath) {\n matchedAction = item.action;\n handlerOptions = {\n contextProvider: options.contextProvider,\n streamManager: options.streamManager,\n };\n break;\n }\n } else {\n if (pathname === item.__action.name) {\n matchedAction = item;\n break;\n }\n }\n }\n\n if (!matchedAction) {\n return new Response(\n JSON.stringify({\n status: 'NOT_FOUND',\n message: 'No action matched the request path.',\n }),\n { status: 404, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n return handleActionRequest(request, matchedAction, handlerOptions);\n };\n}\n"],"mappings":"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","names":[]}
package/lib/index.mjs CHANGED
@@ -110,7 +110,7 @@ ${getErrorStack(err)}`
110
110
  throw e;
111
111
  }
112
112
  }
113
- async function runActionWithDurableStreaming(action, streamManager, streamId, input, context, writer, abortSignal) {
113
+ async function runActionWithDurableStreaming(action, streamManager, streamId, input, init, context, writer, abortSignal) {
114
114
  const encoder = new TextEncoder();
115
115
  let taskQueue;
116
116
  let durableStream;
@@ -136,7 +136,8 @@ async function runActionWithDurableStreaming(action, streamManager, streamId, in
136
136
  const result = await action.run(input, {
137
137
  onChunk,
138
138
  context,
139
- abortSignal
139
+ abortSignal,
140
+ init
140
141
  });
141
142
  if (streamManager && durableStream) {
142
143
  taskQueue.enqueue(() => durableStream.done(result.result));
@@ -193,6 +194,7 @@ async function handleActionRequest(request, action, options) {
193
194
  );
194
195
  }
195
196
  const input = body.data;
197
+ const init = body.init;
196
198
  let context;
197
199
  try {
198
200
  context = await getContext(request, input, options?.contextProvider);
@@ -224,6 +226,7 @@ ${e.stack}`
224
226
  options?.streamManager,
225
227
  streamIdToUse,
226
228
  input,
229
+ init,
227
230
  context,
228
231
  writer,
229
232
  request.signal
@@ -247,7 +250,8 @@ ${e.stack}`
247
250
  try {
248
251
  const result = await action.run(input, {
249
252
  context,
250
- abortSignal: request.signal
253
+ abortSignal: request.signal,
254
+ init
251
255
  });
252
256
  const headers = {
253
257
  "x-genkit-trace-id": result.telemetry.traceId,
package/lib/index.mjs.map CHANGED
@@ -1 +1 @@
1
- {"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 {\n Action,\n ActionStreamInput,\n AsyncTaskQueue,\n StreamNotFoundError,\n type ActionContext,\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 { logger } from 'genkit/logging';\nimport { getErrorMessage, getErrorStack } from './utils.mjs';\n\nconst streamDelimiter = '\\n\\n';\n\n// Let the runtime provide the randomUUID function.\nconst randomUUID = () => globalThis.crypto.randomUUID();\n\n/**\n * A wrapper object containing an action with its associated options.\n */\nexport type ActionWithOptions<\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 options: {\n contextProvider?: ContextProvider<any, I>;\n streamManager?: StreamManager;\n path?: string;\n };\n};\n\n/**\n * Options for a fetch handler (context provider, stream manager).\n */\nexport interface FetchHandlerOptions<\n C extends ActionContext = ActionContext,\n I extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n contextProvider?: ContextProvider<C, I>;\n streamManager?: StreamManager;\n}\n\n/**\n * Wraps an action (flow, model, etc.) with options for use with {@link fetchHandlers}.\n */\nexport function withActionOptions<\n I extends z.ZodTypeAny,\n O extends z.ZodTypeAny,\n S extends z.ZodTypeAny,\n>(\n action: Action<I, O, S>,\n options: {\n contextProvider?: ContextProvider<any, I>;\n streamManager?: StreamManager;\n path?: string;\n }\n): ActionWithOptions<I, O, S> {\n return {\n action,\n options,\n };\n}\n\n/**\n * Converts Headers object to a plain object with lowercase keys.\n */\nfunction headersToObject(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n result[key.toLowerCase()] = value;\n });\n return result;\n}\n\n/**\n * Gets context from the request using the context provider if available.\n */\nasync function getContext<C extends ActionContext, I extends z.ZodTypeAny>(\n request: Request,\n input: z.infer<I>,\n provider?: ContextProvider<C, I>\n): Promise<C> {\n const context = {} as C;\n if (!provider) {\n return context;\n }\n\n const requestData: RequestData = {\n method: request.method as RequestData['method'],\n headers: headersToObject(request.headers),\n input,\n };\n\n return await provider(requestData);\n}\n\n/**\n * Subscribes to an existing stream using StreamManager.\n */\nasync function subscribeToStream(\n streamManager: StreamManager,\n streamId: string\n): Promise<Response | null> {\n try {\n const encoder = new TextEncoder();\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n\n await streamManager.subscribe(streamId, {\n onChunk: (chunk) => {\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter\n )\n );\n },\n onDone: (output) => {\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ result: output }) + streamDelimiter\n )\n );\n writer.close();\n },\n onError: (err) => {\n logger.error(\n `Streaming request failed with error: ${getErrorMessage(err)}\\n${getErrorStack(err)}`\n );\n writer.write(\n encoder.encode(\n `error: ${JSON.stringify({\n error: getCallableJSON(err),\n })}${streamDelimiter}`\n )\n );\n writer.close();\n },\n });\n\n return new Response(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 Response(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 })}${streamDelimiter}`\n )\n );\n writer.close();\n return new Response(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\n/**\n * Runs an action with durable streaming support.\n */\nasync function runActionWithDurableStreaming<\n I extends z.ZodTypeAny,\n O extends z.ZodTypeAny,\n S extends z.ZodTypeAny,\n>(\n action: Action<I, O, S>,\n streamManager: StreamManager | undefined,\n streamId: string,\n input: z.infer<I>,\n context: ActionContext,\n writer: WritableStreamDefaultWriter<Uint8Array>,\n abortSignal: AbortSignal\n): Promise<void> {\n const encoder = new TextEncoder();\n let taskQueue: AsyncTaskQueue | undefined;\n let durableStream: ActionStreamInput<any, any> | undefined;\n\n if (streamManager) {\n taskQueue = new AsyncTaskQueue();\n durableStream = await streamManager.open(streamId);\n }\n\n try {\n let onChunk = (chunk: z.infer<S>) => {\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter\n )\n );\n };\n\n if (streamManager && durableStream) {\n const originalOnChunk = onChunk;\n onChunk = (chunk: z.infer<S>) => {\n originalOnChunk(chunk);\n taskQueue!.enqueue(() => durableStream!.write(chunk));\n };\n }\n\n const result = await action.run(input, {\n onChunk,\n context,\n abortSignal,\n });\n\n if (streamManager && durableStream) {\n taskQueue!.enqueue(() => durableStream!.done(result.result));\n await taskQueue!.merge();\n }\n\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ result: result.result }) + streamDelimiter\n )\n );\n writer.close();\n } catch (e) {\n if (durableStream) {\n taskQueue!.enqueue(() => durableStream!.error(e));\n await taskQueue!.merge();\n }\n logger.error(\n `Streaming request failed with error: ${(e as Error).message}\\n${\n (e as Error).stack\n }`\n );\n writer.write(\n encoder.encode(\n `error: ${JSON.stringify({\n error: getCallableJSON(e),\n })}${streamDelimiter}`\n )\n );\n writer.close();\n }\n}\n\n/**\n * Returns a Fetch handler for a single action (flow, model, or any Genkit action).\n * Express-like API: pass the action first, then call the returned handler with the request.\n *\n * @param action - The Genkit action to execute (flow, model, etc.)\n * @param options - Optional configuration including contextProvider and streamManager\n * @returns A handler function that takes a Request and returns a Promise<Response>\n *\n * @example\n * ```typescript\n * import { fetchHandler } from '@genkit-ai/fetch';\n *\n * // Flow or model\n * app.post('/myFlow', (c) => fetchHandler(myFlow)(c.req.raw));\n * app.post('/models/gpt5', (c) => fetchHandler(gpt5)(c.req.raw));\n *\n * // With options\n * app.post('/secure', (c) => fetchHandler(secureFlow, { contextProvider })(c.req.raw));\n * ```\n */\nasync function handleActionRequest<\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 request: Request,\n action: Action<I, O, S>,\n options?: FetchHandlerOptions<C, I>\n): Promise<Response> {\n const url = new URL(request.url);\n const streamParam = url.searchParams.get('stream');\n const shouldStream = streamParam === 'true';\n\n const streamIdHeader = request.headers.get('x-genkit-stream-id');\n const streamId = streamIdHeader || undefined;\n\n let body: any;\n try {\n body = await request.json();\n } catch (e) {\n const errMsg =\n `Error: Failed to parse request body as JSON. ` +\n `Make sure the request has 'Content-Type: application/json' header.`;\n logger.error(errMsg);\n return new Response(\n JSON.stringify({ message: errMsg, status: 'INVALID_ARGUMENT' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n if (!body || typeof body !== 'object' || !('data' in body)) {\n const errMsg =\n `Error: Request body must be a JSON object with a 'data' field. ` +\n `Expected format: {\"data\": ...}`;\n logger.error(errMsg);\n return new Response(\n JSON.stringify({ message: errMsg, status: 'INVALID_ARGUMENT' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const input = body.data as z.infer<I>;\n\n let context: C;\n try {\n context = await getContext(request, input, options?.contextProvider);\n } catch (e: any) {\n logger.error(\n `Context provider failed with error: ${(e as Error).message}\\n${\n (e as Error).stack\n }`\n );\n return new Response(JSON.stringify(getCallableJSON(e)), {\n status: getHttpStatus(e),\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n const acceptHeader = request.headers.get('Accept') || '';\n const isStreaming =\n acceptHeader.toLowerCase().includes('text/event-stream') || shouldStream;\n\n if (isStreaming) {\n const streamManager = options?.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 { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n\n runActionWithDurableStreaming(\n action,\n options?.streamManager,\n streamIdToUse,\n input,\n context,\n writer,\n request.signal\n ).catch((err) => {\n logger.error(`Error in streaming handler: ${err}`);\n });\n\n const headers: Record<string, string> = {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'Transfer-Encoding': 'chunked',\n };\n\n if (options?.streamManager) {\n headers['x-genkit-stream-id'] = streamIdToUse;\n }\n\n return new Response(readable, {\n status: 200,\n headers,\n });\n }\n\n try {\n const result = await action.run(input, {\n context,\n abortSignal: request.signal,\n });\n\n const headers: Record<string, string> = {\n 'x-genkit-trace-id': result.telemetry.traceId,\n 'x-genkit-span-id': result.telemetry.spanId,\n };\n\n return new Response(JSON.stringify({ result: result.result }), {\n status: 200,\n headers: { 'Content-Type': 'application/json', ...headers },\n });\n } catch (e) {\n logger.error(\n `Non-streaming request failed with error: ${(e as Error).message}\\n${\n (e as Error).stack\n }`\n );\n return new Response(JSON.stringify(getCallableJSON(e)), {\n status: getHttpStatus(e),\n headers: { 'Content-Type': 'application/json' },\n });\n }\n}\n\nexport function fetchHandler<\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 options?: FetchHandlerOptions<C, I>\n): (request: Request) => Promise<Response> {\n return (request: Request) => handleActionRequest(request, action, options);\n}\n\n/**\n * Returns a Fetch handler for multiple actions with path-based routing.\n * Express-like API: pass actions and optional path prefix, then call the returned handler with the request.\n *\n * @param actions - Array of actions (or ActionWithOptions) to expose; each is routed by path (action name or options.path)\n * @param pathPrefix - Optional path prefix to strip from the URL (e.g., '/api')\n * @returns A handler function that takes a Request and returns a Promise<Response>\n *\n * @example\n * ```typescript\n * import { fetchHandlers } from '@genkit-ai/fetch';\n *\n * // Mount all actions under /api - routes by path (e.g. POST /api/hello, POST /api/models/gpt5)\n * app.all('/api/*', (c) => fetchHandlers(actions, '/api')(c.req.raw));\n * ```\n */\nexport function fetchHandlers(\n actions: (Action<any, any, any> | ActionWithOptions<any, any, any>)[],\n pathPrefix?: string\n): (request: Request) => Promise<Response> {\n return async (request: Request): Promise<Response> => {\n const url = new URL(request.url);\n let pathname = url.pathname;\n\n if (pathPrefix) {\n const prefix = pathPrefix.endsWith('/') ? pathPrefix : pathPrefix + '/';\n if (pathname === pathPrefix) {\n pathname = '';\n } else if (pathname.startsWith(prefix)) {\n pathname = pathname.slice(prefix.length);\n } else {\n return new Response(\n JSON.stringify({\n status: 'NOT_FOUND',\n message: 'No action matched the request path.',\n }),\n { status: 404, headers: { 'Content-Type': 'application/json' } }\n );\n }\n }\n\n pathname = pathname.replace(/^\\//, '');\n\n let matchedAction: Action<any, any, any> | null = null;\n let handlerOptions: FetchHandlerOptions<any, any> | undefined = undefined;\n\n for (const item of actions) {\n if ('action' in item) {\n const options = item.options;\n const actionPath = options.path ?? item.action.__action.name;\n if (pathname === actionPath) {\n matchedAction = item.action;\n handlerOptions = {\n contextProvider: options.contextProvider,\n streamManager: options.streamManager,\n };\n break;\n }\n } else {\n if (pathname === item.__action.name) {\n matchedAction = item;\n break;\n }\n }\n }\n\n if (!matchedAction) {\n return new Response(\n JSON.stringify({\n status: 'NOT_FOUND',\n message: 'No action matched the request path.',\n }),\n { status: 404, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n return handleActionRequest(request, matchedAction, handlerOptions);\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"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 {\n Action,\n ActionStreamInput,\n AsyncTaskQueue,\n StreamNotFoundError,\n type ActionContext,\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 { logger } from 'genkit/logging';\nimport { getErrorMessage, getErrorStack } from './utils.mjs';\n\nconst streamDelimiter = '\\n\\n';\n\n// Let the runtime provide the randomUUID function.\nconst randomUUID = () => globalThis.crypto.randomUUID();\n\n/**\n * A wrapper object containing an action with its associated options.\n */\nexport type ActionWithOptions<\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 options: {\n contextProvider?: ContextProvider<any, I>;\n streamManager?: StreamManager;\n path?: string;\n };\n};\n\n/**\n * Options for a fetch handler (context provider, stream manager).\n */\nexport interface FetchHandlerOptions<\n C extends ActionContext = ActionContext,\n I extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n contextProvider?: ContextProvider<C, I>;\n streamManager?: StreamManager;\n}\n\n/**\n * Wraps an action (flow, model, etc.) with options for use with {@link fetchHandlers}.\n */\nexport function withActionOptions<\n I extends z.ZodTypeAny,\n O extends z.ZodTypeAny,\n S extends z.ZodTypeAny,\n>(\n action: Action<I, O, S>,\n options: {\n contextProvider?: ContextProvider<any, I>;\n streamManager?: StreamManager;\n path?: string;\n }\n): ActionWithOptions<I, O, S> {\n return {\n action,\n options,\n };\n}\n\n/**\n * Converts Headers object to a plain object with lowercase keys.\n */\nfunction headersToObject(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n result[key.toLowerCase()] = value;\n });\n return result;\n}\n\n/**\n * Gets context from the request using the context provider if available.\n */\nasync function getContext<C extends ActionContext, I extends z.ZodTypeAny>(\n request: Request,\n input: z.infer<I>,\n provider?: ContextProvider<C, I>\n): Promise<C> {\n const context = {} as C;\n if (!provider) {\n return context;\n }\n\n const requestData: RequestData = {\n method: request.method as RequestData['method'],\n headers: headersToObject(request.headers),\n input,\n };\n\n return await provider(requestData);\n}\n\n/**\n * Subscribes to an existing stream using StreamManager.\n */\nasync function subscribeToStream(\n streamManager: StreamManager,\n streamId: string\n): Promise<Response | null> {\n try {\n const encoder = new TextEncoder();\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n\n await streamManager.subscribe(streamId, {\n onChunk: (chunk) => {\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter\n )\n );\n },\n onDone: (output) => {\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ result: output }) + streamDelimiter\n )\n );\n writer.close();\n },\n onError: (err) => {\n logger.error(\n `Streaming request failed with error: ${getErrorMessage(err)}\\n${getErrorStack(err)}`\n );\n writer.write(\n encoder.encode(\n `error: ${JSON.stringify({\n error: getCallableJSON(err),\n })}${streamDelimiter}`\n )\n );\n writer.close();\n },\n });\n\n return new Response(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 Response(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 })}${streamDelimiter}`\n )\n );\n writer.close();\n return new Response(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\n/**\n * Runs an action with durable streaming support.\n */\nasync function runActionWithDurableStreaming<\n I extends z.ZodTypeAny,\n O extends z.ZodTypeAny,\n S extends z.ZodTypeAny,\n Init extends z.ZodTypeAny = z.ZodTypeAny,\n>(\n action: Action<I, O, S, any, Init>,\n streamManager: StreamManager | undefined,\n streamId: string,\n input: z.infer<I>,\n init: z.infer<Init> | undefined,\n context: ActionContext,\n writer: WritableStreamDefaultWriter<Uint8Array>,\n abortSignal: AbortSignal\n): Promise<void> {\n const encoder = new TextEncoder();\n let taskQueue: AsyncTaskQueue | undefined;\n let durableStream: ActionStreamInput<any, any> | undefined;\n\n if (streamManager) {\n taskQueue = new AsyncTaskQueue();\n durableStream = await streamManager.open(streamId);\n }\n\n try {\n let onChunk = (chunk: z.infer<S>) => {\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter\n )\n );\n };\n\n if (streamManager && durableStream) {\n const originalOnChunk = onChunk;\n onChunk = (chunk: z.infer<S>) => {\n originalOnChunk(chunk);\n taskQueue!.enqueue(() => durableStream!.write(chunk));\n };\n }\n\n const result = await action.run(input, {\n onChunk,\n context,\n abortSignal,\n init,\n });\n\n if (streamManager && durableStream) {\n taskQueue!.enqueue(() => durableStream!.done(result.result));\n await taskQueue!.merge();\n }\n\n writer.write(\n encoder.encode(\n 'data: ' + JSON.stringify({ result: result.result }) + streamDelimiter\n )\n );\n writer.close();\n } catch (e) {\n if (durableStream) {\n taskQueue!.enqueue(() => durableStream!.error(e));\n await taskQueue!.merge();\n }\n logger.error(\n `Streaming request failed with error: ${(e as Error).message}\\n${\n (e as Error).stack\n }`\n );\n writer.write(\n encoder.encode(\n `error: ${JSON.stringify({\n error: getCallableJSON(e),\n })}${streamDelimiter}`\n )\n );\n writer.close();\n }\n}\n\n/**\n * Returns a Fetch handler for a single action (flow, model, or any Genkit action).\n * Express-like API: pass the action first, then call the returned handler with the request.\n *\n * @param action - The Genkit action to execute (flow, model, etc.)\n * @param options - Optional configuration including contextProvider and streamManager\n * @returns A handler function that takes a Request and returns a Promise<Response>\n *\n * @example\n * ```typescript\n * import { fetchHandler } from '@genkit-ai/fetch';\n *\n * // Flow or model\n * app.post('/myFlow', (c) => fetchHandler(myFlow)(c.req.raw));\n * app.post('/models/gpt5', (c) => fetchHandler(gpt5)(c.req.raw));\n *\n * // With options\n * app.post('/secure', (c) => fetchHandler(secureFlow, { contextProvider })(c.req.raw));\n * ```\n */\nasync function handleActionRequest<\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 Init extends z.ZodTypeAny = z.ZodTypeAny,\n>(\n request: Request,\n action: Action<I, O, S, any, Init>,\n options?: FetchHandlerOptions<C, I>\n): Promise<Response> {\n const url = new URL(request.url);\n const streamParam = url.searchParams.get('stream');\n const shouldStream = streamParam === 'true';\n\n const streamIdHeader = request.headers.get('x-genkit-stream-id');\n const streamId = streamIdHeader || undefined;\n\n let body: any;\n try {\n body = await request.json();\n } catch (e) {\n const errMsg =\n `Error: Failed to parse request body as JSON. ` +\n `Make sure the request has 'Content-Type: application/json' header.`;\n logger.error(errMsg);\n return new Response(\n JSON.stringify({ message: errMsg, status: 'INVALID_ARGUMENT' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n if (!body || typeof body !== 'object' || !('data' in body)) {\n const errMsg =\n `Error: Request body must be a JSON object with a 'data' field. ` +\n `Expected format: {\"data\": ...}`;\n logger.error(errMsg);\n return new Response(\n JSON.stringify({ message: errMsg, status: 'INVALID_ARGUMENT' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const input = body.data as z.infer<I>;\n const init = body.init as z.infer<Init> | undefined;\n\n let context: C;\n try {\n context = await getContext(request, input, options?.contextProvider);\n } catch (e: any) {\n logger.error(\n `Context provider failed with error: ${(e as Error).message}\\n${\n (e as Error).stack\n }`\n );\n return new Response(JSON.stringify(getCallableJSON(e)), {\n status: getHttpStatus(e),\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n const acceptHeader = request.headers.get('Accept') || '';\n const isStreaming =\n acceptHeader.toLowerCase().includes('text/event-stream') || shouldStream;\n\n if (isStreaming) {\n const streamManager = options?.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 { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n\n runActionWithDurableStreaming(\n action,\n options?.streamManager,\n streamIdToUse,\n input,\n init,\n context,\n writer,\n request.signal\n ).catch((err) => {\n logger.error(`Error in streaming handler: ${err}`);\n });\n\n const headers: Record<string, string> = {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'Transfer-Encoding': 'chunked',\n };\n\n if (options?.streamManager) {\n headers['x-genkit-stream-id'] = streamIdToUse;\n }\n\n return new Response(readable, {\n status: 200,\n headers,\n });\n }\n\n try {\n const result = await action.run(input, {\n context,\n abortSignal: request.signal,\n init,\n });\n\n const headers: Record<string, string> = {\n 'x-genkit-trace-id': result.telemetry.traceId,\n 'x-genkit-span-id': result.telemetry.spanId,\n };\n\n return new Response(JSON.stringify({ result: result.result }), {\n status: 200,\n headers: { 'Content-Type': 'application/json', ...headers },\n });\n } catch (e) {\n logger.error(\n `Non-streaming request failed with error: ${(e as Error).message}\\n${\n (e as Error).stack\n }`\n );\n return new Response(JSON.stringify(getCallableJSON(e)), {\n status: getHttpStatus(e),\n headers: { 'Content-Type': 'application/json' },\n });\n }\n}\n\nexport function fetchHandler<\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 Init extends z.ZodTypeAny = z.ZodTypeAny,\n>(\n action: Action<I, O, S, any, Init>,\n options?: FetchHandlerOptions<C, I>\n): (request: Request) => Promise<Response> {\n return (request: Request) => handleActionRequest(request, action, options);\n}\n\n/**\n * Returns a Fetch handler for multiple actions with path-based routing.\n * Express-like API: pass actions and optional path prefix, then call the returned handler with the request.\n *\n * @param actions - Array of actions (or ActionWithOptions) to expose; each is routed by path (action name or options.path)\n * @param pathPrefix - Optional path prefix to strip from the URL (e.g., '/api')\n * @returns A handler function that takes a Request and returns a Promise<Response>\n *\n * @example\n * ```typescript\n * import { fetchHandlers } from '@genkit-ai/fetch';\n *\n * // Mount all actions under /api - routes by path (e.g. POST /api/hello, POST /api/models/gpt5)\n * app.all('/api/*', (c) => fetchHandlers(actions, '/api')(c.req.raw));\n * ```\n */\nexport function fetchHandlers(\n actions: (Action<any, any, any> | ActionWithOptions<any, any, any>)[],\n pathPrefix?: string\n): (request: Request) => Promise<Response> {\n return async (request: Request): Promise<Response> => {\n const url = new URL(request.url);\n let pathname = url.pathname;\n\n if (pathPrefix) {\n const prefix = pathPrefix.endsWith('/') ? pathPrefix : pathPrefix + '/';\n if (pathname === pathPrefix) {\n pathname = '';\n } else if (pathname.startsWith(prefix)) {\n pathname = pathname.slice(prefix.length);\n } else {\n return new Response(\n JSON.stringify({\n status: 'NOT_FOUND',\n message: 'No action matched the request path.',\n }),\n { status: 404, headers: { 'Content-Type': 'application/json' } }\n );\n }\n }\n\n pathname = pathname.replace(/^\\//, '');\n\n let matchedAction: Action<any, any, any> | null = null;\n let handlerOptions: FetchHandlerOptions<any, any> | undefined = undefined;\n\n for (const item of actions) {\n if ('action' in item) {\n const options = item.options;\n const actionPath = options.path ?? item.action.__action.name;\n if (pathname === actionPath) {\n matchedAction = item.action;\n handlerOptions = {\n contextProvider: options.contextProvider,\n streamManager: options.streamManager,\n };\n break;\n }\n } else {\n if (pathname === item.__action.name) {\n matchedAction = item;\n break;\n }\n }\n }\n\n if (!matchedAction) {\n return new Response(\n JSON.stringify({\n status: 'NOT_FOUND',\n message: 'No action matched the request path.',\n }),\n { status: 404, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n return handleActionRequest(request, matchedAction, handlerOptions);\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "bun",
17
17
  "deno"
18
18
  ],
19
- "version": "0.1.3",
19
+ "version": "0.2.0-rc.0",
20
20
  "type": "commonjs",
21
21
  "repository": {
22
22
  "type": "git",
@@ -26,7 +26,7 @@
26
26
  "author": "genkit",
27
27
  "license": "Apache-2.0",
28
28
  "peerDependencies": {
29
- "genkit": "^1.38.0"
29
+ "genkit": "^1.39.0-rc.0"
30
30
  },
31
31
  "devDependencies": {
32
32
  "get-port": "^5.1.0",
@@ -36,7 +36,7 @@
36
36
  "tsup": "^8.3.5",
37
37
  "tsx": "^4.19.2",
38
38
  "typescript": "^5.9.3",
39
- "genkit": "1.38.0"
39
+ "genkit": "1.39.0-rc.0"
40
40
  },
41
41
  "types": "./lib/index.d.ts",
42
42
  "exports": {
package/src/index.ts CHANGED
@@ -208,11 +208,13 @@ async function runActionWithDurableStreaming<
208
208
  I extends z.ZodTypeAny,
209
209
  O extends z.ZodTypeAny,
210
210
  S extends z.ZodTypeAny,
211
+ Init extends z.ZodTypeAny = z.ZodTypeAny,
211
212
  >(
212
- action: Action<I, O, S>,
213
+ action: Action<I, O, S, any, Init>,
213
214
  streamManager: StreamManager | undefined,
214
215
  streamId: string,
215
216
  input: z.infer<I>,
217
+ init: z.infer<Init> | undefined,
216
218
  context: ActionContext,
217
219
  writer: WritableStreamDefaultWriter<Uint8Array>,
218
220
  abortSignal: AbortSignal
@@ -247,6 +249,7 @@ async function runActionWithDurableStreaming<
247
249
  onChunk,
248
250
  context,
249
251
  abortSignal,
252
+ init,
250
253
  });
251
254
 
252
255
  if (streamManager && durableStream) {
@@ -306,9 +309,10 @@ async function handleActionRequest<
306
309
  I extends z.ZodTypeAny = z.ZodTypeAny,
307
310
  O extends z.ZodTypeAny = z.ZodTypeAny,
308
311
  S extends z.ZodTypeAny = z.ZodTypeAny,
312
+ Init extends z.ZodTypeAny = z.ZodTypeAny,
309
313
  >(
310
314
  request: Request,
311
- action: Action<I, O, S>,
315
+ action: Action<I, O, S, any, Init>,
312
316
  options?: FetchHandlerOptions<C, I>
313
317
  ): Promise<Response> {
314
318
  const url = new URL(request.url);
@@ -344,6 +348,7 @@ async function handleActionRequest<
344
348
  }
345
349
 
346
350
  const input = body.data as z.infer<I>;
351
+ const init = body.init as z.infer<Init> | undefined;
347
352
 
348
353
  let context: C;
349
354
  try {
@@ -382,6 +387,7 @@ async function handleActionRequest<
382
387
  options?.streamManager,
383
388
  streamIdToUse,
384
389
  input,
390
+ init,
385
391
  context,
386
392
  writer,
387
393
  request.signal
@@ -410,6 +416,7 @@ async function handleActionRequest<
410
416
  const result = await action.run(input, {
411
417
  context,
412
418
  abortSignal: request.signal,
419
+ init,
413
420
  });
414
421
 
415
422
  const headers: Record<string, string> = {
@@ -439,8 +446,9 @@ export function fetchHandler<
439
446
  I extends z.ZodTypeAny = z.ZodTypeAny,
440
447
  O extends z.ZodTypeAny = z.ZodTypeAny,
441
448
  S extends z.ZodTypeAny = z.ZodTypeAny,
449
+ Init extends z.ZodTypeAny = z.ZodTypeAny,
442
450
  >(
443
- action: Action<I, O, S>,
451
+ action: Action<I, O, S, any, Init>,
444
452
  options?: FetchHandlerOptions<C, I>
445
453
  ): (request: Request) => Promise<Response> {
446
454
  return (request: Request) => handleActionRequest(request, action, options);
package/tests/web_test.ts CHANGED
@@ -314,6 +314,115 @@ describe('fetchHandler (single action)', () => {
314
314
  assert.ok(json.message?.includes('not authorized'));
315
315
  });
316
316
 
317
+ it('should pass init data to the action', async () => {
318
+ const ai = genkit({});
319
+ const flow = ai.defineFlow(
320
+ { name: 'flowWithInit', inputSchema: z.string() },
321
+ async (input) => `input: ${input}`
322
+ );
323
+ // Monkey-patch the run method to capture and return init data.
324
+ const originalRun = flow.run.bind(flow);
325
+ flow.run = async (input: any, options: any) => {
326
+ const result = await originalRun(input, options);
327
+ result.result = `input: ${input}, init: ${JSON.stringify(options?.init)}`;
328
+ return result;
329
+ };
330
+ const request = new Request('http://localhost/flowWithInit', {
331
+ method: 'POST',
332
+ headers: { 'Content-Type': 'application/json' },
333
+ body: JSON.stringify({
334
+ data: 'hello',
335
+ init: { sessionId: 'abc123', temperature: 0.7 },
336
+ }),
337
+ });
338
+ const response = await fetchHandler(flow)(request);
339
+ assert.strictEqual(response.status, 200);
340
+ const json = (await response.json()) as { result: string };
341
+ assert.strictEqual(
342
+ json.result,
343
+ 'input: hello, init: {"sessionId":"abc123","temperature":0.7}'
344
+ );
345
+ });
346
+
347
+ it('should pass undefined init when not provided', async () => {
348
+ const ai = genkit({});
349
+ const flow = ai.defineFlow(
350
+ { name: 'flowWithInit', inputSchema: z.string() },
351
+ async (input) => `input: ${input}`
352
+ );
353
+ const originalRun = flow.run.bind(flow);
354
+ flow.run = async (input: any, options: any) => {
355
+ const result = await originalRun(input, options);
356
+ result.result = `input: ${input}, init: ${JSON.stringify(options?.init)}`;
357
+ return result;
358
+ };
359
+ const request = new Request('http://localhost/flowWithInit', {
360
+ method: 'POST',
361
+ headers: { 'Content-Type': 'application/json' },
362
+ body: JSON.stringify({ data: 'hello' }),
363
+ });
364
+ const response = await fetchHandler(flow)(request);
365
+ assert.strictEqual(response.status, 200);
366
+ const json = (await response.json()) as { result: string };
367
+ assert.strictEqual(json.result, 'input: hello, init: undefined');
368
+ });
369
+
370
+ it('should validate init against initSchema and pass it to the action', async () => {
371
+ const ai = genkit({});
372
+ const flow = ai.defineFlow(
373
+ {
374
+ name: 'flowWithInitSchema',
375
+ inputSchema: z.string(),
376
+ initSchema: z.object({ sessionId: z.string() }),
377
+ },
378
+ async (input, { init }) =>
379
+ `input: ${input}, sessionId: ${(init as { sessionId: string }).sessionId}`
380
+ );
381
+ const request = new Request('http://localhost/flowWithInitSchema', {
382
+ method: 'POST',
383
+ headers: { 'Content-Type': 'application/json' },
384
+ body: JSON.stringify({
385
+ data: 'hello',
386
+ init: { sessionId: 'abc123' },
387
+ }),
388
+ });
389
+ const response = await fetchHandler(flow)(request);
390
+ assert.strictEqual(response.status, 200);
391
+ const json = (await response.json()) as { result: string };
392
+ assert.strictEqual(json.result, 'input: hello, sessionId: abc123');
393
+ });
394
+
395
+ it('should reject init that does not conform to initSchema', async () => {
396
+ const ai = genkit({});
397
+ const flow = ai.defineFlow(
398
+ {
399
+ name: 'flowWithInitSchema',
400
+ inputSchema: z.string(),
401
+ initSchema: z.object({ sessionId: z.string() }),
402
+ },
403
+ async (input, { init }) =>
404
+ `input: ${input}, sessionId: ${(init as { sessionId: string }).sessionId}`
405
+ );
406
+ const request = new Request('http://localhost/flowWithInitSchema', {
407
+ method: 'POST',
408
+ headers: { 'Content-Type': 'application/json' },
409
+ body: JSON.stringify({
410
+ data: 'hello',
411
+ // sessionId should be a string, not a number.
412
+ init: { sessionId: 123 },
413
+ }),
414
+ });
415
+ const response = await fetchHandler(flow)(request);
416
+ assert.strictEqual(response.status, 400);
417
+ const json = (await response.json()) as {
418
+ status?: string;
419
+ message?: string;
420
+ };
421
+ assert.ok(
422
+ json.status === 'INVALID_ARGUMENT' || json.message?.includes('INVALID')
423
+ );
424
+ });
425
+
317
426
  it('should set x-genkit-trace-id and x-genkit-span-id headers', async () => {
318
427
  const ai = genkit({});
319
428
  const flow = ai.defineFlow('traceFlow', async () => 'ok');