import { GoogleAuth } from "google-auth-library"; import { SDKOptions } from "../lib/config.js"; import { ClientSDK } from "../lib/sdks.js"; import { Chat } from "./chat.js"; import { Fim } from "./fim.js"; import { SDKHooks } from "../hooks/index.js"; export type GoogleCloudOptions = { /** The region of the Google Cloud AI Platform endpoint */ region?: string; projectId?: string; } | { /** The region of the Google Cloud AI Platform endpoint */ region?: string; apiKey: () => Promise; projectId: string; } const LEGACY_MODEL_ID_FORMAT: { [key: string]: string } = { "codestral-2405": "codestral@2405", "mistral-large-2407": "mistral-large@2407", "mistral-nemo-2407": "mistral-nemo@2407", }; function getModelInfo(model: string): [string, string] { const modelId = LEGACY_MODEL_ID_FORMAT[model]; if (modelId === undefined) { return [model, model]; } model = model.split("-").slice(0, -1).join("-"); return [model, modelId]; } export class MistralGCP extends ClientSDK { constructor(options: SDKOptions & GoogleCloudOptions = {}) { let projectId = options.projectId ?? ""; options.region = options.region ?? "europe-west4"; options.serverURL = `https://${options.region}-aiplatform.googleapis.com`; if (options.apiKey) { if (!options.projectId) { throw new Error("if apiKey is provided, projectId must also be provided to be able to use the Google Cloud API"); } } else { const auth = new GoogleAuth({ scopes: "https://www.googleapis.com/auth/cloud-platform", }); options.apiKey = async () => { const authClient = await auth.getClient(); const authHeaders = await authClient.getRequestHeaders(); const token = authHeaders.get("Authorization"); if (!token) { throw new Error("failed to get Google Cloud API key from the default authorization provider, check you are authenticated"); } if (!options.projectId) { const userProject = authHeaders.get("x-goog-user-project"); if (!userProject) { throw new Error( "no project id available in default google credentials. Please provide a project id in the input arguments to MistralGCP." ); } projectId = userProject; } return token; } } const hooks = new SDKHooks(); const superOptions: SDKOptions & { hooks?: SDKHooks } = options; superOptions.hooks = hooks; super(options) hooks.registerBeforeCreateRequestHook({ beforeCreateRequest: (_, input) => { if (!projectId) { throw new Error("projectId was not resolved by the auth hook"); } if (!input.options?.body) { throw new Error("Expected body"); } let body: unknown = {}; const bodyStream = input.options.body; if (bodyStream) { body = JSON.parse(bodyStream.toString()); } if (typeof body !== "object" || !body) { throw new Error("Expected object body"); } const rawPredictType = input.url.pathname.includes("streamRawPredict") ? "streamRawPredict" : "rawPredict"; if (!("model" in body) || typeof body.model !== "string") { throw new Error("body.model is required and must be a string"); } const [model, modelId] = getModelInfo(body.model); if (!model || !modelId) { throw new Error("model must be in the format 'model-version'"); } input.url.pathname = `v1/projects/${projectId}/locations/${options.region}/publishers/mistralai/models/${modelId}:${rawPredictType}`; body.model = model input.options.body = JSON.stringify(body); return input; }, }); } private _chat?: Chat; get chat(): Chat { return (this._chat ??= new Chat(this._options)); } private _fim?: Fim; get fim(): Fim { return (this._fim ??= new Fim(this._options)); } }