@genkit-ai/express 1.0.0-rc.4 → 1.0.0-rc.6
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/lib/index.d.mts +2 -2
- package/lib/index.d.ts +2 -2
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +2 -2
- package/lib/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +4 -4
- package/tests/express_test.ts +2 -2
package/lib/index.d.mts
CHANGED
|
@@ -25,7 +25,7 @@ import { z, Action, Flow } from 'genkit';
|
|
|
25
25
|
interface AuthPolicyContext<I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny> {
|
|
26
26
|
action?: Action<I, O, S>;
|
|
27
27
|
input: z.infer<I>;
|
|
28
|
-
auth
|
|
28
|
+
auth?: Record<string, any>;
|
|
29
29
|
request: RequestWithAuth;
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
@@ -41,7 +41,7 @@ interface AuthPolicy<I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeA
|
|
|
41
41
|
* the flow context.
|
|
42
42
|
*/
|
|
43
43
|
interface RequestWithAuth extends express.Request {
|
|
44
|
-
auth?:
|
|
44
|
+
auth?: Record<string, any>;
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* Exposes provided flow or an action as express handler.
|
package/lib/index.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ import { z, Action, Flow } from 'genkit';
|
|
|
25
25
|
interface AuthPolicyContext<I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny> {
|
|
26
26
|
action?: Action<I, O, S>;
|
|
27
27
|
input: z.infer<I>;
|
|
28
|
-
auth
|
|
28
|
+
auth?: Record<string, any>;
|
|
29
29
|
request: RequestWithAuth;
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
@@ -41,7 +41,7 @@ interface AuthPolicy<I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeA
|
|
|
41
41
|
* the flow context.
|
|
42
42
|
*/
|
|
43
43
|
interface RequestWithAuth extends express.Request {
|
|
44
|
-
auth?:
|
|
44
|
+
auth?: Record<string, any>;
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* Exposes provided flow or an action as express handler.
|
package/lib/index.js
CHANGED
|
@@ -80,7 +80,7 @@ function expressHandler(action, opts) {
|
|
|
80
80
|
onChunk,
|
|
81
81
|
() => action.run(input, {
|
|
82
82
|
onChunk,
|
|
83
|
-
context: auth
|
|
83
|
+
context: { auth }
|
|
84
84
|
})
|
|
85
85
|
);
|
|
86
86
|
response.write(
|
|
@@ -102,7 +102,7 @@ function expressHandler(action, opts) {
|
|
|
102
102
|
}
|
|
103
103
|
} else {
|
|
104
104
|
try {
|
|
105
|
-
const result = await action.run(input, { context: auth });
|
|
105
|
+
const result = await action.run(input, { context: { auth } });
|
|
106
106
|
response.setHeader("x-genkit-trace-id", result.telemetry.traceId);
|
|
107
107
|
response.setHeader("x-genkit-span-id", result.telemetry.spanId);
|
|
108
108
|
response.status(200).send({
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Copyright 2024 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 * as bodyParser from 'body-parser';\nimport cors, { CorsOptions } from 'cors';\nimport express, { RequestHandler } from 'express';\nimport { Action, Flow, runWithStreamingCallback, z } from 'genkit';\nimport { logger } from 'genkit/logging';\nimport { Server } from 'http';\nimport { getErrorMessage, getErrorStack } from './utils';\n\nconst streamDelimiter = '\\n\\n';\n\n/**\n * Auth policy context is an object passed to the auth policy providing details necessary for auth.\n */\nexport interface AuthPolicyContext<\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 input: z.infer<I>;\n auth: any | undefined;\n request: RequestWithAuth;\n}\n\n/**\n * Flow Auth policy. Consumes the authorization context of the flow and\n * performs checks before the flow runs. If this throws, the flow will not\n * be executed.\n */\nexport interface AuthPolicy<\n I extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n S extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n (ctx: AuthPolicyContext<I, O, S>): void | Promise<void>;\n}\n\n/**\n * For express-based flows, req.auth should contain the value to bepassed into\n * the flow context.\n */\nexport interface RequestWithAuth extends express.Request {\n auth?: unknown;\n}\n\n/**\n * Exposes provided flow or an action as express handler.\n */\nexport function expressHandler<\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 authPolicy?: AuthPolicy<I, O, S>;\n }\n): express.RequestHandler {\n return async (\n request: RequestWithAuth,\n response: express.Response\n ): Promise<void> => {\n const { stream } = request.query;\n let input = request.body.data;\n const auth = request.auth;\n\n try {\n await opts?.authPolicy?.({\n action,\n auth,\n input,\n request,\n });\n } catch (e: any) {\n logger.debug(e);\n const respBody = {\n error: {\n status: 'PERMISSION_DENIED',\n message: e.message || 'Permission denied to resource',\n },\n };\n response.status(403).send(respBody).end();\n return;\n }\n\n if (request.get('Accept') === 'text/event-stream' || stream === 'true') {\n response.writeHead(200, {\n 'Content-Type': 'text/plain',\n 'Transfer-Encoding': 'chunked',\n });\n try {\n const onChunk = (chunk: z.infer<S>) => {\n response.write(\n 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter\n );\n };\n const result = await runWithStreamingCallback(\n action.__registry,\n onChunk,\n () =>\n action.run(input, {\n onChunk,\n context: auth,\n })\n );\n response.write(\n 'data: ' + JSON.stringify({ result: result.result }) + streamDelimiter\n );\n response.end();\n } catch (e) {\n logger.error(e);\n response.write(\n 'data: ' +\n JSON.stringify({\n error: {\n status: 'INTERNAL',\n message: getErrorMessage(e),\n details: getErrorStack(e),\n },\n }) +\n streamDelimiter\n );\n response.end();\n }\n } else {\n try {\n const result = await action.run(input, { context: auth });\n response.setHeader('x-genkit-trace-id', result.telemetry.traceId);\n response.setHeader('x-genkit-span-id', result.telemetry.spanId);\n // Responses for non-streaming flows are passed back with the flow result stored in a field called \"result.\"\n response\n .status(200)\n .send({\n result: result.result,\n })\n .end();\n } catch (e) {\n // Errors for non-streaming flows are passed back as standard API errors.\n response\n .status(500)\n .send({\n error: {\n status: 'INTERNAL',\n message: getErrorMessage(e),\n details: getErrorStack(e),\n },\n })\n .end();\n }\n }\n };\n}\n\n/**\n * A wrapper object containing a flow with its associated auth policy.\n */\nexport type FlowWithAuthPolicy<\n I extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n S extends z.ZodTypeAny = z.ZodTypeAny,\n> = {\n flow: Flow<I, O, S>;\n authProvider: RequestHandler;\n authPolicy: AuthPolicy;\n};\n\n/**\n * Adds an auth policy to the flow.\n */\nexport function withAuth<\n I extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n S extends z.ZodTypeAny = z.ZodTypeAny,\n>(\n flow: Flow<I, O, S>,\n authProvider: RequestHandler,\n authPolicy: AuthPolicy\n): FlowWithAuthPolicy<I, O, S> {\n return {\n flow,\n authProvider,\n authPolicy,\n };\n}\n\n/**\n * Options to configure the flow server.\n */\nexport interface FlowServerOptions {\n /** List of flows to expose via the flow server. */\n flows: (Flow<any, any, any> | FlowWithAuthPolicy<any, any, any>)[];\n /** Port to run the server on. Defaults to env.PORT or 3400. */\n port?: number;\n /** CORS options for the server. */\n cors?: CorsOptions;\n /** HTTP method path prefix for the exposed flows. */\n pathPrefix?: string;\n /** JSON body parser options. */\n jsonParserOptions?: bodyParser.OptionsJson;\n}\n\n/**\n * Starts an express server with the provided flows and options.\n */\nexport function startFlowServer(options: FlowServerOptions): FlowServer {\n const server = new FlowServer(options);\n server.start();\n return server;\n}\n\n/**\n * Flow server exposes registered flows as HTTP endpoints.\n *\n * This is for use in production environments.\n *\n * @hidden\n */\nexport class FlowServer {\n /** List of all running servers needed to be cleaned up on process exit. */\n private static RUNNING_SERVERS: FlowServer[] = [];\n\n /** Options for the flow server configured by the developer. */\n private options: FlowServerOptions;\n /** Port the server is actually running on. This may differ from `options.port` if the original was occupied. Null is server is not running. */\n private port: number | null = null;\n /** Express server instance. Null if server is not running. */\n private server: Server | null = null;\n\n constructor(options: FlowServerOptions) {\n this.options = {\n ...options,\n };\n }\n\n /**\n * Starts the server and adds it to the list of running servers to clean up on exit.\n */\n async start() {\n const server = express();\n\n server.use(bodyParser.json(this.options.jsonParserOptions));\n server.use(cors(this.options.cors));\n\n logger.debug('Running flow server with flow paths:');\n const pathPrefix = this.options.pathPrefix ?? '';\n this.options.flows?.forEach((flow) => {\n if ((flow as FlowWithAuthPolicy).authPolicy) {\n const flowWithPolicy = flow as FlowWithAuthPolicy;\n const flowPath = `/${pathPrefix}${flowWithPolicy.flow.__action.name}`;\n logger.debug(` - ${flowPath}`);\n server.post(\n flowPath,\n flowWithPolicy.authProvider,\n expressHandler(flowWithPolicy.flow, {\n authPolicy: flowWithPolicy.authPolicy,\n })\n );\n } else {\n const resolvedFlow = flow as Flow;\n const flowPath = `/${pathPrefix}${resolvedFlow.__action.name}`;\n logger.debug(` - ${flowPath}`);\n server.post(flowPath, expressHandler(resolvedFlow));\n }\n });\n this.port =\n this.options?.port ||\n (process.env.PORT ? parseInt(process.env.PORT) : 0) ||\n 3400;\n this.server = server.listen(this.port, () => {\n logger.debug(`Flow server running on http://localhost:${this.port}`);\n FlowServer.RUNNING_SERVERS.push(this);\n });\n }\n\n /**\n * Stops the server and removes it from the list of running servers to clean up on exit.\n */\n async stop(): Promise<void> {\n if (!this.server) {\n return;\n }\n return new Promise<void>((resolve, reject) => {\n this.server!.close((err) => {\n if (err) {\n logger.error(\n `Error shutting down flow server on port ${this.port}: ${err}`\n );\n reject(err);\n }\n const index = FlowServer.RUNNING_SERVERS.indexOf(this);\n if (index > -1) {\n FlowServer.RUNNING_SERVERS.splice(index, 1);\n }\n logger.debug(\n `Flow server on port ${this.port} has successfully shut down.`\n );\n this.port = null;\n this.server = null;\n resolve();\n });\n });\n }\n\n /**\n * Stops all running servers.\n */\n static async stopAll() {\n return Promise.all(\n FlowServer.RUNNING_SERVERS.map((server) => server.stop())\n );\n }\n}\n"],"mappings":"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","names":["express","cors"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Copyright 2024 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 * as bodyParser from 'body-parser';\nimport cors, { CorsOptions } from 'cors';\nimport express, { RequestHandler } from 'express';\nimport { Action, Flow, runWithStreamingCallback, z } from 'genkit';\nimport { logger } from 'genkit/logging';\nimport { Server } from 'http';\nimport { getErrorMessage, getErrorStack } from './utils';\n\nconst streamDelimiter = '\\n\\n';\n\n/**\n * Auth policy context is an object passed to the auth policy providing details necessary for auth.\n */\nexport interface AuthPolicyContext<\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 input: z.infer<I>;\n auth?: Record<string, any>;\n request: RequestWithAuth;\n}\n\n/**\n * Flow Auth policy. Consumes the authorization context of the flow and\n * performs checks before the flow runs. If this throws, the flow will not\n * be executed.\n */\nexport interface AuthPolicy<\n I extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n S extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n (ctx: AuthPolicyContext<I, O, S>): void | Promise<void>;\n}\n\n/**\n * For express-based flows, req.auth should contain the value to bepassed into\n * the flow context.\n */\nexport interface RequestWithAuth extends express.Request {\n auth?: Record<string, any>;\n}\n\n/**\n * Exposes provided flow or an action as express handler.\n */\nexport function expressHandler<\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 authPolicy?: AuthPolicy<I, O, S>;\n }\n): express.RequestHandler {\n return async (\n request: RequestWithAuth,\n response: express.Response\n ): Promise<void> => {\n const { stream } = request.query;\n let input = request.body.data;\n const auth = request.auth;\n\n try {\n await opts?.authPolicy?.({\n action,\n auth,\n input,\n request,\n });\n } catch (e: any) {\n logger.debug(e);\n const respBody = {\n error: {\n status: 'PERMISSION_DENIED',\n message: e.message || 'Permission denied to resource',\n },\n };\n response.status(403).send(respBody).end();\n return;\n }\n\n if (request.get('Accept') === 'text/event-stream' || stream === 'true') {\n response.writeHead(200, {\n 'Content-Type': 'text/plain',\n 'Transfer-Encoding': 'chunked',\n });\n try {\n const onChunk = (chunk: z.infer<S>) => {\n response.write(\n 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter\n );\n };\n const result = await runWithStreamingCallback(\n action.__registry,\n onChunk,\n () =>\n action.run(input, {\n onChunk,\n context: { auth },\n })\n );\n response.write(\n 'data: ' + JSON.stringify({ result: result.result }) + streamDelimiter\n );\n response.end();\n } catch (e) {\n logger.error(e);\n response.write(\n 'data: ' +\n JSON.stringify({\n error: {\n status: 'INTERNAL',\n message: getErrorMessage(e),\n details: getErrorStack(e),\n },\n }) +\n streamDelimiter\n );\n response.end();\n }\n } else {\n try {\n const result = await action.run(input, { context: { auth } });\n response.setHeader('x-genkit-trace-id', result.telemetry.traceId);\n response.setHeader('x-genkit-span-id', result.telemetry.spanId);\n // Responses for non-streaming flows are passed back with the flow result stored in a field called \"result.\"\n response\n .status(200)\n .send({\n result: result.result,\n })\n .end();\n } catch (e) {\n // Errors for non-streaming flows are passed back as standard API errors.\n response\n .status(500)\n .send({\n error: {\n status: 'INTERNAL',\n message: getErrorMessage(e),\n details: getErrorStack(e),\n },\n })\n .end();\n }\n }\n };\n}\n\n/**\n * A wrapper object containing a flow with its associated auth policy.\n */\nexport type FlowWithAuthPolicy<\n I extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n S extends z.ZodTypeAny = z.ZodTypeAny,\n> = {\n flow: Flow<I, O, S>;\n authProvider: RequestHandler;\n authPolicy: AuthPolicy;\n};\n\n/**\n * Adds an auth policy to the flow.\n */\nexport function withAuth<\n I extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n S extends z.ZodTypeAny = z.ZodTypeAny,\n>(\n flow: Flow<I, O, S>,\n authProvider: RequestHandler,\n authPolicy: AuthPolicy\n): FlowWithAuthPolicy<I, O, S> {\n return {\n flow,\n authProvider,\n authPolicy,\n };\n}\n\n/**\n * Options to configure the flow server.\n */\nexport interface FlowServerOptions {\n /** List of flows to expose via the flow server. */\n flows: (Flow<any, any, any> | FlowWithAuthPolicy<any, any, any>)[];\n /** Port to run the server on. Defaults to env.PORT or 3400. */\n port?: number;\n /** CORS options for the server. */\n cors?: CorsOptions;\n /** HTTP method path prefix for the exposed flows. */\n pathPrefix?: string;\n /** JSON body parser options. */\n jsonParserOptions?: bodyParser.OptionsJson;\n}\n\n/**\n * Starts an express server with the provided flows and options.\n */\nexport function startFlowServer(options: FlowServerOptions): FlowServer {\n const server = new FlowServer(options);\n server.start();\n return server;\n}\n\n/**\n * Flow server exposes registered flows as HTTP endpoints.\n *\n * This is for use in production environments.\n *\n * @hidden\n */\nexport class FlowServer {\n /** List of all running servers needed to be cleaned up on process exit. */\n private static RUNNING_SERVERS: FlowServer[] = [];\n\n /** Options for the flow server configured by the developer. */\n private options: FlowServerOptions;\n /** Port the server is actually running on. This may differ from `options.port` if the original was occupied. Null is server is not running. */\n private port: number | null = null;\n /** Express server instance. Null if server is not running. */\n private server: Server | null = null;\n\n constructor(options: FlowServerOptions) {\n this.options = {\n ...options,\n };\n }\n\n /**\n * Starts the server and adds it to the list of running servers to clean up on exit.\n */\n async start() {\n const server = express();\n\n server.use(bodyParser.json(this.options.jsonParserOptions));\n server.use(cors(this.options.cors));\n\n logger.debug('Running flow server with flow paths:');\n const pathPrefix = this.options.pathPrefix ?? '';\n this.options.flows?.forEach((flow) => {\n if ((flow as FlowWithAuthPolicy).authPolicy) {\n const flowWithPolicy = flow as FlowWithAuthPolicy;\n const flowPath = `/${pathPrefix}${flowWithPolicy.flow.__action.name}`;\n logger.debug(` - ${flowPath}`);\n server.post(\n flowPath,\n flowWithPolicy.authProvider,\n expressHandler(flowWithPolicy.flow, {\n authPolicy: flowWithPolicy.authPolicy,\n })\n );\n } else {\n const resolvedFlow = flow as Flow;\n const flowPath = `/${pathPrefix}${resolvedFlow.__action.name}`;\n logger.debug(` - ${flowPath}`);\n server.post(flowPath, expressHandler(resolvedFlow));\n }\n });\n this.port =\n this.options?.port ||\n (process.env.PORT ? parseInt(process.env.PORT) : 0) ||\n 3400;\n this.server = server.listen(this.port, () => {\n logger.debug(`Flow server running on http://localhost:${this.port}`);\n FlowServer.RUNNING_SERVERS.push(this);\n });\n }\n\n /**\n * Stops the server and removes it from the list of running servers to clean up on exit.\n */\n async stop(): Promise<void> {\n if (!this.server) {\n return;\n }\n return new Promise<void>((resolve, reject) => {\n this.server!.close((err) => {\n if (err) {\n logger.error(\n `Error shutting down flow server on port ${this.port}: ${err}`\n );\n reject(err);\n }\n const index = FlowServer.RUNNING_SERVERS.indexOf(this);\n if (index > -1) {\n FlowServer.RUNNING_SERVERS.splice(index, 1);\n }\n logger.debug(\n `Flow server on port ${this.port} has successfully shut down.`\n );\n this.port = null;\n this.server = null;\n resolve();\n });\n });\n }\n\n /**\n * Stops all running servers.\n */\n static async stopAll() {\n return Promise.all(\n FlowServer.RUNNING_SERVERS.map((server) => server.stop())\n );\n }\n}\n"],"mappings":"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","names":["express","cors"]}
|
package/lib/index.mjs
CHANGED
|
@@ -44,7 +44,7 @@ function expressHandler(action, opts) {
|
|
|
44
44
|
onChunk,
|
|
45
45
|
() => action.run(input, {
|
|
46
46
|
onChunk,
|
|
47
|
-
context: auth
|
|
47
|
+
context: { auth }
|
|
48
48
|
})
|
|
49
49
|
);
|
|
50
50
|
response.write(
|
|
@@ -66,7 +66,7 @@ function expressHandler(action, opts) {
|
|
|
66
66
|
}
|
|
67
67
|
} else {
|
|
68
68
|
try {
|
|
69
|
-
const result = await action.run(input, { context: auth });
|
|
69
|
+
const result = await action.run(input, { context: { auth } });
|
|
70
70
|
response.setHeader("x-genkit-trace-id", result.telemetry.traceId);
|
|
71
71
|
response.setHeader("x-genkit-span-id", result.telemetry.spanId);
|
|
72
72
|
response.status(200).send({
|
package/lib/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Copyright 2024 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 * as bodyParser from 'body-parser';\nimport cors, { CorsOptions } from 'cors';\nimport express, { RequestHandler } from 'express';\nimport { Action, Flow, runWithStreamingCallback, z } from 'genkit';\nimport { logger } from 'genkit/logging';\nimport { Server } from 'http';\nimport { getErrorMessage, getErrorStack } from './utils';\n\nconst streamDelimiter = '\\n\\n';\n\n/**\n * Auth policy context is an object passed to the auth policy providing details necessary for auth.\n */\nexport interface AuthPolicyContext<\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 input: z.infer<I>;\n auth: any | undefined;\n request: RequestWithAuth;\n}\n\n/**\n * Flow Auth policy. Consumes the authorization context of the flow and\n * performs checks before the flow runs. If this throws, the flow will not\n * be executed.\n */\nexport interface AuthPolicy<\n I extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n S extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n (ctx: AuthPolicyContext<I, O, S>): void | Promise<void>;\n}\n\n/**\n * For express-based flows, req.auth should contain the value to bepassed into\n * the flow context.\n */\nexport interface RequestWithAuth extends express.Request {\n auth?: unknown;\n}\n\n/**\n * Exposes provided flow or an action as express handler.\n */\nexport function expressHandler<\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 authPolicy?: AuthPolicy<I, O, S>;\n }\n): express.RequestHandler {\n return async (\n request: RequestWithAuth,\n response: express.Response\n ): Promise<void> => {\n const { stream } = request.query;\n let input = request.body.data;\n const auth = request.auth;\n\n try {\n await opts?.authPolicy?.({\n action,\n auth,\n input,\n request,\n });\n } catch (e: any) {\n logger.debug(e);\n const respBody = {\n error: {\n status: 'PERMISSION_DENIED',\n message: e.message || 'Permission denied to resource',\n },\n };\n response.status(403).send(respBody).end();\n return;\n }\n\n if (request.get('Accept') === 'text/event-stream' || stream === 'true') {\n response.writeHead(200, {\n 'Content-Type': 'text/plain',\n 'Transfer-Encoding': 'chunked',\n });\n try {\n const onChunk = (chunk: z.infer<S>) => {\n response.write(\n 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter\n );\n };\n const result = await runWithStreamingCallback(\n action.__registry,\n onChunk,\n () =>\n action.run(input, {\n onChunk,\n context: auth,\n })\n );\n response.write(\n 'data: ' + JSON.stringify({ result: result.result }) + streamDelimiter\n );\n response.end();\n } catch (e) {\n logger.error(e);\n response.write(\n 'data: ' +\n JSON.stringify({\n error: {\n status: 'INTERNAL',\n message: getErrorMessage(e),\n details: getErrorStack(e),\n },\n }) +\n streamDelimiter\n );\n response.end();\n }\n } else {\n try {\n const result = await action.run(input, { context: auth });\n response.setHeader('x-genkit-trace-id', result.telemetry.traceId);\n response.setHeader('x-genkit-span-id', result.telemetry.spanId);\n // Responses for non-streaming flows are passed back with the flow result stored in a field called \"result.\"\n response\n .status(200)\n .send({\n result: result.result,\n })\n .end();\n } catch (e) {\n // Errors for non-streaming flows are passed back as standard API errors.\n response\n .status(500)\n .send({\n error: {\n status: 'INTERNAL',\n message: getErrorMessage(e),\n details: getErrorStack(e),\n },\n })\n .end();\n }\n }\n };\n}\n\n/**\n * A wrapper object containing a flow with its associated auth policy.\n */\nexport type FlowWithAuthPolicy<\n I extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n S extends z.ZodTypeAny = z.ZodTypeAny,\n> = {\n flow: Flow<I, O, S>;\n authProvider: RequestHandler;\n authPolicy: AuthPolicy;\n};\n\n/**\n * Adds an auth policy to the flow.\n */\nexport function withAuth<\n I extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n S extends z.ZodTypeAny = z.ZodTypeAny,\n>(\n flow: Flow<I, O, S>,\n authProvider: RequestHandler,\n authPolicy: AuthPolicy\n): FlowWithAuthPolicy<I, O, S> {\n return {\n flow,\n authProvider,\n authPolicy,\n };\n}\n\n/**\n * Options to configure the flow server.\n */\nexport interface FlowServerOptions {\n /** List of flows to expose via the flow server. */\n flows: (Flow<any, any, any> | FlowWithAuthPolicy<any, any, any>)[];\n /** Port to run the server on. Defaults to env.PORT or 3400. */\n port?: number;\n /** CORS options for the server. */\n cors?: CorsOptions;\n /** HTTP method path prefix for the exposed flows. */\n pathPrefix?: string;\n /** JSON body parser options. */\n jsonParserOptions?: bodyParser.OptionsJson;\n}\n\n/**\n * Starts an express server with the provided flows and options.\n */\nexport function startFlowServer(options: FlowServerOptions): FlowServer {\n const server = new FlowServer(options);\n server.start();\n return server;\n}\n\n/**\n * Flow server exposes registered flows as HTTP endpoints.\n *\n * This is for use in production environments.\n *\n * @hidden\n */\nexport class FlowServer {\n /** List of all running servers needed to be cleaned up on process exit. */\n private static RUNNING_SERVERS: FlowServer[] = [];\n\n /** Options for the flow server configured by the developer. */\n private options: FlowServerOptions;\n /** Port the server is actually running on. This may differ from `options.port` if the original was occupied. Null is server is not running. */\n private port: number | null = null;\n /** Express server instance. Null if server is not running. */\n private server: Server | null = null;\n\n constructor(options: FlowServerOptions) {\n this.options = {\n ...options,\n };\n }\n\n /**\n * Starts the server and adds it to the list of running servers to clean up on exit.\n */\n async start() {\n const server = express();\n\n server.use(bodyParser.json(this.options.jsonParserOptions));\n server.use(cors(this.options.cors));\n\n logger.debug('Running flow server with flow paths:');\n const pathPrefix = this.options.pathPrefix ?? '';\n this.options.flows?.forEach((flow) => {\n if ((flow as FlowWithAuthPolicy).authPolicy) {\n const flowWithPolicy = flow as FlowWithAuthPolicy;\n const flowPath = `/${pathPrefix}${flowWithPolicy.flow.__action.name}`;\n logger.debug(` - ${flowPath}`);\n server.post(\n flowPath,\n flowWithPolicy.authProvider,\n expressHandler(flowWithPolicy.flow, {\n authPolicy: flowWithPolicy.authPolicy,\n })\n );\n } else {\n const resolvedFlow = flow as Flow;\n const flowPath = `/${pathPrefix}${resolvedFlow.__action.name}`;\n logger.debug(` - ${flowPath}`);\n server.post(flowPath, expressHandler(resolvedFlow));\n }\n });\n this.port =\n this.options?.port ||\n (process.env.PORT ? parseInt(process.env.PORT) : 0) ||\n 3400;\n this.server = server.listen(this.port, () => {\n logger.debug(`Flow server running on http://localhost:${this.port}`);\n FlowServer.RUNNING_SERVERS.push(this);\n });\n }\n\n /**\n * Stops the server and removes it from the list of running servers to clean up on exit.\n */\n async stop(): Promise<void> {\n if (!this.server) {\n return;\n }\n return new Promise<void>((resolve, reject) => {\n this.server!.close((err) => {\n if (err) {\n logger.error(\n `Error shutting down flow server on port ${this.port}: ${err}`\n );\n reject(err);\n }\n const index = FlowServer.RUNNING_SERVERS.indexOf(this);\n if (index > -1) {\n FlowServer.RUNNING_SERVERS.splice(index, 1);\n }\n logger.debug(\n `Flow server on port ${this.port} has successfully shut down.`\n );\n this.port = null;\n this.server = null;\n resolve();\n });\n });\n }\n\n /**\n * Stops all running servers.\n */\n static async stopAll() {\n return Promise.all(\n FlowServer.RUNNING_SERVERS.map((server) => server.stop())\n );\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Copyright 2024 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 * as bodyParser from 'body-parser';\nimport cors, { CorsOptions } from 'cors';\nimport express, { RequestHandler } from 'express';\nimport { Action, Flow, runWithStreamingCallback, z } from 'genkit';\nimport { logger } from 'genkit/logging';\nimport { Server } from 'http';\nimport { getErrorMessage, getErrorStack } from './utils';\n\nconst streamDelimiter = '\\n\\n';\n\n/**\n * Auth policy context is an object passed to the auth policy providing details necessary for auth.\n */\nexport interface AuthPolicyContext<\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 input: z.infer<I>;\n auth?: Record<string, any>;\n request: RequestWithAuth;\n}\n\n/**\n * Flow Auth policy. Consumes the authorization context of the flow and\n * performs checks before the flow runs. If this throws, the flow will not\n * be executed.\n */\nexport interface AuthPolicy<\n I extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n S extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n (ctx: AuthPolicyContext<I, O, S>): void | Promise<void>;\n}\n\n/**\n * For express-based flows, req.auth should contain the value to bepassed into\n * the flow context.\n */\nexport interface RequestWithAuth extends express.Request {\n auth?: Record<string, any>;\n}\n\n/**\n * Exposes provided flow or an action as express handler.\n */\nexport function expressHandler<\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 authPolicy?: AuthPolicy<I, O, S>;\n }\n): express.RequestHandler {\n return async (\n request: RequestWithAuth,\n response: express.Response\n ): Promise<void> => {\n const { stream } = request.query;\n let input = request.body.data;\n const auth = request.auth;\n\n try {\n await opts?.authPolicy?.({\n action,\n auth,\n input,\n request,\n });\n } catch (e: any) {\n logger.debug(e);\n const respBody = {\n error: {\n status: 'PERMISSION_DENIED',\n message: e.message || 'Permission denied to resource',\n },\n };\n response.status(403).send(respBody).end();\n return;\n }\n\n if (request.get('Accept') === 'text/event-stream' || stream === 'true') {\n response.writeHead(200, {\n 'Content-Type': 'text/plain',\n 'Transfer-Encoding': 'chunked',\n });\n try {\n const onChunk = (chunk: z.infer<S>) => {\n response.write(\n 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter\n );\n };\n const result = await runWithStreamingCallback(\n action.__registry,\n onChunk,\n () =>\n action.run(input, {\n onChunk,\n context: { auth },\n })\n );\n response.write(\n 'data: ' + JSON.stringify({ result: result.result }) + streamDelimiter\n );\n response.end();\n } catch (e) {\n logger.error(e);\n response.write(\n 'data: ' +\n JSON.stringify({\n error: {\n status: 'INTERNAL',\n message: getErrorMessage(e),\n details: getErrorStack(e),\n },\n }) +\n streamDelimiter\n );\n response.end();\n }\n } else {\n try {\n const result = await action.run(input, { context: { auth } });\n response.setHeader('x-genkit-trace-id', result.telemetry.traceId);\n response.setHeader('x-genkit-span-id', result.telemetry.spanId);\n // Responses for non-streaming flows are passed back with the flow result stored in a field called \"result.\"\n response\n .status(200)\n .send({\n result: result.result,\n })\n .end();\n } catch (e) {\n // Errors for non-streaming flows are passed back as standard API errors.\n response\n .status(500)\n .send({\n error: {\n status: 'INTERNAL',\n message: getErrorMessage(e),\n details: getErrorStack(e),\n },\n })\n .end();\n }\n }\n };\n}\n\n/**\n * A wrapper object containing a flow with its associated auth policy.\n */\nexport type FlowWithAuthPolicy<\n I extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n S extends z.ZodTypeAny = z.ZodTypeAny,\n> = {\n flow: Flow<I, O, S>;\n authProvider: RequestHandler;\n authPolicy: AuthPolicy;\n};\n\n/**\n * Adds an auth policy to the flow.\n */\nexport function withAuth<\n I extends z.ZodTypeAny = z.ZodTypeAny,\n O extends z.ZodTypeAny = z.ZodTypeAny,\n S extends z.ZodTypeAny = z.ZodTypeAny,\n>(\n flow: Flow<I, O, S>,\n authProvider: RequestHandler,\n authPolicy: AuthPolicy\n): FlowWithAuthPolicy<I, O, S> {\n return {\n flow,\n authProvider,\n authPolicy,\n };\n}\n\n/**\n * Options to configure the flow server.\n */\nexport interface FlowServerOptions {\n /** List of flows to expose via the flow server. */\n flows: (Flow<any, any, any> | FlowWithAuthPolicy<any, any, any>)[];\n /** Port to run the server on. Defaults to env.PORT or 3400. */\n port?: number;\n /** CORS options for the server. */\n cors?: CorsOptions;\n /** HTTP method path prefix for the exposed flows. */\n pathPrefix?: string;\n /** JSON body parser options. */\n jsonParserOptions?: bodyParser.OptionsJson;\n}\n\n/**\n * Starts an express server with the provided flows and options.\n */\nexport function startFlowServer(options: FlowServerOptions): FlowServer {\n const server = new FlowServer(options);\n server.start();\n return server;\n}\n\n/**\n * Flow server exposes registered flows as HTTP endpoints.\n *\n * This is for use in production environments.\n *\n * @hidden\n */\nexport class FlowServer {\n /** List of all running servers needed to be cleaned up on process exit. */\n private static RUNNING_SERVERS: FlowServer[] = [];\n\n /** Options for the flow server configured by the developer. */\n private options: FlowServerOptions;\n /** Port the server is actually running on. This may differ from `options.port` if the original was occupied. Null is server is not running. */\n private port: number | null = null;\n /** Express server instance. Null if server is not running. */\n private server: Server | null = null;\n\n constructor(options: FlowServerOptions) {\n this.options = {\n ...options,\n };\n }\n\n /**\n * Starts the server and adds it to the list of running servers to clean up on exit.\n */\n async start() {\n const server = express();\n\n server.use(bodyParser.json(this.options.jsonParserOptions));\n server.use(cors(this.options.cors));\n\n logger.debug('Running flow server with flow paths:');\n const pathPrefix = this.options.pathPrefix ?? '';\n this.options.flows?.forEach((flow) => {\n if ((flow as FlowWithAuthPolicy).authPolicy) {\n const flowWithPolicy = flow as FlowWithAuthPolicy;\n const flowPath = `/${pathPrefix}${flowWithPolicy.flow.__action.name}`;\n logger.debug(` - ${flowPath}`);\n server.post(\n flowPath,\n flowWithPolicy.authProvider,\n expressHandler(flowWithPolicy.flow, {\n authPolicy: flowWithPolicy.authPolicy,\n })\n );\n } else {\n const resolvedFlow = flow as Flow;\n const flowPath = `/${pathPrefix}${resolvedFlow.__action.name}`;\n logger.debug(` - ${flowPath}`);\n server.post(flowPath, expressHandler(resolvedFlow));\n }\n });\n this.port =\n this.options?.port ||\n (process.env.PORT ? parseInt(process.env.PORT) : 0) ||\n 3400;\n this.server = server.listen(this.port, () => {\n logger.debug(`Flow server running on http://localhost:${this.port}`);\n FlowServer.RUNNING_SERVERS.push(this);\n });\n }\n\n /**\n * Stops the server and removes it from the list of running servers to clean up on exit.\n */\n async stop(): Promise<void> {\n if (!this.server) {\n return;\n }\n return new Promise<void>((resolve, reject) => {\n this.server!.close((err) => {\n if (err) {\n logger.error(\n `Error shutting down flow server on port ${this.port}: ${err}`\n );\n reject(err);\n }\n const index = FlowServer.RUNNING_SERVERS.indexOf(this);\n if (index > -1) {\n FlowServer.RUNNING_SERVERS.splice(index, 1);\n }\n logger.debug(\n `Flow server on port ${this.port} has successfully shut down.`\n );\n this.port = null;\n this.server = null;\n resolve();\n });\n });\n }\n\n /**\n * Stops all running servers.\n */\n static async stopAll() {\n return Promise.all(\n FlowServer.RUNNING_SERVERS.map((server) => server.stop())\n );\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"genai",
|
|
10
10
|
"generative-ai"
|
|
11
11
|
],
|
|
12
|
-
"version": "1.0.0-rc.
|
|
12
|
+
"version": "1.0.0-rc.6",
|
|
13
13
|
"type": "commonjs",
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"express": "^4.21.1",
|
|
27
|
-
"genkit": "1.0.0-rc.
|
|
27
|
+
"genkit": "1.0.0-rc.6"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"get-port": "^5.1.0",
|
package/src/index.ts
CHANGED
|
@@ -34,7 +34,7 @@ export interface AuthPolicyContext<
|
|
|
34
34
|
> {
|
|
35
35
|
action?: Action<I, O, S>;
|
|
36
36
|
input: z.infer<I>;
|
|
37
|
-
auth
|
|
37
|
+
auth?: Record<string, any>;
|
|
38
38
|
request: RequestWithAuth;
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -56,7 +56,7 @@ export interface AuthPolicy<
|
|
|
56
56
|
* the flow context.
|
|
57
57
|
*/
|
|
58
58
|
export interface RequestWithAuth extends express.Request {
|
|
59
|
-
auth?:
|
|
59
|
+
auth?: Record<string, any>;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
@@ -116,7 +116,7 @@ export function expressHandler<
|
|
|
116
116
|
() =>
|
|
117
117
|
action.run(input, {
|
|
118
118
|
onChunk,
|
|
119
|
-
context: auth,
|
|
119
|
+
context: { auth },
|
|
120
120
|
})
|
|
121
121
|
);
|
|
122
122
|
response.write(
|
|
@@ -140,7 +140,7 @@ export function expressHandler<
|
|
|
140
140
|
}
|
|
141
141
|
} else {
|
|
142
142
|
try {
|
|
143
|
-
const result = await action.run(input, { context: auth });
|
|
143
|
+
const result = await action.run(input, { context: { auth } });
|
|
144
144
|
response.setHeader('x-genkit-trace-id', result.telemetry.traceId);
|
|
145
145
|
response.setHeader('x-genkit-span-id', result.telemetry.spanId);
|
|
146
146
|
// Responses for non-streaming flows are passed back with the flow result stored in a field called "result."
|
package/tests/express_test.ts
CHANGED
|
@@ -82,7 +82,7 @@ describe('expressHandler', async () => {
|
|
|
82
82
|
inputSchema: z.object({ question: z.string() }),
|
|
83
83
|
},
|
|
84
84
|
async (input, { context }) => {
|
|
85
|
-
return `${input.question} - ${JSON.stringify(context)}`;
|
|
85
|
+
return `${input.question} - ${JSON.stringify(context.auth)}`;
|
|
86
86
|
}
|
|
87
87
|
);
|
|
88
88
|
|
|
@@ -382,7 +382,7 @@ describe('startFlowServer', async () => {
|
|
|
382
382
|
inputSchema: z.object({ question: z.string() }),
|
|
383
383
|
},
|
|
384
384
|
async (input, { context }) => {
|
|
385
|
-
return `${input.question} - ${JSON.stringify(context)}`;
|
|
385
|
+
return `${input.question} - ${JSON.stringify(context.auth)}`;
|
|
386
386
|
}
|
|
387
387
|
);
|
|
388
388
|
|