@effect-uai/mistral 0.17.0 → 0.18.0

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.
Files changed (53) hide show
  1. package/README.md +3 -3
  2. package/dist/Mistral.d.mts +1 -1
  3. package/dist/Mistral.mjs +2 -2
  4. package/dist/Mistral.mjs.map +1 -1
  5. package/dist/MistralRealtimeTranscriber.d.mts +2 -2
  6. package/dist/MistralRealtimeTranscriber.mjs +2 -2
  7. package/dist/MistralRealtimeTranscriber.mjs.map +1 -1
  8. package/dist/MistralSynthesizer.d.mts +1 -1
  9. package/dist/MistralSynthesizer.d.mts.map +1 -1
  10. package/dist/MistralSynthesizer.mjs +5 -4
  11. package/dist/MistralSynthesizer.mjs.map +1 -1
  12. package/dist/MistralTranscriber.d.mts +1 -1
  13. package/dist/MistralTranscriber.mjs +2 -2
  14. package/dist/MistralTranscriber.mjs.map +1 -1
  15. package/dist/audioCodec.d.mts.map +1 -1
  16. package/dist/audioCodec.mjs +3 -2
  17. package/dist/audioCodec.mjs.map +1 -1
  18. package/dist/codec.d.mts.map +1 -1
  19. package/dist/codec.mjs +4 -3
  20. package/dist/codec.mjs.map +1 -1
  21. package/dist/codec.test.mjs +1 -1
  22. package/dist/dist-DHer1cql.mjs +1533 -0
  23. package/dist/dist-DHer1cql.mjs.map +1 -0
  24. package/dist/http.d.mts +1 -1
  25. package/dist/http.mjs.map +1 -1
  26. package/dist/{test.DNmyFkvJ-N3QqMRwZ.mjs → index.C4tKZ0yW-CUBm3Bmz.mjs} +9446 -5818
  27. package/dist/index.C4tKZ0yW-CUBm3Bmz.mjs.map +1 -0
  28. package/dist/onHalt.test.mjs +2 -2
  29. package/dist/onHalt.test.mjs.map +1 -1
  30. package/dist/{realtimeStt-BcthMc57.d.mts → realtimeStt-CcuQ6EZS.d.mts} +7 -1
  31. package/dist/realtimeStt-CcuQ6EZS.d.mts.map +1 -0
  32. package/dist/realtimeStt.d.mts +1 -1
  33. package/dist/realtimeStt.mjs +27 -30
  34. package/dist/realtimeStt.mjs.map +1 -1
  35. package/dist/realtimeStt.test.d.mts +1 -0
  36. package/dist/realtimeStt.test.mjs +298 -0
  37. package/dist/realtimeStt.test.mjs.map +1 -0
  38. package/dist/{rolldown-runtime-D7D4PA-g.mjs → rolldown-runtime-EAsCxrDo.mjs} +2 -1
  39. package/package.json +5 -5
  40. package/src/Mistral.ts +1 -1
  41. package/src/MistralRealtimeTranscriber.ts +1 -1
  42. package/src/MistralSynthesizer.ts +4 -13
  43. package/src/MistralTranscriber.ts +1 -1
  44. package/src/audioCodec.ts +3 -2
  45. package/src/codec.ts +3 -2
  46. package/src/http.ts +1 -1
  47. package/src/onHalt.test.ts +1 -1
  48. package/src/realtimeStt.test.ts +116 -0
  49. package/src/realtimeStt.ts +68 -64
  50. package/dist/magic-string.es-CUAnjKYC.mjs +0 -1014
  51. package/dist/magic-string.es-CUAnjKYC.mjs.map +0 -1
  52. package/dist/realtimeStt-BcthMc57.d.mts.map +0 -1
  53. package/dist/test.DNmyFkvJ-N3QqMRwZ.mjs.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { make } from "./Mistral.mjs";
2
- import { n as describe, r as it, t as globalExpect } from "./test.DNmyFkvJ-N3QqMRwZ.mjs";
2
+ import { n as globalExpect, o as describe, s as it } from "./index.C4tKZ0yW-CUBm3Bmz.mjs";
3
3
  import { Effect, Exit, Redacted, Stream } from "effect";
4
- import { HttpClient, HttpClientResponse } from "effect/unstable/http";
4
+ import { HttpClient, HttpClientResponse } from "effect/http";
5
5
  //#region src/onHalt.test.ts
