@effect-uai/mistral 0.18.0 → 0.19.0-dev-20261110065930

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -69,7 +69,7 @@ dependency.
69
69
 
70
70
  ## Docs
71
71
 
72
- <https://effect-uai.betalyra.com/providers/mistral/>
72
+ <https://effect-uai.com/providers/mistral/>
73
73
 
74
74
  ## License
75
75
 
@@ -1 +1 @@
1
- {"version":3,"file":"Mistral.d.mts","names":[],"sources":["../src/Mistral.ts"],"mappings":";;;;;;;;;KAgCY,iBAAiB,KAAK;;;;;WAKvB,OAAO;;;;WAIP;;WAEA;;KAGC;;WAED,aAAa,SAAS,mBAAmB,OAAO,OAAO,WAAW,QAAQ;;WAE1E,OAAO,SAAS,mBAAmB,OAAO,OAAO,MAAM,QAAQ;;;;;;;;cAQ7D,gBAAgB;KAIjB;WACD,QAAQ,SAAS;WACjB;;;cA2GE,OAAI,KAAS,WAAS,OAAO,OAAO,uBAAuB,WAAW;;;;;cActE,QAAK,KACX,WACJ,MAAM,MAAM,UAAU,sBAAsB,WAAW"}
1
+ {"version":3,"file":"Mistral.d.mts","names":[],"sources":["../src/Mistral.ts"],"mappings":";;;;;;;;;KAiCY,iBAAiB,KAAK;;;;;WAKvB,OAAO;;;;WAIP;;WAEA;;KAGC;;WAED,aAAa,SAAS,mBAAmB,OAAO,OAAO,WAAW,QAAQ;;WAE1E,OAAO,SAAS,mBAAmB,OAAO,OAAO,MAAM,QAAQ;;;;;;;;cAQ7D,gBAAgB;KAIjB;WACD,QAAQ,SAAS;WACjB;;;cA4GE,OAAI,KAAS,WAAS,OAAO,OAAO,uBAAuB,WAAW;;;;;cActE,QAAK,KACX,WACJ,MAAM,MAAM,UAAU,sBAAsB,WAAW"}
package/dist/Mistral.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as __exportAll } from "./rolldown-runtime-EAsCxrDo.mjs";
2
- import { accumulatorToTurn, applyChunk, decodeChunk, emptyAccumulator, itemsToMessages, responseFormatWire, toolChoiceWire, toolsWire } from "./codec.mjs";
2
+ import { accumulatorToTurn, applyChunk, decodeChunk, emptyAccumulator, itemsToMessages, rejectToolOutputMedia, responseFormatWire, toolChoiceWire, toolsWire } from "./codec.mjs";
3
3
  import { httpStatusError } from "./http.mjs";
4
4
  import { Context, Effect, Layer, Option, Redacted, Result, Schema, Stream } from "effect";
5
5
  import { HttpClient, HttpClientRequest } from "effect/http";
@@ -53,6 +53,7 @@ const buildStream = (cfg) => {
53
53
  capability: "output_image",
54
54
  reason: "Assistant messages carry no image on this wire. Use `Turn.imagesAsInput` to resend it as user content."
55
55
  });
56
+ yield* rejectToolOutputMedia("mistral", request.history);
56
57
  const httpRequest = HttpClientRequest.post(url).pipe(HttpClientRequest.setHeader("authorization", `Bearer ${Redacted.value(cfg.apiKey)}`), HttpClientRequest.bodyJsonUnsafe(buildRequestBody(request)), HttpClientRequest.accept("text/event-stream"));
