UNPKG

genkit

Version:
1 lines 7.41 kB
{"version":3,"sources":["../../src/client/client.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 { Channel } from '@genkit-ai/core/async';\n\nconst __flowStreamDelimiter = '\\n\\n';\n\n/**\n * Invoke and stream response from a deployed flow.\n *\n * For example:\n *\n * ```js\n * import { streamFlow } from 'genkit/beta/client';\n *\n * const response = streamFlow({\n * url: 'https://my-flow-deployed-url',\n * input: 'foo',\n * });\n * for await (const chunk of response.stream) {\n * console.log(chunk);\n * }\n * console.log(await response.output);\n * ```\n */\nexport function streamFlow<O = any, S = any>({\n url,\n input,\n headers,\n}: {\n /** URL of the deployed flow. */\n url: string;\n /** Flow input. */\n input?: any;\n /** A map of HTTP headers to be added to the HTTP call. */\n headers?: Record<string, string>;\n}): {\n readonly output: Promise<O>;\n readonly stream: AsyncIterable<S>;\n} {\n const channel = new Channel<S>();\n\n const operationPromise = __flowRunEnvelope({\n url,\n input,\n sendChunk: (c) => channel.send(c),\n headers,\n });\n operationPromise.then(\n () => channel.close(),\n (err) => channel.error(err)\n );\n\n return {\n output: operationPromise,\n stream: channel,\n };\n}\n\nasync function __flowRunEnvelope({\n url,\n input,\n sendChunk,\n headers,\n}: {\n url: string;\n input: any;\n sendChunk: (chunk: any) => void;\n headers?: Record<string, string>;\n}) {\n const response = await fetch(url, {\n method: 'POST',\n body: JSON.stringify({\n data: input,\n }),\n headers: {\n Accept: 'text/event-stream',\n 'Content-Type': 'application/json',\n ...headers,\n },\n });\n if (response.status !== 200) {\n throw new Error(\n `Server returned: ${response.status}: ${await response.text()}`\n );\n }\n if (!response.body) {\n throw new Error('Response body is empty');\n }\n var reader = response.body.getReader();\n var decoder = new TextDecoder();\n\n let buffer = '';\n while (true) {\n const result = await reader.read();\n const decodedValue = decoder.decode(result.value);\n if (decodedValue) {\n buffer += decodedValue;\n }\n // If buffer includes the delimiter that means we are still recieving chunks.\n while (buffer.includes(__flowStreamDelimiter)) {\n const chunk = JSON.parse(\n buffer\n .substring(0, buffer.indexOf(__flowStreamDelimiter))\n .substring('data: '.length)\n );\n if (chunk.hasOwnProperty('message')) {\n sendChunk(chunk.message);\n } else if (chunk.hasOwnProperty('result')) {\n return chunk.result;\n } else if (chunk.hasOwnProperty('error')) {\n throw new Error(\n `${chunk.error.status}: ${chunk.error.message}\\n${chunk.error.details}`\n );\n } else {\n throw new Error('unkown chunk format: ' + JSON.stringify(chunk));\n }\n buffer = buffer.substring(\n buffer.indexOf(__flowStreamDelimiter) + __flowStreamDelimiter.length\n );\n }\n }\n throw new Error('stream did not terminate correctly');\n}\n\n/**\n * Invoke a deployed flow over HTTP(s).\n *\n * For example:\n *\n * ```js\n * import { runFlow } from 'genkit/beta/client';\n *\n * const response = await runFlow({\n * url: 'https://my-flow-deployed-url',\n * input: 'foo',\n * });\n * console.log(await response);\n * ```\n */\nexport async function runFlow<O = any>({\n url,\n input,\n headers,\n}: {\n /** URL of the deployed flow. */\n url: string;\n /** Flow input. */\n input?: any;\n /** A map of HTTP headers to be added to the HTTP call. */\n headers?: Record<string, string>;\n}): Promise<O> {\n const response = await fetch(url, {\n method: 'POST',\n body: JSON.stringify({\n data: input,\n }),\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n });\n if (response.status !== 200) {\n throw new Error(\n `Server returned: ${response.status}: ${await response.text()}`\n );\n }\n const wrappedResult = (await response.json()) as\n | { result: O }\n | { error: unknown };\n if ('error' in wrappedResult) {\n if (typeof wrappedResult.error === 'string') {\n throw new Error(wrappedResult.error);\n }\n // TODO: The callable protocol defines an HttpError that has a JSON format of\n // details?: string\n // httpErrorCode: { canonicalName: string }\n // message: string\n // Should we create a new error class that parses this and exposes it as fields?\n throw new Error(JSON.stringify(wrappedResult.error));\n }\n return wrappedResult.result;\n}\n"],"mappings":"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","names":[]}