6
6
  const clientWithBody = (makeBody) => HttpClient.make((request) => Effect.succeed(HttpClientResponse.fromWeb(request, new Response(makeBody(), {
7
7
  status: 200,
@@ -1 +1 @@
1
- {"version":3,"file":"onHalt.test.mjs","names":["makeMistral"],"sources":["../src/onHalt.test.ts"],"sourcesContent":["import { Effect, Exit, Redacted, Stream } from \"effect\"\nimport { HttpClient, HttpClientResponse } from \"effect/unstable/http\"\nimport { describe, expect, it } from \"vitest\"\nimport { TurnEvent } from \"@effect-uai/core/Turn\"\nimport { make as makeMistral } from \"./Mistral.js\"\n\n// An HttpClient backed by a scripted SSE body\nconst clientWithBody = (makeBody: () => ReadableStream<Uint8Array>): HttpClient.HttpClient =>\n HttpClient.make((request) =>\n Effect.succeed(\n HttpClientResponse.fromWeb(\n request,\n new Response(makeBody(), {\n status: 200,\n headers: { \"content-type\": \"text/event-stream\" },\n }),\n ),\n ),\n )\n\nconst enc = new TextEncoder()\nconst sse = (obj: unknown) => enc.encode(`data: ${JSON.stringify(obj)}\\n\\n`)\n\nconst runStream = (client: HttpClient.HttpClient) =>\n Effect.gen(function* () {\n const svc = yield* makeMistral({ apiKey: Redacted.make(\"k\") })\n const events: Array<TurnEvent> = []\n const exit = yield* svc.streamTurn({ model: \"mistral-small-latest\", history: [] }).pipe(\n Stream.runForEach((e) => Effect.sync(() => events.push(e))),\n Effect.exit,\n )\n return { tags: events.map((e) => e._tag), events, exit }\n }).pipe(Effect.provideService(HttpClient.HttpClient, client))\n\ndescribe(\"Mistral streamTurn: truncated turn presented as complete\", () => {\n it(\"truncated EOF (no finish_reason) must not synthesize a complete turn\", async () => {\n const body = () =>\n new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(sse({ choices: [{ delta: { content: \"Hel\" } }] }))\n controller.close() // no finish_reason, no [DONE]\n },\n })\n const { tags } = await Effect.runPromise(runStream(clientWithBody(body)))\n expect(tags).toContain(\"TextDelta\")\n expect(tags).not.toContain(\"TurnComplete\")\n })\n\n it(\"mid-stream error must surface as a failure, not a fake TurnComplete\", async () => {\n const body = () =>\n new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(sse({ choices: [{ delta: { content: \"Hel\" } }] }))\n controller.error(new Error(\"connection reset\"))\n },\n })\n const { tags, exit } = await Effect.runPromise(runStream(clientWithBody(body)))\n expect(Exit.isFailure(exit)).toBe(true)\n expect(tags).not.toContain(\"TurnComplete\")\n })\n\n it(\"a well-formed stream (finish_reason present) still completes\", async () => {\n const body = () =>\n new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(sse({ choices: [{ delta: { content: \"Hel\" } }] }))\n controller.enqueue(sse({ choices: [{ delta: {}, finish_reason: \"stop\" }] }))\n controller.enqueue(enc.encode(\"data: [DONE]\\n\\n\"))\n controller.close()\n },\n })\n const { tags } = await Effect.runPromise(runStream(clientWithBody(body)))\n expect(tags).toContain(\"TurnComplete\")\n })\n})\n"],"mappings":";;;;;AAOA,MAAM,kBAAkB,aACtB,WAAW,MAAM,YACf,OAAO,QACL,mBAAmB,QACjB,SACA,IAAI,SAAS,SAAS,GAAG;CACvB,QAAQ;CACR,SAAS,EAAE,gBAAgB,oBAAoB;AACjD,CAAC,CACH,CACF,CACF;AAEF,MAAM,MAAM,IAAI,YAAY;AAC5B,MAAM,OAAO,QAAiB,IAAI,OAAO,SAAS,KAAK,UAAU,GAAG,EAAE,KAAK;AAE3E,MAAM,aAAa,WACjB,OAAO,IAAI,aAAa;CACtB,MAAM,MAAM,OAAOA,KAAY,EAAE,QAAQ,SAAS,KAAK,GAAG,EAAE,CAAC;CAC7D,MAAM,SAA2B,CAAC;CAClC,MAAM,OAAO,OAAO,IAAI,WAAW;EAAE,OAAO;EAAwB,SAAS,CAAC;CAAE,CAAC,CAAC,CAAC,KACjF,OAAO,YAAY,MAAM,OAAO,WAAW,OAAO,KAAK,CAAC,CAAC,CAAC,GAC1D,OAAO,IACT;CACA,OAAO;EAAE,MAAM,OAAO,KAAK,MAAM,EAAE,IAAI;EAAG;EAAQ;CAAK;AACzD,CAAC,CAAC,CAAC,KAAK,OAAO,eAAe,WAAW,YAAY,MAAM,CAAC;AAE9D,SAAS,kEAAkE;CACzE,GAAG,wEAAwE,YAAY;EACrF,MAAM,aACJ,IAAI,eAA2B,EAC7B,MAAM,YAAY;GAChB,WAAW,QAAQ,IAAI,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;GACpE,WAAW,MAAM;EACnB,EACF,CAAC;EACH,MAAM,EAAE,SAAS,MAAM,OAAO,WAAW,UAAU,eAAe,IAAI,CAAC,CAAC;EACxE,aAAO,IAAI,CAAC,CAAC,UAAU,WAAW;EAClC,aAAO,IAAI,CAAC,CAAC,IAAI,UAAU,cAAc;CAC3C,CAAC;CAED,GAAG,uEAAuE,YAAY;EACpF,MAAM,aACJ,IAAI,eAA2B,EAC7B,MAAM,YAAY;GAChB,WAAW,QAAQ,IAAI,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;GACpE,WAAW,sBAAM,IAAI,MAAM,kBAAkB,CAAC;EAChD,EACF,CAAC;EACH,MAAM,EAAE,MAAM,SAAS,MAAM,OAAO,WAAW,UAAU,eAAe,IAAI,CAAC,CAAC;EAC9E,aAAO,KAAK,UAAU,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI;EACtC,aAAO,IAAI,CAAC,CAAC,IAAI,UAAU,cAAc;CAC3C,CAAC;CAED,GAAG,gEAAgE,YAAY;EAC7E,MAAM,aACJ,IAAI,eAA2B,EAC7B,MAAM,YAAY;GAChB,WAAW,QAAQ,IAAI,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;GACpE,WAAW,QAAQ,IAAI,EAAE,SAAS,CAAC;IAAE,OAAO,CAAC;IAAG,eAAe;GAAO,CAAC,EAAE,CAAC,CAAC;GAC3E,WAAW,QAAQ,IAAI,OAAO,kBAAkB,CAAC;GACjD,WAAW,MAAM;EACnB,EACF,CAAC;EACH,MAAM,EAAE,SAAS,MAAM,OAAO,WAAW,UAAU,eAAe,IAAI,CAAC,CAAC;EACxE,aAAO,IAAI,CAAC,CAAC,UAAU,cAAc;CACvC,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"onHalt.test.mjs","names":["makeMistral"],"sources":["../src/onHalt.test.ts"],"sourcesContent":["import { Effect, Exit, Redacted, Stream } from \"effect\"\nimport { HttpClient, HttpClientResponse } from \"effect/http\"\nimport { describe, expect, it } from \"vitest\"\nimport { TurnEvent } from \"@effect-uai/core/Turn\"\nimport { make as makeMistral } from \"./Mistral.js\"\n\n// An HttpClient backed by a scripted SSE body\nconst clientWithBody = (makeBody: () => ReadableStream<Uint8Array>): HttpClient.HttpClient =>\n HttpClient.make((request) =>\n Effect.succeed(\n HttpClientResponse.fromWeb(\n request,\n new Response(makeBody(), {\n status: 200,\n headers: { \"content-type\": \"text/event-stream\" },\n }),\n ),\n ),\n )\n\nconst enc = new TextEncoder()\nconst sse = (obj: unknown) => enc.encode(`data: ${JSON.stringify(obj)}\\n\\n`)\n\nconst runStream = (client: HttpClient.HttpClient) =>\n Effect.gen(function* () {\n const svc = yield* makeMistral({ apiKey: Redacted.make(\"k\") })\n const events: Array<TurnEvent> = []\n const exit = yield* svc.streamTurn({ model: \"mistral-small-latest\", history: [] }).pipe(\n Stream.runForEach((e) => Effect.sync(() => events.push(e))),\n Effect.exit,\n )\n return { tags: events.map((e) => e._tag), events, exit }\n }).pipe(Effect.provideService(HttpClient.HttpClient, client))\n\ndescribe(\"Mistral streamTurn: truncated turn presented as complete\", () => {\n it(\"truncated EOF (no finish_reason) must not synthesize a complete turn\", async () => {\n const body = () =>\n new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(sse({ choices: [{ delta: { content: \"Hel\" } }] }))\n controller.close() // no finish_reason, no [DONE]\n },\n })\n const { tags } = await Effect.runPromise(runStream(clientWithBody(body)))\n expect(tags).toContain(\"TextDelta\")\n expect(tags).not.toContain(\"TurnComplete\")\n })\n\n it(\"mid-stream error must surface as a failure, not a fake TurnComplete\", async () => {\n const body = () =>\n new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(sse({ choices: [{ delta: { content: \"Hel\" } }] }))\n controller.error(new Error(\"connection reset\"))\n },\n })\n const { tags, exit } = await Effect.runPromise(runStream(clientWithBody(body)))\n expect(Exit.isFailure(exit)).toBe(true)\n expect(tags).not.toContain(\"TurnComplete\")\n })\n\n it(\"a well-formed stream (finish_reason present) still completes\", async () => {\n const body = () =>\n new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(sse({ choices: [{ delta: { content: \"Hel\" } }] }))\n controller.enqueue(sse({ choices: [{ delta: {}, finish_reason: \"stop\" }] }))\n controller.enqueue(enc.encode(\"data: [DONE]\\n\\n\"))\n controller.close()\n },\n })\n const { tags } = await Effect.runPromise(runStream(clientWithBody(body)))\n expect(tags).toContain(\"TurnComplete\")\n })\n})\n"],"mappings":";;;;;AAOA,MAAM,kBAAkB,aACtB,WAAW,MAAM,YACf,OAAO,QACL,mBAAmB,QACjB,SACA,IAAI,SAAS,SAAS,GAAG;CACvB,QAAQ;CACR,SAAS,EAAE,gBAAgB,oBAAoB;AACjD,CAAC,CACH,CACF,CACF;AAEF,MAAM,MAAM,IAAI,YAAY;AAC5B,MAAM,OAAO,QAAiB,IAAI,OAAO,SAAS,KAAK,UAAU,GAAG,EAAE,KAAK;AAE3E,MAAM,aAAa,WACjB,OAAO,IAAI,aAAa;CACtB,MAAM,MAAM,OAAOA,KAAY,EAAE,QAAQ,SAAS,KAAK,GAAG,EAAE,CAAC;CAC7D,MAAM,SAA2B,CAAC;CAClC,MAAM,OAAO,OAAO,IAAI,WAAW;EAAE,OAAO;EAAwB,SAAS,CAAC;CAAE,CAAC,CAAC,CAAC,KACjF,OAAO,YAAY,MAAM,OAAO,WAAW,OAAO,KAAK,CAAC,CAAC,CAAC,GAC1D,OAAO,IACT;CACA,OAAO;EAAE,MAAM,OAAO,KAAK,MAAM,EAAE,IAAI;EAAG;EAAQ;CAAK;AACzD,CAAC,CAAC,CAAC,KAAK,OAAO,eAAe,WAAW,YAAY,MAAM,CAAC;AAE9D,SAAS,kEAAkE;CACzE,GAAG,wEAAwE,YAAY;EACrF,MAAM,aACJ,IAAI,eAA2B,EAC7B,MAAM,YAAY;GAChB,WAAW,QAAQ,IAAI,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;GACpE,WAAW,MAAM;EACnB,EACF,CAAC;EACH,MAAM,EAAE,SAAS,MAAM,OAAO,WAAW,UAAU,eAAe,IAAI,CAAC,CAAC;EACxE,aAAO,IAAI,CAAC,CAAC,UAAU,WAAW;EAClC,aAAO,IAAI,CAAC,CAAC,IAAI,UAAU,cAAc;CAC3C,CAAC;CAED,GAAG,uEAAuE,YAAY;EACpF,MAAM,aACJ,IAAI,eAA2B,EAC7B,MAAM,YAAY;GAChB,WAAW,QAAQ,IAAI,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;GACpE,WAAW,sBAAM,IAAI,MAAM,kBAAkB,CAAC;EAChD,EACF,CAAC;EACH,MAAM,EAAE,MAAM,SAAS,MAAM,OAAO,WAAW,UAAU,eAAe,IAAI,CAAC,CAAC;EAC9E,aAAO,KAAK,UAAU,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI;EACtC,aAAO,IAAI,CAAC,CAAC,IAAI,UAAU,cAAc;CAC3C,CAAC;CAED,GAAG,gEAAgE,YAAY;EAC7E,MAAM,aACJ,IAAI,eAA2B,EAC7B,MAAM,YAAY;GAChB,WAAW,QAAQ,IAAI,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;GACpE,WAAW,QAAQ,IAAI,EAAE,SAAS,CAAC;IAAE,OAAO,CAAC;IAAG,eAAe;GAAO,CAAC,EAAE,CAAC,CAAC;GAC3E,WAAW,QAAQ,IAAI,OAAO,kBAAkB,CAAC;GACjD,WAAW,MAAM;EACnB,EACF,CAAC;EACH,MAAM,EAAE,SAAS,MAAM,OAAO,WAAW,UAAU,eAAe,IAAI,CAAC,CAAC;EACxE,aAAO,IAAI,CAAC,CAAC,UAAU,cAAc;CACvC,CAAC;AACH,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { Duration, Redacted, Stream } from "effect";
2
2
  import * as AiError from "@effect-uai/core/AiError";
3
3
  import { CommonStreamTranscribeRequest } from "@effect-uai/core/Transcriber";
4
+ import * as Socket from "effect/socket/Socket";
4
5
  import { TranscriptEvent } from "@effect-uai/core/Transcript";
5
6
  //#region src/realtimeStt.d.ts
6
7
  type Config = {
@@ -14,8 +15,13 @@ type Config = {
14
15
  * adapter delimits turns. Default 700 ms.
15
16
  */
16
17
  readonly utteranceSilence?: Duration.Duration;
18
+ /**
19
+ * Build the socket yourself, for a proxy or an in-memory transport. The
20
+ * default attaches the bearer token through the `ws` package.
21
+ */
22
+ readonly webSocket?: Socket.WebSocketConstructor["Service"];
17
23
  };
18
24
  declare const streamTranscription: (cfg: Config) => <E, R>(audioIn: Stream.Stream<Uint8Array, E, R>, request: CommonStreamTranscribeRequest) => Stream.Stream<TranscriptEvent, AiError.AiError | E, R>;
19
25
  //#endregion
20
26
  export { streamTranscription as n, Config as t };
21
- //# sourceMappingURL=realtimeStt-BcthMc57.d.mts.map
27
+ //# sourceMappingURL=realtimeStt-CcuQ6EZS.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtimeStt-CcuQ6EZS.d.mts","names":[],"sources":["../src/realtimeStt.ts"],"mappings":";;;;;;KA+CY;WACD,QAAQ,SAAS;WACjB;;WAEA,uBAAuB,SAAS;;;;;;WAMhC,mBAAmB,SAAS;;;;;WAK5B,YAAY,OAAO;;cAsJjB,sBAAmB,KACxB,YACL,GAAG,GAAC,SACM,OAAO,OAAO,YAAY,GAAG,IAAE,SAC/B,kCACR,OAAO,OAAO,iBAAiB,QAAQ,UAAU,GAAG"}
@@ -1,2 +1,2 @@
1
- import { n as streamTranscription, t as Config } from "./realtimeStt-BcthMc57.mjs";
1
+ import { n as streamTranscription, t as Config } from "./realtimeStt-CcuQ6EZS.mjs";
2
2
  export { Config, streamTranscription };
@@ -1,7 +1,9 @@
1
- import { Clock, Duration, Effect, Encoding, Match, Queue, Redacted, Ref, Schema, Stream } from "effect";
1
+ import { Clock, Duration, Effect, Match, Option, Predicate, Redacted, Ref, Schema, Stream } from "effect";
2
2
  import * as AiError from "@effect-uai/core/AiError";
3
- import * as Socket from "effect/unstable/socket/Socket";
3
+ import { Base64 } from "effect/encoding";
4
+ import * as Socket from "effect/socket/Socket";
4
5
  import * as JSONL from "@effect-uai/core/JSONL";
6
+ import * as WebSocketSession from "@effect-uai/core/WebSocketSession";
5
7
  import { WebSocket } from "ws";
6
8
  //#region src/realtimeStt.ts
7
9
  /**
@@ -50,7 +52,7 @@ const sessionUpdateFrame = (cfg) => JSON.stringify({
50
52
  });
51
53
  const audioAppendFrame = (bytes) => JSON.stringify({
52
54
  type: "input_audio.append",
53
- audio: Encoding.encodeBase64(bytes)
55
+ audio: Base64.encode(bytes)
54
56
  });
55
57
  const audioEndFrame = JSON.stringify({ type: "input_audio.end" });
56
58
  const ServerEvent = Schema.Union([
@@ -75,60 +77,55 @@ const ServerEvent = Schema.Union([
75
77
  })
76
78
  ]);
77
79
  const decodeServerEvent = Schema.decodeUnknownEffect(ServerEvent);
78
- const emitFinal = (queue, state, override) => Effect.gen(function* () {
80
+ const takeFinal = (state, override) => Effect.gen(function* () {
79
81
  const acc = yield* Ref.getAndSet(state.text, "");
80
82
  const text = (override !== void 0 && override.length > 0 ? override : acc).trim();
81
- if (text.length > 0) yield* Queue.offer(queue, {
83
+ return text.length > 0 ? {
82
84
  _tag: "final",
83
85
  text
84
- });
86
+ } : void 0;
85
87
  });
86
- const handleServerMessage = (queue, state) => (raw) => Effect.gen(function* () {
88
+ const frameToEvent = (state) => (raw) => Effect.gen(function* () {
87
89
  const json = yield* JSONL.parseSafe(raw);
88
- if (json === void 0) return;
90
+ if (json === void 0) return void 0;
89
91
  const decoded = yield* decodeServerEvent(json).pipe(Effect.option);
90
- if (decoded._tag === "None") return;
91
- yield* Match.value(decoded.value).pipe(Match.when({ type: "transcription.text.delta" }, (m) => Effect.gen(function* () {
92
+ if (Option.isNone(decoded)) return void 0;
93
+ return yield* Match.value(decoded.value).pipe(Match.when({ type: "transcription.text.delta" }, (m) => Effect.gen(function* () {
92
94
  const now = yield* Clock.currentTimeMillis;
93
95
  const next = yield* Ref.updateAndGet(state.text, (t) => t + m.text);
94
96
  yield* Ref.set(state.lastActivityMs, now);
95
- yield* Queue.offer(queue, {
97
+ return {
96
98
  _tag: "partial",
97
99
  text: next
98
- });
99
- })), Match.when({ type: "transcription.done" }, (m) => emitFinal(queue, state, m.text)), Match.when({ type: "error" }, (m) => Queue.offer(queue, {
100
+ };
101
+ })), Match.when({ type: "transcription.done" }, (m) => takeFinal(state, m.text)), Match.when({ type: "error" }, (m) => Effect.succeed({
100
102
  _tag: "error",
101
103
  ...m.error.code != null && { code: String(m.error.code) },
102
104
  message: m.error.message
103
- })), Match.orElse(() => Effect.void));
105
+ })), Match.orElse(() => Effect.succeed(void 0)));
104
106
  });
105
- const silenceFinalizer = (queue, state, silence) => {
107
+ const silenceFinals = (state, silence) => {
106
108
  const silenceMs = Duration.toMillis(silence);
107
- return Effect.gen(function* () {
109
+ return Stream.tick("150 millis").pipe(Stream.mapEffect(() => Effect.gen(function* () {
108
110
  const now = yield* Clock.currentTimeMillis;
109
111
  const last = yield* Ref.get(state.lastActivityMs);
110
- if ((yield* Ref.get(state.text)).trim().length > 0 && now - last >= silenceMs) yield* emitFinal(queue, state);
111
- yield* Effect.sleep("150 millis");
112
- }).pipe(Effect.forever);
112
+ return (yield* Ref.get(state.text)).trim().length > 0 && now - last >= silenceMs ? yield* takeFinal(state) : void 0;
113
+ })), Stream.filter(Predicate.isNotUndefined));
113
114
  };
114
115
  const authedWsConstructor = (cfg) => (url) => new WebSocket(url, void 0, { headers: { Authorization: `Bearer ${Redacted.value(cfg.apiKey)}` } });
115
116
  const streamTranscription = (cfg) => (audioIn, request) => Stream.unwrap(Effect.gen(function* () {
116
117
  yield* ensureInputFormat(request.inputFormat);
117
- const socket = yield* Socket.makeWebSocket(buildWsUrl(cfg, request.model), { closeCodeIsError: (code) => code !== 1e3 && code !== 1001 && code !== 1005 }).pipe(Effect.provideService(Socket.WebSocketConstructor, authedWsConstructor(cfg)));
118
- const queue = yield* Queue.bounded(64);
118
+ const socket = yield* Socket.makeWebSocket(buildWsUrl(cfg, request.model)).pipe(Effect.provideService(Socket.WebSocketConstructor, cfg.webSocket ?? authedWsConstructor(cfg)));
119
119
  const state = {
120
120
  text: yield* Ref.make(""),
121
121
  lastActivityMs: yield* Ref.make(0)
122
122
  };
123
- const write = yield* socket.writer;
124
- yield* Effect.gen(function* () {
125
- yield* write(sessionUpdateFrame(cfg));
126
- yield* Stream.runForEach(audioIn, (bytes) => write(audioAppendFrame(bytes)));
127
- yield* write(audioEndFrame);
128
- }).pipe(Effect.tapError((cause) => Effect.logWarning("[voxtral-realtime] audio send failed", { cause })), Effect.ignore, Effect.forkScoped);
129
- yield* silenceFinalizer(queue, state, cfg.utteranceSilence ?? DEFAULT_SILENCE).pipe(Effect.forkScoped);
130
- yield* socket.runString(handleServerMessage(queue, state)).pipe(Effect.tapError((cause) => Effect.logWarning("[voxtral-realtime] socket closed", { cause })), Effect.ensuring(Queue.end(queue)), Effect.forkScoped);
131
- return Stream.fromQueue(queue);
123
+ const { write } = yield* socket.writer;
124
+ const asAiError = WebSocketSession.toAiError("mistral");
125
+ const frames = Stream.fromPull(Socket.readerString(socket)).pipe(Stream.scoped, Stream.catchIf(WebSocketSession.isCleanClose, () => Stream.empty), Stream.mapError(asAiError), Stream.mapEffect(frameToEvent(state)), Stream.filter(Predicate.isNotUndefined));
126
+ const events = Stream.merge(frames, silenceFinals(state, cfg.utteranceSilence ?? DEFAULT_SILENCE), { haltStrategy: "left" });
127
+ const outgoing = Stream.concat(Stream.concat(Stream.make(sessionUpdateFrame(cfg)), Stream.map(audioIn, audioAppendFrame)), Stream.make(audioEndFrame)).pipe(Stream.mapEffect((frame) => Effect.mapError(write(frame), asAiError)));
128
+ return Stream.merge(events, Stream.drain(outgoing), { haltStrategy: "left" });
132
129
  }));
133
130
  //#endregion
134
131
  export { streamTranscription };
@@ -1 +1 @@
1
- {"version":3,"file":"realtimeStt.mjs","names":["WSWebSocket"],"sources":["../src/realtimeStt.ts"],"sourcesContent":["/**\n * Voxtral Realtime STT over WebSocket.\n *\n * Protocol mirrors the `mistralai[realtime]` Python client:\n * - URL: `wss://api.mistral.ai/v1/audio/transcriptions/realtime?model=…`\n * (the model is a query param, not a session field).\n * - Auth: `Authorization: Bearer …` header on the upgrade. The browser\n * `WebSocket` API can't set headers, so this module uses the `ws` peer\n * dep; `ws` is only pulled in transitively via\n * `MistralRealtimeTranscriber` (the sync `MistralTranscriber` stays free\n * of it).\n * - Client → server (JSON): `session.update` (audio_format +\n * target_streaming_delay_ms), then `input_audio.append` with base64 PCM,\n * and `input_audio.end` when the mic stream stops.\n * - Server → client (JSON): `session.created` / `session.updated`,\n * `transcription.text.delta` (`{ text }`), `transcription.done`\n * (`{ text }`), `transcription.segment`, `transcription.language`,\n * and `error` (`{ error: { message, code } }`).\n *\n * Voxtral Realtime is a *continuous* transcriber: it streams text deltas and\n * only emits one `transcription.done` at end-of-audio (no server-side\n * utterance segmentation). To satisfy the `Transcriber` contract's `final`\n * semantics for conversational use, this adapter synthesizes a `final` event\n * when the deltas go quiet for `utteranceSilence`.\n */\nimport {\n Cause,\n Clock,\n Duration,\n Effect,\n Encoding,\n Match,\n Queue,\n Redacted,\n Ref,\n Schema,\n Stream,\n} from \"effect\"\nimport * as Socket from \"effect/unstable/socket/Socket\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { AudioFormat } from \"@effect-uai/core/Audio\"\nimport * as JSONL from \"@effect-uai/core/JSONL\"\nimport type { TranscriptEvent } from \"@effect-uai/core/Transcript\"\nimport type { CommonStreamTranscribeRequest } from \"@effect-uai/core/Transcriber\"\nimport { WebSocket as WSWebSocket } from \"ws\"\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n /** Target latency knob (`target_streaming_delay_ms`); lower = faster, less accurate. */\n readonly targetStreamingDelay?: Duration.Duration\n /**\n * Silence gap after which the accumulated deltas are committed as a synthetic\n * `final`. Voxtral Realtime does no utterance segmentation, so this is how the\n * adapter delimits turns. Default 700 ms.\n */\n readonly utteranceSilence?: Duration.Duration\n}\n\nconst DEFAULT_SILENCE = Duration.millis(700)\n\n// ---------------------------------------------------------------------------\n// AudioFormat gate — Voxtral Realtime ingests pcm_s16le @ 16000 mono only.\n// ---------------------------------------------------------------------------\n\nconst ensureInputFormat = (format: AudioFormat): Effect.Effect<void, AiError.AiError> =>\n format.container === \"raw\" && format.encoding === \"pcm_s16le\" && format.sampleRate === 16000\n ? Effect.void\n : Effect.fail(\n new AiError.Unsupported({\n provider: \"mistral\",\n capability: \"inputFormat\",\n reason: `Voxtral Realtime accepts pcm_s16le @ 16000 mono only. Got ${JSON.stringify(format)}.`,\n }),\n )\n\n// ---------------------------------------------------------------------------\n// URL + client frame builders\n// ---------------------------------------------------------------------------\n\nconst buildWsUrl = (cfg: Config, model: string) => {\n const base = (cfg.baseUrl ?? \"https://api.mistral.ai\").replace(/^http/, \"ws\")\n return `${base}/v1/audio/transcriptions/realtime?model=${encodeURIComponent(model)}`\n}\n\nconst sessionUpdateFrame = (cfg: Config) =>\n JSON.stringify({\n type: \"session.update\",\n session: {\n audio_format: { encoding: \"pcm_s16le\", sample_rate: 16000 },\n ...(cfg.targetStreamingDelay !== undefined && {\n target_streaming_delay_ms: Duration.toMillis(cfg.targetStreamingDelay),\n }),\n },\n })\n\nconst audioAppendFrame = (bytes: Uint8Array) =>\n JSON.stringify({ type: \"input_audio.append\", audio: Encoding.encodeBase64(bytes) })\n\nconst audioEndFrame = JSON.stringify({ type: \"input_audio.end\" })\n\n// ---------------------------------------------------------------------------\n// Wire schemas (server → client)\n// ---------------------------------------------------------------------------\n\nconst ServerEvent = Schema.Union([\n Schema.Struct({ type: Schema.Literal(\"session.created\") }),\n Schema.Struct({ type: Schema.Literal(\"session.updated\") }),\n Schema.Struct({ type: Schema.Literal(\"transcription.language\") }),\n Schema.Struct({ type: Schema.Literal(\"transcription.segment\") }),\n Schema.Struct({ type: Schema.Literal(\"transcription.text.delta\"), text: Schema.String }),\n Schema.Struct({ type: Schema.Literal(\"transcription.done\"), text: Schema.String }),\n Schema.Struct({\n type: Schema.Literal(\"error\"),\n error: Schema.Struct({\n message: Schema.String,\n code: Schema.optional(Schema.NullOr(Schema.Number)),\n }),\n }),\n])\nconst decodeServerEvent = Schema.decodeUnknownEffect(ServerEvent)\n\n/** Mutable turn state shared by the message handler and the silence finalizer. */\ntype TurnState = {\n readonly text: Ref.Ref<string>\n readonly lastActivityMs: Ref.Ref<number>\n}\n\nconst emitFinal = (\n queue: Queue.Queue<TranscriptEvent, Cause.Done>,\n state: TurnState,\n override?: string,\n): Effect.Effect<void> =>\n Effect.gen(function* () {\n const acc = yield* Ref.getAndSet(state.text, \"\")\n const text = (override !== undefined && override.length > 0 ? override : acc).trim()\n if (text.length > 0) yield* Queue.offer(queue, { _tag: \"final\", text })\n })\n\nconst handleServerMessage =\n (queue: Queue.Queue<TranscriptEvent, Cause.Done>, state: TurnState) => (raw: string) =>\n Effect.gen(function* () {\n const json = yield* JSONL.parseSafe(raw)\n if (json === undefined) return\n const decoded = yield* decodeServerEvent(json).pipe(Effect.option)\n if (decoded._tag === \"None\") return\n yield* Match.value(decoded.value).pipe(\n Match.when({ type: \"transcription.text.delta\" }, (m) =>\n Effect.gen(function* () {\n const now = yield* Clock.currentTimeMillis\n const next = yield* Ref.updateAndGet(state.text, (t) => t + m.text)\n yield* Ref.set(state.lastActivityMs, now)\n // Cumulative partial so the UI shows the growing sentence.\n yield* Queue.offer(queue, { _tag: \"partial\", text: next })\n }),\n ),\n // End-of-audio: commit whatever's left as the final utterance.\n Match.when({ type: \"transcription.done\" }, (m) => emitFinal(queue, state, m.text)),\n Match.when({ type: \"error\" }, (m) =>\n Queue.offer(queue, {\n _tag: \"error\",\n ...(m.error.code != null && { code: String(m.error.code) }),\n message: m.error.message,\n }),\n ),\n // session.created / .updated / language / segment: no user-visible event.\n Match.orElse(() => Effect.void),\n )\n })\n\n// Background loop: commit a synthetic final once the deltas go quiet.\nconst silenceFinalizer = (\n queue: Queue.Queue<TranscriptEvent, Cause.Done>,\n state: TurnState,\n silence: Duration.Duration,\n): Effect.Effect<never> => {\n const silenceMs = Duration.toMillis(silence)\n return Effect.gen(function* () {\n const now = yield* Clock.currentTimeMillis\n const last = yield* Ref.get(state.lastActivityMs)\n const acc = yield* Ref.get(state.text)\n if (acc.trim().length > 0 && now - last >= silenceMs) yield* emitFinal(queue, state)\n yield* Effect.sleep(\"150 millis\")\n }).pipe(Effect.forever)\n}\n\n// ---------------------------------------------------------------------------\n// Stream<Uint8Array> → Stream<TranscriptEvent>\n// ---------------------------------------------------------------------------\n\n// `@types/ws`'s WebSocket extends Node's EventEmitter while\n// `globalThis.WebSocket` extends EventTarget; the browser-style surface\n// Effect's Socket reads (`addEventListener` / `send` / `close`) is identical at\n// runtime, hence the single contained cast.\nconst authedWsConstructor =\n (cfg: Config): Socket.WebSocketConstructor[\"Service\"] =>\n (url) =>\n new WSWebSocket(url, undefined, {\n headers: { Authorization: `Bearer ${Redacted.value(cfg.apiKey)}` },\n }) as unknown as globalThis.WebSocket\n\nexport const streamTranscription =\n (cfg: Config) =>\n <E, R>(\n audioIn: Stream.Stream<Uint8Array, E, R>,\n request: CommonStreamTranscribeRequest,\n ): Stream.Stream<TranscriptEvent, AiError.AiError | E, R> =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* ensureInputFormat(request.inputFormat)\n const socket = yield* Socket.makeWebSocket(buildWsUrl(cfg, request.model), {\n // Effect's Socket treats all close codes as errors by default —\n // whitelist standard clean-close codes (1000 / 1001 / 1005).\n closeCodeIsError: (code) => code !== 1000 && code !== 1001 && code !== 1005,\n }).pipe(Effect.provideService(Socket.WebSocketConstructor, authedWsConstructor(cfg)))\n const queue = yield* Queue.bounded<TranscriptEvent, Cause.Done>(64)\n const state: TurnState = {\n text: yield* Ref.make(\"\"),\n lastActivityMs: yield* Ref.make(0),\n }\n const write = yield* socket.writer\n\n // session.update first, then stream base64 PCM frames, then signal end.\n // Fork-scoped so the Stream's downstream scope tears them down on\n // disconnect / cancel.\n yield* Effect.gen(function* () {\n yield* write(sessionUpdateFrame(cfg))\n yield* Stream.runForEach(audioIn, (bytes) => write(audioAppendFrame(bytes)))\n yield* write(audioEndFrame)\n }).pipe(\n Effect.tapError((cause) =>\n Effect.logWarning(\"[voxtral-realtime] audio send failed\", { cause }),\n ),\n Effect.ignore,\n Effect.forkScoped,\n )\n\n yield* silenceFinalizer(queue, state, cfg.utteranceSilence ?? DEFAULT_SILENCE).pipe(\n Effect.forkScoped,\n )\n\n // `Queue.end` flushes pending events then ends the stream cleanly;\n // `Queue.shutdown` would drop queued items and interrupt takes. A\n // connection/read failure is logged (otherwise the stream would end\n // silently with no transcripts).\n yield* socket.runString(handleServerMessage(queue, state)).pipe(\n Effect.tapError((cause) =>\n Effect.logWarning(\"[voxtral-realtime] socket closed\", { cause }),\n ),\n Effect.ensuring(Queue.end(queue)),\n Effect.forkScoped,\n )\n\n return Stream.fromQueue(queue)\n }),\n )\n"],"mappings":"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"}
1
+ {"version":3,"file":"realtimeStt.mjs","names":["WSWebSocket"],"sources":["../src/realtimeStt.ts"],"sourcesContent":["/**\n * Voxtral Realtime STT over WebSocket.\n *\n * Protocol mirrors the `mistralai[realtime]` Python client:\n * - URL: `wss://api.mistral.ai/v1/audio/transcriptions/realtime?model=…`\n * (the model is a query param, not a session field).\n * - Auth: `Authorization: Bearer …` header on the upgrade. The browser\n * `WebSocket` API can't set headers, so this module uses the `ws` peer\n * dep; `ws` is only pulled in transitively via\n * `MistralRealtimeTranscriber` (the sync `MistralTranscriber` stays free\n * of it).\n * - Client → server (JSON): `session.update` (audio_format +\n * target_streaming_delay_ms), then `input_audio.append` with base64 PCM,\n * and `input_audio.end` when the mic stream stops.\n * - Server → client (JSON): `session.created` / `session.updated`,\n * `transcription.text.delta` (`{ text }`), `transcription.done`\n * (`{ text }`), `transcription.segment`, `transcription.language`,\n * and `error` (`{ error: { message, code } }`).\n *\n * Voxtral Realtime is a *continuous* transcriber: it streams text deltas and\n * only emits one `transcription.done` at end-of-audio (no server-side\n * utterance segmentation). To satisfy the `Transcriber` contract's `final`\n * semantics for conversational use, this adapter synthesizes a `final` event\n * when the deltas go quiet for `utteranceSilence`.\n */\nimport {\n Clock,\n Duration,\n Effect,\n Match,\n Option,\n Predicate,\n Redacted,\n Ref,\n Schema,\n Stream,\n} from \"effect\"\nimport { Base64 } from \"effect/encoding\"\nimport * as Socket from \"effect/socket/Socket\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { AudioFormat } from \"@effect-uai/core/Audio\"\nimport * as JSONL from \"@effect-uai/core/JSONL\"\nimport type { TranscriptEvent } from \"@effect-uai/core/Transcript\"\nimport type { CommonStreamTranscribeRequest } from \"@effect-uai/core/Transcriber\"\nimport * as WebSocketSession from \"@effect-uai/core/WebSocketSession\"\nimport { WebSocket as WSWebSocket } from \"ws\"\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n /** Target latency knob (`target_streaming_delay_ms`); lower = faster, less accurate. */\n readonly targetStreamingDelay?: Duration.Duration\n /**\n * Silence gap after which the accumulated deltas are committed as a synthetic\n * `final`. Voxtral Realtime does no utterance segmentation, so this is how the\n * adapter delimits turns. Default 700 ms.\n */\n readonly utteranceSilence?: Duration.Duration\n /**\n * Build the socket yourself, for a proxy or an in-memory transport. The\n * default attaches the bearer token through the `ws` package.\n */\n readonly webSocket?: Socket.WebSocketConstructor[\"Service\"]\n}\n\nconst DEFAULT_SILENCE = Duration.millis(700)\n\n// ---------------------------------------------------------------------------\n// AudioFormat gate — Voxtral Realtime ingests pcm_s16le @ 16000 mono only.\n// ---------------------------------------------------------------------------\n\nconst ensureInputFormat = (format: AudioFormat): Effect.Effect<void, AiError.AiError> =>\n format.container === \"raw\" && format.encoding === \"pcm_s16le\" && format.sampleRate === 16000\n ? Effect.void\n : Effect.fail(\n new AiError.Unsupported({\n provider: \"mistral\",\n capability: \"inputFormat\",\n reason: `Voxtral Realtime accepts pcm_s16le @ 16000 mono only. Got ${JSON.stringify(format)}.`,\n }),\n )\n\n// ---------------------------------------------------------------------------\n// URL + client frame builders\n// ---------------------------------------------------------------------------\n\nconst buildWsUrl = (cfg: Config, model: string) => {\n const base = (cfg.baseUrl ?? \"https://api.mistral.ai\").replace(/^http/, \"ws\")\n return `${base}/v1/audio/transcriptions/realtime?model=${encodeURIComponent(model)}`\n}\n\nconst sessionUpdateFrame = (cfg: Config) =>\n JSON.stringify({\n type: \"session.update\",\n session: {\n audio_format: { encoding: \"pcm_s16le\", sample_rate: 16000 },\n ...(cfg.targetStreamingDelay !== undefined && {\n target_streaming_delay_ms: Duration.toMillis(cfg.targetStreamingDelay),\n }),\n },\n })\n\nconst audioAppendFrame = (bytes: Uint8Array) =>\n JSON.stringify({ type: \"input_audio.append\", audio: Base64.encode(bytes) })\n\nconst audioEndFrame = JSON.stringify({ type: \"input_audio.end\" })\n\n// ---------------------------------------------------------------------------\n// Wire schemas (server → client)\n// ---------------------------------------------------------------------------\n\nconst ServerEvent = Schema.Union([\n Schema.Struct({ type: Schema.Literal(\"session.created\") }),\n Schema.Struct({ type: Schema.Literal(\"session.updated\") }),\n Schema.Struct({ type: Schema.Literal(\"transcription.language\") }),\n Schema.Struct({ type: Schema.Literal(\"transcription.segment\") }),\n Schema.Struct({ type: Schema.Literal(\"transcription.text.delta\"), text: Schema.String }),\n Schema.Struct({ type: Schema.Literal(\"transcription.done\"), text: Schema.String }),\n Schema.Struct({\n type: Schema.Literal(\"error\"),\n error: Schema.Struct({\n message: Schema.String,\n code: Schema.optional(Schema.NullOr(Schema.Number)),\n }),\n }),\n])\nconst decodeServerEvent = Schema.decodeUnknownEffect(ServerEvent)\n\n/** Mutable turn state shared by the frame handler and the silence finalizer. */\ntype TurnState = {\n readonly text: Ref.Ref<string>\n readonly lastActivityMs: Ref.Ref<number>\n}\n\nconst takeFinal = (\n state: TurnState,\n override?: string,\n): Effect.Effect<TranscriptEvent | undefined> =>\n Effect.gen(function* () {\n const acc = yield* Ref.getAndSet(state.text, \"\")\n const text = (override !== undefined && override.length > 0 ? override : acc).trim()\n return text.length > 0 ? { _tag: \"final\", text } : undefined\n })\n\nconst frameToEvent =\n (state: TurnState) =>\n (raw: string): Effect.Effect<TranscriptEvent | undefined> =>\n Effect.gen(function* () {\n const json = yield* JSONL.parseSafe(raw)\n if (json === undefined) return undefined\n const decoded = yield* decodeServerEvent(json).pipe(Effect.option)\n if (Option.isNone(decoded)) return undefined\n return yield* Match.value(decoded.value).pipe(\n Match.when({ type: \"transcription.text.delta\" }, (m) =>\n Effect.gen(function* () {\n const now = yield* Clock.currentTimeMillis\n const next = yield* Ref.updateAndGet(state.text, (t) => t + m.text)\n yield* Ref.set(state.lastActivityMs, now)\n // Cumulative partial so the UI shows the growing sentence.\n return { _tag: \"partial\", text: next } satisfies TranscriptEvent\n }),\n ),\n // End-of-audio: commit whatever's left as the final utterance.\n Match.when({ type: \"transcription.done\" }, (m) => takeFinal(state, m.text)),\n Match.when({ type: \"error\" }, (m) =>\n Effect.succeed<TranscriptEvent>({\n _tag: \"error\",\n ...(m.error.code != null && { code: String(m.error.code) }),\n message: m.error.message,\n }),\n ),\n // session.created / .updated / language / segment: no user-visible event.\n Match.orElse(() => Effect.succeed(undefined)),\n )\n })\n\n// Commits a synthetic final once the deltas go quiet; checks now, then every 150 ms.\nconst silenceFinals = (\n state: TurnState,\n silence: Duration.Duration,\n): Stream.Stream<TranscriptEvent> => {\n const silenceMs = Duration.toMillis(silence)\n return Stream.tick(\"150 millis\").pipe(\n Stream.mapEffect(() =>\n Effect.gen(function* () {\n const now = yield* Clock.currentTimeMillis\n const last = yield* Ref.get(state.lastActivityMs)\n const acc = yield* Ref.get(state.text)\n return acc.trim().length > 0 && now - last >= silenceMs\n ? yield* takeFinal(state)\n : undefined\n }),\n ),\n Stream.filter(Predicate.isNotUndefined),\n )\n}\n\n// ---------------------------------------------------------------------------\n// Stream<Uint8Array> → Stream<TranscriptEvent>\n// ---------------------------------------------------------------------------\n\n// `@types/ws`'s WebSocket extends Node's EventEmitter while\n// `globalThis.WebSocket` extends EventTarget; the browser-style surface\n// Effect's Socket reads (`addEventListener` / `send` / `close`) is identical at\n// runtime, hence the single contained cast.\nconst authedWsConstructor =\n (cfg: Config): Socket.WebSocketConstructor[\"Service\"] =>\n (url) =>\n new WSWebSocket(url, undefined, {\n headers: { Authorization: `Bearer ${Redacted.value(cfg.apiKey)}` },\n }) as unknown as globalThis.WebSocket\n\nexport const streamTranscription =\n (cfg: Config) =>\n <E, R>(\n audioIn: Stream.Stream<Uint8Array, E, R>,\n request: CommonStreamTranscribeRequest,\n ): Stream.Stream<TranscriptEvent, AiError.AiError | E, R> =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* ensureInputFormat(request.inputFormat)\n const socket = yield* Socket.makeWebSocket(buildWsUrl(cfg, request.model)).pipe(\n Effect.provideService(\n Socket.WebSocketConstructor,\n cfg.webSocket ?? authedWsConstructor(cfg),\n ),\n )\n const state: TurnState = {\n text: yield* Ref.make(\"\"),\n lastActivityMs: yield* Ref.make(0),\n }\n const { write } = yield* socket.writer\n const asAiError = WebSocketSession.toAiError(\"mistral\")\n\n const frames = Stream.fromPull(Socket.readerString(socket)).pipe(\n Stream.scoped,\n Stream.catchIf(WebSocketSession.isCleanClose, () => Stream.empty),\n Stream.mapError(asAiError),\n Stream.mapEffect(frameToEvent(state)),\n Stream.filter(Predicate.isNotUndefined),\n )\n const events = Stream.merge(\n frames,\n silenceFinals(state, cfg.utteranceSilence ?? DEFAULT_SILENCE),\n { haltStrategy: \"left\" },\n )\n\n // session.update first, then base64 PCM, then signal end. Writes wait for the reader to connect.\n const outgoing = Stream.concat(\n Stream.concat(\n Stream.make(sessionUpdateFrame(cfg)),\n Stream.map(audioIn, audioAppendFrame),\n ),\n Stream.make(audioEndFrame),\n ).pipe(Stream.mapEffect((frame) => Effect.mapError(write(frame), asAiError)))\n\n // The server's close ends the transcript; the audio running out does not.\n return Stream.merge(events, Stream.drain(outgoing), { haltStrategy: \"left\" })\n }),\n )\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,298 @@
1
+ import { streamTranscription } from "./realtimeStt.mjs";
2
+ import { a as beforeEach, i as beforeAll, n as globalExpect, o as describe, r as afterAll, s as it$2, t as TestRunner } from "./index.C4tKZ0yW-CUBm3Bmz.mjs";
3
+ import { Effect, Exit, Fiber, Redacted, Stream } from "effect";
4
+ import * as Arbitrary from "effect/Arbitrary";
5
+ import * as Cause from "effect/Cause";
6
+ import * as Duration$1 from "effect/Duration";
7
+ import * as Effect$1 from "effect/Effect";
8
+ import * as Exit$1 from "effect/Exit";
9
+ import { constVoid, flow, pipe } from "effect/Function";
10
+ import * as Layer$1 from "effect/Layer";
11
+ import * as Schedule from "effect/Schedule";
12
+ import * as Scope from "effect/Scope";
13
+ import * as TestClock$1 from "effect/testing/TestClock";
14
+ import * as TestConsole from "effect/testing/TestConsole";
15
+ import { TestClock } from "effect/testing";
16
+ 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
+ /**
19
+ * @since 4.0.0
20
+ */
21
+ const getCurrentSuite = TestRunner.getCurrentSuite;
22
+ const runPromise = /*#__PURE__*/ Effect$1.fnUntraced(function* (effect, _ctx) {
23
+ const exit = yield* Effect$1.exit(effect);
24
+ if (Exit$1.isFailure(exit)) {
25
+ const errors = Cause.prettyErrors(exit.cause);
26
+ for (let i = 0; i < errors.length; i++) yield* Effect$1.logError(errors[i]);
27
+ }
28
+ return yield* exit;
29
+ }, (effect, _, ctx) => Effect$1.runPromise(effect, { signal: ctx?.signal }));
30
+ /** @internal */
31
+ const runTest = (ctx) => (effect) => {
32
+ 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;
41
+ };
42
+ const TestEnv = /*#__PURE__*/ Layer$1.mergeAll(TestConsole.layer, /*#__PURE__*/ TestClock$1.layer());
43
+ /** @internal */
44
+ const testOptions = (timeout) => typeof timeout === "number" ? { timeout } : timeout ?? {};
45
+ const hookTimeout = (timeout) => timeout === void 0 ? void 0 : Duration$1.toMillis(Duration$1.fromInputUnsafe(timeout));
46
+ const propertyTestOptions = (timeout) => typeof timeout === "number" ? void 0 : timeout;
47
+ const checkOptions = (timeout) => propertyTestOptions(timeout)?.arbitrary;
48
+ const compileArbitraryInput = (input) => Arbitrary.isArbitrary(input) ? input : Arbitrary.schema(input);
49
+ const makeArbitrary = (arbitraries) => Arbitrary.all(Array.isArray(arbitraries) ? arbitraries.map(compileArbitraryInput) : Object.fromEntries(Object.entries(arbitraries).map(([key, input]) => [key, compileArbitraryInput(input)])));
50
+ const normalizeProperty = (property, value) => Effect$1.catchCause(Effect$1.suspend(() => {
51
+ const output = property(value);
52
+ return Effect$1.isEffect(output) ? output : Effect$1.succeed(output);
53
+ }), (cause) => Cause.hasInterrupts(cause) ? Effect$1.failCause(cause) : Effect$1.fail(cause));
54
+ const runCheck = (ctx, arbitrary, property, options) => runTest(ctx)(Effect$1.flatMapEager(Arbitrary.checkEffect(arbitrary, (value) => normalizeProperty(property, value), options), (result) => {
55
+ const failure = Arbitrary.formatCheckFailure(result);
56
+ return failure === void 0 ? Effect$1.void : Effect$1.die(new Error(failure));
57
+ }));
58
+ const withFixturesOf = (source, f) => Object.defineProperty(f, "toString", { value: () => source.toString() });
59
+ const makeItProxy = (it, overrides) => new Proxy(it, {
60
+ apply(target, thisArg, argArray) {
61
+ return Reflect.apply(target, thisArg, argArray);
62
+ },
63
+ get(target, property, receiver) {
64
+ if (Object.hasOwn(overrides, property)) return Reflect.get(overrides, property);
65
+ return Reflect.get(target, property, receiver);
66
+ }
67
+ });
68
+ const collectTasks = (tasks, acc = []) => {
69
+ for (let i = 0; i < tasks.length; i++) {
70
+ const task = tasks[i];
71
+ if (task.type === "test" && task.mode !== "skip" && task.mode !== "todo") acc.push(task);
72
+ else if (task.tasks !== void 0) collectTasks(task.tasks, acc);
73
+ }
74
+ return acc;
75
+ };
76
+ const registerProp = (it, name, arbitraries, property, timeout) => {
77
+ const arbitrary = makeArbitrary(arbitraries);
78
+ it(name, testOptions(timeout), withFixturesOf(() => {}, (ctx) => runCheck(ctx, arbitrary, (values) => property(values, ctx), checkOptions(timeout))));
79
+ };
80
+ const makeTester = (mapEffect, it) => {
81
+ const run = (ctx, args, self) => pipe(Effect$1.suspend(() => self(...args)), mapEffect, runTest(ctx));
82
+ const register = (test) => (name, self, timeout) => test(name, testOptions(timeout), withFixturesOf(self, (ctx) => run(ctx, [ctx], self)));
83
+ const each = (cases) => (name, self, timeout) => it.for(cases)(name, testOptions(timeout), withFixturesOf(self, (args, ctx) => run(ctx, [args, ctx], self).then(constVoid)));
84
+ const prop = (name, arbitraries, self, timeout) => registerProp(it, name, arbitraries, (values, ctx) => Effect$1.mapEager(mapEffect(Effect$1.suspend(() => self(values, ctx))), (value) => value !== false), timeout);
85
+ return Object.assign(register(it), {
86
+ skip: register(it.skip),
87
+ skipIf: (condition) => register(it.skipIf(condition)),
88
+ runIf: (condition) => register(it.runIf(condition)),
89
+ only: register(it.only),
90
+ each,
91
+ fails: register(it.fails),
92
+ prop
93
+ });
94
+ };
95
+ const makeProp = (it) => (name, arbitraries, self, timeout) => registerProp(it, name, arbitraries, (values, ctx) => self(values, ctx) !== false, timeout);
96
+ const makeLayer = (it) => (layer_, options) => (...args) => {
97
+ const excludeTestServices = options?.excludeTestServices ?? false;
98
+ const withTestEnv = excludeTestServices ? layer_ : Layer$1.provideMerge(layer_, TestEnv);
99
+ const memoMap = options?.memoMap ?? Effect$1.runSync(Layer$1.makeMemoMap);
100
+ const scope = Effect$1.runSync(Scope.make());
101
+ const contextEffect = Layer$1.buildWithMemoMap(withTestEnv, memoMap, scope).pipe(Effect$1.orDie, Effect$1.cached, Effect$1.runSync);
102
+ let closed = false;
103
+ const closeScope = () => {
104
+ if (closed) return Promise.resolve();
105
+ closed = true;
106
+ return runPromise(Scope.close(scope, Exit$1.void));
107
+ };
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
+ });
120
+ if (args.length === 1) {
121
+ const currentSuite = getCurrentSuite();
122
+ const previousTasks = new Set(currentSuite.tasks);
123
+ args[0](methods);
124
+ const blockTasks = collectTasks(currentSuite.tasks.filter((task) => !previousTasks.has(task)));
125
+ if (blockTasks.length === 0) {
126
+ afterAll(() => closeScope(), hookTimeout(options?.timeout));
127
+ return;
128
+ }
129
+ const blockTaskSet = new Set(blockTasks);
130
+ let remaining = blockTasks.length;
131
+ beforeEach(({ onTestFinished, signal, task }) => {
132
+ if (!blockTaskSet.has(task)) return;
133
+ onTestFinished(() => {
134
+ remaining--;
135
+ if (remaining === 0) return closeScope();
136
+ });
137
+ return runPromise(Effect$1.asVoid(contextEffect), { signal });
138
+ }, hookTimeout(options?.timeout));
139
+ afterAll(() => closeScope(), hookTimeout(options?.timeout));
140
+ return;
141
+ }
142
+ const suiteOptions = options?.concurrent === void 0 ? {} : { concurrent: options.concurrent };
143
+ return describe(args[0], suiteOptions, () => {
144
+ beforeAll(() => runPromise(Effect$1.asVoid(contextEffect)), hookTimeout(options?.timeout));
145
+ afterAll(() => closeScope(), hookTimeout(options?.timeout));
146
+ return args[1](methods);
147
+ });
148
+ };
149
+ /** @internal */
150
+ const flakyTest = (self, timeout = Duration$1.seconds(30)) => pipe(self, Effect$1.scoped, Effect$1.sandbox, Effect$1.retry(pipe(Schedule.recurs(10), Schedule.while((_) => Effect$1.succeed(Duration$1.isLessThanOrEqualTo(Duration$1.fromInputUnsafe(_.elapsed), Duration$1.fromInputUnsafe(timeout)))))), Effect$1.orDie);
151
+ /** @internal */
152
+ const makeMethods = (it) => makeItProxy(it, {
153
+ effect: makeTester(flow(Effect$1.scoped, Effect$1.provide(TestEnv)), it),
154
+ live: makeTester(Effect$1.scoped, it),
155
+ flakyTest,
156
+ layer: makeLayer(it),
157
+ prop: makeProp(it)
158
+ });
159
+ /** @internal */
160
+ const it$1 = /*#__PURE__*/ makeMethods(it$2);
161
+ /** @internal */
162
+ const { effect, layer, live, prop } = it$1;
163
+ //#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
+ */
168
+ /**
169
+ * @since 4.0.0
170
+ */
171
+ const it = it$1;
172
+ //#endregion
173
+ //#region src/realtimeStt.test.ts
174
+ const request = {
175
+ model: "voxtral-mini-transcribe-realtime-2602",
176
+ inputFormat: {
177
+ container: "raw",
178
+ encoding: "pcm_s16le",
179
+ sampleRate: 16e3,
180
+ channels: 1
181
+ }
182
+ };
183
+ const sessionCreated = {
184
+ type: "session.created",
185
+ session: {
186
+ request_id: "req_1",
187
+ model: request.model
188
+ }
189
+ };
190
+ const delta = (text) => ({
191
+ type: "transcription.text.delta",
192
+ text
193
+ });
194
+ const start = (server, audioIn) => streamTranscription({
195
+ apiKey: Redacted.make("test-key"),
196
+ webSocket: server.connect
197
+ })(audioIn, request).pipe(Stream.runCollect, Effect.exit, Effect.forkChild);
198
+ /** Run a transcription against `server`, closing it, if asked, once the frames are in. */
199
+ const transcribe = (server, audioIn, close) => Effect.gen(function* () {
200
+ const collector = yield* start(server, audioIn);
201
+ if (close !== void 0) {
202
+ yield* server.greeted;
203
+ yield* server.awaitSent(close.afterSent ?? 0);
204
+ yield* server.close(close.code);
205
+ }
206
+ return yield* Fiber.join(collector);
207
+ });
208
+ describe("Mistral Realtime STT socket", () => {
209
+ it.live("configures the session before any audio, and ends on a clean close", () => Effect.gen(function* () {
210
+ const server = yield* FakeWebSocket.make({ greeting: [
211
+ sessionCreated,
212
+ delta("hel"),
213
+ delta("lo"),
214
+ {
215
+ type: "transcription.done",
216
+ text: "hello",
217
+ model: request.model,
218
+ segments: []
219
+ },
220
+ {
221
+ type: "error",
222
+ error: {
223
+ message: "slow down",
224
+ code: 3001
225
+ }
226
+ }
227
+ ] });
228
+ const exit = yield* transcribe(server, Stream.make(new Uint8Array([1, 2])), {
229
+ code: 1e3,
230
+ afterSent: 3
231
+ });
232
+ globalExpect(exit).toEqual(Exit.succeed([
233
+ {
234
+ _tag: "partial",
235
+ text: "hel"
236
+ },
237
+ {
238
+ _tag: "partial",
239
+ text: "hello"
240
+ },
241
+ {
242
+ _tag: "final",
243
+ text: "hello"
244
+ },
245
+ {
246
+ _tag: "error",
247
+ code: "3001",
248
+ message: "slow down"
249
+ }
250
+ ]));
251
+ const sent = yield* server.sent;
252
+ globalExpect(sent.map((f) => f.type)).toEqual([
253
+ "session.update",
254
+ "input_audio.append",
255
+ "input_audio.end"
256
+ ]);
257
+ }).pipe(Effect.scoped));
258
+ it.live("fails rather than ending when the connection drops", () => Effect.gen(function* () {
259
+ const server = yield* FakeWebSocket.make({ greeting: [sessionCreated] });
260
+ const exit = yield* transcribe(server, Stream.never, { code: 1011 });
261
+ globalExpect(Exit.isFailure(exit) && JSON.stringify(exit.cause)).toContain("Unavailable");
262
+ }).pipe(Effect.scoped));
263
+ it.live("fails with the audio input's own error", () => Effect.gen(function* () {
264
+ const server = yield* FakeWebSocket.make({ greeting: [sessionCreated] });
265
+ const exit = yield* transcribe(server, Stream.fail("microphone unplugged"));
266
+ globalExpect(Exit.isFailure(exit) && JSON.stringify(exit.cause)).toContain("microphone unplugged");
267
+ }).pipe(Effect.scoped));
268
+ it.effect("commits a synthetic final once the deltas go quiet", () => Effect.gen(function* () {
269
+ const server = yield* FakeWebSocket.make({ greeting: [
270
+ sessionCreated,
271
+ delta("hel"),
272
+ delta("lo ")
273
+ ] });
274
+ const collector = yield* start(server, Stream.never);
275
+ yield* server.greeted;
276
+ yield* TestClock.withLive(Effect.sleep("5 millis"));
277
+ yield* TestClock.adjust("1 second");
278
+ yield* server.close(1e3);
279
+ globalExpect(yield* Fiber.join(collector)).toEqual(Exit.succeed([
280
+ {
281
+ _tag: "partial",
282
+ text: "hel"
283
+ },
284
+ {
285
+ _tag: "partial",
286
+ text: "hello "
287
+ },
288
+ {
289
+ _tag: "final",
290
+ text: "hello"
291
+ }
292
+ ]));
293
+ }).pipe(Effect.scoped));
294
+ });
295
+ //#endregion
296
+ export {};
297
+
298
+ //# sourceMappingURL=realtimeStt.test.mjs.map
@@ -0,0 +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,5 +1,6 @@
1
1
  //#region \0rolldown/runtime.js
2
2
  var __defProp = Object.defineProperty;
3
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
3
4
  var __exportAll = (all, no_symbols) => {
4
5
  let target = {};
5
6
  for (var name in all) __defProp(target, name, {
@@ -10,4 +11,4 @@ var __exportAll = (all, no_symbols) => {
10
11
  return target;
11
12
  };
12
13
  //#endregion
13
- export { __exportAll as t };
14
+ export { __exportAll as n, __commonJSMin as t };