UNPKG

@langchain/community

Version:
1 lines 15 kB
{"version":3,"file":"googlevertexai-connection.cjs","names":[],"sources":["../../src/utils/googlevertexai-connection.ts"],"sourcesContent":["import { BaseLanguageModelCallOptions } from \"@langchain/core/language_models/base\";\nimport {\n AsyncCaller,\n AsyncCallerCallOptions,\n} from \"@langchain/core/utils/async_caller\";\nimport { GenerationChunk } from \"@langchain/core/outputs\";\nimport type {\n GoogleVertexAIBaseLLMInput,\n GoogleVertexAIBasePrediction,\n GoogleVertexAIConnectionParams,\n GoogleVertexAILLMPredictions,\n GoogleVertexAIModelParams,\n GoogleResponse,\n GoogleAbstractedClient,\n GoogleAbstractedClientOps,\n GoogleAbstractedClientOpsMethod,\n} from \"../types/googlevertexai-types.js\";\n\nexport abstract class GoogleConnection<\n CallOptions extends AsyncCallerCallOptions,\n ResponseType extends GoogleResponse,\n> {\n caller: AsyncCaller;\n\n client: GoogleAbstractedClient;\n\n streaming: boolean;\n\n constructor(\n caller: AsyncCaller,\n client: GoogleAbstractedClient,\n streaming?: boolean\n ) {\n this.caller = caller;\n this.client = client;\n this.streaming = streaming ?? false;\n }\n\n abstract buildUrl(): Promise<string>;\n\n abstract buildMethod(): GoogleAbstractedClientOpsMethod;\n\n async _request(\n data: unknown | undefined,\n options: CallOptions\n ): Promise<ResponseType> {\n const url = await this.buildUrl();\n const method = this.buildMethod();\n\n const opts: GoogleAbstractedClientOps = {\n url,\n method,\n };\n if (data && method === \"POST\") {\n opts.data = data;\n }\n if (this.streaming) {\n opts.responseType = \"stream\";\n } else {\n opts.responseType = \"json\";\n }\n\n const callResponse = await this.caller.callWithOptions(\n { signal: options?.signal },\n async () => this.client.request(opts)\n );\n const response: unknown = callResponse; // Done for typecast safety, I guess\n return <ResponseType>response;\n }\n}\n\nexport abstract class GoogleVertexAIConnection<\n CallOptions extends AsyncCallerCallOptions,\n ResponseType extends GoogleResponse,\n AuthOptions,\n>\n extends GoogleConnection<CallOptions, ResponseType>\n implements GoogleVertexAIConnectionParams<AuthOptions>\n{\n endpoint = \"us-central1-aiplatform.googleapis.com\";\n\n location = \"us-central1\";\n\n apiVersion = \"v1\";\n\n constructor(\n fields: GoogleVertexAIConnectionParams<AuthOptions> | undefined,\n caller: AsyncCaller,\n client: GoogleAbstractedClient,\n streaming?: boolean\n ) {\n super(caller, client, streaming);\n this.caller = caller;\n\n this.endpoint = fields?.endpoint ?? this.endpoint;\n this.location = fields?.location ?? this.location;\n this.apiVersion = fields?.apiVersion ?? this.apiVersion;\n this.client = client;\n }\n\n buildMethod(): GoogleAbstractedClientOpsMethod {\n return \"POST\";\n }\n}\n\nexport function complexValue(value: unknown): unknown {\n if (value === null || typeof value === \"undefined\") {\n // I dunno what to put here. An error, probably\n return undefined;\n } else if (typeof value === \"object\") {\n if (Array.isArray(value)) {\n return {\n list_val: value.map((avalue) => complexValue(avalue)),\n };\n } else {\n const ret: Record<string, unknown> = {};\n // oxlint-disable-next-line typescript/no-explicit-any\n const v: Record<string, any> = value;\n Object.keys(v).forEach((key) => {\n ret[key] = complexValue(v[key]);\n });\n return { struct_val: ret };\n }\n } else if (typeof value === \"number\") {\n if (Number.isInteger(value)) {\n return { int_val: value };\n } else {\n return { float_val: value };\n }\n } else {\n return {\n string_val: [value],\n };\n }\n}\n\nexport function simpleValue(val: unknown): unknown {\n if (val && typeof val === \"object\" && !Array.isArray(val)) {\n // eslint-disable-next-line no-prototype-builtins\n if (val.hasOwnProperty(\"stringVal\")) {\n return (val as { stringVal: string[] }).stringVal[0];\n\n // eslint-disable-next-line no-prototype-builtins\n } else if (val.hasOwnProperty(\"boolVal\")) {\n return (val as { boolVal: boolean[] }).boolVal[0];\n\n // eslint-disable-next-line no-prototype-builtins\n } else if (val.hasOwnProperty(\"listVal\")) {\n const { listVal } = val as { listVal: unknown[] };\n return listVal.map((aval) => simpleValue(aval));\n\n // eslint-disable-next-line no-prototype-builtins\n } else if (val.hasOwnProperty(\"structVal\")) {\n const ret: Record<string, unknown> = {};\n const struct = (val as { structVal: Record<string, unknown> }).structVal;\n Object.keys(struct).forEach((key) => {\n ret[key] = simpleValue(struct[key]);\n });\n return ret;\n } else {\n const ret: Record<string, unknown> = {};\n const struct = val as Record<string, unknown>;\n Object.keys(struct).forEach((key) => {\n ret[key] = simpleValue(struct[key]);\n });\n return ret;\n }\n } else if (Array.isArray(val)) {\n return val.map((aval) => simpleValue(aval));\n } else {\n return val;\n }\n}\n\nexport class GoogleVertexAILLMConnection<\n CallOptions extends BaseLanguageModelCallOptions,\n InstanceType,\n PredictionType extends GoogleVertexAIBasePrediction,\n AuthOptions,\n>\n extends GoogleVertexAIConnection<\n CallOptions,\n GoogleVertexAILLMResponse<PredictionType>,\n AuthOptions\n >\n implements GoogleVertexAIBaseLLMInput<AuthOptions>\n{\n model: string;\n\n client: GoogleAbstractedClient;\n\n customModelURL: string;\n\n constructor(\n fields: GoogleVertexAIBaseLLMInput<AuthOptions> | undefined,\n caller: AsyncCaller,\n client: GoogleAbstractedClient,\n streaming?: boolean\n ) {\n super(fields, caller, client, streaming);\n this.client = client;\n this.model = fields?.model ?? this.model;\n\n this.customModelURL = fields?.customModelURL ?? \"\";\n }\n\n async buildUrl(): Promise<string> {\n const method = this.streaming ? \"serverStreamingPredict\" : \"predict\";\n\n if (this.customModelURL.trim() !== \"\") {\n return `${this.customModelURL}:${method}`;\n }\n\n const projectId = await this.client.getProjectId();\n\n return `https://${this.endpoint}/v1/projects/${projectId}/locations/${this.location}/publishers/google/models/${this.model}:${method}`;\n }\n\n formatStreamingData(\n inputs: InstanceType[],\n parameters: GoogleVertexAIModelParams\n ): unknown {\n return {\n inputs: [inputs.map((i) => complexValue(i))],\n parameters: complexValue(parameters),\n };\n }\n\n formatStandardData(\n instances: InstanceType[],\n parameters: GoogleVertexAIModelParams\n ): unknown {\n return {\n instances,\n parameters,\n };\n }\n\n formatData(\n instances: InstanceType[],\n parameters: GoogleVertexAIModelParams\n ): unknown {\n return this.streaming\n ? this.formatStreamingData(instances, parameters)\n : this.formatStandardData(instances, parameters);\n }\n\n async request(\n instances: InstanceType[],\n parameters: GoogleVertexAIModelParams,\n options: CallOptions\n ): Promise<GoogleVertexAILLMResponse<PredictionType>> {\n const data = this.formatData(instances, parameters);\n const response = await this._request(data, options);\n return response;\n }\n}\n\nexport interface GoogleVertexAILLMResponse<\n PredictionType extends GoogleVertexAIBasePrediction,\n> extends GoogleResponse {\n data: GoogleVertexAIStream | GoogleVertexAILLMPredictions<PredictionType>;\n}\n\nexport class GoogleVertexAIStream {\n _buffer = \"\";\n\n _bufferOpen = true;\n\n _firstRun = true;\n\n /**\n * Add data to the buffer. This may cause chunks to be generated, if available.\n * @param data\n */\n appendBuffer(data: string): void {\n this._buffer += data;\n // Our first time, skip to the opening of the array\n if (this._firstRun) {\n this._skipTo(\"[\");\n this._firstRun = false;\n }\n\n this._parseBuffer();\n }\n\n /**\n * Indicate there is no more data that will be added to the text buffer.\n * This should be called when all the data has been read and added to indicate\n * that we should process everything remaining in the buffer.\n */\n closeBuffer(): void {\n this._bufferOpen = false;\n this._parseBuffer();\n }\n\n /**\n * Skip characters in the buffer till we get to the start of an object.\n * Then attempt to read a full object.\n * If we do read a full object, turn it into a chunk and send it to the chunk handler.\n * Repeat this for as much as we can.\n */\n _parseBuffer(): void {\n let obj = null;\n do {\n this._skipTo(\"{\");\n obj = this._getFullObject();\n if (obj !== null) {\n const chunk = this._simplifyObject(obj);\n this._handleChunk(chunk);\n }\n } while (obj !== null);\n\n if (!this._bufferOpen) {\n // No more data will be added, and we have parsed everything we could,\n // so everything else is garbage.\n this._handleChunk(null);\n this._buffer = \"\";\n }\n }\n\n /**\n * If the string is present, move the start of the buffer to the first occurrence\n * of that string. This is useful for skipping over elements or parts that we're not\n * really interested in parsing. (ie - the opening characters, comma separators, etc.)\n * @param start The string to start the buffer with\n */\n _skipTo(start: string): void {\n const index = this._buffer.indexOf(start);\n if (index > 0) {\n this._buffer = this._buffer.slice(index);\n }\n }\n\n /**\n * Given what is in the buffer, parse a single object out of it.\n * If a complete object isn't available, return null.\n * Assumes that we are at the start of an object to parse.\n */\n _getFullObject(): object | null {\n let ret: object | null = null;\n\n // Loop while we don't have something to return AND we have something in the buffer\n let index = 0;\n while (ret === null && this._buffer.length > index) {\n // Advance to the next close bracket after our current index\n index = this._buffer.indexOf(\"}\", index + 1);\n\n // If we don't find one, exit with null\n if (index === -1) {\n return null;\n }\n\n // If we have one, try to turn it into an object to return\n try {\n const objStr = this._buffer.substring(0, index + 1);\n ret = JSON.parse(objStr);\n\n // We only get here if it parsed it ok\n // If we did turn it into an object, remove it from the buffer\n this._buffer = this._buffer.slice(index + 1);\n } catch {\n // It didn't parse it correctly, so we swallow the exception and continue\n }\n }\n\n return ret;\n }\n\n _simplifyObject(obj: unknown): object {\n return simpleValue(obj) as object;\n }\n\n // Set up a potential Promise that the handler can resolve.\n // oxlint-disable-next-line typescript/no-explicit-any\n _chunkResolution: (chunk: any) => void;\n\n // If there is no Promise (it is null), the handler must add it to the queue\n // oxlint-disable-next-line typescript/no-explicit-any\n _chunkPending: Promise<any> | null = null;\n\n // A queue that will collect chunks while there is no Promise\n // oxlint-disable-next-line typescript/no-explicit-any\n _chunkQueue: any[] = [];\n\n /**\n * Register that we have another chunk available for consumption.\n * If we are waiting for a chunk, resolve the promise waiting for it immediately.\n * If not, then add it to the queue.\n * @param chunk\n */\n // oxlint-disable-next-line typescript/no-explicit-any\n _handleChunk(chunk: any): void {\n if (this._chunkPending) {\n this._chunkResolution(chunk);\n this._chunkPending = null;\n } else {\n this._chunkQueue.push(chunk);\n }\n }\n\n /**\n * Get the next chunk that is coming from the stream.\n * This chunk may be null, usually indicating the last chunk in the stream.\n */\n // oxlint-disable-next-line typescript/no-explicit-any\n async nextChunk(): Promise<any> {\n if (this._chunkQueue.length > 0) {\n // If there is data in the queue, return the next queue chunk\n return this._chunkQueue.shift() as GenerationChunk;\n } else {\n // Otherwise, set up a promise that handleChunk will cause to be resolved\n this._chunkPending = new Promise((resolve) => {\n this._chunkResolution = resolve;\n });\n return this._chunkPending;\n }\n }\n\n /**\n * Is the stream done?\n * A stream is only done if all of the following are true:\n * - There is no more data to be added to the text buffer\n * - There is no more data in the text buffer\n * - There are no chunks that are waiting to be consumed\n */\n get streamDone(): boolean {\n return (\n !this._bufferOpen &&\n this._buffer.length === 0 &&\n this._chunkQueue.length === 0 &&\n this._chunkPending === null\n );\n }\n}\n"],"mappings":";AAkBA,IAAsB,mBAAtB,MAGE;CACA;CAEA;CAEA;CAEA,YACE,QACA,QACA,WACA;AACA,OAAK,SAAS;AACd,OAAK,SAAS;AACd,OAAK,YAAY,aAAa;;CAOhC,MAAM,SACJ,MACA,SACuB;EACvB,MAAM,MAAM,MAAM,KAAK,UAAU;EACjC,MAAM,SAAS,KAAK,aAAa;EAEjC,MAAM,OAAkC;GACtC;GACA;GACD;AACD,MAAI,QAAQ,WAAW,OACrB,MAAK,OAAO;AAEd,MAAI,KAAK,UACP,MAAK,eAAe;MAEpB,MAAK,eAAe;AAQtB,SALqB,MAAM,KAAK,OAAO,gBACrC,EAAE,QAAQ,SAAS,QAAQ,EAC3B,YAAY,KAAK,OAAO,QAAQ,KAAK,CACtC;;;AAML,IAAsB,2BAAtB,cAKU,iBAEV;CACE,WAAW;CAEX,WAAW;CAEX,aAAa;CAEb,YACE,QACA,QACA,QACA,WACA;AACA,QAAM,QAAQ,QAAQ,UAAU;AAChC,OAAK,SAAS;AAEd,OAAK,WAAW,QAAQ,YAAY,KAAK;AACzC,OAAK,WAAW,QAAQ,YAAY,KAAK;AACzC,OAAK,aAAa,QAAQ,cAAc,KAAK;AAC7C,OAAK,SAAS;;CAGhB,cAA+C;AAC7C,SAAO;;;AAIX,SAAgB,aAAa,OAAyB;AACpD,KAAI,UAAU,QAAQ,OAAO,UAAU,YAErC;UACS,OAAO,UAAU,SAC1B,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,EACL,UAAU,MAAM,KAAK,WAAW,aAAa,OAAO,CAAC,EACtD;MACI;EACL,MAAM,MAA+B,EAAE;EAEvC,MAAM,IAAyB;AAC/B,SAAO,KAAK,EAAE,CAAC,SAAS,QAAQ;AAC9B,OAAI,OAAO,aAAa,EAAE,KAAK;IAC/B;AACF,SAAO,EAAE,YAAY,KAAK;;UAEnB,OAAO,UAAU,SAC1B,KAAI,OAAO,UAAU,MAAM,CACzB,QAAO,EAAE,SAAS,OAAO;KAEzB,QAAO,EAAE,WAAW,OAAO;KAG7B,QAAO,EACL,YAAY,CAAC,MAAM,EACpB;;AA0CL,IAAa,8BAAb,cAMU,yBAMV;CACE;CAEA;CAEA;CAEA,YACE,QACA,QACA,QACA,WACA;AACA,QAAM,QAAQ,QAAQ,QAAQ,UAAU;AACxC,OAAK,SAAS;AACd,OAAK,QAAQ,QAAQ,SAAS,KAAK;AAEnC,OAAK,iBAAiB,QAAQ,kBAAkB;;CAGlD,MAAM,WAA4B;EAChC,MAAM,SAAS,KAAK,YAAY,2BAA2B;AAE3D,MAAI,KAAK,eAAe,MAAM,KAAK,GACjC,QAAO,GAAG,KAAK,eAAe,GAAG;EAGnC,MAAM,YAAY,MAAM,KAAK,OAAO,cAAc;AAElD,SAAO,WAAW,KAAK,SAAS,eAAe,UAAU,aAAa,KAAK,SAAS,4BAA4B,KAAK,MAAM,GAAG;;CAGhI,oBACE,QACA,YACS;AACT,SAAO;GACL,QAAQ,CAAC,OAAO,KAAK,MAAM,aAAa,EAAE,CAAC,CAAC;GAC5C,YAAY,aAAa,WAAW;GACrC;;CAGH,mBACE,WACA,YACS;AACT,SAAO;GACL;GACA;GACD;;CAGH,WACE,WACA,YACS;AACT,SAAO,KAAK,YACR,KAAK,oBAAoB,WAAW,WAAW,GAC/C,KAAK,mBAAmB,WAAW,WAAW;;CAGpD,MAAM,QACJ,WACA,YACA,SACoD;EACpD,MAAM,OAAO,KAAK,WAAW,WAAW,WAAW;AAEnD,SADiB,MAAM,KAAK,SAAS,MAAM,QAAQ"}