|
|
@@ -1,5 +1,6 @@
|
|
|
import { APICallError } from "@ai-sdk/provider"
|
|
|
import type { LanguageModelV3, LanguageModelV3StreamPart } from "@ai-sdk/provider"
|
|
|
+import { createMistral } from "@ai-sdk/mistral"
|
|
|
import { AISDK } from "@opencode-ai/core/aisdk"
|
|
|
import { SessionRunnerRetry } from "@opencode-ai/core/session/runner/retry"
|
|
|
import { toSessionError } from "@opencode-ai/core/session/to-session-error"
|
|
|
@@ -341,6 +342,66 @@ it.effect("preserves multimodal messages in AI SDK prompts", () =>
|
|
|
}),
|
|
|
)
|
|
|
|
|
|
+it.effect("sends images through the real Mistral AI SDK provider", () =>
|
|
|
+ Effect.gen(function* () {
|
|
|
+ const aisdk = yield* AISDK.Service
|
|
|
+ let body: { messages?: unknown[] } | undefined
|
|
|
+ const mockFetch = Object.assign(
|
|
|
+ async (_input: Parameters<typeof fetch>[0], init?: RequestInit) => {
|
|
|
+ body = JSON.parse(String(init?.body))
|
|
|
+ const chunks = [
|
|
|
+ {
|
|
|
+ id: "response-1",
|
|
|
+ created: 0,
|
|
|
+ model: "pixtral-large-latest",
|
|
|
+ choices: [{ index: 0, delta: { content: [{ type: "text", text: "I see it." }] } }],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "response-1",
|
|
|
+ created: 0,
|
|
|
+ model: "pixtral-large-latest",
|
|
|
+ choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
|
|
+ usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
|
|
|
+ },
|
|
|
+ ]
|
|
|
+ return new Response(chunks.map((chunk) => `data: ${JSON.stringify(chunk)}\n\n`).join(""), {
|
|
|
+ headers: { "Content-Type": "text/event-stream" },
|
|
|
+ })
|
|
|
+ },
|
|
|
+ { preconnect: fetch.preconnect },
|
|
|
+ )
|
|
|
+ yield* aisdk.hook.sdk((event) => {
|
|
|
+ event.sdk = createMistral({ apiKey: "test", fetch: mockFetch })
|
|
|
+ })
|
|
|
+
|
|
|
+ const resolved = yield* aisdk.model({
|
|
|
+ ...model("@ai-sdk/mistral"),
|
|
|
+ modelID: Model.ID.make("pixtral-large-latest"),
|
|
|
+ })
|
|
|
+ yield* LLMClient.generate(
|
|
|
+ LLM.request({
|
|
|
+ model: resolved,
|
|
|
+ messages: [
|
|
|
+ Message.user([
|
|
|
+ { type: "text", text: "What is in this image?" },
|
|
|
+ { type: "media", mediaType: "image/png", data: "AAAA", filename: "pixel.png" },
|
|
|
+ ]),
|
|
|
+ ],
|
|
|
+ }),
|
|
|
+ ).pipe(Effect.provide(client))
|
|
|
+
|
|
|
+ expect(body?.messages).toEqual([
|
|
|
+ {
|
|
|
+ role: "user",
|
|
|
+ content: [
|
|
|
+ { type: "text", text: "What is in this image?" },
|
|
|
+ { type: "image_url", image_url: "data:image/png;base64,AAAA" },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ])
|
|
|
+ }),
|
|
|
+)
|
|
|
+
|
|
|
it.effect("preserves tool result content in AI SDK prompts", () =>
|
|
|
Effect.gen(function* () {
|
|
|
const aisdk = yield* AISDK.Service
|