mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-12 09:12:50 +03:00
feat(ai): add Vertex Mistral route (#54058)
This commit is contained in:
committed by
GitHub
Unverified
parent
d7f34d4662
commit
93a7c8404f
@@ -125,7 +125,7 @@ export type ProviderOptionsInput = {
|
||||
readonly [key: string]: unknown
|
||||
}
|
||||
|
||||
const MistralBody = Schema.Struct({
|
||||
export const MistralBody = Schema.Struct({
|
||||
model: Schema.String,
|
||||
messages: Schema.Array(MistralMessage),
|
||||
tools: optionalArray(MistralTool),
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import { Effect, Schema, Struct } from "effect"
|
||||
import type { ProviderPackage } from "../provider-package.js"
|
||||
import { MistralChat } from "../protocols/mistral-chat.js"
|
||||
import { Auth } from "../route/auth.js"
|
||||
import { Route, type RouteDefaultsInput } from "../route/client.js"
|
||||
import { Endpoint } from "../route/endpoint.js"
|
||||
import { Protocol } from "../route/protocol.js"
|
||||
import { HttpTransport } from "../route/transport/index.js"
|
||||
import { ProviderConfigurationError, ProviderID, type ModelID } from "../schema/index.js"
|
||||
import { GoogleVertexShared } from "./google-vertex-shared.js"
|
||||
|
||||
export const id = ProviderID.make("google-vertex")
|
||||
|
||||
export type Config = RouteDefaultsInput &
|
||||
GoogleVertexShared.OAuthOptions & {
|
||||
readonly baseURL?: string
|
||||
readonly location?: string
|
||||
readonly project?: string
|
||||
readonly providerOptions?: MistralChat.ProviderOptionsInput
|
||||
}
|
||||
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
MistralChat.ProviderOptionsInput & {
|
||||
readonly accessToken?: string
|
||||
readonly apiKey?: never
|
||||
readonly baseURL?: string
|
||||
readonly location?: string
|
||||
readonly project?: string
|
||||
}
|
||||
|
||||
type Body = Omit<MistralChat.MistralBody, "prompt_cache_key">
|
||||
|
||||
// Mistral on Vertex takes the Mistral chat body, model included, through the publisher's streamRawPredict method.
|
||||
// Vertex validates the body strictly and rejects prompt_cache_key, which Mistral's own API accepts.
|
||||
const route = Route.make({
|
||||
id: "google-vertex-mistral",
|
||||
provider: id,
|
||||
providerMetadataKey: "vertex",
|
||||
protocol: Protocol.make({
|
||||
id: MistralChat.protocol.id,
|
||||
body: {
|
||||
schema: Schema.Struct(Struct.omit(MistralChat.MistralBody.fields, ["prompt_cache_key"])),
|
||||
from: (request) =>
|
||||
MistralChat.protocol.body.from(request).pipe(Effect.map((body) => Struct.omit(body, ["prompt_cache_key"]))),
|
||||
},
|
||||
stream: MistralChat.protocol.stream,
|
||||
}),
|
||||
endpoint: Endpoint.path(({ request }) => `/${request.model.id}:streamRawPredict`),
|
||||
auth: Auth.none,
|
||||
transport: HttpTransport.sseJson.with<Body>().with({ framing: MistralChat.framing }),
|
||||
})
|
||||
|
||||
export const routes = [route]
|
||||
|
||||
const configuredRoute = (input: Config) => {
|
||||
if ("apiKey" in input && input.apiKey !== undefined)
|
||||
throw new ProviderConfigurationError({ provider: id, message: "Google Vertex Mistral does not support API keys" })
|
||||
const {
|
||||
accessToken: _accessToken,
|
||||
auth: _auth,
|
||||
baseURL,
|
||||
location: inputLocation,
|
||||
project: inputProject,
|
||||
...rest
|
||||
} = input
|
||||
// Mistral is served from regional endpoints only; the global endpoint returns 404.
|
||||
const location = GoogleVertexShared.location(inputLocation, "us-central1")
|
||||
const project = GoogleVertexShared.project(inputProject)
|
||||
return route.with({
|
||||
...rest,
|
||||
endpoint: {
|
||||
baseURL:
|
||||
baseURL ??
|
||||
`https://${GoogleVertexShared.host(location)}/v1/projects/${GoogleVertexShared.requireProject(project)}/locations/${location}/publishers/mistralai/models`,
|
||||
},
|
||||
auth: GoogleVertexShared.oauth(input, project),
|
||||
})
|
||||
}
|
||||
|
||||
export const configure = (input: Config = {}) => {
|
||||
const route = configuredRoute(input)
|
||||
return {
|
||||
id,
|
||||
model: (modelID: string | ModelID) => route.model<MistralChat.ProviderOptionsInput>({ id: modelID }),
|
||||
configure,
|
||||
}
|
||||
}
|
||||
|
||||
export const provider = {
|
||||
id,
|
||||
configure,
|
||||
}
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, MistralChat.ProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ accessToken, apiKey, baseURL, body, headers, location, project, ...providerOptions },
|
||||
) => {
|
||||
if (apiKey !== undefined)
|
||||
throw new ProviderConfigurationError({ provider: id, message: "Google Vertex Mistral does not support API keys" })
|
||||
return configure({
|
||||
accessToken,
|
||||
baseURL,
|
||||
headers,
|
||||
http: body === undefined ? undefined : { body },
|
||||
location,
|
||||
project,
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { model } from "../google-vertex-mistral.js"
|
||||
export type { Settings } from "../google-vertex-mistral.js"
|
||||
@@ -23,6 +23,7 @@ export * as Google from "./google.js"
|
||||
export * as GoogleVertex from "./google-vertex.js"
|
||||
export * as GoogleVertexChat from "./google-vertex-chat.js"
|
||||
export * as GoogleVertexMessages from "./google-vertex-messages.js"
|
||||
export * as GoogleVertexMistral from "./google-vertex-mistral.js"
|
||||
export * as GoogleVertexResponses from "./google-vertex-responses.js"
|
||||
export * as Groq from "./groq.js"
|
||||
export * as Meta from "./meta.js"
|
||||
|
||||
@@ -104,7 +104,10 @@ const HOSTS: Readonly<Record<string, Readonly<Record<string, string>>>> = {
|
||||
deepseek: { "@ai-sdk/openai-compatible": "@opencode/ai/providers/deepseek" },
|
||||
digitalocean: { "@ai-sdk/openai-compatible": "@opencode/ai/providers/digitalocean" },
|
||||
"fireworks-ai": { "@ai-sdk/openai-compatible": "@opencode/ai/providers/fireworks" },
|
||||
"google-vertex": { "@ai-sdk/openai-compatible": "@opencode/ai/providers/google-vertex/chat" },
|
||||
"google-vertex": {
|
||||
"@ai-sdk/openai-compatible": "@opencode/ai/providers/google-vertex/chat",
|
||||
"@ai-sdk/mistral": "@opencode/ai/providers/google-vertex/mistral",
|
||||
},
|
||||
"kimi-for-coding": protocols("moonshot"),
|
||||
meta: protocols("meta"),
|
||||
minimax: protocols("minimax"),
|
||||
@@ -149,8 +152,7 @@ function resolve(specifier: string, context: Context & { readonly settings?: Pro
|
||||
if (Provider.isAISDK(specifier) || npm in PACKAGES || npm in (HOSTS[context.providerID] ?? {}))
|
||||
return native(npm, context)
|
||||
if (npm === "@opencode/ai/providers/amazon-bedrock/mantle") return mantle(context.modelID)
|
||||
if (npm === "@opencode/ai/providers/azure/responses" && azureChat(context))
|
||||
return "@opencode/ai/providers/azure/chat"
|
||||
if (npm === "@opencode/ai/providers/azure/responses" && azureChat(context)) return "@opencode/ai/providers/azure/chat"
|
||||
return NATIVE.has(npm) ? npm : undefined
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ const builtins = new Map<string, () => Promise<unknown>>([
|
||||
["@opencode/ai/providers/google-vertex/chat", () => import("@opencode/ai/providers/google-vertex/chat")],
|
||||
["@opencode/ai/providers/google-vertex/responses", () => import("@opencode/ai/providers/google-vertex/responses")],
|
||||
["@opencode/ai/providers/google-vertex/messages", () => import("@opencode/ai/providers/google-vertex/messages")],
|
||||
["@opencode/ai/providers/google-vertex/mistral", () => import("@opencode/ai/providers/google-vertex/mistral")],
|
||||
["@opencode/ai/providers/groq", () => import("@opencode/ai/providers/groq")],
|
||||
["@opencode/ai/providers/meta/chat", () => import("@opencode/ai/providers/meta/chat")],
|
||||
["@opencode/ai/providers/meta/messages", () => import("@opencode/ai/providers/meta/messages")],
|
||||
|
||||
@@ -610,6 +610,7 @@ const PROTOCOLS: Readonly<Record<string, Protocol>> = {
|
||||
|
||||
"@opencode/ai/providers/anthropic": anthropicMessages,
|
||||
"@opencode/ai/providers/google-vertex/messages": anthropicMessages,
|
||||
"@opencode/ai/providers/google-vertex/mistral": openaiChat,
|
||||
"@opencode/ai/providers/alibaba/messages": alibabaMessages,
|
||||
"@opencode/ai/providers/meta/messages": anthropicMessages,
|
||||
"@opencode/ai/providers/minimax/messages": minimaxMessages,
|
||||
|
||||
Reference in New Issue
Block a user