{"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"}