57
58
  const response = yield* client.execute(httpRequest).pipe(Effect.mapError((cause) => new AiError.Unavailable({
58
59
  provider: "mistral",
@@ -1 +1 @@
1
- {"version":3,"file":"Mistral.mjs","names":[],"sources":["../src/Mistral.ts"],"sourcesContent":["import { Context, Effect, Layer, Option, Redacted, Result, Schema, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport * as Capabilities from \"@effect-uai/core/Capabilities\"\nimport {\n type CommonRequest,\n LanguageModel,\n type LanguageModelService,\n turnFromStream,\n} from \"@effect-uai/core/LanguageModel\"\nimport * as SSE from \"@effect-uai/core/SSE\"\nimport { descriptorsOf } from \"@effect-uai/core/Tool\"\nimport { type Turn, TurnEvent } from \"@effect-uai/core/Turn\"\nimport {\n type Accumulator,\n type WireChunk,\n accumulatorToTurn,\n applyChunk,\n decodeChunk,\n emptyAccumulator,\n itemsToMessages,\n responseFormatWire,\n toolChoiceWire,\n toolsWire,\n} from \"./codec.js\"\nimport { httpStatusError } from \"./http.js\"\nimport type { MistralModel } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport type MistralRequest = Omit<CommonRequest, \"model\"> & {\n /**\n * Narrows `CommonRequest.model` (`string`) to the typed `MistralModel`\n * literal union for autocomplete.\n */\n readonly model: MistralModel\n /**\n * Prepend a safety system prompt (`safe_prompt` on the wire). Off by default.\n */\n readonly safePrompt?: boolean\n /** Deterministic sampling seed (`random_seed` on the wire). */\n readonly randomSeed?: number\n}\n\nexport type MistralService = {\n /** Stream canonical `TurnEvent`s for one turn. */\n readonly streamTurn: (request: MistralRequest) => Stream.Stream<TurnEvent, AiError.AiError>\n /** Drain a single turn and return the assembled `Turn`. */\n readonly turn: (request: MistralRequest) => Effect.Effect<Turn, AiError.AiError>\n}\n\n/**\n * Provider-typed service tag. Yield this when you want Mistral-specific options\n * (`safePrompt`, `randomSeed`); yield the generic `LanguageModel` tag for\n * provider-portable code. Both are registered by `layer`.\n */\nexport class Mistral extends Context.Service<Mistral, MistralService>()(\n \"@betalyra/effect-uai/providers/mistral/Mistral\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n}\n\n// ---------------------------------------------------------------------------\n// Request body\n// ---------------------------------------------------------------------------\n\nconst buildRequestBody = (request: MistralRequest): Record<string, unknown> => {\n const tools = toolsWire(descriptorsOf(request.tools))\n return {\n model: request.model,\n stream: true,\n messages: itemsToMessages(request.history),\n ...(request.temperature !== undefined && { temperature: request.temperature }),\n ...(request.topP !== undefined && { top_p: request.topP }),\n ...(request.maxOutputTokens !== undefined && { max_tokens: request.maxOutputTokens }),\n ...(request.safePrompt !== undefined && { safe_prompt: request.safePrompt }),\n ...(request.randomSeed !== undefined && { random_seed: request.randomSeed }),\n ...(Option.isSome(tools) && { tools: tools.value }),\n ...(request.toolChoice !== undefined && { tool_choice: toolChoiceWire(request.toolChoice) }),\n ...(request.structured !== undefined && {\n response_format: responseFormatWire(request.structured),\n }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// SSE → WireChunk\n// ---------------------------------------------------------------------------\n\nconst parseJsonUnknown = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Unknown))\n\n/**\n * Decode one SSE event's `data` payload into a `WireChunk`. Returns `None`\n * for the `[DONE]` sentinel and for any payload that isn't a chunk we model,\n * so a stray keep-alive never aborts the turn.\n */\nconst decodeEvent = (data: string): Effect.Effect<Option.Option<WireChunk>> =>\n data.trim() === \"[DONE]\"\n ? Effect.succeedNone\n : parseJsonUnknown(data).pipe(Effect.flatMap(decodeChunk), Effect.option)\n\n// ---------------------------------------------------------------------------\n// Stream\n// ---------------------------------------------------------------------------\n\nconst buildStream = (cfg: Config) => {\n const baseUrl = cfg.baseUrl ?? \"https://api.mistral.ai\"\n const url = `${baseUrl}/v1/chat/completions`\n return (\n request: MistralRequest,\n ): Stream.Stream<TurnEvent, AiError.AiError, HttpClient.HttpClient> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n yield* Capabilities.warnDroppedBlocks(request.history, \"output_image\", {\n provider: \"mistral\",\n capability: \"output_image\",\n reason:\n \"Assistant messages carry no image on this wire. Use `Turn.imagesAsInput` to resend it as user content.\",\n })\n const httpRequest = HttpClientRequest.post(url).pipe(\n HttpClientRequest.setHeader(\"authorization\", `Bearer ${Redacted.value(cfg.apiKey)}`),\n HttpClientRequest.bodyJsonUnsafe(buildRequestBody(request)),\n HttpClientRequest.accept(\"text/event-stream\"),\n )\n const response = yield* client\n .execute(httpRequest)\n .pipe(\n Effect.mapError(\n (cause): AiError.AiError =>\n new AiError.Unavailable({ provider: \"mistral\", raw: cause }),\n ),\n )\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(httpStatusError(response.status, text))\n }\n\n return response.stream.pipe(\n Stream.mapError(\n (cause): AiError.AiError =>\n new AiError.Unavailable({ provider: \"mistral\", raw: cause }),\n ),\n SSE.fromBytes,\n Stream.mapEffect((ev) => decodeEvent(ev.data)),\n Stream.filterMap((chunk) =>\n Option.isSome(chunk) ? Result.succeed(chunk.value) : Result.failVoid,\n ),\n Stream.mapAccum((): Accumulator => emptyAccumulator, applyChunk, {\n // `onHalt` also fires on upstream failure and truncated streams,\n // so only emit `TurnComplete` once a `finish_reason` was observed\n onHalt: (acc) =>\n Option.isSome(acc.finishReason)\n ? [TurnEvent.TurnComplete({ turn: accumulatorToTurn(acc) })]\n : [],\n }),\n )\n }),\n )\n}\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/** Build a `MistralService` value. For Layer-based setup, prefer `layer`. */\nexport const make = (cfg: Config): Effect.Effect<MistralService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => {\n const streamTurn: MistralService[\"streamTurn\"] = (request) =>\n buildStream(cfg)(request).pipe(Stream.provideService(HttpClient.HttpClient, client))\n return {\n streamTurn,\n turn: turnFromStream(streamTurn),\n }\n })\n\n/**\n * Layer that registers both the provider-specific `Mistral` tag and the\n * generic `LanguageModel` tag, sharing one underlying implementation.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<Mistral | LanguageModel, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(Mistral, make(cfg))\n const generic = Layer.effect(\n LanguageModel,\n Effect.map(make(cfg), (s): LanguageModelService => ({\n streamTurn: (request) => s.streamTurn(request as MistralRequest),\n turn: (request) => s.turn(request as MistralRequest),\n })),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Mistral.mjs","names":[],"sources":["../src/Mistral.ts"],"sourcesContent":["import { Context, Effect, Layer, Option, Redacted, Result, Schema, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport * as Capabilities from \"@effect-uai/core/Capabilities\"\nimport {\n type CommonRequest,\n LanguageModel,\n type LanguageModelService,\n turnFromStream,\n} from \"@effect-uai/core/LanguageModel\"\nimport * as SSE from \"@effect-uai/core/SSE\"\nimport { descriptorsOf } from \"@effect-uai/core/Tool\"\nimport { type Turn, TurnEvent } from \"@effect-uai/core/Turn\"\nimport {\n type Accumulator,\n type WireChunk,\n accumulatorToTurn,\n applyChunk,\n decodeChunk,\n emptyAccumulator,\n itemsToMessages,\n rejectToolOutputMedia,\n responseFormatWire,\n toolChoiceWire,\n toolsWire,\n} from \"./codec.js\"\nimport { httpStatusError } from \"./http.js\"\nimport type { MistralModel } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport type MistralRequest = Omit<CommonRequest, \"model\"> & {\n /**\n * Narrows `CommonRequest.model` (`string`) to the typed `MistralModel`\n * literal union for autocomplete.\n */\n readonly model: MistralModel\n /**\n * Prepend a safety system prompt (`safe_prompt` on the wire). Off by default.\n */\n readonly safePrompt?: boolean\n /** Deterministic sampling seed (`random_seed` on the wire). */\n readonly randomSeed?: number\n}\n\nexport type MistralService = {\n /** Stream canonical `TurnEvent`s for one turn. */\n readonly streamTurn: (request: MistralRequest) => Stream.Stream<TurnEvent, AiError.AiError>\n /** Drain a single turn and return the assembled `Turn`. */\n readonly turn: (request: MistralRequest) => Effect.Effect<Turn, AiError.AiError>\n}\n\n/**\n * Provider-typed service tag. Yield this when you want Mistral-specific options\n * (`safePrompt`, `randomSeed`); yield the generic `LanguageModel` tag for\n * provider-portable code. Both are registered by `layer`.\n */\nexport class Mistral extends Context.Service<Mistral, MistralService>()(\n \"@betalyra/effect-uai/providers/mistral/Mistral\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n}\n\n// ---------------------------------------------------------------------------\n// Request body\n// ---------------------------------------------------------------------------\n\nconst buildRequestBody = (request: MistralRequest): Record<string, unknown> => {\n const tools = toolsWire(descriptorsOf(request.tools))\n return {\n model: request.model,\n stream: true,\n messages: itemsToMessages(request.history),\n ...(request.temperature !== undefined && { temperature: request.temperature }),\n ...(request.topP !== undefined && { top_p: request.topP }),\n ...(request.maxOutputTokens !== undefined && { max_tokens: request.maxOutputTokens }),\n ...(request.safePrompt !== undefined && { safe_prompt: request.safePrompt }),\n ...(request.randomSeed !== undefined && { random_seed: request.randomSeed }),\n ...(Option.isSome(tools) && { tools: tools.value }),\n ...(request.toolChoice !== undefined && { tool_choice: toolChoiceWire(request.toolChoice) }),\n ...(request.structured !== undefined && {\n response_format: responseFormatWire(request.structured),\n }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// SSE → WireChunk\n// ---------------------------------------------------------------------------\n\nconst parseJsonUnknown = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Unknown))\n\n/**\n * Decode one SSE event's `data` payload into a `WireChunk`. Returns `None`\n * for the `[DONE]` sentinel and for any payload that isn't a chunk we model,\n * so a stray keep-alive never aborts the turn.\n */\nconst decodeEvent = (data: string): Effect.Effect<Option.Option<WireChunk>> =>\n data.trim() === \"[DONE]\"\n ? Effect.succeedNone\n : parseJsonUnknown(data).pipe(Effect.flatMap(decodeChunk), Effect.option)\n\n// ---------------------------------------------------------------------------\n// Stream\n// ---------------------------------------------------------------------------\n\nconst buildStream = (cfg: Config) => {\n const baseUrl = cfg.baseUrl ?? \"https://api.mistral.ai\"\n const url = `${baseUrl}/v1/chat/completions`\n return (\n request: MistralRequest,\n ): Stream.Stream<TurnEvent, AiError.AiError, HttpClient.HttpClient> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n yield* Capabilities.warnDroppedBlocks(request.history, \"output_image\", {\n provider: \"mistral\",\n capability: \"output_image\",\n reason:\n \"Assistant messages carry no image on this wire. Use `Turn.imagesAsInput` to resend it as user content.\",\n })\n yield* rejectToolOutputMedia(\"mistral\", request.history)\n const httpRequest = HttpClientRequest.post(url).pipe(\n HttpClientRequest.setHeader(\"authorization\", `Bearer ${Redacted.value(cfg.apiKey)}`),\n HttpClientRequest.bodyJsonUnsafe(buildRequestBody(request)),\n HttpClientRequest.accept(\"text/event-stream\"),\n )\n const response = yield* client\n .execute(httpRequest)\n .pipe(\n Effect.mapError(\n (cause): AiError.AiError =>\n new AiError.Unavailable({ provider: \"mistral\", raw: cause }),\n ),\n )\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(httpStatusError(response.status, text))\n }\n\n return response.stream.pipe(\n Stream.mapError(\n (cause): AiError.AiError =>\n new AiError.Unavailable({ provider: \"mistral\", raw: cause }),\n ),\n SSE.fromBytes,\n Stream.mapEffect((ev) => decodeEvent(ev.data)),\n Stream.filterMap((chunk) =>\n Option.isSome(chunk) ? Result.succeed(chunk.value) : Result.failVoid,\n ),\n Stream.mapAccum((): Accumulator => emptyAccumulator, applyChunk, {\n // `onHalt` also fires on upstream failure and truncated streams,\n // so only emit `TurnComplete` once a `finish_reason` was observed\n onHalt: (acc) =>\n Option.isSome(acc.finishReason)\n ? [TurnEvent.TurnComplete({ turn: accumulatorToTurn(acc) })]\n : [],\n }),\n )\n }),\n )\n}\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/** Build a `MistralService` value. For Layer-based setup, prefer `layer`. */\nexport const make = (cfg: Config): Effect.Effect<MistralService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => {\n const streamTurn: MistralService[\"streamTurn\"] = (request) =>\n buildStream(cfg)(request).pipe(Stream.provideService(HttpClient.HttpClient, client))\n return {\n streamTurn,\n turn: turnFromStream(streamTurn),\n }\n })\n\n/**\n * Layer that registers both the provider-specific `Mistral` tag and the\n * generic `LanguageModel` tag, sharing one underlying implementation.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<Mistral | LanguageModel, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(Mistral, make(cfg))\n const generic = Layer.effect(\n LanguageModel,\n Effect.map(make(cfg), (s): LanguageModelService => ({\n streamTurn: (request) => s.streamTurn(request as MistralRequest),\n turn: (request) => s.turn(request as MistralRequest),\n })),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
package/dist/codec.d.mts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Effect } from "effect";
2
- import { Accumulator, WireChunk, WireChunk as WireChunk$1, accumulatorToTurn, applyChunk, emptyAccumulator, responseFormatWire, toolsWire } from "@effect-uai/chat-completions/codec";
2
+ import { Accumulator, WireChunk, WireChunk as WireChunk$1, accumulatorToTurn, applyChunk, emptyAccumulator, rejectToolOutputMedia, responseFormatWire, toolsWire } from "@effect-uai/chat-completions/codec";
3
3
  import { HistoryItem } from "@effect-uai/core/Items";
4
4
  declare namespace codec_d_exports {
5
- export { Accumulator, WireChunk, accumulatorToTurn, applyChunk, decodeChunk, emptyAccumulator, itemsToMessages, responseFormatWire, toolChoiceWire, toolsWire };
5
+ export { Accumulator, WireChunk, accumulatorToTurn, applyChunk, decodeChunk, emptyAccumulator, itemsToMessages, rejectToolOutputMedia, responseFormatWire, toolChoiceWire, toolsWire };
6
6
  }
7
7
  type WireMessage = {
8
8
  role: string;
@@ -19,5 +19,5 @@ type ToolChoice = "auto" | "required" | "none" | {
19
19
  declare const toolChoiceWire: (choice: ToolChoice) => string | Record<string, unknown>;
20
20
  declare const decodeChunk: (u: unknown) => Effect.Effect<WireChunk$1, unknown>;
21
21
  //#endregion
22
- export { type Accumulator, type WireChunk, accumulatorToTurn, applyChunk, decodeChunk, emptyAccumulator, itemsToMessages, responseFormatWire, codec_d_exports as t, toolChoiceWire, toolsWire };
22
+ export { type Accumulator, type WireChunk, accumulatorToTurn, applyChunk, decodeChunk, emptyAccumulator, itemsToMessages, rejectToolOutputMedia, responseFormatWire, codec_d_exports as t, toolChoiceWire, toolsWire };
23
23
  //# sourceMappingURL=codec.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"codec.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":";;;;;;KA6BK;EACH;EACA,kBAAkB,cAAc;EAChC,aAAa,cAAc;EAC3B;;;cAkFW,kBAAe,OAAW,cAAc,iBAAe,cAAc;KAO7E;WAIU;WAA2B;;cAG7B,iBAAc,QAAY,wBAAsB;cAyBhD,cAAW,eAAiB,OAAO,OAAO"}
1
+ {"version":3,"file":"codec.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":";;;;;;KA+BK;EACH;EACA,kBAAkB,cAAc;EAChC,aAAa,cAAc;EAC3B;;;cAkFW,kBAAe,OAAW,cAAc,iBAAe,cAAc;KAO7E;WAIU;WAA2B;;cAG7B,iBAAc,QAAY,wBAAsB;cAyBhD,cAAW,eAAiB,OAAO,OAAO"}
package/dist/codec.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { n as __exportAll } from "./rolldown-runtime-EAsCxrDo.mjs";
2
2
  import { Array, Effect, Match, Result } from "effect";
3
3
  import { Base64 } from "effect/encoding";
4
- import { accumulatorToTurn, applyChunk, decodeChunk as decodeChunk$1, emptyAccumulator, responseFormatWire, toolsWire } from "@effect-uai/chat-completions/codec";
4
+ import { accumulatorToTurn, applyChunk, decodeChunk as decodeChunk$1, emptyAccumulator, rejectToolOutputMedia, responseFormatWire, toolOutputText, toolsWire } from "@effect-uai/chat-completions/codec";
5
5
  //#region src/codec.ts
6
6
  var codec_exports = /* @__PURE__ */ __exportAll({
7
7
  accumulatorToTurn: () => accumulatorToTurn,
@@ -9,6 +9,7 @@ var codec_exports = /* @__PURE__ */ __exportAll({
9
9
  decodeChunk: () => decodeChunk,
10
10
  emptyAccumulator: () => emptyAccumulator,
11
11
  itemsToMessages: () => itemsToMessages,
12
+ rejectToolOutputMedia: () => rejectToolOutputMedia,
12
13
  responseFormatWire: () => responseFormatWire,
13
14
  toolChoiceWire: () => toolChoiceWire,
14
15
  toolsWire: () => toolsWire
@@ -75,7 +76,7 @@ const foldItem = (acc, item) => Match.value(item).pipe(Match.discriminatorsExhau
75
76
  function_call_output: (o) => [...acc, {
76
77
  role: "tool",
77
78
  tool_call_id: o.call_id,
78
- content: o.output
79
+ content: toolOutputText(o)
79
80
  }],
80
81
  reasoning: () => acc
81
82
  }));
@@ -94,6 +95,6 @@ const normalizeChunk = (chunk) => chunk.choices === void 0 ? chunk : {
94
95
  };
95
96
  const decodeChunk = (u) => decodeChunk$1(u).pipe(Effect.map(normalizeChunk));
96
97
  //#endregion
97
- export { accumulatorToTurn, applyChunk, decodeChunk, emptyAccumulator, itemsToMessages, responseFormatWire, codec_exports as t, toolChoiceWire, toolsWire };
98
+ export { accumulatorToTurn, applyChunk, decodeChunk, emptyAccumulator, itemsToMessages, rejectToolOutputMedia, responseFormatWire, codec_exports as t, toolChoiceWire, toolsWire };
98
99
 
99
100
  //# sourceMappingURL=codec.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"codec.mjs","names":["Arr","decodeChunkShared"],"sources":["../src/codec.ts"],"sourcesContent":["import { Array as Arr, Effect, Match, Option, Result } from \"effect\"\nimport { Base64 } from \"effect/encoding\"\nimport type { ContentBlock, HistoryItem, InputImage } from \"@effect-uai/core/Items\"\n\n// The streaming decoder, tool encoding, and terminal-turn assembly are the\n// generic OpenAI chat-completions codec: Mistral speaks that dialect on the\n// wire, so it reuses them verbatim. Only request encoding diverges (below).\nexport {\n type Accumulator,\n type WireChunk,\n accumulatorToTurn,\n applyChunk,\n emptyAccumulator,\n responseFormatWire,\n toolsWire,\n} from \"@effect-uai/chat-completions/codec\"\nimport {\n type WireChunk,\n decodeChunk as decodeChunkShared,\n} from \"@effect-uai/chat-completions/codec\"\n\n// ---------------------------------------------------------------------------\n// History → Mistral chat `messages`\n//\n// Same shape as the generic codec, with one wire quirk: Mistral takes a bare\n// `image_url` string, not OpenAI's `image_url: { url }` object. That is the\n// reason this half stays local rather than importing `itemsToMessages`.\n// ---------------------------------------------------------------------------\n\ntype WireMessage = {\n role: string\n content: string | ReadonlyArray<Record<string, unknown>>\n tool_calls?: ReadonlyArray<Record<string, unknown>>\n tool_call_id?: string\n}\n\n/** A `data:` URI for inline image bytes; URLs pass through untouched. */\nconst imageSourceToUrl = Match.type<InputImage[\"source\"]>().pipe(\n Match.tag(\"url\", (s) => s.url),\n Match.tag(\"base64\", (s) => `data:${s.mimeType};base64,${s.base64}`),\n Match.tag(\"bytes\", (s) => `data:${s.mimeType};base64,${Base64.encode(s.bytes)}`),\n Match.exhaustive,\n)\n\nconst blockToPart = Match.type<ContentBlock>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) => ({ type: \"text\", text: b.text }),\n output_text: (b) => ({ type: \"text\", text: b.text }),\n refusal: (b) => ({ type: \"text\", text: b.text }),\n // Bare string, not `{ url }`: the Mistral divergence from OpenAI.\n input_image: (b) => ({ type: \"image_url\", image_url: imageSourceToUrl(b.source) }),\n // No assistant-image part here. Dropped rather than relocated onto a\n // user message, which is the caller's call: `Turn.imagesAsInput`.\n output_image: () => undefined,\n }),\n)\n\nconst isTextBlock = (b: ContentBlock): boolean =>\n b.type === \"input_text\" || b.type === \"output_text\" || b.type === \"refusal\"\n\nconst textOfBlock = (b: ContentBlock): string =>\n b.type === \"input_image\" || b.type === \"output_image\" ? \"\" : b.text\n\n/**\n * Collapse a message's content to a plain string when it is text-only\n * (the common case Mistral prefers), otherwise emit the multimodal parts\n * array.\n */\nconst encodeContent = (\n content: ReadonlyArray<ContentBlock>,\n): string | ReadonlyArray<Record<string, unknown>> =>\n Arr.every(content, isTextBlock)\n ? content.map(textOfBlock).join(\"\")\n : Arr.filterMap(content, (b) => {\n const part = blockToPart(b)\n return part === undefined ? Result.failVoid : Result.succeed(part)\n })\n\nconst toolCallWire = (call_id: string, name: string, args: string): Record<string, unknown> => ({\n id: call_id,\n type: \"function\",\n function: { name, arguments: args },\n})\n\nconst appendToolCall = (\n acc: ReadonlyArray<WireMessage>,\n call_id: string,\n name: string,\n args: string,\n): ReadonlyArray<WireMessage> => {\n const last = acc[acc.length - 1]\n const wire = toolCallWire(call_id, name, args)\n // Attach to the immediately-preceding assistant message (not a tool result),\n // matching the OpenAI/Mistral shape where tool calls ride the assistant turn.\n if (last !== undefined && last.role === \"assistant\" && last.tool_call_id === undefined) {\n return [...acc.slice(0, -1), { ...last, tool_calls: [...(last.tool_calls ?? []), wire] }]\n }\n return [...acc, { role: \"assistant\", content: \"\", tool_calls: [wire] }]\n}\n\nconst foldItem = (acc: ReadonlyArray<WireMessage>, item: HistoryItem): ReadonlyArray<WireMessage> =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m) => [...acc, { role: m.role, content: encodeContent(m.content) }],\n function_call: (f) => appendToolCall(acc, f.call_id, f.name, f.arguments),\n function_call_output: (o) => [\n ...acc,\n { role: \"tool\", tool_call_id: o.call_id, content: o.output },\n ],\n // Reasoning items are not replayed to the chat-completions endpoint.\n reasoning: () => acc,\n }),\n )\n\n/** Convert our `HistoryItem[]` history into Mistral chat `messages`. */\nexport const itemsToMessages = (items: ReadonlyArray<HistoryItem>): ReadonlyArray<WireMessage> =>\n Arr.reduce(items, [] as ReadonlyArray<WireMessage>, foldItem)\n\n// ---------------------------------------------------------------------------\n// tool_choice\n// ---------------------------------------------------------------------------\n\ntype ToolChoice =\n | \"auto\"\n | \"required\"\n | \"none\"\n | { readonly type: \"function\"; readonly name: string }\n\n// Mistral uses \"any\" for forced tool use (OpenAI's \"required\").\nexport const toolChoiceWire = (choice: ToolChoice): string | Record<string, unknown> =>\n Match.value(choice).pipe(\n Match.when(\"auto\", () => \"auto\" as const),\n Match.when(\"required\", () => \"any\" as const),\n Match.when(\"none\", () => \"none\" as const),\n Match.orElse((c) => ({ type: \"function\", function: { name: c.name } })),\n )\n\n// ---------------------------------------------------------------------------\n// Streaming decode\n// ---------------------------------------------------------------------------\n\n// Mistral emits `model_length` when it truncates on the context window; the\n// shared decoder only knows OpenAI's `length`. Rewrite it before folding so the\n// shared accumulator maps it to `max_tokens` rather than the `stop` fallback.\nconst normalizeChunk = (chunk: WireChunk): WireChunk =>\n chunk.choices === undefined\n ? chunk\n : {\n ...chunk,\n choices: chunk.choices.map((c) =>\n c.finish_reason === \"model_length\" ? { ...c, finish_reason: \"length\" } : c,\n ),\n }\n\nexport const decodeChunk = (u: unknown): Effect.Effect<WireChunk, unknown> =>\n decodeChunkShared(u).pipe(Effect.map(normalizeChunk))\n"],"mappings":"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"}
1
+ {"version":3,"file":"codec.mjs","names":["Arr","decodeChunkShared"],"sources":["../src/codec.ts"],"sourcesContent":["import { Array as Arr, Effect, Match, Option, Result } from \"effect\"\nimport { Base64 } from \"effect/encoding\"\nimport type { ContentBlock, HistoryItem, InputImage } from \"@effect-uai/core/Items\"\n\n// The streaming decoder, tool encoding, and terminal-turn assembly are the\n// generic OpenAI chat-completions codec: Mistral speaks that dialect on the\n// wire, so it reuses them verbatim. Only request encoding diverges (below).\nexport {\n type Accumulator,\n type WireChunk,\n accumulatorToTurn,\n applyChunk,\n emptyAccumulator,\n rejectToolOutputMedia,\n responseFormatWire,\n toolsWire,\n} from \"@effect-uai/chat-completions/codec\"\nimport {\n type WireChunk,\n decodeChunk as decodeChunkShared,\n toolOutputText,\n} from \"@effect-uai/chat-completions/codec\"\n\n// ---------------------------------------------------------------------------\n// History → Mistral chat `messages`\n//\n// Same shape as the generic codec, with one wire quirk: Mistral takes a bare\n// `image_url` string, not OpenAI's `image_url: { url }` object. That is the\n// reason this half stays local rather than importing `itemsToMessages`.\n// ---------------------------------------------------------------------------\n\ntype WireMessage = {\n role: string\n content: string | ReadonlyArray<Record<string, unknown>>\n tool_calls?: ReadonlyArray<Record<string, unknown>>\n tool_call_id?: string\n}\n\n/** A `data:` URI for inline image bytes; URLs pass through untouched. */\nconst imageSourceToUrl = Match.type<InputImage[\"source\"]>().pipe(\n Match.tag(\"url\", (s) => s.url),\n Match.tag(\"base64\", (s) => `data:${s.mimeType};base64,${s.base64}`),\n Match.tag(\"bytes\", (s) => `data:${s.mimeType};base64,${Base64.encode(s.bytes)}`),\n Match.exhaustive,\n)\n\nconst blockToPart = Match.type<ContentBlock>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) => ({ type: \"text\", text: b.text }),\n output_text: (b) => ({ type: \"text\", text: b.text }),\n refusal: (b) => ({ type: \"text\", text: b.text }),\n // Bare string, not `{ url }`: the Mistral divergence from OpenAI.\n input_image: (b) => ({ type: \"image_url\", image_url: imageSourceToUrl(b.source) }),\n // No assistant-image part here. Dropped rather than relocated onto a\n // user message, which is the caller's call: `Turn.imagesAsInput`.\n output_image: () => undefined,\n }),\n)\n\nconst isTextBlock = (b: ContentBlock): boolean =>\n b.type === \"input_text\" || b.type === \"output_text\" || b.type === \"refusal\"\n\nconst textOfBlock = (b: ContentBlock): string =>\n b.type === \"input_image\" || b.type === \"output_image\" ? \"\" : b.text\n\n/**\n * Collapse a message's content to a plain string when it is text-only\n * (the common case Mistral prefers), otherwise emit the multimodal parts\n * array.\n */\nconst encodeContent = (\n content: ReadonlyArray<ContentBlock>,\n): string | ReadonlyArray<Record<string, unknown>> =>\n Arr.every(content, isTextBlock)\n ? content.map(textOfBlock).join(\"\")\n : Arr.filterMap(content, (b) => {\n const part = blockToPart(b)\n return part === undefined ? Result.failVoid : Result.succeed(part)\n })\n\nconst toolCallWire = (call_id: string, name: string, args: string): Record<string, unknown> => ({\n id: call_id,\n type: \"function\",\n function: { name, arguments: args },\n})\n\nconst appendToolCall = (\n acc: ReadonlyArray<WireMessage>,\n call_id: string,\n name: string,\n args: string,\n): ReadonlyArray<WireMessage> => {\n const last = acc[acc.length - 1]\n const wire = toolCallWire(call_id, name, args)\n // Attach to the immediately-preceding assistant message (not a tool result),\n // matching the OpenAI/Mistral shape where tool calls ride the assistant turn.\n if (last !== undefined && last.role === \"assistant\" && last.tool_call_id === undefined) {\n return [...acc.slice(0, -1), { ...last, tool_calls: [...(last.tool_calls ?? []), wire] }]\n }\n return [...acc, { role: \"assistant\", content: \"\", tool_calls: [wire] }]\n}\n\nconst foldItem = (acc: ReadonlyArray<WireMessage>, item: HistoryItem): ReadonlyArray<WireMessage> =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m) => [...acc, { role: m.role, content: encodeContent(m.content) }],\n function_call: (f) => appendToolCall(acc, f.call_id, f.name, f.arguments),\n function_call_output: (o) => [\n ...acc,\n { role: \"tool\", tool_call_id: o.call_id, content: toolOutputText(o) },\n ],\n // Reasoning items are not replayed to the chat-completions endpoint.\n reasoning: () => acc,\n }),\n )\n\n/** Convert our `HistoryItem[]` history into Mistral chat `messages`. */\nexport const itemsToMessages = (items: ReadonlyArray<HistoryItem>): ReadonlyArray<WireMessage> =>\n Arr.reduce(items, [] as ReadonlyArray<WireMessage>, foldItem)\n\n// ---------------------------------------------------------------------------\n// tool_choice\n// ---------------------------------------------------------------------------\n\ntype ToolChoice =\n | \"auto\"\n | \"required\"\n | \"none\"\n | { readonly type: \"function\"; readonly name: string }\n\n// Mistral uses \"any\" for forced tool use (OpenAI's \"required\").\nexport const toolChoiceWire = (choice: ToolChoice): string | Record<string, unknown> =>\n Match.value(choice).pipe(\n Match.when(\"auto\", () => \"auto\" as const),\n Match.when(\"required\", () => \"any\" as const),\n Match.when(\"none\", () => \"none\" as const),\n Match.orElse((c) => ({ type: \"function\", function: { name: c.name } })),\n )\n\n// ---------------------------------------------------------------------------\n// Streaming decode\n// ---------------------------------------------------------------------------\n\n// Mistral emits `model_length` when it truncates on the context window; the\n// shared decoder only knows OpenAI's `length`. Rewrite it before folding so the\n// shared accumulator maps it to `max_tokens` rather than the `stop` fallback.\nconst normalizeChunk = (chunk: WireChunk): WireChunk =>\n chunk.choices === undefined\n ? chunk\n : {\n ...chunk,\n choices: chunk.choices.map((c) =>\n c.finish_reason === \"model_length\" ? { ...c, finish_reason: \"length\" } : c,\n ),\n }\n\nexport const decodeChunk = (u: unknown): Effect.Effect<WireChunk, unknown> =>\n decodeChunkShared(u).pipe(Effect.map(normalizeChunk))\n"],"mappings":"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"}
@@ -27,7 +27,13 @@ describe("itemsToMessages", () => {
27
27
  {
28
28
  type: "function_call_output",
29
29
  call_id: "c1",
30
- output: "ra"
30
+ output: [{
31
+ type: "input_text",
32
+ text: "ra"
33
+ }, {
34
+ type: "input_json",
35
+ value: { n: 1 }
36
+ }]
31
37
  }
32
38
  ])).toEqual([{
33
39
  role: "assistant",
@@ -50,7 +56,7 @@ describe("itemsToMessages", () => {
50
56
  }, {
51
57
  role: "tool",
52
58
  tool_call_id: "c1",
53
- content: "ra"
59
+ content: "ra\n{\"n\":1}"
54
60
  }]);
55
61
  });
56
62
  it("encodes a message with an image as multimodal parts", () => {
@@ -1 +1 @@
1
- {"version":3,"file":"codec.test.mjs","names":[],"sources":["../src/codec.test.ts"],"sourcesContent":["import type { HistoryItem } from \"@effect-uai/core/Items\"\nimport { describe, expect, it } from \"vitest\"\nimport {\n type Accumulator,\n accumulatorToTurn,\n applyChunk,\n emptyAccumulator,\n itemsToMessages,\n toolChoiceWire,\n type WireChunk,\n} from \"./codec.js\"\n\ndescribe(\"itemsToMessages\", () => {\n // The interesting case: our flat history (assistant message, then separate\n // function_call items) must fold into the OpenAI/Mistral shape where the\n // tool calls ride the assistant message and each result is a `tool` message.\n it(\"folds tool calls onto the assistant message and emits tool results\", () => {\n const history: ReadonlyArray<HistoryItem> = [\n { type: \"message\", role: \"assistant\", content: [{ type: \"output_text\", text: \"checking\" }] },\n { type: \"function_call\", call_id: \"c1\", name: \"a\", arguments: \"{}\" },\n { type: \"function_call\", call_id: \"c2\", name: \"b\", arguments: \"{}\" },\n { type: \"function_call_output\", call_id: \"c1\", output: \"ra\" },\n ]\n expect(itemsToMessages(history)).toEqual([\n {\n role: \"assistant\",\n content: \"checking\",\n tool_calls: [\n { id: \"c1\", type: \"function\", function: { name: \"a\", arguments: \"{}\" } },\n { id: \"c2\", type: \"function\", function: { name: \"b\", arguments: \"{}\" } },\n ],\n },\n { role: \"tool\", tool_call_id: \"c1\", content: \"ra\" },\n ])\n })\n\n it(\"encodes a message with an image as multimodal parts\", () => {\n const history: ReadonlyArray<HistoryItem> = [\n {\n type: \"message\",\n role: \"user\",\n content: [\n { type: \"input_text\", text: \"what is this\" },\n { type: \"input_image\", source: { _tag: \"url\", url: \"https://x/y.png\" } },\n ],\n },\n ]\n expect(itemsToMessages(history)).toEqual([\n {\n role: \"user\",\n content: [\n { type: \"text\", text: \"what is this\" },\n { type: \"image_url\", image_url: \"https://x/y.png\" },\n ],\n },\n ])\n })\n})\n\nit(\"toolChoiceWire maps 'required' to Mistral's 'any'\", () => {\n // Mistral diverges from OpenAI here; the rest pass through unchanged.\n expect(toolChoiceWire(\"required\")).toBe(\"any\")\n})\n\n// Run a sequence of chunks through the accumulator, collecting event tags.\nconst runChunks = (\n chunks: ReadonlyArray<WireChunk>,\n): { acc: Accumulator; tags: ReadonlyArray<string> } => {\n let acc = emptyAccumulator\n const tags: Array<string> = []\n for (const chunk of chunks) {\n const [next, events] = applyChunk(acc, chunk)\n acc = next\n for (const e of events) tags.push(e._tag)\n }\n return { acc, tags }\n}\n\ndescribe(\"streaming accumulation\", () => {\n it(\"accumulates text deltas + usage into an assistant turn\", () => {\n const { acc, tags } = runChunks([\n { choices: [{ delta: { content: \"Hel\" } }] },\n { choices: [{ delta: { content: \"lo\" } }] },\n { choices: [{ delta: {}, finish_reason: \"stop\" }] },\n { usage: { prompt_tokens: 3, completion_tokens: 2, total_tokens: 5 } },\n ])\n expect(tags).toEqual([\"TextDelta\", \"TextDelta\", \"UsageUpdate\"])\n const turn = accumulatorToTurn(acc)\n expect(turn.items).toEqual([\n { type: \"message\", role: \"assistant\", content: [{ type: \"output_text\", text: \"Hello\" }] },\n ])\n expect(turn.usage).toEqual({ input_tokens: 3, output_tokens: 2, total_tokens: 5 })\n expect(turn.stop_reason).toBe(\"stop\")\n })\n\n it(\"stitches a tool call split across chunks\", () => {\n const { acc, tags } = runChunks([\n {\n choices: [\n {\n delta: {\n tool_calls: [\n { index: 0, id: \"c1\", function: { name: \"get_weather\", arguments: '{\"ci' } },\n ],\n },\n },\n ],\n },\n {\n choices: [{ delta: { tool_calls: [{ index: 0, function: { arguments: 'ty\":\"NYC\"}' } }] } }],\n },\n { choices: [{ delta: {}, finish_reason: \"tool_calls\" }] },\n ])\n expect(tags).toEqual([\"ToolCallStart\", \"ToolCallArgsDelta\", \"ToolCallArgsDelta\"])\n const turn = accumulatorToTurn(acc)\n expect(turn.items).toEqual([\n { type: \"function_call\", call_id: \"c1\", name: \"get_weather\", arguments: '{\"city\":\"NYC\"}' },\n ])\n expect(turn.stop_reason).toBe(\"tool_calls\")\n })\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"codec.test.mjs","names":[],"sources":["../src/codec.test.ts"],"sourcesContent":["import type { HistoryItem } from \"@effect-uai/core/Items\"\nimport { describe, expect, it } from \"vitest\"\nimport {\n type Accumulator,\n accumulatorToTurn,\n applyChunk,\n emptyAccumulator,\n itemsToMessages,\n toolChoiceWire,\n type WireChunk,\n} from \"./codec.js\"\n\ndescribe(\"itemsToMessages\", () => {\n // The interesting case: our flat history (assistant message, then separate\n // function_call items) must fold into the OpenAI/Mistral shape where the\n // tool calls ride the assistant message and each result is a `tool` message.\n it(\"folds tool calls onto the assistant message and emits tool results\", () => {\n const history: ReadonlyArray<HistoryItem> = [\n { type: \"message\", role: \"assistant\", content: [{ type: \"output_text\", text: \"checking\" }] },\n { type: \"function_call\", call_id: \"c1\", name: \"a\", arguments: \"{}\" },\n { type: \"function_call\", call_id: \"c2\", name: \"b\", arguments: \"{}\" },\n {\n type: \"function_call_output\",\n call_id: \"c1\",\n output: [\n { type: \"input_text\", text: \"ra\" },\n { type: \"input_json\", value: { n: 1 } },\n ],\n },\n ]\n expect(itemsToMessages(history)).toEqual([\n {\n role: \"assistant\",\n content: \"checking\",\n tool_calls: [\n { id: \"c1\", type: \"function\", function: { name: \"a\", arguments: \"{}\" } },\n { id: \"c2\", type: \"function\", function: { name: \"b\", arguments: \"{}\" } },\n ],\n },\n { role: \"tool\", tool_call_id: \"c1\", content: 'ra\\n{\"n\":1}' },\n ])\n })\n\n it(\"encodes a message with an image as multimodal parts\", () => {\n const history: ReadonlyArray<HistoryItem> = [\n {\n type: \"message\",\n role: \"user\",\n content: [\n { type: \"input_text\", text: \"what is this\" },\n { type: \"input_image\", source: { _tag: \"url\", url: \"https://x/y.png\" } },\n ],\n },\n ]\n expect(itemsToMessages(history)).toEqual([\n {\n role: \"user\",\n content: [\n { type: \"text\", text: \"what is this\" },\n { type: \"image_url\", image_url: \"https://x/y.png\" },\n ],\n },\n ])\n })\n})\n\nit(\"toolChoiceWire maps 'required' to Mistral's 'any'\", () => {\n // Mistral diverges from OpenAI here; the rest pass through unchanged.\n expect(toolChoiceWire(\"required\")).toBe(\"any\")\n})\n\n// Run a sequence of chunks through the accumulator, collecting event tags.\nconst runChunks = (\n chunks: ReadonlyArray<WireChunk>,\n): { acc: Accumulator; tags: ReadonlyArray<string> } => {\n let acc = emptyAccumulator\n const tags: Array<string> = []\n for (const chunk of chunks) {\n const [next, events] = applyChunk(acc, chunk)\n acc = next\n for (const e of events) tags.push(e._tag)\n }\n return { acc, tags }\n}\n\ndescribe(\"streaming accumulation\", () => {\n it(\"accumulates text deltas + usage into an assistant turn\", () => {\n const { acc, tags } = runChunks([\n { choices: [{ delta: { content: \"Hel\" } }] },\n { choices: [{ delta: { content: \"lo\" } }] },\n { choices: [{ delta: {}, finish_reason: \"stop\" }] },\n { usage: { prompt_tokens: 3, completion_tokens: 2, total_tokens: 5 } },\n ])\n expect(tags).toEqual([\"TextDelta\", \"TextDelta\", \"UsageUpdate\"])\n const turn = accumulatorToTurn(acc)\n expect(turn.items).toEqual([\n { type: \"message\", role: \"assistant\", content: [{ type: \"output_text\", text: \"Hello\" }] },\n ])\n expect(turn.usage).toEqual({ input_tokens: 3, output_tokens: 2, total_tokens: 5 })\n expect(turn.stop_reason).toBe(\"stop\")\n })\n\n it(\"stitches a tool call split across chunks\", () => {\n const { acc, tags } = runChunks([\n {\n choices: [\n {\n delta: {\n tool_calls: [\n { index: 0, id: \"c1\", function: { name: \"get_weather\", arguments: '{\"ci' } },\n ],\n },\n },\n ],\n },\n {\n choices: [{ delta: { tool_calls: [{ index: 0, function: { arguments: 'ty\":\"NYC\"}' } }] } }],\n },\n { choices: [{ delta: {}, finish_reason: \"tool_calls\" }] },\n ])\n expect(tags).toEqual([\"ToolCallStart\", \"ToolCallArgsDelta\", \"ToolCallArgsDelta\"])\n const turn = accumulatorToTurn(acc)\n expect(turn.items).toEqual([\n { type: \"function_call\", call_id: \"c1\", name: \"get_weather\", arguments: '{\"city\":\"NYC\"}' },\n ])\n expect(turn.stop_reason).toBe(\"tool_calls\")\n })\n})\n"],"mappings":"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"}
@@ -6,6 +6,7 @@ import * as Cause from "effect/Cause";
6
6
  import * as Duration$1 from "effect/Duration";
7
7
  import * as Effect$1 from "effect/Effect";
8
8
  import * as Exit$1 from "effect/Exit";
9
+ import "effect/Fiber";
9
10
  import { constVoid, flow, pipe } from "effect/Function";
10
11
  import * as Layer$1 from "effect/Layer";
11
12
  import * as Schedule from "effect/Schedule";
@@ -14,30 +15,31 @@ import * as TestClock$1 from "effect/testing/TestClock";
14
15
  import * as TestConsole from "effect/testing/TestConsole";
15
16
  import { TestClock } from "effect/testing";
16
17
  import * as FakeWebSocket from "@effect-uai/core/testing/FakeWebSocket";
17
- //#region ../../../node_modules/.pnpm/@effect+vitest@4.0.1_effect@4.0.1_vitest@5.0.3_@types+node@26.2.0_vite@7.3.2_@types+nod_65816e3353e20bfeb9b2e1494bdddeef/node_modules/@effect/vitest/dist/internal/internal.js
18
+ //#region ../../../node_modules/.pnpm/@effect+vitest@4.1.2_effect@4.1.2_vitest@5.0.3_@types+node@26.2.0_vite@7.3.2_@types+nod_a2fffe0deec250ef9d6207b86a9a1ab8/node_modules/@effect/vitest/dist/internal/internal.js
18
19
  /**
19
20
  * @since 4.0.0
20
21
  */
21
22
  const getCurrentSuite = TestRunner.getCurrentSuite;
22
- const runPromise = /*#__PURE__*/ Effect$1.fnUntraced(function* (effect, _ctx) {
23
- const exit = yield* Effect$1.exit(effect);
23
+ const runPromise = (effect, ctx) => Effect$1.runPromise(Effect$1.uninterruptibleMask((restore) => Effect$1.gen(function* () {
24
+ const exit = yield* Effect$1.exit(restore(effect));
24
25
  if (Exit$1.isFailure(exit)) {
25
26
  const errors = Cause.prettyErrors(exit.cause);
26
27
  for (let i = 0; i < errors.length; i++) yield* Effect$1.logError(errors[i]);
27
28
  }
28
29
  return yield* exit;
29
- }, (effect, _, ctx) => Effect$1.runPromise(effect, { signal: ctx?.signal }));
30
+ })), { signal: ctx?.signal });
31
+ const settleOnAbort = (ctx, promise) => {
32
+ const onAbort = () => ctx.onTestFinished(() => promise.then(constVoid, constVoid));
33
+ ctx.signal.addEventListener("abort", onAbort, { once: true });
34
+ const cleanup = () => ctx.signal.removeEventListener("abort", onAbort);
35
+ promise.then(cleanup, cleanup);
36
+ if (ctx.signal.aborted) onAbort();
37
+ return promise;
38
+ };
30
39
  /** @internal */
31
40
  const runTest = (ctx) => (effect) => {
32
41
  const promise = runPromise(effect, ctx);
33
- if (ctx) {
34
- const onAbort = () => ctx.onTestFinished(() => promise.then(constVoid, constVoid));
35
- ctx.signal.addEventListener("abort", onAbort, { once: true });
36
- const cleanup = () => ctx.signal.removeEventListener("abort", onAbort);
37
- promise.then(cleanup, cleanup);
38
- if (ctx.signal.aborted) onAbort();
39
- }
40
- return promise;
42
+ return ctx ? settleOnAbort(ctx, promise) : promise;
41
43
  };
42
44
  const TestEnv = /*#__PURE__*/ Layer$1.mergeAll(TestConsole.layer, /*#__PURE__*/ TestClock$1.layer());
43
45
  /** @internal */
@@ -73,6 +75,9 @@ const collectTasks = (tasks, acc = []) => {
73
75
  }
74
76
  return acc;
75
77
  };
78
+ const runsMoreThanOnce = (task) => {
79
+ return ((typeof task.retry === "number" ? task.retry : task.retry?.count) ?? 0) > 0 || (task.repeats ?? 0) > 0;
80
+ };
76
81
  const registerProp = (it, name, arbitraries, property, timeout) => {
77
82
  const arbitrary = makeArbitrary(arbitraries);
78
83
  it(name, testOptions(timeout), withFixturesOf(() => {}, (ctx) => runCheck(ctx, arbitrary, (values) => property(values, ctx), checkOptions(timeout))));
@@ -92,11 +97,35 @@ const makeTester = (mapEffect, it) => {
92
97
  prop
93
98
  });
94
99
  };
95
- const makeProp = (it) => (name, arbitraries, self, timeout) => registerProp(it, name, arbitraries, (values, ctx) => self(values, ctx) !== false, timeout);
100
+ const makeProp = (it) => (name, arbitraries, self, timeout) => registerProp(it, name, arbitraries, (values, ctx) => {
101
+ const result = self(values, ctx);
102
+ if (result !== void 0 && typeof result !== "boolean") throw new TypeError("it.prop callbacks must return boolean or void. Use it.effect.prop for Effect callbacks.");
103
+ return result !== false;
104
+ }, timeout);
96
105
  const makeLayer = (it) => (layer_, options) => (...args) => {
97
106
  const excludeTestServices = options?.excludeTestServices ?? false;
98
107
  const withTestEnv = excludeTestServices ? layer_ : Layer$1.provideMerge(layer_, TestEnv);
99
108
  const memoMap = options?.memoMap ?? Effect$1.runSync(Layer$1.makeMemoMap);
109
+ const buildPerTest = options?.buildPerTest ?? false;
110
+ const suiteOptions = options?.concurrent === void 0 ? {} : { concurrent: options.concurrent };
111
+ const makeBlockMethods = (provide) => makeItProxy(it, {
112
+ effect: makeTester((effect) => provide(Effect$1.scoped(effect)), it),
113
+ prop: makeProp(it),
114
+ flakyTest,
115
+ layer(nestedLayer, nestedOptions) {
116
+ return makeLayer(it)(Layer$1.provideMerge(nestedLayer, withTestEnv), {
117
+ ...nestedOptions,
118
+ memoMap: Layer$1.forkMemoMapUnsafe(memoMap),
119
+ excludeTestServices,
120
+ buildPerTest: buildPerTest || nestedOptions?.buildPerTest === true
121
+ });
122
+ }
123
+ });
124
+ if (buildPerTest) {
125
+ const methods = makeBlockMethods((effect) => Effect$1.scopedWith((scope) => Layer$1.buildWithMemoMap(withTestEnv, Layer$1.forkMemoMapUnsafe(memoMap), scope).pipe(Effect$1.orDie, Effect$1.flatMap((context) => Effect$1.provide(effect, context)))));
126
+ if (args.length === 1) return args[0](methods);
127
+ return describe(args[0], suiteOptions, () => args[1](methods));
128
+ }
100
129
  const scope = Effect$1.runSync(Scope.make());
101
130
  const contextEffect = Layer$1.buildWithMemoMap(withTestEnv, memoMap, scope).pipe(Effect$1.orDie, Effect$1.cached, Effect$1.runSync);
102
131
  let closed = false;
@@ -105,18 +134,7 @@ const makeLayer = (it) => (layer_, options) => (...args) => {
105
134
  closed = true;
106
135
  return runPromise(Scope.close(scope, Exit$1.void));
107
136
  };
108
- const methods = makeItProxy(it, {
109
- effect: makeTester((effect) => Effect$1.flatMap(contextEffect, (context) => effect.pipe(Effect$1.scoped, Effect$1.provide(context))), it),
110
- prop: makeProp(it),
111
- flakyTest,
112
- layer(nestedLayer, options) {
113
- return makeLayer(it)(Layer$1.provideMerge(nestedLayer, withTestEnv), {
114
- ...options,
115
- memoMap: Layer$1.forkMemoMapUnsafe(memoMap),
116
- excludeTestServices
117
- });
118
- }
119
- });
137
+ const methods = makeBlockMethods((effect) => Effect$1.flatMap(contextEffect, (context) => Effect$1.provide(effect, context)));
120
138
  if (args.length === 1) {
121
139
  const currentSuite = getCurrentSuite();
122
140
  const previousTasks = new Set(currentSuite.tasks);
@@ -127,10 +145,11 @@ const makeLayer = (it) => (layer_, options) => (...args) => {
127
145
  return;
128
146
  }
129
147
  const blockTaskSet = new Set(blockTasks);
148
+ const releaseAfterLastTest = !blockTasks.some(runsMoreThanOnce);
130
149
  let remaining = blockTasks.length;
131
150
  beforeEach(({ onTestFinished, signal, task }) => {
132
151
  if (!blockTaskSet.has(task)) return;
133
- onTestFinished(() => {
152
+ if (releaseAfterLastTest) onTestFinished(() => {
134
153
  remaining--;
135
154
  if (remaining === 0) return closeScope();
136
155
  });
@@ -139,7 +158,6 @@ const makeLayer = (it) => (layer_, options) => (...args) => {
139
158
  afterAll(() => closeScope(), hookTimeout(options?.timeout));
140
159
  return;
141
160
  }
142
- const suiteOptions = options?.concurrent === void 0 ? {} : { concurrent: options.concurrent };
143
161
  return describe(args[0], suiteOptions, () => {
144
162
  beforeAll(() => runPromise(Effect$1.asVoid(contextEffect)), hookTimeout(options?.timeout));
145
163
  afterAll(() => closeScope(), hookTimeout(options?.timeout));
@@ -161,11 +179,12 @@ const it$1 = /*#__PURE__*/ makeMethods(it$2);
161
179
  /** @internal */
162
180
  const { effect, layer, live, prop } = it$1;
163
181
  //#endregion
164
- //#region ../../../node_modules/.pnpm/@effect+vitest@4.0.1_effect@4.0.1_vitest@5.0.3_@types+node@26.2.0_vite@7.3.2_@types+nod_65816e3353e20bfeb9b2e1494bdddeef/node_modules/@effect/vitest/dist/index.js
165
- /**
166
- * @since 4.0.0
167
- */
182
+ //#region ../../../node_modules/.pnpm/@effect+vitest@4.1.2_effect@4.1.2_vitest@5.0.3_@types+node@26.2.0_vite@7.3.2_@types+nod_a2fffe0deec250ef9d6207b86a9a1ab8/node_modules/@effect/vitest/dist/index.js
168
183
  /**
184
+ * Vitest test API extended with Effect test helpers.
185
+ *
186
+ * @stability unstable
187
+ * @category testing
169
188
  * @since 4.0.0
170
189
  */
171
190
  const it = it$1;
@@ -1 +1 @@
1
- {"version":3,"file":"realtimeStt.test.mjs","names":["Effect","Exit","Duration","Layer","V.describe","it","V.it","internal.it"],"sources":["../../../../node_modules/.pnpm/@effect+vitest@4.0.1_effect@4.0.1_vitest@5.0.3_@types+node@26.2.0_vite@7.3.2_@types+nod_65816e3353e20bfeb9b2e1494bdddeef/node_modules/@effect/vitest/dist/internal/internal.js","../../../../node_modules/.pnpm/@effect+vitest@4.0.1_effect@4.0.1_vitest@5.0.3_@types+node@26.2.0_vite@7.3.2_@types+nod_65816e3353e20bfeb9b2e1494bdddeef/node_modules/@effect/vitest/dist/index.js","../src/realtimeStt.test.ts"],"sourcesContent":["/**\n * @since 4.0.0\n */\nimport * as Arbitrary from \"effect/Arbitrary\";\nimport * as Cause from \"effect/Cause\";\nimport * as Duration from \"effect/Duration\";\nimport * as Effect from \"effect/Effect\";\nimport * as Exit from \"effect/Exit\";\nimport { constVoid, flow, pipe } from \"effect/Function\";\nimport * as Layer from \"effect/Layer\";\nimport * as Schedule from \"effect/Schedule\";\nimport * as Scope from \"effect/Scope\";\nimport * as TestClock from \"effect/testing/TestClock\";\nimport * as TestConsole from \"effect/testing/TestConsole\";\nimport * as V from \"vitest\";\nconst getCurrentSuite = V.TestRunner.getCurrentSuite;\nconst runPromise = /*#__PURE__*/Effect.fnUntraced(function* (effect, _ctx) {\n const exit = yield* Effect.exit(effect);\n if (Exit.isFailure(exit)) {\n const errors = Cause.prettyErrors(exit.cause);\n for (let i = 0; i < errors.length; i++) {\n yield* Effect.logError(errors[i]);\n }\n }\n return yield* exit;\n}, (effect, _, ctx) => Effect.runPromise(effect, {\n signal: ctx?.signal\n}));\n/** @internal */\nconst runTest = ctx => effect => {\n const promise = runPromise(effect, ctx);\n if (ctx) {\n // Vitest stops awaiting the test promise once the signal aborts (timeout or\n // cancellation), so only then add a hook that waits for the finalizers.\n // Registering it unconditionally would change the teardown of every test.\n const onAbort = () => ctx.onTestFinished(() => promise.then(constVoid, constVoid));\n ctx.signal.addEventListener(\"abort\", onAbort, {\n once: true\n });\n const cleanup = () => ctx.signal.removeEventListener(\"abort\", onAbort);\n promise.then(cleanup, cleanup);\n // A retry after a timed-out attempt reuses the aborted signal, so no event fires.\n if (ctx.signal.aborted) onAbort();\n }\n return promise;\n};\nconst TestEnv = /*#__PURE__*/Layer.mergeAll(TestConsole.layer, /*#__PURE__*/TestClock.layer());\n/** @internal */\nexport const addEqualityTesters = () => {\n V.expect.addEqualityTesters([]);\n};\n/** @internal */\nconst testOptions = timeout => typeof timeout === \"number\" ? {\n timeout\n} : timeout ?? {};\nconst hookTimeout = timeout => timeout === undefined ? undefined : Duration.toMillis(Duration.fromInputUnsafe(timeout));\nconst propertyTestOptions = timeout => typeof timeout === \"number\" ? undefined : timeout;\nconst checkOptions = timeout => propertyTestOptions(timeout)?.arbitrary;\nconst compileArbitraryInput = input => Arbitrary.isArbitrary(input) ? input : Arbitrary.schema(input);\nconst makeArbitrary = arbitraries => Arbitrary.all(Array.isArray(arbitraries) ? arbitraries.map(compileArbitraryInput) : Object.fromEntries(Object.entries(arbitraries).map(([key, input]) => [key, compileArbitraryInput(input)])));\nconst normalizeProperty = (property, value) => Effect.catchCause(Effect.suspend(() => {\n const output = property(value);\n return Effect.isEffect(output) ? output : Effect.succeed(output);\n}), cause => Cause.hasInterrupts(cause) ? Effect.failCause(cause) : Effect.fail(cause));\nconst runCheck = (ctx, arbitrary, property, options) => runTest(ctx)(Effect.flatMapEager(Arbitrary.checkEffect(arbitrary, value => normalizeProperty(property, value), options), result => {\n const failure = Arbitrary.formatCheckFailure(result);\n return failure === undefined ? Effect.void : Effect.die(new Error(failure));\n}));\n// Vitest decides which fixtures to set up by parsing the source of a test\n// function's context parameter, and there is no public API to supply the names\n// for a wrapper, so wrappers expose the source of the function they call.\nconst withFixturesOf = (source, f) => Object.defineProperty(f, \"toString\", {\n value: () => source.toString()\n});\nconst makeItProxy = (it, overrides) => new Proxy(it, {\n apply(target, thisArg, argArray) {\n return Reflect.apply(target, thisArg, argArray);\n },\n get(target, property, receiver) {\n if (Object.hasOwn(overrides, property)) {\n return Reflect.get(overrides, property);\n }\n // do not bind: binding would strip vitest's static helpers (e.g. `describe.each`)\n return Reflect.get(target, property, receiver);\n }\n});\nconst collectTasks = (tasks, acc = []) => {\n for (let i = 0; i < tasks.length; i++) {\n const task = tasks[i];\n if (task.type === \"test\" && task.mode !== \"skip\" && task.mode !== \"todo\") {\n acc.push(task);\n } else if (task.tasks !== undefined) {\n collectTasks(task.tasks, acc);\n }\n }\n return acc;\n};\nconst registerProp = (it, name, arbitraries, property, timeout) => {\n const arbitrary = makeArbitrary(arbitraries);\n // Property callbacks receive only the base context; the empty source requests no fixtures.\n it(name, testOptions(timeout), withFixturesOf(() => {}, ctx => runCheck(ctx, arbitrary, values => property(values, ctx), checkOptions(timeout))));\n};\nconst makeTester = (mapEffect, it) => {\n const run = (ctx, args, self) => pipe(Effect.suspend(() => self(...args)), mapEffect, runTest(ctx));\n const register = test => (name, self, timeout) => test(name, testOptions(timeout), withFixturesOf(self, ctx => run(ctx, [ctx], self)));\n const each = cases => (name, self, timeout) => it.for(cases)(name, testOptions(timeout), withFixturesOf(self, (args, ctx) => run(ctx, [args, ctx], self).then(constVoid)));\n const prop = (name, arbitraries, self, timeout) => registerProp(it, name, arbitraries, (values, ctx) => Effect.mapEager(mapEffect(Effect.suspend(() => self(values, ctx))), value => value !== false), timeout);\n return Object.assign(register(it), {\n skip: register(it.skip),\n skipIf: condition => register(it.skipIf(condition)),\n runIf: condition => register(it.runIf(condition)),\n only: register(it.only),\n each,\n fails: register(it.fails),\n prop\n });\n};\nconst makeProp = it => (name, arbitraries, self, timeout) => registerProp(it, name, arbitraries, (values, ctx) => self(values, ctx) !== false, timeout);\nconst makeLayer = it => (layer_, options) => (...args) => {\n const excludeTestServices = options?.excludeTestServices ?? false;\n const withTestEnv = excludeTestServices ? layer_ : Layer.provideMerge(layer_, TestEnv);\n const memoMap = options?.memoMap ?? Effect.runSync(Layer.makeMemoMap);\n const scope = Effect.runSync(Scope.make());\n const contextEffect = Layer.buildWithMemoMap(withTestEnv, memoMap, scope).pipe(Effect.orDie, Effect.cached, Effect.runSync);\n let closed = false;\n const closeScope = () => {\n if (closed) {\n return Promise.resolve();\n }\n closed = true;\n // Layer cleanup must outlive the last test's already-aborted signal.\n return runPromise(Scope.close(scope, Exit.void));\n };\n const methods = makeItProxy(it, {\n effect: makeTester(effect => Effect.flatMap(contextEffect, context => effect.pipe(Effect.scoped, Effect.provide(context))), it),\n prop: makeProp(it),\n flakyTest,\n layer(nestedLayer, options) {\n return makeLayer(it)(Layer.provideMerge(nestedLayer, withTestEnv), {\n ...options,\n memoMap: Layer.forkMemoMapUnsafe(memoMap),\n excludeTestServices\n });\n }\n });\n if (args.length === 1) {\n const currentSuite = getCurrentSuite();\n const previousTasks = new Set(currentSuite.tasks);\n args[0](methods);\n const blockTasks = collectTasks(currentSuite.tasks.filter(task => !previousTasks.has(task)));\n if (blockTasks.length === 0) {\n V.afterAll(() => closeScope(), hookTimeout(options?.timeout));\n return;\n }\n const blockTaskSet = new Set(blockTasks);\n let remaining = blockTasks.length;\n V.beforeEach(\n // Destructured so Vitest can parse the hook once the suite defines fixtures.\n ({\n onTestFinished,\n signal,\n task\n }) => {\n if (!blockTaskSet.has(task)) {\n return;\n }\n onTestFinished(() => {\n remaining--;\n if (remaining === 0) {\n return closeScope();\n }\n });\n return runPromise(Effect.asVoid(contextEffect), {\n signal\n });\n }, hookTimeout(options?.timeout));\n V.afterAll(() => closeScope(), hookTimeout(options?.timeout));\n return;\n }\n const suiteOptions = options?.concurrent === undefined ? {} : {\n concurrent: options.concurrent\n };\n return V.describe(args[0], suiteOptions, () => {\n V.beforeAll(() => runPromise(Effect.asVoid(contextEffect)), hookTimeout(options?.timeout));\n V.afterAll(() => closeScope(), hookTimeout(options?.timeout));\n return args[1](methods);\n });\n};\n/** @internal */\nexport const flakyTest = (self, timeout = Duration.seconds(30)) => pipe(self, Effect.scoped, Effect.sandbox, Effect.retry(pipe(Schedule.recurs(10), Schedule.while(_ => Effect.succeed(Duration.isLessThanOrEqualTo(Duration.fromInputUnsafe(_.elapsed), Duration.fromInputUnsafe(timeout)))))), Effect.orDie);\n/** @internal */\nexport const makeMethods = it => makeItProxy(it, {\n effect: makeTester(flow(Effect.scoped, Effect.provide(TestEnv)), it),\n live: makeTester(Effect.scoped, it),\n flakyTest,\n layer: makeLayer(it),\n prop: makeProp(it)\n});\n/** @internal */\nexport const it = /*#__PURE__*/makeMethods(V.it);\n/** @internal */\nexport const {\n /** @internal */\n effect,\n /** @internal */\n layer,\n /** @internal */\n live,\n /** @internal */\n prop\n} = it;\n/** @internal */\nexport const describeWrapped = (name, f) => V.describe(name, () => f(it));\n//# sourceMappingURL=internal.js.map","import * as internal from \"./internal/internal.js\";\n/**\n * Re-exports the `vitest` API unchanged.\n *\n * @stability unstable\n * @since 4.0.0\n */\nexport * from \"vitest\";\n/**\n * @since 4.0.0\n */\nexport const addEqualityTesters = internal.addEqualityTesters;\n/**\n * @since 4.0.0\n */\nexport const effect = internal.effect;\n/**\n * @since 4.0.0\n */\nexport const live = internal.live;\n/**\n * Share a `Layer` between multiple tests, optionally wrapping\n * the tests in a `describe` block if a name is provided.\n *\n * Named layers accept `concurrent` to override inherited suite concurrency.\n * Anonymous layers always inherit the enclosing suite's concurrency.\n * Use `ctx.expect` in concurrent tests for test-local snapshots and assertion counts.\n *\n * @since 4.0.0\n *\n * ```ts\n * import { assert, layer } from \"@effect/vitest\"\n * import { Effect, Layer, Context } from \"effect\"\n *\n * class Foo extends Context.Service<Foo, \"foo\">()(\"Foo\") {\n * static layer = Layer.succeed(Foo, \"foo\")\n * }\n *\n * class Bar extends Context.Service<Bar, \"bar\">()(\"Bar\") {\n * static layer = Layer.effect(\n * Bar,\n * Effect.map(Foo, () => \"bar\" as const)\n * )\n * }\n *\n * layer(Foo.layer)(\"layer\", (it) => {\n * it.effect(\"adds context\", () =>\n * Effect.gen(function*() {\n * const foo = yield* Foo\n * assert.strictEqual(foo, \"foo\")\n * }))\n *\n * it.layer(Bar.layer)(\"nested\", (it) => {\n * it.effect(\"adds context\", () =>\n * Effect.gen(function*() {\n * const foo = yield* Foo\n * const bar = yield* Bar\n * assert.strictEqual(foo, \"foo\")\n * assert.strictEqual(bar, \"bar\")\n * }))\n * })\n * })\n * ```\n */\nexport const layer = internal.layer;\n/**\n * @since 4.0.0\n */\nexport const flakyTest = internal.flakyTest;\n/**\n * @since 4.0.0\n */\nexport const prop = internal.prop;\n/**\n * @since 4.0.0\n */\n/**\n * @since 4.0.0\n */\nexport const it = internal.it;\n/**\n * Creates the Effect test helpers for a Vitest test API, such as one extended with fixtures.\n *\n * **Details**\n *\n * Tests receive the fixtures they destructure from their context. Vitest sets them up before the test and tears\n * them down after the test's scope closes, as it does for its own tests. `it.effect.each` passes the context\n * after the test case, and named and anonymous `it.layer` blocks keep the fixtures.\n *\n * **Gotchas**\n *\n * Vitest reads the destructured names to decide which fixtures to set up. Once any fixture is defined, a test that\n * takes the whole context as a plain parameter, such as `(ctx) =>`, fails with a `FixtureParseError`. Property\n * tests receive only the base test context and cannot request fixtures; auto fixtures still run.\n *\n * **Example** (Using a Vitest fixture in an Effect test)\n *\n * ```ts\n * import { assert, makeMethods, test } from \"@effect/vitest\"\n * import { Effect } from \"effect\"\n *\n * const it = makeMethods(\n * test.extend(\"config\", { scope: \"file\" }, () => ({ port: 3000 }))\n * )\n *\n * it.effect(\"reads the config fixture\", ({ config }) =>\n * Effect.sync(() => {\n * assert.strictEqual(config.port, 3000)\n * }))\n * ```\n *\n * @since 4.0.0\n */\nexport const makeMethods = internal.makeMethods;\n/**\n * @since 4.0.0\n */\nexport const describeWrapped = internal.describeWrapped;\n//# sourceMappingURL=index.js.map","import { describe, it } from \"@effect/vitest\"\nimport { Effect, Exit, Fiber, Redacted, Stream } from \"effect\"\nimport { TestClock } from \"effect/testing\"\nimport { expect } from \"vitest\"\nimport * as FakeWebSocket from \"@effect-uai/core/testing/FakeWebSocket\"\nimport { streamTranscription } from \"./realtimeStt.js\"\n\nconst pcm = { container: \"raw\", encoding: \"pcm_s16le\", sampleRate: 16000, channels: 1 } as const\n\nconst request = { model: \"voxtral-mini-transcribe-realtime-2602\", inputFormat: pcm }\n\nconst sessionCreated = {\n type: \"session.created\",\n session: { request_id: \"req_1\", model: request.model },\n}\n\nconst delta = (text: string) => ({ type: \"transcription.text.delta\", text })\n\nconst start = <E>(\n server: FakeWebSocket.FakeWebSocketServer,\n audioIn: Stream.Stream<Uint8Array, E>,\n) =>\n streamTranscription({ apiKey: Redacted.make(\"test-key\"), webSocket: server.connect })(\n audioIn,\n request,\n ).pipe(Stream.runCollect, Effect.exit, Effect.forkChild)\n\n/** Run a transcription against `server`, closing it, if asked, once the frames are in. */\nconst transcribe = <E>(\n server: FakeWebSocket.FakeWebSocketServer,\n audioIn: Stream.Stream<Uint8Array, E>,\n close?: { readonly code: number; readonly afterSent?: number },\n) =>\n Effect.gen(function* () {\n const collector = yield* start(server, audioIn)\n if (close !== undefined) {\n yield* server.greeted\n yield* server.awaitSent(close.afterSent ?? 0)\n yield* server.close(close.code)\n }\n return yield* Fiber.join(collector)\n })\n\ndescribe(\"Mistral Realtime STT socket\", () => {\n it.live(\"configures the session before any audio, and ends on a clean close\", () =>\n Effect.gen(function* () {\n const server = yield* FakeWebSocket.make({\n greeting: [\n sessionCreated,\n delta(\"hel\"),\n delta(\"lo\"),\n { type: \"transcription.done\", text: \"hello\", model: request.model, segments: [] },\n { type: \"error\", error: { message: \"slow down\", code: 3001 } },\n ],\n })\n const exit = yield* transcribe(server, Stream.make(new Uint8Array([1, 2])), {\n code: 1000,\n afterSent: 3,\n })\n\n expect(exit).toEqual(\n Exit.succeed([\n { _tag: \"partial\", text: \"hel\" },\n { _tag: \"partial\", text: \"hello\" },\n { _tag: \"final\", text: \"hello\" },\n { _tag: \"error\", code: \"3001\", message: \"slow down\" },\n ]),\n )\n const sent = (yield* server.sent) as ReadonlyArray<{ type: string }>\n expect(sent.map((f) => f.type)).toEqual([\n \"session.update\",\n \"input_audio.append\",\n \"input_audio.end\",\n ])\n }).pipe(Effect.scoped),\n )\n\n it.live(\"fails rather than ending when the connection drops\", () =>\n Effect.gen(function* () {\n const server = yield* FakeWebSocket.make({ greeting: [sessionCreated] })\n const exit = yield* transcribe(server, Stream.never, { code: 1011 })\n\n expect(Exit.isFailure(exit) && JSON.stringify(exit.cause)).toContain(\"Unavailable\")\n }).pipe(Effect.scoped),\n )\n\n it.live(\"fails with the audio input's own error\", () =>\n Effect.gen(function* () {\n const server = yield* FakeWebSocket.make({ greeting: [sessionCreated] })\n const exit = yield* transcribe(server, Stream.fail(\"microphone unplugged\"))\n\n expect(Exit.isFailure(exit) && JSON.stringify(exit.cause)).toContain(\"microphone unplugged\")\n }).pipe(Effect.scoped),\n )\n\n it.effect(\"commits a synthetic final once the deltas go quiet\", () =>\n Effect.gen(function* () {\n const server = yield* FakeWebSocket.make({\n greeting: [sessionCreated, delta(\"hel\"), delta(\"lo \")],\n })\n const collector = yield* start(server, Stream.never)\n yield* server.greeted\n yield* TestClock.withLive(Effect.sleep(\"5 millis\"))\n yield* TestClock.adjust(\"1 second\")\n yield* server.close(1000)\n\n expect(yield* Fiber.join(collector)).toEqual(\n Exit.succeed([\n { _tag: \"partial\", text: \"hel\" },\n { _tag: \"partial\", text: \"hello \" },\n { _tag: \"final\", text: \"hello\" },\n ]),\n )\n }).pipe(Effect.scoped),\n )\n})\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
1
+ {"version":3,"file":"realtimeStt.test.mjs","names":["Effect","Exit","Duration","Layer","V.describe","it","V.it","internal.it"],"sources":["../../../../node_modules/.pnpm/@effect+vitest@4.1.2_effect@4.1.2_vitest@5.0.3_@types+node@26.2.0_vite@7.3.2_@types+nod_a2fffe0deec250ef9d6207b86a9a1ab8/node_modules/@effect/vitest/dist/internal/internal.js","../../../../node_modules/.pnpm/@effect+vitest@4.1.2_effect@4.1.2_vitest@5.0.3_@types+node@26.2.0_vite@7.3.2_@types+nod_a2fffe0deec250ef9d6207b86a9a1ab8/node_modules/@effect/vitest/dist/index.js","../src/realtimeStt.test.ts"],"sourcesContent":["/**\n * @since 4.0.0\n */\nimport * as Arbitrary from \"effect/Arbitrary\";\nimport * as Cause from \"effect/Cause\";\nimport * as Duration from \"effect/Duration\";\nimport * as Effect from \"effect/Effect\";\nimport * as Exit from \"effect/Exit\";\nimport * as Fiber from \"effect/Fiber\";\nimport { constVoid, flow, pipe } from \"effect/Function\";\nimport * as Layer from \"effect/Layer\";\nimport * as Schedule from \"effect/Schedule\";\nimport * as Scope from \"effect/Scope\";\nimport * as TestClock from \"effect/testing/TestClock\";\nimport * as TestConsole from \"effect/testing/TestConsole\";\nimport * as V from \"vitest\";\nconst getCurrentSuite = V.TestRunner.getCurrentSuite;\nconst runPromise = (effect, ctx) => Effect.runPromise(\n// Only the test is interruptible: once the signal aborts, the fiber stops\n// at its next interruptible step, which would skip reporting the defects\n// raised by finalizers.\nEffect.uninterruptibleMask(restore => Effect.gen(function* () {\n const exit = yield* Effect.exit(restore(effect));\n if (Exit.isFailure(exit)) {\n const errors = Cause.prettyErrors(exit.cause);\n for (let i = 0; i < errors.length; i++) {\n yield* Effect.logError(errors[i]);\n }\n }\n return yield* exit;\n})), {\n signal: ctx?.signal\n});\nconst settleOnAbort = (ctx, promise) => {\n // Vitest stops awaiting the promise once the signal aborts (timeout or\n // cancellation), so only then add a hook that waits for the finalizers.\n // Registering it unconditionally would change the teardown of every test.\n const onAbort = () => ctx.onTestFinished(() => promise.then(constVoid, constVoid));\n ctx.signal.addEventListener(\"abort\", onAbort, {\n once: true\n });\n const cleanup = () => ctx.signal.removeEventListener(\"abort\", onAbort);\n promise.then(cleanup, cleanup);\n // A retry after a timed-out attempt reuses the aborted signal, so no event fires.\n if (ctx.signal.aborted) onAbort();\n return promise;\n};\n/** @internal */\nconst runTest = ctx => effect => {\n const promise = runPromise(effect, ctx);\n return ctx ? settleOnAbort(ctx, promise) : promise;\n};\nconst TestEnv = /*#__PURE__*/Layer.mergeAll(TestConsole.layer, /*#__PURE__*/TestClock.layer());\n/** @internal */\nexport const addEqualityTesters = () => {\n V.expect.addEqualityTesters([]);\n};\n/** @internal */\nconst testOptions = timeout => typeof timeout === \"number\" ? {\n timeout\n} : timeout ?? {};\nconst hookTimeout = timeout => timeout === undefined ? undefined : Duration.toMillis(Duration.fromInputUnsafe(timeout));\nconst propertyTestOptions = timeout => typeof timeout === \"number\" ? undefined : timeout;\nconst checkOptions = timeout => propertyTestOptions(timeout)?.arbitrary;\nconst compileArbitraryInput = input => Arbitrary.isArbitrary(input) ? input : Arbitrary.schema(input);\nconst makeArbitrary = arbitraries => Arbitrary.all(Array.isArray(arbitraries) ? arbitraries.map(compileArbitraryInput) : Object.fromEntries(Object.entries(arbitraries).map(([key, input]) => [key, compileArbitraryInput(input)])));\nconst normalizeProperty = (property, value) => Effect.catchCause(Effect.suspend(() => {\n const output = property(value);\n return Effect.isEffect(output) ? output : Effect.succeed(output);\n}), cause => Cause.hasInterrupts(cause) ? Effect.failCause(cause) : Effect.fail(cause));\nconst runCheck = (ctx, arbitrary, property, options) => runTest(ctx)(Effect.flatMapEager(Arbitrary.checkEffect(arbitrary, value => normalizeProperty(property, value), options), result => {\n const failure = Arbitrary.formatCheckFailure(result);\n return failure === undefined ? Effect.void : Effect.die(new Error(failure));\n}));\n// Vitest decides which fixtures to set up by parsing the source of a test\n// function's context parameter, and there is no public API to supply the names\n// for a wrapper, so wrappers expose the source of the function they call.\nconst withFixturesOf = (source, f) => Object.defineProperty(f, \"toString\", {\n value: () => source.toString()\n});\nconst runHook = (fn, run) => withFixturesOf(fn, (...args) => {\n const result = fn(...args);\n if (!Effect.isEffect(result)) {\n return result;\n }\n const scope = Scope.makeUnsafe();\n // Everything the hook leaves behind lives in the scope: the resources it\n // acquired and, ahead of them, a cleanup function it returned. On failure\n // the scope closes inside the fiber, so interrupting it awaits the release.\n const effect = Scope.provide(result, scope).pipe(Effect.flatMap(cleanup => typeof cleanup === \"function\" ? Scope.addFinalizer(scope, Effect.promise(async () => await cleanup())) : Effect.void), Effect.onError(cause => Scope.close(scope, Exit.failCause(cause))));\n return run(args, effect, scope);\n});\n// Closing must outlive an already-aborted signal, so it runs without one.\nconst closeHookScope = scope => runPromise(Scope.close(scope, Exit.void));\n/** @internal */\nexport const beforeAll = (fn, timeout) => {\n let dispose = () => Promise.resolve();\n // Vitest drops the cleanup of a setup that outlives its timeout, so the\n // scope is disposed by the cleanup of a hook registered just before this\n // one. It runs in the same cleanup phase, in the slot next to this hook's,\n // with this hook's timeout.\n V.beforeAll(() => () => dispose(), timeout);\n V.beforeAll(runHook(fn, (_, effect, scope) => {\n const fiber = Effect.runFork(effect);\n // Interrupting a setup that Vitest stopped waiting for awaits its release.\n dispose = () => runPromise(Effect.andThen(Fiber.interrupt(fiber), Scope.close(scope, Exit.void)));\n return runPromise(Fiber.join(fiber));\n }), timeout);\n};\n/** @internal */\nexport const beforeEach = (fn, timeout) => V.beforeEach(runHook(fn, ([ctx], effect, scope) =>\n// The scope closes when Vitest runs the hook's cleanup after the test,\n// and the next test waits for it even if that cleanup times out.\nrunTest(ctx)(effect).then(() => () => settleOnAbort(ctx, closeHookScope(scope)))), timeout);\nconst makeItProxy = (it, overrides) => new Proxy(it, {\n apply(target, thisArg, argArray) {\n return Reflect.apply(target, thisArg, argArray);\n },\n get(target, property, receiver) {\n if (Object.hasOwn(overrides, property)) {\n return Reflect.get(overrides, property);\n }\n // do not bind: binding would strip vitest's static helpers (e.g. `describe.each`)\n return Reflect.get(target, property, receiver);\n }\n});\nconst collectTasks = (tasks, acc = []) => {\n for (let i = 0; i < tasks.length; i++) {\n const task = tasks[i];\n if (task.type === \"test\" && task.mode !== \"skip\" && task.mode !== \"todo\") {\n acc.push(task);\n } else if (task.tasks !== undefined) {\n collectTasks(task.tasks, acc);\n }\n }\n return acc;\n};\nconst runsMoreThanOnce = task => {\n const retry = typeof task.retry === \"number\" ? task.retry : task.retry?.count;\n return (retry ?? 0) > 0 || (task.repeats ?? 0) > 0;\n};\nconst registerProp = (it, name, arbitraries, property, timeout) => {\n const arbitrary = makeArbitrary(arbitraries);\n // Property callbacks receive only the base context; the empty source requests no fixtures.\n it(name, testOptions(timeout), withFixturesOf(() => {}, ctx => runCheck(ctx, arbitrary, values => property(values, ctx), checkOptions(timeout))));\n};\nconst makeTester = (mapEffect, it) => {\n const run = (ctx, args, self) => pipe(Effect.suspend(() => self(...args)), mapEffect, runTest(ctx));\n const register = test => (name, self, timeout) => test(name, testOptions(timeout), withFixturesOf(self, ctx => run(ctx, [ctx], self)));\n const each = cases => (name, self, timeout) => it.for(cases)(name, testOptions(timeout), withFixturesOf(self, (args, ctx) => run(ctx, [args, ctx], self).then(constVoid)));\n const prop = (name, arbitraries, self, timeout) => registerProp(it, name, arbitraries, (values, ctx) => Effect.mapEager(mapEffect(Effect.suspend(() => self(values, ctx))), value => value !== false), timeout);\n return Object.assign(register(it), {\n skip: register(it.skip),\n skipIf: condition => register(it.skipIf(condition)),\n runIf: condition => register(it.runIf(condition)),\n only: register(it.only),\n each,\n fails: register(it.fails),\n prop\n });\n};\nconst makeProp = it => (name, arbitraries, self, timeout) => registerProp(it, name, arbitraries, (values, ctx) => {\n const result = self(values, ctx);\n if (result !== undefined && typeof result !== \"boolean\") {\n throw new TypeError(\"it.prop callbacks must return boolean or void. Use it.effect.prop for Effect callbacks.\");\n }\n return result !== false;\n}, timeout);\nconst makeLayer = it => (layer_, options) => (...args) => {\n const excludeTestServices = options?.excludeTestServices ?? false;\n const withTestEnv = excludeTestServices ? layer_ : Layer.provideMerge(layer_, TestEnv);\n const memoMap = options?.memoMap ?? Effect.runSync(Layer.makeMemoMap);\n const buildPerTest = options?.buildPerTest ?? false;\n const suiteOptions = options?.concurrent === undefined ? {} : {\n concurrent: options.concurrent\n };\n const makeBlockMethods = provide => makeItProxy(it, {\n effect: makeTester(effect => provide(Effect.scoped(effect)), it),\n prop: makeProp(it),\n flakyTest,\n layer(nestedLayer, nestedOptions) {\n return makeLayer(it)(Layer.provideMerge(nestedLayer, withTestEnv), {\n ...nestedOptions,\n memoMap: Layer.forkMemoMapUnsafe(memoMap),\n excludeTestServices,\n // The services of a per-test block only exist during a test, so\n // blocks nested in it are built per test as well.\n buildPerTest: buildPerTest || nestedOptions?.buildPerTest === true\n });\n }\n });\n if (buildPerTest) {\n // Each test builds into its own fork of the memo map, so it reuses the\n // services of enclosing blocks and releases the rest when it finishes.\n const methods = makeBlockMethods(effect => Effect.scopedWith(scope => Layer.buildWithMemoMap(withTestEnv, Layer.forkMemoMapUnsafe(memoMap), scope).pipe(Effect.orDie, Effect.flatMap(context => Effect.provide(effect, context)))));\n if (args.length === 1) {\n return args[0](methods);\n }\n return V.describe(args[0], suiteOptions, () => args[1](methods));\n }\n const scope = Effect.runSync(Scope.make());\n const contextEffect = Layer.buildWithMemoMap(withTestEnv, memoMap, scope).pipe(Effect.orDie, Effect.cached, Effect.runSync);\n let closed = false;\n const closeScope = () => {\n if (closed) {\n return Promise.resolve();\n }\n closed = true;\n // Layer cleanup must outlive the last test's already-aborted signal.\n return runPromise(Scope.close(scope, Exit.void));\n };\n const methods = makeBlockMethods(effect => Effect.flatMap(contextEffect, context => Effect.provide(effect, context)));\n if (args.length === 1) {\n const currentSuite = getCurrentSuite();\n const previousTasks = new Set(currentSuite.tasks);\n args[0](methods);\n const blockTasks = collectTasks(currentSuite.tasks.filter(task => !previousTasks.has(task)));\n if (blockTasks.length === 0) {\n V.afterAll(() => closeScope(), hookTimeout(options?.timeout));\n return;\n }\n const blockTaskSet = new Set(blockTasks);\n // Vitest runs hooks once per retry and repeat attempt, so a test's last\n // attempt cannot be counted. Blocks with such tests are released by `afterAll`.\n const releaseAfterLastTest = !blockTasks.some(runsMoreThanOnce);\n let remaining = blockTasks.length;\n V.beforeEach(\n // Destructured so Vitest can parse the hook once the suite defines fixtures.\n ({\n onTestFinished,\n signal,\n task\n }) => {\n if (!blockTaskSet.has(task)) {\n return;\n }\n if (releaseAfterLastTest) {\n onTestFinished(() => {\n remaining--;\n if (remaining === 0) {\n return closeScope();\n }\n });\n }\n return runPromise(Effect.asVoid(contextEffect), {\n signal\n });\n }, hookTimeout(options?.timeout));\n V.afterAll(() => closeScope(), hookTimeout(options?.timeout));\n return;\n }\n return V.describe(args[0], suiteOptions, () => {\n V.beforeAll(() => runPromise(Effect.asVoid(contextEffect)), hookTimeout(options?.timeout));\n V.afterAll(() => closeScope(), hookTimeout(options?.timeout));\n return args[1](methods);\n });\n};\n/** @internal */\nexport const flakyTest = (self, timeout = Duration.seconds(30)) => pipe(self, Effect.scoped, Effect.sandbox, Effect.retry(pipe(Schedule.recurs(10), Schedule.while(_ => Effect.succeed(Duration.isLessThanOrEqualTo(Duration.fromInputUnsafe(_.elapsed), Duration.fromInputUnsafe(timeout)))))), Effect.orDie);\n/** @internal */\nexport const makeMethods = it => makeItProxy(it, {\n effect: makeTester(flow(Effect.scoped, Effect.provide(TestEnv)), it),\n live: makeTester(Effect.scoped, it),\n flakyTest,\n layer: makeLayer(it),\n prop: makeProp(it)\n});\n/** @internal */\nexport const it = /*#__PURE__*/makeMethods(V.it);\n/** @internal */\nexport const {\n /** @internal */\n effect,\n /** @internal */\n layer,\n /** @internal */\n live,\n /** @internal */\n prop\n} = it;\n/** @internal */\nexport const describeWrapped = (name, f) => V.describe(name, () => f(it));\n//# sourceMappingURL=internal.js.map","import * as internal from \"./internal/internal.js\";\n/**\n * Re-exports the `vitest` API unchanged.\n *\n * @stability unstable\n * @since 4.0.0\n */\nexport * from \"vitest\";\n/**\n * Invokes the Effect Vitest equality-tester registration hook.\n *\n * @stability unstable\n * @category utils\n * @since 4.0.0\n */\nexport const addEqualityTesters = internal.addEqualityTesters;\n/**\n * Registers a callback to run once before all tests in the current suite. If\n * the callback returns an `Effect`, it is run and awaited with a `Scope` that\n * closes in a cleanup after the suite's `afterAll` hooks, within the hook's\n * timeout. The `Effect` must not require any other services.\n *\n * **Details**\n *\n * If Vitest stops waiting for the setup, the setup is interrupted before the\n * `Scope` closes. As with any `beforeAll` cleanup, Vitest reports a closing\n * that exceeds the hook's timeout and does not wait for it.\n *\n * @stability unstable\n * @category hooks\n * @since 4.0.0\n */\nexport const beforeAll = internal.beforeAll;\n/**\n * Registers a callback to run before each test in the current suite. If the\n * callback returns an `Effect`, it is run and awaited with a `Scope` that\n * closes when the test finishes, and interrupted if the hook times out. The\n * `Effect` must not require any other services.\n *\n * @stability unstable\n * @category hooks\n * @since 4.0.0\n */\nexport const beforeEach = internal.beforeEach;\n/**\n * Runs a scoped Effect test with test clock and console services.\n *\n * @stability unstable\n * @category testing\n * @since 4.0.0\n */\nexport const effect = internal.effect;\n/**\n * Runs a scoped Effect test with live services.\n *\n * @stability unstable\n * @category testing\n * @since 4.0.0\n */\nexport const live = internal.live;\n/**\n * Share a `Layer` between multiple tests, optionally wrapping\n * the tests in a `describe` block if a name is provided.\n *\n * **Details**\n *\n * Named layers accept `concurrent` to override inherited suite concurrency.\n * Anonymous layers always inherit the enclosing suite's concurrency.\n * Use `ctx.expect` in concurrent tests for test-local snapshots and assertion counts.\n *\n * Pass `buildPerTest: true` to build the layer once per test instead of once\n * per block. Each test gets new services, released when the test finishes,\n * while services from an enclosing `layer` block stay shared. A build failure\n * fails only that test. Blocks nested inside a per-test block are built per\n * test as well. `timeout` has no effect on a per-test block, since the test's\n * own timeout covers the build.\n *\n * **Example** (Sharing layers between tests)\n *\n * ```ts\n * import { assert, layer } from \"@effect/vitest\"\n * import { Effect, Layer, Context } from \"effect\"\n *\n * class Foo extends Context.Service<Foo, \"foo\">()(\"Foo\") {\n * static layer = Layer.succeed(Foo, \"foo\")\n * }\n *\n * class Bar extends Context.Service<Bar, \"bar\">()(\"Bar\") {\n * static layer = Layer.effect(\n * Bar,\n * Effect.map(Foo, () => \"bar\" as const)\n * )\n * }\n *\n * layer(Foo.layer)(\"layer\", (it) => {\n * it.effect(\"adds context\", () =>\n * Effect.gen(function*() {\n * const foo = yield* Foo\n * assert.strictEqual(foo, \"foo\")\n * }))\n *\n * it.layer(Bar.layer)(\"nested\", (it) => {\n * it.effect(\"adds context\", () =>\n * Effect.gen(function*() {\n * const foo = yield* Foo\n * const bar = yield* Bar\n * assert.strictEqual(foo, \"foo\")\n * assert.strictEqual(bar, \"bar\")\n * }))\n * })\n * })\n * ```\n *\n * **Example** (Building a layer for each test)\n *\n * ```ts\n * import { assert, layer } from \"@effect/vitest\"\n * import { Effect, Layer, Context, Ref } from \"effect\"\n *\n * class Database extends Context.Service<Database, \"database\">()(\"Database\") {\n * static layer = Layer.succeed(Database, \"database\")\n * }\n *\n * class Cache extends Context.Service<Cache, Ref.Ref<Array<string>>>()(\"Cache\") {\n * static layer = Layer.effect(\n * Cache,\n * Effect.andThen(Database, Ref.make<Array<string>>([]))\n * )\n * }\n *\n * layer(Database.layer)(\"repository\", (it) => {\n * // Database is shared, Cache is built for each test\n * it.layer(Cache.layer, { buildPerTest: true })((it) => {\n * it.effect(\"writes to the cache\", () =>\n * Effect.gen(function*() {\n * const cache = yield* Cache\n * yield* Ref.update(cache, (keys) => [...keys, \"a\"])\n * assert.deepStrictEqual(yield* Ref.get(cache), [\"a\"])\n * }))\n *\n * it.effect(\"starts with an empty cache\", () =>\n * Effect.gen(function*() {\n * const cache = yield* Cache\n * assert.deepStrictEqual(yield* Ref.get(cache), [])\n * }))\n * })\n * })\n * ```\n *\n * @stability unstable\n * @category testing\n * @since 4.0.0\n */\nexport const layer = internal.layer;\n/**\n * Retries a scoped Effect to tolerate intermittent test failures, dying on exhausted retries.\n *\n * @stability unstable\n * @category testing\n * @since 4.0.0\n */\nexport const flakyTest = internal.flakyTest;\n/**\n * Runs a synchronous property test using schemas or arbitraries.\n *\n * @stability unstable\n * @category testing\n * @since 4.0.0\n */\nexport const prop = internal.prop;\n/**\n * Vitest test API extended with Effect test helpers.\n *\n * @stability unstable\n * @category testing\n * @since 4.0.0\n */\nexport const it = internal.it;\n/**\n * Creates the Effect test helpers for a Vitest test API, such as one extended with fixtures.\n *\n * **Details**\n *\n * Tests receive the fixtures they destructure from their context. Vitest sets them up before the test and tears\n * them down after the test's scope closes, as it does for its own tests. `it.effect.each` passes the context\n * after the test case, and named and anonymous `it.layer` blocks keep the fixtures.\n *\n * **Gotchas**\n *\n * Vitest reads the destructured names to decide which fixtures to set up. Once any fixture is defined, a test that\n * takes the whole context as a plain parameter, such as `(ctx) =>`, fails with a `FixtureParseError`. Property\n * tests receive only the base test context and cannot request fixtures; auto fixtures still run.\n *\n * **Example** (Using a Vitest fixture in an Effect test)\n *\n * ```ts\n * import { assert, makeMethods, test } from \"@effect/vitest\"\n * import { Effect } from \"effect\"\n *\n * const it = makeMethods(\n * test.extend(\"config\", { scope: \"file\" }, () => ({ port: 3000 }))\n * )\n *\n * it.effect(\"reads the config fixture\", ({ config }) =>\n * Effect.sync(() => {\n * assert.strictEqual(config.port, 3000)\n * }))\n * ```\n *\n * @stability unstable\n * @category constructors\n * @since 4.0.0\n */\nexport const makeMethods = internal.makeMethods;\n/**\n * Creates a named Vitest suite whose callback receives Effect test helpers.\n *\n * @stability unstable\n * @category testing\n * @since 4.0.0\n */\nexport const describeWrapped = internal.describeWrapped;\n//# sourceMappingURL=index.js.map","import { describe, it } from \"@effect/vitest\"\nimport { Effect, Exit, Fiber, Redacted, Stream } from \"effect\"\nimport { TestClock } from \"effect/testing\"\nimport { expect } from \"vitest\"\nimport * as FakeWebSocket from \"@effect-uai/core/testing/FakeWebSocket\"\nimport { streamTranscription } from \"./realtimeStt.js\"\n\nconst pcm = { container: \"raw\", encoding: \"pcm_s16le\", sampleRate: 16000, channels: 1 } as const\n\nconst request = { model: \"voxtral-mini-transcribe-realtime-2602\", inputFormat: pcm }\n\nconst sessionCreated = {\n type: \"session.created\",\n session: { request_id: \"req_1\", model: request.model },\n}\n\nconst delta = (text: string) => ({ type: \"transcription.text.delta\", text })\n\nconst start = <E>(\n server: FakeWebSocket.FakeWebSocketServer,\n audioIn: Stream.Stream<Uint8Array, E>,\n) =>\n streamTranscription({ apiKey: Redacted.make(\"test-key\"), webSocket: server.connect })(\n audioIn,\n request,\n ).pipe(Stream.runCollect, Effect.exit, Effect.forkChild)\n\n/** Run a transcription against `server`, closing it, if asked, once the frames are in. */\nconst transcribe = <E>(\n server: FakeWebSocket.FakeWebSocketServer,\n audioIn: Stream.Stream<Uint8Array, E>,\n close?: { readonly code: number; readonly afterSent?: number },\n) =>\n Effect.gen(function* () {\n const collector = yield* start(server, audioIn)\n if (close !== undefined) {\n yield* server.greeted\n yield* server.awaitSent(close.afterSent ?? 0)\n yield* server.close(close.code)\n }\n return yield* Fiber.join(collector)\n })\n\ndescribe(\"Mistral Realtime STT socket\", () => {\n it.live(\"configures the session before any audio, and ends on a clean close\", () =>\n Effect.gen(function* () {\n const server = yield* FakeWebSocket.make({\n greeting: [\n sessionCreated,\n delta(\"hel\"),\n delta(\"lo\"),\n { type: \"transcription.done\", text: \"hello\", model: request.model, segments: [] },\n { type: \"error\", error: { message: \"slow down\", code: 3001 } },\n ],\n })\n const exit = yield* transcribe(server, Stream.make(new Uint8Array([1, 2])), {\n code: 1000,\n afterSent: 3,\n })\n\n expect(exit).toEqual(\n Exit.succeed([\n { _tag: \"partial\", text: \"hel\" },\n { _tag: \"partial\", text: \"hello\" },\n { _tag: \"final\", text: \"hello\" },\n { _tag: \"error\", code: \"3001\", message: \"slow down\" },\n ]),\n )\n const sent = (yield* server.sent) as ReadonlyArray<{ type: string }>\n expect(sent.map((f) => f.type)).toEqual([\n \"session.update\",\n \"input_audio.append\",\n \"input_audio.end\",\n ])\n }).pipe(Effect.scoped),\n )\n\n it.live(\"fails rather than ending when the connection drops\", () =>\n Effect.gen(function* () {\n const server = yield* FakeWebSocket.make({ greeting: [sessionCreated] })\n const exit = yield* transcribe(server, Stream.never, { code: 1011 })\n\n expect(Exit.isFailure(exit) && JSON.stringify(exit.cause)).toContain(\"Unavailable\")\n }).pipe(Effect.scoped),\n )\n\n it.live(\"fails with the audio input's own error\", () =>\n Effect.gen(function* () {\n const server = yield* FakeWebSocket.make({ greeting: [sessionCreated] })\n const exit = yield* transcribe(server, Stream.fail(\"microphone unplugged\"))\n\n expect(Exit.isFailure(exit) && JSON.stringify(exit.cause)).toContain(\"microphone unplugged\")\n }).pipe(Effect.scoped),\n )\n\n it.effect(\"commits a synthetic final once the deltas go quiet\", () =>\n Effect.gen(function* () {\n const server = yield* FakeWebSocket.make({\n greeting: [sessionCreated, delta(\"hel\"), delta(\"lo \")],\n })\n const collector = yield* start(server, Stream.never)\n yield* server.greeted\n yield* TestClock.withLive(Effect.sleep(\"5 millis\"))\n yield* TestClock.adjust(\"1 second\")\n yield* server.close(1000)\n\n expect(yield* Fiber.join(collector)).toEqual(\n Exit.succeed([\n { _tag: \"partial\", text: \"hel\" },\n { _tag: \"partial\", text: \"hello \" },\n { _tag: \"final\", text: \"hello\" },\n ]),\n )\n }).pipe(Effect.scoped),\n )\n})\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-uai/mistral",
3
- "version": "0.18.0",
3
+ "version": "0.19.0-dev-20261110065930",
4
4
  "description": "Mistral provider for @effect-uai/core: chat-completions plus Voxtral speech-to-text and text-to-speech.",
5
5
  "keywords": [
6
6
  "ai",
@@ -59,17 +59,17 @@
59
59
  "access": "public"
60
60
  },
61
61
  "dependencies": {
62
- "@effect-uai/chat-completions": "0.18.0"
62
+ "@effect-uai/chat-completions": "0.19.0-dev-20261110065930"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@types/ws": "^8.5.13",
66
- "effect": "4.0.1",
66
+ "effect": "4.1.2",
67
67
  "typescript": "^7.0.2",
68
68
  "ws": "^8.18.0",
69
- "@effect-uai/core": "0.18.0"
69
+ "@effect-uai/core": "0.19.0-dev-20261110065930"
70
70
  },
71
71
  "peerDependencies": {
72
- "@effect-uai/core": ">=0.2.0 <1",
72
+ "@effect-uai/core": "0.19.0-dev-20261110065930",
73
73
  "effect": ">=4.0.1 <5.0.0",
74
74
  "ws": "^8.0.0"
75
75
  },
package/src/Mistral.ts CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  decodeChunk,
20
20
  emptyAccumulator,
21
21
  itemsToMessages,
22
+ rejectToolOutputMedia,
22
23
  responseFormatWire,
23
24
  toolChoiceWire,
24
25
  toolsWire,
@@ -123,6 +124,7 @@ const buildStream = (cfg: Config) => {
123
124
  reason:
124
125
  "Assistant messages carry no image on this wire. Use `Turn.imagesAsInput` to resend it as user content.",
125
126
  })
127
+ yield* rejectToolOutputMedia("mistral", request.history)
126
128
  const httpRequest = HttpClientRequest.post(url).pipe(
127
129
  HttpClientRequest.setHeader("authorization", `Bearer ${Redacted.value(cfg.apiKey)}`),
128
130
  HttpClientRequest.bodyJsonUnsafe(buildRequestBody(request)),
package/src/codec.test.ts CHANGED
@@ -19,7 +19,14 @@ describe("itemsToMessages", () => {
19
19
  { type: "message", role: "assistant", content: [{ type: "output_text", text: "checking" }] },
20
20
  { type: "function_call", call_id: "c1", name: "a", arguments: "{}" },
21
21
  { type: "function_call", call_id: "c2", name: "b", arguments: "{}" },
22
- { type: "function_call_output", call_id: "c1", output: "ra" },
22
+ {
23
+ type: "function_call_output",
24
+ call_id: "c1",
25
+ output: [
26
+ { type: "input_text", text: "ra" },
27
+ { type: "input_json", value: { n: 1 } },
28
+ ],
29
+ },
23
30
  ]
24
31
  expect(itemsToMessages(history)).toEqual([
25
32
  {
@@ -30,7 +37,7 @@ describe("itemsToMessages", () => {
30
37
  { id: "c2", type: "function", function: { name: "b", arguments: "{}" } },
31
38
  ],
32
39
  },
33
- { role: "tool", tool_call_id: "c1", content: "ra" },
40
+ { role: "tool", tool_call_id: "c1", content: 'ra\n{"n":1}' },
34
41
  ])
35
42
  })
36
43
 
package/src/codec.ts CHANGED
@@ -11,12 +11,14 @@ export {
11
11
  accumulatorToTurn,
12
12
  applyChunk,
13
13
  emptyAccumulator,
14
+ rejectToolOutputMedia,
14
15
  responseFormatWire,
15
16
  toolsWire,
16
17
  } from "@effect-uai/chat-completions/codec"
17
18
  import {
18
19
  type WireChunk,
19
20
  decodeChunk as decodeChunkShared,
21
+ toolOutputText,
20
22
  } from "@effect-uai/chat-completions/codec"
21
23
 
22
24
  // ---------------------------------------------------------------------------
@@ -105,7 +107,7 @@ const foldItem = (acc: ReadonlyArray<WireMessage>, item: HistoryItem): ReadonlyA
105
107
  function_call: (f) => appendToolCall(acc, f.call_id, f.name, f.arguments),
106
108
  function_call_output: (o) => [
107
109
  ...acc,
108
- { role: "tool", tool_call_id: o.call_id, content: o.output },
110
+ { role: "tool", tool_call_id: o.call_id, content: toolOutputText(o) },
109
111
  ],
110
112
  // Reasoning items are not replayed to the chat-completions endpoint.
111
113
  reasoning: () => acc,