@effect-uai/core 0.5.2 → 0.6.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.
- package/README.md +1 -1
- package/dist/{Items-BH8xUkoR.d.mts → Items-pXR5BAgW.d.mts} +12 -12
- package/dist/Items-pXR5BAgW.d.mts.map +1 -0
- package/dist/SandboxError-BvKypkJm.d.mts +158 -0
- package/dist/SandboxError-BvKypkJm.d.mts.map +1 -0
- package/dist/{Tool-87ViKCCO.d.mts → Tool-DP-AwKOf.d.mts} +11 -11
- package/dist/{Tool-87ViKCCO.d.mts.map → Tool-DP-AwKOf.d.mts.map} +1 -1
- package/dist/domain/Items.d.mts +2 -2
- package/dist/domain/Items.mjs +15 -15
- package/dist/domain/Items.mjs.map +1 -1
- package/dist/domain/Turn.d.mts +388 -2
- package/dist/domain/Turn.d.mts.map +1 -0
- package/dist/domain/Turn.mjs +13 -13
- package/dist/domain/Turn.mjs.map +1 -1
- package/dist/index.d.mts +11 -6
- package/dist/index.mjs +8 -3
- package/dist/language-model/LanguageModel.d.mts +5 -5
- package/dist/language-model/LanguageModel.d.mts.map +1 -1
- package/dist/language-model/LanguageModel.mjs.map +1 -1
- package/dist/loop/Loop.d.mts +37 -90
- package/dist/loop/Loop.d.mts.map +1 -1
- package/dist/loop/Loop.mjs +39 -93
- package/dist/loop/Loop.mjs.map +1 -1
- package/dist/loop/Loop.test.mjs +46 -54
- package/dist/loop/Loop.test.mjs.map +1 -1
- package/dist/sandbox/Memory.d.mts +53 -0
- package/dist/sandbox/Memory.d.mts.map +1 -0
- package/dist/sandbox/Memory.mjs +68 -0
- package/dist/sandbox/Memory.mjs.map +1 -0
- package/dist/sandbox/Sandbox.d.mts +717 -0
- package/dist/sandbox/Sandbox.d.mts.map +1 -0
- package/dist/sandbox/Sandbox.mjs +192 -0
- package/dist/sandbox/Sandbox.mjs.map +1 -0
- package/dist/sandbox/SandboxError.d.mts +2 -0
- package/dist/sandbox/SandboxError.mjs +89 -0
- package/dist/sandbox/SandboxError.mjs.map +1 -0
- package/dist/sandbox/SandboxImage.d.mts +28 -0
- package/dist/sandbox/SandboxImage.d.mts.map +1 -0
- package/dist/sandbox/SandboxImage.mjs +32 -0
- package/dist/sandbox/SandboxImage.mjs.map +1 -0
- package/dist/sandbox/SandboxNetwork.d.mts +34 -0
- package/dist/sandbox/SandboxNetwork.d.mts.map +1 -0
- package/dist/sandbox/SandboxNetwork.mjs +36 -0
- package/dist/sandbox/SandboxNetwork.mjs.map +1 -0
- package/dist/speech-synthesizer/SpeechSynthesizer.d.mts +98 -2
- package/dist/speech-synthesizer/SpeechSynthesizer.d.mts.map +1 -1
- package/dist/speech-synthesizer/SpeechSynthesizer.mjs +32 -2
- package/dist/speech-synthesizer/SpeechSynthesizer.mjs.map +1 -1
- package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs +56 -2
- package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs.map +1 -1
- package/dist/testing/MockProvider.d.mts +3 -3
- package/dist/testing/MockProvider.d.mts.map +1 -1
- package/dist/testing/MockProvider.mjs.map +1 -1
- package/dist/testing/MockSandbox.d.mts +604 -0
- package/dist/testing/MockSandbox.d.mts.map +1 -0
- package/dist/testing/MockSandbox.mjs +160 -0
- package/dist/testing/MockSandbox.mjs.map +1 -0
- package/dist/testing/MockSpeechSynthesizer.d.mts +26 -11
- package/dist/testing/MockSpeechSynthesizer.d.mts.map +1 -1
- package/dist/testing/MockSpeechSynthesizer.mjs +75 -51
- package/dist/testing/MockSpeechSynthesizer.mjs.map +1 -1
- package/dist/tool/{Resolvers.d.mts → Approval.d.mts} +27 -27
- package/dist/tool/Approval.d.mts.map +1 -0
- package/dist/tool/{Resolvers.mjs → Approval.mjs} +18 -18
- package/dist/tool/Approval.mjs.map +1 -0
- package/dist/tool/{Resolvers.test.mjs → Approval.test.mjs} +40 -40
- package/dist/tool/Approval.test.mjs.map +1 -0
- package/dist/tool/HistoryCheck.d.mts +6 -6
- package/dist/tool/HistoryCheck.d.mts.map +1 -1
- package/dist/tool/HistoryCheck.mjs +5 -5
- package/dist/tool/HistoryCheck.mjs.map +1 -1
- package/dist/tool/Tool.d.mts +2 -2
- package/dist/tool/Tool.mjs +4 -4
- package/dist/tool/Tool.mjs.map +1 -1
- package/dist/tool/ToolEvent.d.mts +19 -19
- package/dist/tool/ToolEvent.d.mts.map +1 -1
- package/dist/tool/ToolEvent.mjs +8 -8
- package/dist/tool/ToolEvent.mjs.map +1 -1
- package/dist/tool/{Outcome.d.mts → ToolResult.d.mts} +29 -29
- package/dist/tool/ToolResult.d.mts.map +1 -0
- package/dist/tool/ToolResult.mjs +61 -0
- package/dist/tool/ToolResult.mjs.map +1 -0
- package/dist/tool/Toolkit.d.mts +58 -26
- package/dist/tool/Toolkit.d.mts.map +1 -1
- package/dist/tool/Toolkit.mjs +62 -34
- package/dist/tool/Toolkit.mjs.map +1 -1
- package/dist/tool/Toolkit.test.mjs +13 -13
- package/dist/tool/Toolkit.test.mjs.map +1 -1
- package/package.json +31 -7
- package/src/domain/Items.ts +9 -9
- package/src/domain/Turn.ts +16 -16
- package/src/index.ts +7 -2
- package/src/language-model/LanguageModel.ts +3 -3
- package/src/loop/Loop.test.ts +93 -91
- package/src/loop/Loop.ts +72 -170
- package/src/sandbox/Memory.ts +128 -0
- package/src/sandbox/Sandbox.ts +639 -0
- package/src/sandbox/SandboxError.ts +140 -0
- package/src/sandbox/SandboxImage.ts +26 -0
- package/src/sandbox/SandboxNetwork.ts +34 -0
- package/src/speech-synthesizer/SpeechSynthesizer.test.ts +89 -0
- package/src/speech-synthesizer/SpeechSynthesizer.ts +124 -0
- package/src/testing/MockProvider.ts +21 -20
- package/src/testing/MockSandbox.ts +342 -0
- package/src/testing/MockSpeechSynthesizer.ts +137 -77
- package/src/tool/{Resolvers.test.ts → Approval.test.ts} +45 -54
- package/src/tool/{Resolvers.ts → Approval.ts} +25 -25
- package/src/tool/HistoryCheck.ts +10 -13
- package/src/tool/Tool.ts +13 -15
- package/src/tool/ToolEvent.ts +7 -7
- package/src/tool/{Outcome.ts → ToolResult.ts} +20 -20
- package/src/tool/Toolkit.test.ts +13 -13
- package/src/tool/Toolkit.ts +97 -73
- package/dist/Items-BH8xUkoR.d.mts.map +0 -1
- package/dist/Turn-0CwCAyVe.d.mts +0 -388
- package/dist/Turn-0CwCAyVe.d.mts.map +0 -1
- package/dist/tool/Outcome.d.mts.map +0 -1
- package/dist/tool/Outcome.mjs +0 -61
- package/dist/tool/Outcome.mjs.map +0 -1
- package/dist/tool/Resolvers.d.mts.map +0 -1
- package/dist/tool/Resolvers.mjs.map +0 -1
- package/dist/tool/Resolvers.test.mjs.map +0 -1
- /package/dist/tool/{Resolvers.test.d.mts → Approval.test.d.mts} +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { ExecEvent, Sandbox, SandboxCustomImage, SandboxHostnameAllowlist, SandboxId, SandboxKernelSession, SandboxPauseResume, SandboxPortExposure, SandboxPty, SandboxSecretInjection, SandboxSnapshots, SandboxVolumes, SnapshotId, VolumeId } from "../sandbox/Sandbox.mjs";
|
|
2
|
+
import { SandboxExecFailed, SandboxInvalidRequest } from "../sandbox/SandboxError.mjs";
|
|
3
|
+
import { Array, Data, Effect, Layer, Ref, Stream } from "effect";
|
|
4
|
+
//#region src/testing/MockSandbox.ts
|
|
5
|
+
const Call = Data.taggedEnum();
|
|
6
|
+
const defaultExec = {
|
|
7
|
+
exitCode: 0,
|
|
8
|
+
stdout: "",
|
|
9
|
+
stderr: "",
|
|
10
|
+
durationMs: 0
|
|
11
|
+
};
|
|
12
|
+
const toBytes = (contents) => typeof contents === "string" ? new TextEncoder().encode(contents) : contents;
|
|
13
|
+
const seedFs = (files) => new Map(Object.entries(files ?? {}).map(([k, v]) => [k, toBytes(v)]));
|
|
14
|
+
const exhausted = (method, scripted, attempt) => new SandboxInvalidRequest({
|
|
15
|
+
provider: "mock",
|
|
16
|
+
reason: `MockSandbox exhausted: ${scripted} ${method}(s) scripted, but call ${attempt} was made`
|
|
17
|
+
});
|
|
18
|
+
const consume = (cursor, scripted, onMiss) => Ref.getAndUpdate(cursor, (n) => n + 1).pipe(Effect.flatMap((i) => i < scripted.length ? Effect.succeed(scripted[i]) : onMiss(i + 1)));
|
|
19
|
+
const buildInstance = (id, script, fs, execCursor, streamCursor, spawnCursor, record) => ({
|
|
20
|
+
id,
|
|
21
|
+
exec: (request) => record(Call.Exec({
|
|
22
|
+
id,
|
|
23
|
+
request
|
|
24
|
+
})).pipe(Effect.andThen(consume(execCursor, script.execs ?? [], () => Effect.succeed(defaultExec)))),
|
|
25
|
+
execStream: (request) => Stream.unwrap(record(Call.ExecStream({
|
|
26
|
+
id,
|
|
27
|
+
request
|
|
28
|
+
})).pipe(Effect.andThen(consume(streamCursor, script.streams ?? [], () => Effect.succeed([ExecEvent.Complete({
|
|
29
|
+
exitCode: 0,
|
|
30
|
+
durationMs: 0
|
|
31
|
+
})]))), Effect.map((events) => Stream.fromIterable(events)))),
|
|
32
|
+
spawn: (request) => record(Call.Spawn({
|
|
33
|
+
id,
|
|
34
|
+
request
|
|
35
|
+
})).pipe(Effect.andThen(consume(spawnCursor, script.spawns ?? [], (n) => Effect.fail(exhausted("spawn", (script.spawns ?? []).length, n)))), Effect.tap((handle) => Effect.addFinalizer(() => Effect.orDie(handle.kill)))),
|
|
36
|
+
files: {
|
|
37
|
+
read: (path) => record(Call.FileRead({
|
|
38
|
+
id,
|
|
39
|
+
path
|
|
40
|
+
})).pipe(Effect.andThen(Ref.get(fs)), Effect.flatMap((map) => map.has(path) ? Effect.succeed(map.get(path)) : Effect.fail(new SandboxExecFailed({
|
|
41
|
+
provider: "mock",
|
|
42
|
+
reason: `ENOENT: ${path}`,
|
|
43
|
+
raw: void 0
|
|
44
|
+
})))),
|
|
45
|
+
write: (path, contents) => record(Call.FileWrite({
|
|
46
|
+
id,
|
|
47
|
+
path,
|
|
48
|
+
contents
|
|
49
|
+
})).pipe(Effect.andThen(Ref.update(fs, (m) => new Map(m).set(path, toBytes(contents))))),
|
|
50
|
+
remove: (path) => record(Call.FileRemove({
|
|
51
|
+
id,
|
|
52
|
+
path
|
|
53
|
+
})).pipe(Effect.andThen(Ref.update(fs, (m) => {
|
|
54
|
+
const next = new Map(m);
|
|
55
|
+
next.delete(path);
|
|
56
|
+
return next;
|
|
57
|
+
}))),
|
|
58
|
+
mkdir: (path) => record(Call.FileMkdir({
|
|
59
|
+
id,
|
|
60
|
+
path
|
|
61
|
+
})),
|
|
62
|
+
list: (path) => record(Call.FileList({
|
|
63
|
+
id,
|
|
64
|
+
path
|
|
65
|
+
})).pipe(Effect.andThen(Ref.get(fs)), Effect.map((map) => {
|
|
66
|
+
const prefix = path.endsWith("/") ? path : `${path}/`;
|
|
67
|
+
const out = [];
|
|
68
|
+
for (const k of map.keys()) if (k.startsWith(prefix)) out.push({
|
|
69
|
+
path: k,
|
|
70
|
+
kind: "file"
|
|
71
|
+
});
|
|
72
|
+
return out;
|
|
73
|
+
})),
|
|
74
|
+
exists: (path) => record(Call.FileExists({
|
|
75
|
+
id,
|
|
76
|
+
path
|
|
77
|
+
})).pipe(Effect.andThen(Ref.get(fs)), Effect.map((map) => map.has(path)))
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
const buildService = (script, record) => {
|
|
81
|
+
const id = SandboxId(script.id ?? "mock-sandbox");
|
|
82
|
+
const acquireInstance = (sandboxId, onClose) => Effect.gen(function* () {
|
|
83
|
+
const fs = yield* Ref.make(seedFs(script.files));
|
|
84
|
+
const execCursor = yield* Ref.make(0);
|
|
85
|
+
const streamCursor = yield* Ref.make(0);
|
|
86
|
+
const spawnCursor = yield* Ref.make(0);
|
|
87
|
+
yield* Effect.addFinalizer(() => onClose);
|
|
88
|
+
return buildInstance(sandboxId, script, fs, execCursor, streamCursor, spawnCursor, record);
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
create: (request) => record(Call.Create({ request })).pipe(Effect.andThen(acquireInstance(id, record(Call.Destroy({ id }))))),
|
|
92
|
+
attach: (sandboxId) => record(Call.Attach({ id: sandboxId })).pipe(Effect.andThen(acquireInstance(sandboxId, Effect.void))),
|
|
93
|
+
list: Effect.succeed(script.sandboxes ?? []),
|
|
94
|
+
destroy: (sandboxId) => record(Call.Destroy({ id: sandboxId })),
|
|
95
|
+
snapshots: {
|
|
96
|
+
create: (_from, name) => record(name === void 0 ? Call.SnapshotCreate({ id }) : Call.SnapshotCreate({
|
|
97
|
+
id,
|
|
98
|
+
name
|
|
99
|
+
})).pipe(Effect.as(SnapshotId(`mock-snapshot-${name ?? Math.random().toString(36).slice(2, 8)}`))),
|
|
100
|
+
destroy: (sid) => record(Call.SnapshotDestroy({ id: sid })),
|
|
101
|
+
list: Effect.succeed(script.snapshots ?? [])
|
|
102
|
+
},
|
|
103
|
+
volumes: {
|
|
104
|
+
create: (name, options) => record(options?.quotaBytes === void 0 ? Call.VolumeCreate({ name }) : Call.VolumeCreate({
|
|
105
|
+
name,
|
|
106
|
+
quotaBytes: options.quotaBytes
|
|
107
|
+
})).pipe(Effect.as(VolumeId(`mock-volume-${name}`))),
|
|
108
|
+
destroy: (vid) => record(Call.VolumeDestroy({ id: vid })),
|
|
109
|
+
list: Effect.succeed(script.volumes ?? [])
|
|
110
|
+
},
|
|
111
|
+
ports: { expose: (instance, port) => record(Call.ExposePort({
|
|
112
|
+
id: instance.id,
|
|
113
|
+
port
|
|
114
|
+
})).pipe(Effect.as({ url: (script.portUrl ?? ((p) => `http://mock.local:${p}`))(port) })) }
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
const makeRecorderUnsafe = () => {
|
|
118
|
+
const ref = Ref.makeUnsafe([]);
|
|
119
|
+
return {
|
|
120
|
+
record: (call) => Ref.update(ref, Array.append(call)),
|
|
121
|
+
recorder: Ref.get(ref).pipe(Effect.map((calls) => ({ calls })))
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
const allCapabilities = Layer.mergeAll(Layer.succeed(SandboxHostnameAllowlist, void 0), Layer.succeed(SandboxSecretInjection, void 0), Layer.succeed(SandboxSnapshots, void 0), Layer.succeed(SandboxPauseResume, void 0), Layer.succeed(SandboxVolumes, void 0), Layer.succeed(SandboxKernelSession, void 0), Layer.succeed(SandboxPty, void 0), Layer.succeed(SandboxCustomImage, void 0), Layer.succeed(SandboxPortExposure, void 0));
|
|
125
|
+
/**
|
|
126
|
+
* Layer that registers a `MockSandbox` against the `Sandbox` tag, with
|
|
127
|
+
* every capability marker shipped — use when the code under test
|
|
128
|
+
* exercises any of them.
|
|
129
|
+
*/
|
|
130
|
+
const layer = (script = {}) => {
|
|
131
|
+
const { record, recorder } = makeRecorderUnsafe();
|
|
132
|
+
return {
|
|
133
|
+
layer: Layer.merge(Layer.succeed(Sandbox, buildService(script, record)), allCapabilities),
|
|
134
|
+
recorder
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Layer with the `Sandbox` service but no capability markers. Use to
|
|
139
|
+
* verify that consumers calling `Sandbox.snapshot` / `createVolume` /
|
|
140
|
+
* etc. fail at `Effect.provide` with a type error.
|
|
141
|
+
*/
|
|
142
|
+
const layerWithoutCapabilities = (script = {}) => {
|
|
143
|
+
const { record, recorder } = makeRecorderUnsafe();
|
|
144
|
+
return {
|
|
145
|
+
layer: Layer.succeed(Sandbox, buildService(script, record)),
|
|
146
|
+
recorder
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
/** Bare service value + recorder. Use with `Effect.provideService`. */
|
|
150
|
+
const make = (script = {}) => {
|
|
151
|
+
const { record, recorder } = makeRecorderUnsafe();
|
|
152
|
+
return {
|
|
153
|
+
service: buildService(script, record),
|
|
154
|
+
recorder
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
//#endregion
|
|
158
|
+
export { Call, layer, layerWithoutCapabilities, make };
|
|
159
|
+
|
|
160
|
+
//# sourceMappingURL=MockSandbox.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MockSandbox.mjs","names":["SandboxError.SandboxInvalidRequest","SandboxError.SandboxExecFailed","Arr"],"sources":["../../src/testing/MockSandbox.ts"],"sourcesContent":["import { Array as Arr, Data, Effect, Layer, Ref, Stream } from \"effect\"\nimport {\n ExecEvent,\n Sandbox,\n SandboxCustomImage,\n SandboxHostnameAllowlist,\n SandboxKernelSession,\n SandboxPauseResume,\n SandboxPortExposure,\n SandboxPty,\n SandboxSecretInjection,\n SandboxSnapshots,\n SandboxVolumes,\n type CommonCreateRequest,\n type CommonExecRequest,\n type CommonSpawnRequest,\n type ExecResult,\n type FileEntry,\n type ProcessHandle,\n SandboxId,\n type SandboxInstance,\n type SandboxRef,\n type SandboxService,\n SnapshotId,\n VolumeId,\n} from \"../sandbox/Sandbox.js\"\nimport * as SandboxError from \"../sandbox/SandboxError.js\"\n\n// ---------------------------------------------------------------------------\n// Call log\n// ---------------------------------------------------------------------------\n\n/**\n * One entry per side-effecting method call. Tests filter via\n * `Call.$is(...)` / `Call.$match`.\n */\nexport type Call = Data.TaggedEnum<{\n Create: { readonly request: CommonCreateRequest }\n Attach: { readonly id: SandboxId }\n Destroy: { readonly id: SandboxId }\n Exec: { readonly id: SandboxId; readonly request: CommonExecRequest }\n ExecStream: { readonly id: SandboxId; readonly request: CommonExecRequest }\n Spawn: { readonly id: SandboxId; readonly request: CommonSpawnRequest }\n FileRead: { readonly id: SandboxId; readonly path: string }\n FileWrite: {\n readonly id: SandboxId\n readonly path: string\n readonly contents: Uint8Array | string\n }\n FileRemove: { readonly id: SandboxId; readonly path: string }\n FileMkdir: { readonly id: SandboxId; readonly path: string }\n FileList: { readonly id: SandboxId; readonly path: string }\n FileExists: { readonly id: SandboxId; readonly path: string }\n ExposePort: { readonly id: SandboxId; readonly port: number }\n SnapshotCreate: { readonly id: SandboxId; readonly name?: string }\n SnapshotDestroy: { readonly id: SnapshotId }\n VolumeCreate: { readonly name: string; readonly quotaBytes?: number }\n VolumeDestroy: { readonly id: VolumeId }\n}>\n\nexport const Call = Data.taggedEnum<Call>()\n\nexport type MockSandboxRecorder = {\n readonly calls: ReadonlyArray<Call>\n}\n\n// ---------------------------------------------------------------------------\n// Script\n// ---------------------------------------------------------------------------\n\n/**\n * What this mock should hand back per call. Each method has its own\n * scripted queue and a sensible default when the queue is empty.\n * The filesystem is an in-memory mutable map seeded by `files`.\n */\nexport type MockSandboxScript = {\n readonly id?: string\n readonly execs?: ReadonlyArray<ExecResult>\n readonly streams?: ReadonlyArray<ReadonlyArray<ExecEvent>>\n readonly spawns?: ReadonlyArray<ProcessHandle>\n readonly files?: Readonly<Record<string, Uint8Array | string>>\n readonly sandboxes?: ReadonlyArray<SandboxRef>\n readonly snapshots?: ReadonlyArray<{ readonly id: SnapshotId; readonly name?: string }>\n readonly volumes?: ReadonlyArray<{ readonly id: VolumeId; readonly name: string }>\n readonly portUrl?: (port: number) => string\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst defaultExec: ExecResult = { exitCode: 0, stdout: \"\", stderr: \"\", durationMs: 0 }\n\nconst toBytes = (contents: Uint8Array | string) =>\n typeof contents === \"string\" ? new TextEncoder().encode(contents) : contents\n\nconst seedFs = (files: MockSandboxScript[\"files\"]) =>\n new Map(Object.entries(files ?? {}).map(([k, v]) => [k, toBytes(v)]))\n\nconst exhausted = (method: string, scripted: number, attempt: number) =>\n new SandboxError.SandboxInvalidRequest({\n provider: \"mock\",\n reason: `MockSandbox exhausted: ${scripted} ${method}(s) scripted, but call ${attempt} was made`,\n })\n\nconst consume = <A>(\n cursor: Ref.Ref<number>,\n scripted: ReadonlyArray<A>,\n onMiss: (attempt: number) => Effect.Effect<A, SandboxError.SandboxError>,\n) =>\n Ref.getAndUpdate(cursor, (n) => n + 1).pipe(\n Effect.flatMap((i) => (i < scripted.length ? Effect.succeed(scripted[i]!) : onMiss(i + 1))),\n )\n\n// ---------------------------------------------------------------------------\n// Service builder\n// ---------------------------------------------------------------------------\n\nconst buildInstance = (\n id: SandboxId,\n script: MockSandboxScript,\n fs: Ref.Ref<Map<string, Uint8Array>>,\n execCursor: Ref.Ref<number>,\n streamCursor: Ref.Ref<number>,\n spawnCursor: Ref.Ref<number>,\n record: (call: Call) => Effect.Effect<void>,\n): SandboxInstance => ({\n id,\n\n exec: (request) =>\n record(Call.Exec({ id, request })).pipe(\n Effect.andThen(consume(execCursor, script.execs ?? [], () => Effect.succeed(defaultExec))),\n ),\n\n execStream: (request) =>\n Stream.unwrap(\n record(Call.ExecStream({ id, request })).pipe(\n Effect.andThen(\n consume(streamCursor, script.streams ?? [], () =>\n Effect.succeed<ReadonlyArray<ExecEvent>>([\n ExecEvent.Complete({ exitCode: 0, durationMs: 0 }),\n ]),\n ),\n ),\n Effect.map(\n (events) =>\n Stream.fromIterable(events) as Stream.Stream<ExecEvent, SandboxError.SandboxError>,\n ),\n ),\n ),\n\n spawn: (request) =>\n record(Call.Spawn({ id, request })).pipe(\n Effect.andThen(\n consume(spawnCursor, script.spawns ?? [], (n) =>\n Effect.fail(exhausted(\"spawn\", (script.spawns ?? []).length, n)),\n ),\n ),\n Effect.tap((handle) => Effect.addFinalizer(() => Effect.orDie(handle.kill))),\n ),\n\n files: {\n read: (path) =>\n record(Call.FileRead({ id, path })).pipe(\n Effect.andThen(Ref.get(fs)),\n Effect.flatMap((map) =>\n map.has(path)\n ? Effect.succeed(map.get(path)!)\n : Effect.fail(\n new SandboxError.SandboxExecFailed({\n provider: \"mock\",\n reason: `ENOENT: ${path}`,\n raw: undefined,\n }),\n ),\n ),\n ),\n\n write: (path, contents) =>\n record(Call.FileWrite({ id, path, contents })).pipe(\n Effect.andThen(Ref.update(fs, (m) => new Map(m).set(path, toBytes(contents)))),\n ),\n\n remove: (path) =>\n record(Call.FileRemove({ id, path })).pipe(\n Effect.andThen(\n Ref.update(fs, (m) => {\n const next = new Map(m)\n next.delete(path)\n return next\n }),\n ),\n ),\n\n mkdir: (path) => record(Call.FileMkdir({ id, path })),\n\n list: (path) =>\n record(Call.FileList({ id, path })).pipe(\n Effect.andThen(Ref.get(fs)),\n Effect.map((map) => {\n const prefix = path.endsWith(\"/\") ? path : `${path}/`\n const out: Array<FileEntry> = []\n for (const k of map.keys()) {\n if (k.startsWith(prefix)) out.push({ path: k, kind: \"file\" })\n }\n return out\n }),\n ),\n\n exists: (path) =>\n record(Call.FileExists({ id, path })).pipe(\n Effect.andThen(Ref.get(fs)),\n Effect.map((map) => map.has(path)),\n ),\n },\n})\n\nconst buildService = (\n script: MockSandboxScript,\n record: (call: Call) => Effect.Effect<void>,\n): SandboxService => {\n const id = SandboxId(script.id ?? \"mock-sandbox\")\n\n const acquireInstance = (sandboxId: SandboxId, onClose: Effect.Effect<void>) =>\n Effect.gen(function* () {\n const fs = yield* Ref.make(seedFs(script.files))\n const execCursor = yield* Ref.make(0)\n const streamCursor = yield* Ref.make(0)\n const spawnCursor = yield* Ref.make(0)\n yield* Effect.addFinalizer(() => onClose)\n return buildInstance(sandboxId, script, fs, execCursor, streamCursor, spawnCursor, record)\n })\n\n return {\n create: (request) =>\n record(Call.Create({ request })).pipe(\n Effect.andThen(acquireInstance(id, record(Call.Destroy({ id })))),\n ),\n\n attach: (sandboxId) =>\n record(Call.Attach({ id: sandboxId })).pipe(\n // Attach detaches on scope close (no destroy).\n Effect.andThen(acquireInstance(sandboxId, Effect.void)),\n ),\n\n list: Effect.succeed(script.sandboxes ?? []),\n\n destroy: (sandboxId) => record(Call.Destroy({ id: sandboxId })),\n\n snapshots: {\n create: (_from, name) =>\n record(\n name === undefined ? Call.SnapshotCreate({ id }) : Call.SnapshotCreate({ id, name }),\n ).pipe(\n Effect.as(SnapshotId(`mock-snapshot-${name ?? Math.random().toString(36).slice(2, 8)}`)),\n ),\n destroy: (sid) => record(Call.SnapshotDestroy({ id: sid })),\n list: Effect.succeed(script.snapshots ?? []),\n },\n\n volumes: {\n create: (name, options) =>\n record(\n options?.quotaBytes === undefined\n ? Call.VolumeCreate({ name })\n : Call.VolumeCreate({ name, quotaBytes: options.quotaBytes }),\n ).pipe(Effect.as(VolumeId(`mock-volume-${name}`))),\n destroy: (vid) => record(Call.VolumeDestroy({ id: vid })),\n list: Effect.succeed(script.volumes ?? []),\n },\n\n ports: {\n expose: (instance, port) =>\n record(Call.ExposePort({ id: instance.id, port })).pipe(\n Effect.as({ url: (script.portUrl ?? ((p) => `http://mock.local:${p}`))(port) }),\n ),\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Recorder\n// ---------------------------------------------------------------------------\n\nconst makeRecorderUnsafe = () => {\n const ref = Ref.makeUnsafe<ReadonlyArray<Call>>([])\n return {\n record: (call: Call) => Ref.update(ref, Arr.append(call)),\n recorder: Ref.get(ref).pipe(Effect.map((calls): MockSandboxRecorder => ({ calls }))),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nconst allCapabilities = Layer.mergeAll(\n Layer.succeed(SandboxHostnameAllowlist, undefined),\n Layer.succeed(SandboxSecretInjection, undefined),\n Layer.succeed(SandboxSnapshots, undefined),\n Layer.succeed(SandboxPauseResume, undefined),\n Layer.succeed(SandboxVolumes, undefined),\n Layer.succeed(SandboxKernelSession, undefined),\n Layer.succeed(SandboxPty, undefined),\n Layer.succeed(SandboxCustomImage, undefined),\n Layer.succeed(SandboxPortExposure, undefined),\n)\n\n/**\n * Layer that registers a `MockSandbox` against the `Sandbox` tag, with\n * every capability marker shipped — use when the code under test\n * exercises any of them.\n */\nexport const layer = (script: MockSandboxScript = {}) => {\n const { record, recorder } = makeRecorderUnsafe()\n return {\n layer: Layer.merge(Layer.succeed(Sandbox, buildService(script, record)), allCapabilities),\n recorder,\n }\n}\n\n/**\n * Layer with the `Sandbox` service but no capability markers. Use to\n * verify that consumers calling `Sandbox.snapshot` / `createVolume` /\n * etc. fail at `Effect.provide` with a type error.\n */\nexport const layerWithoutCapabilities = (script: MockSandboxScript = {}) => {\n const { record, recorder } = makeRecorderUnsafe()\n return {\n layer: Layer.succeed(Sandbox, buildService(script, record)),\n recorder,\n }\n}\n\n/** Bare service value + recorder. Use with `Effect.provideService`. */\nexport const make = (script: MockSandboxScript = {}) => {\n const { record, recorder } = makeRecorderUnsafe()\n return {\n service: buildService(script, record),\n recorder,\n }\n}\n"],"mappings":";;;;AA4DA,MAAa,OAAO,KAAK,YAAkB;AA+B3C,MAAM,cAA0B;CAAE,UAAU;CAAG,QAAQ;CAAI,QAAQ;CAAI,YAAY;CAAG;AAEtF,MAAM,WAAW,aACf,OAAO,aAAa,WAAW,IAAI,aAAa,CAAC,OAAO,SAAS,GAAG;AAEtE,MAAM,UAAU,UACd,IAAI,IAAI,OAAO,QAAQ,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC;AAEvE,MAAM,aAAa,QAAgB,UAAkB,YACnD,IAAIA,sBAAmC;CACrC,UAAU;CACV,QAAQ,0BAA0B,SAAS,GAAG,OAAO,yBAAyB,QAAQ;CACvF,CAAC;AAEJ,MAAM,WACJ,QACA,UACA,WAEA,IAAI,aAAa,SAAS,MAAM,IAAI,EAAE,CAAC,KACrC,OAAO,SAAS,MAAO,IAAI,SAAS,SAAS,OAAO,QAAQ,SAAS,GAAI,GAAG,OAAO,IAAI,EAAE,CAAE,CAC5F;AAMH,MAAM,iBACJ,IACA,QACA,IACA,YACA,cACA,aACA,YACqB;CACrB;CAEA,OAAO,YACL,OAAO,KAAK,KAAK;EAAE;EAAI;EAAS,CAAC,CAAC,CAAC,KACjC,OAAO,QAAQ,QAAQ,YAAY,OAAO,SAAS,EAAE,QAAQ,OAAO,QAAQ,YAAY,CAAC,CAAC,CAC3F;CAEH,aAAa,YACX,OAAO,OACL,OAAO,KAAK,WAAW;EAAE;EAAI;EAAS,CAAC,CAAC,CAAC,KACvC,OAAO,QACL,QAAQ,cAAc,OAAO,WAAW,EAAE,QACxC,OAAO,QAAkC,CACvC,UAAU,SAAS;EAAE,UAAU;EAAG,YAAY;EAAG,CAAC,CACnD,CAAC,CACH,CACF,EACD,OAAO,KACJ,WACC,OAAO,aAAa,OAAO,CAC9B,CACF,CACF;CAEH,QAAQ,YACN,OAAO,KAAK,MAAM;EAAE;EAAI;EAAS,CAAC,CAAC,CAAC,KAClC,OAAO,QACL,QAAQ,aAAa,OAAO,UAAU,EAAE,GAAG,MACzC,OAAO,KAAK,UAAU,UAAU,OAAO,UAAU,EAAE,EAAE,QAAQ,EAAE,CAAC,CACjE,CACF,EACD,OAAO,KAAK,WAAW,OAAO,mBAAmB,OAAO,MAAM,OAAO,KAAK,CAAC,CAAC,CAC7E;CAEH,OAAO;EACL,OAAO,SACL,OAAO,KAAK,SAAS;GAAE;GAAI;GAAM,CAAC,CAAC,CAAC,KAClC,OAAO,QAAQ,IAAI,IAAI,GAAG,CAAC,EAC3B,OAAO,SAAS,QACd,IAAI,IAAI,KAAK,GACT,OAAO,QAAQ,IAAI,IAAI,KAAK,CAAE,GAC9B,OAAO,KACL,IAAIC,kBAA+B;GACjC,UAAU;GACV,QAAQ,WAAW;GACnB,KAAK,KAAA;GACN,CAAC,CACH,CACN,CACF;EAEH,QAAQ,MAAM,aACZ,OAAO,KAAK,UAAU;GAAE;GAAI;GAAM;GAAU,CAAC,CAAC,CAAC,KAC7C,OAAO,QAAQ,IAAI,OAAO,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,IAAI,MAAM,QAAQ,SAAS,CAAC,CAAC,CAAC,CAC/E;EAEH,SAAS,SACP,OAAO,KAAK,WAAW;GAAE;GAAI;GAAM,CAAC,CAAC,CAAC,KACpC,OAAO,QACL,IAAI,OAAO,KAAK,MAAM;GACpB,MAAM,OAAO,IAAI,IAAI,EAAE;AACvB,QAAK,OAAO,KAAK;AACjB,UAAO;IACP,CACH,CACF;EAEH,QAAQ,SAAS,OAAO,KAAK,UAAU;GAAE;GAAI;GAAM,CAAC,CAAC;EAErD,OAAO,SACL,OAAO,KAAK,SAAS;GAAE;GAAI;GAAM,CAAC,CAAC,CAAC,KAClC,OAAO,QAAQ,IAAI,IAAI,GAAG,CAAC,EAC3B,OAAO,KAAK,QAAQ;GAClB,MAAM,SAAS,KAAK,SAAS,IAAI,GAAG,OAAO,GAAG,KAAK;GACnD,MAAM,MAAwB,EAAE;AAChC,QAAK,MAAM,KAAK,IAAI,MAAM,CACxB,KAAI,EAAE,WAAW,OAAO,CAAE,KAAI,KAAK;IAAE,MAAM;IAAG,MAAM;IAAQ,CAAC;AAE/D,UAAO;IACP,CACH;EAEH,SAAS,SACP,OAAO,KAAK,WAAW;GAAE;GAAI;GAAM,CAAC,CAAC,CAAC,KACpC,OAAO,QAAQ,IAAI,IAAI,GAAG,CAAC,EAC3B,OAAO,KAAK,QAAQ,IAAI,IAAI,KAAK,CAAC,CACnC;EACJ;CACF;AAED,MAAM,gBACJ,QACA,WACmB;CACnB,MAAM,KAAK,UAAU,OAAO,MAAM,eAAe;CAEjD,MAAM,mBAAmB,WAAsB,YAC7C,OAAO,IAAI,aAAa;EACtB,MAAM,KAAK,OAAO,IAAI,KAAK,OAAO,OAAO,MAAM,CAAC;EAChD,MAAM,aAAa,OAAO,IAAI,KAAK,EAAE;EACrC,MAAM,eAAe,OAAO,IAAI,KAAK,EAAE;EACvC,MAAM,cAAc,OAAO,IAAI,KAAK,EAAE;AACtC,SAAO,OAAO,mBAAmB,QAAQ;AACzC,SAAO,cAAc,WAAW,QAAQ,IAAI,YAAY,cAAc,aAAa,OAAO;GAC1F;AAEJ,QAAO;EACL,SAAS,YACP,OAAO,KAAK,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,KAC/B,OAAO,QAAQ,gBAAgB,IAAI,OAAO,KAAK,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAClE;EAEH,SAAS,cACP,OAAO,KAAK,OAAO,EAAE,IAAI,WAAW,CAAC,CAAC,CAAC,KAErC,OAAO,QAAQ,gBAAgB,WAAW,OAAO,KAAK,CAAC,CACxD;EAEH,MAAM,OAAO,QAAQ,OAAO,aAAa,EAAE,CAAC;EAE5C,UAAU,cAAc,OAAO,KAAK,QAAQ,EAAE,IAAI,WAAW,CAAC,CAAC;EAE/D,WAAW;GACT,SAAS,OAAO,SACd,OACE,SAAS,KAAA,IAAY,KAAK,eAAe,EAAE,IAAI,CAAC,GAAG,KAAK,eAAe;IAAE;IAAI;IAAM,CAAC,CACrF,CAAC,KACA,OAAO,GAAG,WAAW,iBAAiB,QAAQ,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE,GAAG,CAAC,CACzF;GACH,UAAU,QAAQ,OAAO,KAAK,gBAAgB,EAAE,IAAI,KAAK,CAAC,CAAC;GAC3D,MAAM,OAAO,QAAQ,OAAO,aAAa,EAAE,CAAC;GAC7C;EAED,SAAS;GACP,SAAS,MAAM,YACb,OACE,SAAS,eAAe,KAAA,IACpB,KAAK,aAAa,EAAE,MAAM,CAAC,GAC3B,KAAK,aAAa;IAAE;IAAM,YAAY,QAAQ;IAAY,CAAC,CAChE,CAAC,KAAK,OAAO,GAAG,SAAS,eAAe,OAAO,CAAC,CAAC;GACpD,UAAU,QAAQ,OAAO,KAAK,cAAc,EAAE,IAAI,KAAK,CAAC,CAAC;GACzD,MAAM,OAAO,QAAQ,OAAO,WAAW,EAAE,CAAC;GAC3C;EAED,OAAO,EACL,SAAS,UAAU,SACjB,OAAO,KAAK,WAAW;GAAE,IAAI,SAAS;GAAI;GAAM,CAAC,CAAC,CAAC,KACjD,OAAO,GAAG,EAAE,MAAM,OAAO,aAAa,MAAM,qBAAqB,MAAM,KAAK,EAAE,CAAC,CAChF,EACJ;EACF;;AAOH,MAAM,2BAA2B;CAC/B,MAAM,MAAM,IAAI,WAAgC,EAAE,CAAC;AACnD,QAAO;EACL,SAAS,SAAe,IAAI,OAAO,KAAKC,MAAI,OAAO,KAAK,CAAC;EACzD,UAAU,IAAI,IAAI,IAAI,CAAC,KAAK,OAAO,KAAK,WAAgC,EAAE,OAAO,EAAE,CAAC;EACrF;;AAOH,MAAM,kBAAkB,MAAM,SAC5B,MAAM,QAAQ,0BAA0B,KAAA,EAAU,EAClD,MAAM,QAAQ,wBAAwB,KAAA,EAAU,EAChD,MAAM,QAAQ,kBAAkB,KAAA,EAAU,EAC1C,MAAM,QAAQ,oBAAoB,KAAA,EAAU,EAC5C,MAAM,QAAQ,gBAAgB,KAAA,EAAU,EACxC,MAAM,QAAQ,sBAAsB,KAAA,EAAU,EAC9C,MAAM,QAAQ,YAAY,KAAA,EAAU,EACpC,MAAM,QAAQ,oBAAoB,KAAA,EAAU,EAC5C,MAAM,QAAQ,qBAAqB,KAAA,EAAU,CAC9C;;;;;;AAOD,MAAa,SAAS,SAA4B,EAAE,KAAK;CACvD,MAAM,EAAE,QAAQ,aAAa,oBAAoB;AACjD,QAAO;EACL,OAAO,MAAM,MAAM,MAAM,QAAQ,SAAS,aAAa,QAAQ,OAAO,CAAC,EAAE,gBAAgB;EACzF;EACD;;;;;;;AAQH,MAAa,4BAA4B,SAA4B,EAAE,KAAK;CAC1E,MAAM,EAAE,QAAQ,aAAa,oBAAoB;AACjD,QAAO;EACL,OAAO,MAAM,QAAQ,SAAS,aAAa,QAAQ,OAAO,CAAC;EAC3D;EACD;;;AAIH,MAAa,QAAQ,SAA4B,EAAE,KAAK;CACtD,MAAM,EAAE,QAAQ,aAAa,oBAAoB;AACjD,QAAO;EACL,SAAS,aAAa,QAAQ,OAAO;EACrC;EACD"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as AudioChunk, t as AudioBlob } from "../Audio-BfCTGnH3.mjs";
|
|
2
|
-
import { CommonStreamSynthesizeRequest, CommonSynthesizeRequest, SpeechSynthesizer, TtsIncrementalText } from "../speech-synthesizer/SpeechSynthesizer.mjs";
|
|
2
|
+
import { CommonStreamSynthesizeRequest, CommonSynthesizeDialogueRequest, CommonSynthesizeRequest, MultiSpeakerTts, SpeechSynthesizer, TtsIncrementalText } from "../speech-synthesizer/SpeechSynthesizer.mjs";
|
|
3
3
|
import { Effect, Layer } from "effect";
|
|
4
4
|
|
|
5
5
|
//#region src/testing/MockSpeechSynthesizer.d.ts
|
|
@@ -7,31 +7,46 @@ type MockSynthesizerRecorder = {
|
|
|
7
7
|
readonly synthesizeCalls: ReadonlyArray<CommonSynthesizeRequest>;
|
|
8
8
|
readonly streamSynthesisCalls: ReadonlyArray<CommonSynthesizeRequest>;
|
|
9
9
|
readonly streamSynthesisFromCalls: ReadonlyArray<CommonStreamSynthesizeRequest>;
|
|
10
|
+
readonly synthesizeDialogueCalls: ReadonlyArray<CommonSynthesizeDialogueRequest>;
|
|
11
|
+
readonly streamSynthesizeDialogueCalls: ReadonlyArray<CommonSynthesizeDialogueRequest>;
|
|
10
12
|
};
|
|
11
13
|
type MockSynthesizerScript = {
|
|
12
14
|
/** One blob per `synthesize` call, consumed in order. */readonly blobs?: ReadonlyArray<AudioBlob>; /** One chunk-list per `streamSynthesis` call, consumed in order. */
|
|
13
15
|
readonly streamSynthesisChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>; /** One chunk-list per `streamSynthesisFrom` call, consumed in order. */
|
|
14
|
-
readonly streamSynthesisFromChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>;
|
|
16
|
+
readonly streamSynthesisFromChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>; /** One blob per `synthesizeDialogue` call, consumed in order. */
|
|
17
|
+
readonly dialogueBlobs?: ReadonlyArray<AudioBlob>; /** One chunk-list per `streamSynthesizeDialogue` call, consumed in order. */
|
|
18
|
+
readonly streamSynthesizeDialogueChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>;
|
|
15
19
|
};
|
|
16
20
|
/**
|
|
17
|
-
* Layer providing
|
|
18
|
-
* `TtsIncrementalText`
|
|
19
|
-
* where code under test exercises
|
|
21
|
+
* Layer providing `SpeechSynthesizer` plus both capability markers
|
|
22
|
+
* (`TtsIncrementalText` and `MultiSpeakerTts`). Use for the common case
|
|
23
|
+
* where code under test exercises any of the optional methods.
|
|
20
24
|
*/
|
|
21
25
|
declare const layer: (script: MockSynthesizerScript) => {
|
|
22
|
-
readonly layer: Layer.Layer<SpeechSynthesizer | TtsIncrementalText>;
|
|
26
|
+
readonly layer: Layer.Layer<SpeechSynthesizer | TtsIncrementalText | MultiSpeakerTts>;
|
|
23
27
|
readonly recorder: Effect.Effect<MockSynthesizerRecorder>;
|
|
24
28
|
};
|
|
25
29
|
/**
|
|
26
30
|
* Variant that omits the `TtsIncrementalText` marker — simulates a
|
|
27
|
-
* provider without incremental-text-in support (e.g. OpenAI
|
|
28
|
-
*
|
|
29
|
-
*
|
|
31
|
+
* provider without incremental-text-in support (e.g. OpenAI). Calls to
|
|
32
|
+
* `streamSynthesisFrom` in code under test should be a compile-time
|
|
33
|
+
* error. `MultiSpeakerTts` is still provided.
|
|
30
34
|
*/
|
|
31
35
|
declare const layerWithoutIncremental: (script: MockSynthesizerScript) => {
|
|
32
|
-
readonly layer: Layer.Layer<SpeechSynthesizer>;
|
|
36
|
+
readonly layer: Layer.Layer<SpeechSynthesizer | MultiSpeakerTts>;
|
|
37
|
+
readonly recorder: Effect.Effect<MockSynthesizerRecorder>;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Variant that omits the `MultiSpeakerTts` marker — simulates a
|
|
41
|
+
* provider without multi-speaker dialogue support (e.g. OpenAI,
|
|
42
|
+
* Inworld). Calls to `synthesizeDialogue` /
|
|
43
|
+
* `streamSynthesizeDialogue` in code under test should be a
|
|
44
|
+
* compile-time error.
|
|
45
|
+
*/
|
|
46
|
+
declare const layerWithoutMultiSpeaker: (script: MockSynthesizerScript) => {
|
|
47
|
+
readonly layer: Layer.Layer<SpeechSynthesizer | TtsIncrementalText>;
|
|
33
48
|
readonly recorder: Effect.Effect<MockSynthesizerRecorder>;
|
|
34
49
|
};
|
|
35
50
|
//#endregion
|
|
36
|
-
export { MockSynthesizerRecorder, MockSynthesizerScript, layer, layerWithoutIncremental };
|
|
51
|
+
export { MockSynthesizerRecorder, MockSynthesizerScript, layer, layerWithoutIncremental, layerWithoutMultiSpeaker };
|
|
37
52
|
//# sourceMappingURL=MockSpeechSynthesizer.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MockSpeechSynthesizer.d.mts","names":[],"sources":["../../src/testing/MockSpeechSynthesizer.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"MockSpeechSynthesizer.d.mts","names":[],"sources":["../../src/testing/MockSpeechSynthesizer.ts"],"mappings":";;;;;KAaY,uBAAA;EAAA,SACD,eAAA,EAAiB,aAAA,CAAc,uBAAA;EAAA,SAC/B,oBAAA,EAAsB,aAAA,CAAc,uBAAA;EAAA,SACpC,wBAAA,EAA0B,aAAA,CAAc,6BAAA;EAAA,SACxC,uBAAA,EAAyB,aAAA,CAAc,+BAAA;EAAA,SACvC,6BAAA,EAA+B,aAAA,CAAc,+BAAA;AAAA;AAAA,KAG5C,qBAAA;EANqB,kEAQtB,KAAA,GAAQ,aAAA,CAAc,SAAA,GAPI;EAAA,SAS1B,qBAAA,GAAwB,aAAA,CAAc,aAAA,CAAc,UAAA,IAR3B;EAAA,SAUzB,yBAAA,GAA4B,aAAA,CAAc,aAAA,CAAc,UAAA,IATzB;EAAA,SAW/B,aAAA,GAAgB,aAAA,CAAc,SAAA,GAXc;EAAA,SAa5C,8BAAA,GAAiC,aAAA,CAAc,aAAA,CAAc,UAAA;AAAA;;;;;;cAwI3D,KAAA,GACX,MAAA,EAAQ,qBAAA;EAAA,SAEC,KAAA,EAAO,KAAA,CAAM,KAAA,CAAM,iBAAA,GAAoB,kBAAA,GAAqB,eAAA;EAAA,SAC5D,QAAA,EAAU,MAAA,CAAO,MAAA,CAAO,uBAAA;AAAA;;;;;;;cAkBtB,uBAAA,GACX,MAAA,EAAQ,qBAAA;EAAA,SAEC,KAAA,EAAO,KAAA,CAAM,KAAA,CAAM,iBAAA,GAAoB,eAAA;EAAA,SACvC,QAAA,EAAU,MAAA,CAAO,MAAA,CAAO,uBAAA;AAAA;;;;;;;;cAiBtB,wBAAA,GACX,MAAA,EAAQ,qBAAA;EAAA,SAEC,KAAA,EAAO,KAAA,CAAM,KAAA,CAAM,iBAAA,GAAoB,kBAAA;EAAA,SACvC,QAAA,EAAU,MAAA,CAAO,MAAA,CAAO,uBAAA;AAAA"}
|
|
@@ -1,95 +1,119 @@
|
|
|
1
1
|
import { InvalidRequest } from "../domain/AiError.mjs";
|
|
2
|
-
import { SpeechSynthesizer, TtsIncrementalText } from "../speech-synthesizer/SpeechSynthesizer.mjs";
|
|
2
|
+
import { MultiSpeakerTts, SpeechSynthesizer, TtsIncrementalText } from "../speech-synthesizer/SpeechSynthesizer.mjs";
|
|
3
3
|
import { Effect, Layer, Ref, Stream } from "effect";
|
|
4
4
|
//#region src/testing/MockSpeechSynthesizer.ts
|
|
5
|
+
const exhausted = (label, scripted, call) => new InvalidRequest({
|
|
6
|
+
provider: "mock",
|
|
7
|
+
raw: `MockSpeechSynthesizer exhausted: ${scripted} ${label} scripted, but call ${call} was made`
|
|
8
|
+
});
|
|
5
9
|
const makeService = (script, record) => Effect.gen(function* () {
|
|
6
10
|
const bCursor = yield* Ref.make(0);
|
|
7
11
|
const ssCursor = yield* Ref.make(0);
|
|
8
12
|
const ssfCursor = yield* Ref.make(0);
|
|
13
|
+
const sdCursor = yield* Ref.make(0);
|
|
14
|
+
const ssdCursor = yield* Ref.make(0);
|
|
9
15
|
return {
|
|
10
16
|
synthesize: (request) => Effect.gen(function* () {
|
|
11
17
|
yield* record.synthesize(request);
|
|
12
18
|
const i = yield* Ref.getAndUpdate(bCursor, (n) => n + 1);
|
|
13
19
|
const scripted = script.blobs ?? [];
|
|
14
|
-
if (i >= scripted.length) return yield*
|
|
15
|
-
provider: "mock",
|
|
16
|
-
raw: `MockSpeechSynthesizer exhausted: ${scripted.length} blobs scripted, but call ${i + 1} was made`
|
|
17
|
-
});
|
|
20
|
+
if (i >= scripted.length) return yield* exhausted("blobs", scripted.length, i + 1);
|
|
18
21
|
return scripted[i];
|
|
19
22
|
}),
|
|
20
23
|
streamSynthesis: (request) => Stream.unwrap(Effect.gen(function* () {
|
|
21
24
|
yield* record.streamSynthesis(request);
|
|
22
25
|
const i = yield* Ref.getAndUpdate(ssCursor, (n) => n + 1);
|
|
23
26
|
const scripted = script.streamSynthesisChunks ?? [];
|
|
24
|
-
if (i >= scripted.length) return Stream.fail(
|
|
25
|
-
provider: "mock",
|
|
26
|
-
raw: `MockSpeechSynthesizer exhausted: ${scripted.length} streamSynthesis lists scripted, but call ${i + 1} was made`
|
|
27
|
-
}));
|
|
27
|
+
if (i >= scripted.length) return Stream.fail(exhausted("streamSynthesis lists", scripted.length, i + 1));
|
|
28
28
|
return Stream.fromIterable(scripted[i]);
|
|
29
29
|
})),
|
|
30
30
|
streamSynthesisFrom: (textIn, request) => Stream.unwrap(Effect.gen(function* () {
|
|
31
31
|
yield* record.streamSynthesisFrom(request);
|
|
32
32
|
const i = yield* Ref.getAndUpdate(ssfCursor, (n) => n + 1);
|
|
33
33
|
const scripted = script.streamSynthesisFromChunks ?? [];
|
|
34
|
-
if (i >= scripted.length) return Stream.fail(
|
|
35
|
-
provider: "mock",
|
|
36
|
-
raw: `MockSpeechSynthesizer exhausted: ${scripted.length} streamSynthesisFrom lists scripted, but call ${i + 1} was made`
|
|
37
|
-
}));
|
|
34
|
+
if (i >= scripted.length) return Stream.fail(exhausted("streamSynthesisFrom lists", scripted.length, i + 1));
|
|
38
35
|
return Stream.drain(textIn).pipe(Stream.concat(Stream.fromIterable(scripted[i])));
|
|
36
|
+
})),
|
|
37
|
+
synthesizeDialogue: (request) => Effect.gen(function* () {
|
|
38
|
+
yield* record.synthesizeDialogue(request);
|
|
39
|
+
const i = yield* Ref.getAndUpdate(sdCursor, (n) => n + 1);
|
|
40
|
+
const scripted = script.dialogueBlobs ?? [];
|
|
41
|
+
if (i >= scripted.length) return yield* exhausted("dialogueBlobs", scripted.length, i + 1);
|
|
42
|
+
return scripted[i];
|
|
43
|
+
}),
|
|
44
|
+
streamSynthesizeDialogue: (request) => Stream.unwrap(Effect.gen(function* () {
|
|
45
|
+
yield* record.streamSynthesizeDialogue(request);
|
|
46
|
+
const i = yield* Ref.getAndUpdate(ssdCursor, (n) => n + 1);
|
|
47
|
+
const scripted = script.streamSynthesizeDialogueChunks ?? [];
|
|
48
|
+
if (i >= scripted.length) return Stream.fail(exhausted("streamSynthesizeDialogue lists", scripted.length, i + 1));
|
|
49
|
+
return Stream.fromIterable(scripted[i]);
|
|
39
50
|
}))
|
|
40
51
|
};
|
|
41
52
|
});
|
|
53
|
+
const newBuffers = () => ({
|
|
54
|
+
bCalls: Ref.makeUnsafe([]),
|
|
55
|
+
ssCalls: Ref.makeUnsafe([]),
|
|
56
|
+
ssfCalls: Ref.makeUnsafe([]),
|
|
57
|
+
sdCalls: Ref.makeUnsafe([]),
|
|
58
|
+
ssdCalls: Ref.makeUnsafe([])
|
|
59
|
+
});
|
|
60
|
+
const recordFor = (b) => ({
|
|
61
|
+
synthesize: (req) => Ref.update(b.bCalls, (xs) => [...xs, req]),
|
|
62
|
+
streamSynthesis: (req) => Ref.update(b.ssCalls, (xs) => [...xs, req]),
|
|
63
|
+
streamSynthesisFrom: (req) => Ref.update(b.ssfCalls, (xs) => [...xs, req]),
|
|
64
|
+
synthesizeDialogue: (req) => Ref.update(b.sdCalls, (xs) => [...xs, req]),
|
|
65
|
+
streamSynthesizeDialogue: (req) => Ref.update(b.ssdCalls, (xs) => [...xs, req])
|
|
66
|
+
});
|
|
67
|
+
const recorderEffect = (b) => Effect.gen(function* () {
|
|
68
|
+
return {
|
|
69
|
+
synthesizeCalls: yield* Ref.get(b.bCalls),
|
|
70
|
+
streamSynthesisCalls: yield* Ref.get(b.ssCalls),
|
|
71
|
+
streamSynthesisFromCalls: yield* Ref.get(b.ssfCalls),
|
|
72
|
+
synthesizeDialogueCalls: yield* Ref.get(b.sdCalls),
|
|
73
|
+
streamSynthesizeDialogueCalls: yield* Ref.get(b.ssdCalls)
|
|
74
|
+
};
|
|
75
|
+
});
|
|
42
76
|
/**
|
|
43
|
-
* Layer providing
|
|
44
|
-
* `TtsIncrementalText`
|
|
45
|
-
* where code under test exercises
|
|
77
|
+
* Layer providing `SpeechSynthesizer` plus both capability markers
|
|
78
|
+
* (`TtsIncrementalText` and `MultiSpeakerTts`). Use for the common case
|
|
79
|
+
* where code under test exercises any of the optional methods.
|
|
46
80
|
*/
|
|
47
81
|
const layer = (script) => {
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const ssfCalls = Ref.makeUnsafe([]);
|
|
51
|
-
const synthesizerLayer = Layer.effect(SpeechSynthesizer, makeService(script, {
|
|
52
|
-
synthesize: (req) => Ref.update(bCalls, (xs) => [...xs, req]),
|
|
53
|
-
streamSynthesis: (req) => Ref.update(ssCalls, (xs) => [...xs, req]),
|
|
54
|
-
streamSynthesisFrom: (req) => Ref.update(ssfCalls, (xs) => [...xs, req])
|
|
55
|
-
}));
|
|
82
|
+
const buffers = newBuffers();
|
|
83
|
+
const synthesizerLayer = Layer.effect(SpeechSynthesizer, makeService(script, recordFor(buffers)));
|
|
56
84
|
return {
|
|
57
|
-
layer: Layer.
|
|
58
|
-
recorder:
|
|
59
|
-
return {
|
|
60
|
-
synthesizeCalls: yield* Ref.get(bCalls),
|
|
61
|
-
streamSynthesisCalls: yield* Ref.get(ssCalls),
|
|
62
|
-
streamSynthesisFromCalls: yield* Ref.get(ssfCalls)
|
|
63
|
-
};
|
|
64
|
-
})
|
|
85
|
+
layer: Layer.mergeAll(synthesizerLayer, Layer.succeed(TtsIncrementalText, void 0), Layer.succeed(MultiSpeakerTts, void 0)),
|
|
86
|
+
recorder: recorderEffect(buffers)
|
|
65
87
|
};
|
|
66
88
|
};
|
|
67
89
|
/**
|
|
68
90
|
* Variant that omits the `TtsIncrementalText` marker — simulates a
|
|
69
|
-
* provider without incremental-text-in support (e.g. OpenAI
|
|
70
|
-
*
|
|
71
|
-
*
|
|
91
|
+
* provider without incremental-text-in support (e.g. OpenAI). Calls to
|
|
92
|
+
* `streamSynthesisFrom` in code under test should be a compile-time
|
|
93
|
+
* error. `MultiSpeakerTts` is still provided.
|
|
72
94
|
*/
|
|
73
95
|
const layerWithoutIncremental = (script) => {
|
|
74
|
-
const
|
|
75
|
-
const ssCalls = Ref.makeUnsafe([]);
|
|
76
|
-
const ssfCalls = Ref.makeUnsafe([]);
|
|
96
|
+
const buffers = newBuffers();
|
|
77
97
|
return {
|
|
78
|
-
layer: Layer.effect(SpeechSynthesizer, makeService(script,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
98
|
+
layer: Layer.merge(Layer.effect(SpeechSynthesizer, makeService(script, recordFor(buffers))), Layer.succeed(MultiSpeakerTts, void 0)),
|
|
99
|
+
recorder: recorderEffect(buffers)
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Variant that omits the `MultiSpeakerTts` marker — simulates a
|
|
104
|
+
* provider without multi-speaker dialogue support (e.g. OpenAI,
|
|
105
|
+
* Inworld). Calls to `synthesizeDialogue` /
|
|
106
|
+
* `streamSynthesizeDialogue` in code under test should be a
|
|
107
|
+
* compile-time error.
|
|
108
|
+
*/
|
|
109
|
+
const layerWithoutMultiSpeaker = (script) => {
|
|
110
|
+
const buffers = newBuffers();
|
|
111
|
+
return {
|
|
112
|
+
layer: Layer.merge(Layer.effect(SpeechSynthesizer, makeService(script, recordFor(buffers))), Layer.succeed(TtsIncrementalText, void 0)),
|
|
113
|
+
recorder: recorderEffect(buffers)
|
|
90
114
|
};
|
|
91
115
|
};
|
|
92
116
|
//#endregion
|
|
93
|
-
export { layer, layerWithoutIncremental };
|
|
117
|
+
export { layer, layerWithoutIncremental, layerWithoutMultiSpeaker };
|
|
94
118
|
|
|
95
119
|
//# sourceMappingURL=MockSpeechSynthesizer.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MockSpeechSynthesizer.mjs","names":["AiError.InvalidRequest"],"sources":["../../src/testing/MockSpeechSynthesizer.ts"],"sourcesContent":["import { Effect, Layer, Ref, Stream } from \"effect\"\nimport type { AudioBlob, AudioChunk } from \"../domain/Audio.js\"\nimport * as AiError from \"../domain/AiError.js\"\nimport {\n SpeechSynthesizer,\n TtsIncrementalText,\n type CommonStreamSynthesizeRequest,\n type CommonSynthesizeRequest,\n type SpeechSynthesizerService,\n} from \"../speech-synthesizer/SpeechSynthesizer.js\"\n\nexport type MockSynthesizerRecorder = {\n readonly synthesizeCalls: ReadonlyArray<CommonSynthesizeRequest>\n readonly streamSynthesisCalls: ReadonlyArray<CommonSynthesizeRequest>\n readonly streamSynthesisFromCalls: ReadonlyArray<CommonStreamSynthesizeRequest>\n}\n\nexport type MockSynthesizerScript = {\n /** One blob per `synthesize` call, consumed in order. */\n readonly blobs?: ReadonlyArray<AudioBlob>\n /** One chunk-list per `streamSynthesis` call, consumed in order. */\n readonly streamSynthesisChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>\n /** One chunk-list per `streamSynthesisFrom` call, consumed in order. */\n readonly streamSynthesisFromChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>\n}\n\nconst makeService = (\n script: MockSynthesizerScript,\n record: {\n readonly synthesize: (req: CommonSynthesizeRequest) => Effect.Effect<void>\n readonly streamSynthesis: (req: CommonSynthesizeRequest) => Effect.Effect<void>\n readonly streamSynthesisFrom: (req: CommonStreamSynthesizeRequest) => Effect.Effect<void>\n },\n) =>\n Effect.gen(function* () {\n const bCursor = yield* Ref.make(0)\n const ssCursor = yield* Ref.make(0)\n const ssfCursor = yield* Ref.make(0)\n const service: SpeechSynthesizerService = {\n synthesize: (request) =>\n Effect.gen(function* () {\n yield* record.synthesize(request)\n const i = yield* Ref.getAndUpdate(bCursor, (n) => n + 1)\n const scripted = script.blobs ?? []\n if (i >= scripted.length) {\n return yield* new AiError.InvalidRequest({\n provider: \"mock\",\n raw: `MockSpeechSynthesizer exhausted: ${scripted.length} blobs scripted, but call ${i + 1} was made`,\n })\n }\n return scripted[i]!\n }),\n streamSynthesis: (request) =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* record.streamSynthesis(request)\n const i = yield* Ref.getAndUpdate(ssCursor, (n) => n + 1)\n const scripted = script.streamSynthesisChunks ?? []\n if (i >= scripted.length) {\n return Stream.fail(\n new AiError.InvalidRequest({\n provider: \"mock\",\n raw: `MockSpeechSynthesizer exhausted: ${scripted.length} streamSynthesis lists scripted, but call ${i + 1} was made`,\n }),\n )\n }\n return Stream.fromIterable(scripted[i]!)\n }),\n ),\n streamSynthesisFrom: <E, R>(\n textIn: Stream.Stream<string, E, R>,\n request: CommonStreamSynthesizeRequest,\n ): Stream.Stream<AudioChunk, AiError.AiError | E, R> =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* record.streamSynthesisFrom(request)\n const i = yield* Ref.getAndUpdate(ssfCursor, (n) => n + 1)\n const scripted = script.streamSynthesisFromChunks ?? []\n if (i >= scripted.length) {\n const exhausted: Stream.Stream<AudioChunk, AiError.AiError | E, R> = Stream.fail(\n new AiError.InvalidRequest({\n provider: \"mock\",\n raw: `MockSpeechSynthesizer exhausted: ${scripted.length} streamSynthesisFrom lists scripted, but call ${i + 1} was made`,\n }),\n )\n return exhausted\n }\n // Drain the input text fully before emitting scripted audio chunks,\n // so consumers can assert on what text was pushed.\n return Stream.drain(textIn).pipe(Stream.concat(Stream.fromIterable(scripted[i]!)))\n }),\n ),\n }\n return service\n })\n\n/**\n * Layer providing the `SpeechSynthesizer` service AND the\n * `TtsIncrementalText` capability marker. Use for the common case\n * where code under test exercises `streamSynthesisFrom`.\n */\nexport const layer = (\n script: MockSynthesizerScript,\n): {\n readonly layer: Layer.Layer<SpeechSynthesizer | TtsIncrementalText>\n readonly recorder: Effect.Effect<MockSynthesizerRecorder>\n} => {\n const bCalls = Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeRequest>>([])\n const ssCalls = Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeRequest>>([])\n const ssfCalls = Ref.makeUnsafe<ReadonlyArray<CommonStreamSynthesizeRequest>>([])\n const synthesizerLayer = Layer.effect(\n SpeechSynthesizer,\n makeService(script, {\n synthesize: (req) => Ref.update(bCalls, (xs) => [...xs, req]),\n streamSynthesis: (req) => Ref.update(ssCalls, (xs) => [...xs, req]),\n streamSynthesisFrom: (req) => Ref.update(ssfCalls, (xs) => [...xs, req]),\n }),\n )\n const live = Layer.merge(synthesizerLayer, Layer.succeed(TtsIncrementalText, undefined))\n return {\n layer: live,\n recorder: Effect.gen(function* () {\n const synthesizeCalls = yield* Ref.get(bCalls)\n const streamSynthesisCalls = yield* Ref.get(ssCalls)\n const streamSynthesisFromCalls = yield* Ref.get(ssfCalls)\n return { synthesizeCalls, streamSynthesisCalls, streamSynthesisFromCalls }\n }),\n }\n}\n\n/**\n * Variant that omits the `TtsIncrementalText` marker — simulates a\n * provider without incremental-text-in support (e.g. OpenAI, AWS\n * Polly non-Generative). Calls to `streamSynthesisFrom` in code under\n * test should be a compile-time error.\n */\nexport const layerWithoutIncremental = (\n script: MockSynthesizerScript,\n): {\n readonly layer: Layer.Layer<SpeechSynthesizer>\n readonly recorder: Effect.Effect<MockSynthesizerRecorder>\n} => {\n const bCalls = Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeRequest>>([])\n const ssCalls = Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeRequest>>([])\n const ssfCalls = Ref.makeUnsafe<ReadonlyArray<CommonStreamSynthesizeRequest>>([])\n const live = Layer.effect(\n SpeechSynthesizer,\n makeService(script, {\n synthesize: (req) => Ref.update(bCalls, (xs) => [...xs, req]),\n streamSynthesis: (req) => Ref.update(ssCalls, (xs) => [...xs, req]),\n streamSynthesisFrom: (req) => Ref.update(ssfCalls, (xs) => [...xs, req]),\n }),\n )\n return {\n layer: live,\n recorder: Effect.gen(function* () {\n const synthesizeCalls = yield* Ref.get(bCalls)\n const streamSynthesisCalls = yield* Ref.get(ssCalls)\n const streamSynthesisFromCalls = yield* Ref.get(ssfCalls)\n return { synthesizeCalls, streamSynthesisCalls, streamSynthesisFromCalls }\n }),\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MockSpeechSynthesizer.mjs","names":["AiError.InvalidRequest"],"sources":["../../src/testing/MockSpeechSynthesizer.ts"],"sourcesContent":["import { Effect, Layer, Ref, Stream } from \"effect\"\nimport type { AudioBlob, AudioChunk } from \"../domain/Audio.js\"\nimport * as AiError from \"../domain/AiError.js\"\nimport {\n type CommonStreamSynthesizeRequest,\n type CommonSynthesizeDialogueRequest,\n type CommonSynthesizeRequest,\n MultiSpeakerTts,\n SpeechSynthesizer,\n type SpeechSynthesizerService,\n TtsIncrementalText,\n} from \"../speech-synthesizer/SpeechSynthesizer.js\"\n\nexport type MockSynthesizerRecorder = {\n readonly synthesizeCalls: ReadonlyArray<CommonSynthesizeRequest>\n readonly streamSynthesisCalls: ReadonlyArray<CommonSynthesizeRequest>\n readonly streamSynthesisFromCalls: ReadonlyArray<CommonStreamSynthesizeRequest>\n readonly synthesizeDialogueCalls: ReadonlyArray<CommonSynthesizeDialogueRequest>\n readonly streamSynthesizeDialogueCalls: ReadonlyArray<CommonSynthesizeDialogueRequest>\n}\n\nexport type MockSynthesizerScript = {\n /** One blob per `synthesize` call, consumed in order. */\n readonly blobs?: ReadonlyArray<AudioBlob>\n /** One chunk-list per `streamSynthesis` call, consumed in order. */\n readonly streamSynthesisChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>\n /** One chunk-list per `streamSynthesisFrom` call, consumed in order. */\n readonly streamSynthesisFromChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>\n /** One blob per `synthesizeDialogue` call, consumed in order. */\n readonly dialogueBlobs?: ReadonlyArray<AudioBlob>\n /** One chunk-list per `streamSynthesizeDialogue` call, consumed in order. */\n readonly streamSynthesizeDialogueChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>\n}\n\ntype Record = {\n readonly synthesize: (req: CommonSynthesizeRequest) => Effect.Effect<void>\n readonly streamSynthesis: (req: CommonSynthesizeRequest) => Effect.Effect<void>\n readonly streamSynthesisFrom: (req: CommonStreamSynthesizeRequest) => Effect.Effect<void>\n readonly synthesizeDialogue: (req: CommonSynthesizeDialogueRequest) => Effect.Effect<void>\n readonly streamSynthesizeDialogue: (req: CommonSynthesizeDialogueRequest) => Effect.Effect<void>\n}\n\nconst exhausted = (label: string, scripted: number, call: number): AiError.AiError =>\n new AiError.InvalidRequest({\n provider: \"mock\",\n raw: `MockSpeechSynthesizer exhausted: ${scripted} ${label} scripted, but call ${call} was made`,\n })\n\nconst makeService = (script: MockSynthesizerScript, record: Record) =>\n Effect.gen(function* () {\n const bCursor = yield* Ref.make(0)\n const ssCursor = yield* Ref.make(0)\n const ssfCursor = yield* Ref.make(0)\n const sdCursor = yield* Ref.make(0)\n const ssdCursor = yield* Ref.make(0)\n const service: SpeechSynthesizerService = {\n synthesize: (request) =>\n Effect.gen(function* () {\n yield* record.synthesize(request)\n const i = yield* Ref.getAndUpdate(bCursor, (n) => n + 1)\n const scripted = script.blobs ?? []\n if (i >= scripted.length) return yield* exhausted(\"blobs\", scripted.length, i + 1)\n return scripted[i]!\n }),\n streamSynthesis: (request) =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* record.streamSynthesis(request)\n const i = yield* Ref.getAndUpdate(ssCursor, (n) => n + 1)\n const scripted = script.streamSynthesisChunks ?? []\n if (i >= scripted.length) {\n return Stream.fail(exhausted(\"streamSynthesis lists\", scripted.length, i + 1))\n }\n return Stream.fromIterable(scripted[i]!)\n }),\n ),\n streamSynthesisFrom: <E, R>(\n textIn: Stream.Stream<string, E, R>,\n request: CommonStreamSynthesizeRequest,\n ): Stream.Stream<AudioChunk, AiError.AiError | E, R> =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* record.streamSynthesisFrom(request)\n const i = yield* Ref.getAndUpdate(ssfCursor, (n) => n + 1)\n const scripted = script.streamSynthesisFromChunks ?? []\n if (i >= scripted.length) {\n const fail: Stream.Stream<AudioChunk, AiError.AiError | E, R> = Stream.fail(\n exhausted(\"streamSynthesisFrom lists\", scripted.length, i + 1),\n )\n return fail\n }\n return Stream.drain(textIn).pipe(Stream.concat(Stream.fromIterable(scripted[i]!)))\n }),\n ),\n synthesizeDialogue: (request) =>\n Effect.gen(function* () {\n yield* record.synthesizeDialogue(request)\n const i = yield* Ref.getAndUpdate(sdCursor, (n) => n + 1)\n const scripted = script.dialogueBlobs ?? []\n if (i >= scripted.length) {\n return yield* exhausted(\"dialogueBlobs\", scripted.length, i + 1)\n }\n return scripted[i]!\n }),\n streamSynthesizeDialogue: (request) =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* record.streamSynthesizeDialogue(request)\n const i = yield* Ref.getAndUpdate(ssdCursor, (n) => n + 1)\n const scripted = script.streamSynthesizeDialogueChunks ?? []\n if (i >= scripted.length) {\n return Stream.fail(\n exhausted(\"streamSynthesizeDialogue lists\", scripted.length, i + 1),\n )\n }\n return Stream.fromIterable(scripted[i]!)\n }),\n ),\n }\n return service\n })\n\ntype CallBuffers = {\n readonly bCalls: Ref.Ref<ReadonlyArray<CommonSynthesizeRequest>>\n readonly ssCalls: Ref.Ref<ReadonlyArray<CommonSynthesizeRequest>>\n readonly ssfCalls: Ref.Ref<ReadonlyArray<CommonStreamSynthesizeRequest>>\n readonly sdCalls: Ref.Ref<ReadonlyArray<CommonSynthesizeDialogueRequest>>\n readonly ssdCalls: Ref.Ref<ReadonlyArray<CommonSynthesizeDialogueRequest>>\n}\n\nconst newBuffers = (): CallBuffers => ({\n bCalls: Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeRequest>>([]),\n ssCalls: Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeRequest>>([]),\n ssfCalls: Ref.makeUnsafe<ReadonlyArray<CommonStreamSynthesizeRequest>>([]),\n sdCalls: Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeDialogueRequest>>([]),\n ssdCalls: Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeDialogueRequest>>([]),\n})\n\nconst recordFor = (b: CallBuffers): Record => ({\n synthesize: (req) => Ref.update(b.bCalls, (xs) => [...xs, req]),\n streamSynthesis: (req) => Ref.update(b.ssCalls, (xs) => [...xs, req]),\n streamSynthesisFrom: (req) => Ref.update(b.ssfCalls, (xs) => [...xs, req]),\n synthesizeDialogue: (req) => Ref.update(b.sdCalls, (xs) => [...xs, req]),\n streamSynthesizeDialogue: (req) => Ref.update(b.ssdCalls, (xs) => [...xs, req]),\n})\n\nconst recorderEffect = (b: CallBuffers): Effect.Effect<MockSynthesizerRecorder> =>\n Effect.gen(function* () {\n const synthesizeCalls = yield* Ref.get(b.bCalls)\n const streamSynthesisCalls = yield* Ref.get(b.ssCalls)\n const streamSynthesisFromCalls = yield* Ref.get(b.ssfCalls)\n const synthesizeDialogueCalls = yield* Ref.get(b.sdCalls)\n const streamSynthesizeDialogueCalls = yield* Ref.get(b.ssdCalls)\n return {\n synthesizeCalls,\n streamSynthesisCalls,\n streamSynthesisFromCalls,\n synthesizeDialogueCalls,\n streamSynthesizeDialogueCalls,\n }\n })\n\n/**\n * Layer providing `SpeechSynthesizer` plus both capability markers\n * (`TtsIncrementalText` and `MultiSpeakerTts`). Use for the common case\n * where code under test exercises any of the optional methods.\n */\nexport const layer = (\n script: MockSynthesizerScript,\n): {\n readonly layer: Layer.Layer<SpeechSynthesizer | TtsIncrementalText | MultiSpeakerTts>\n readonly recorder: Effect.Effect<MockSynthesizerRecorder>\n} => {\n const buffers = newBuffers()\n const synthesizerLayer = Layer.effect(SpeechSynthesizer, makeService(script, recordFor(buffers)))\n const live = Layer.mergeAll(\n synthesizerLayer,\n Layer.succeed(TtsIncrementalText, undefined),\n Layer.succeed(MultiSpeakerTts, undefined),\n )\n return { layer: live, recorder: recorderEffect(buffers) }\n}\n\n/**\n * Variant that omits the `TtsIncrementalText` marker — simulates a\n * provider without incremental-text-in support (e.g. OpenAI). Calls to\n * `streamSynthesisFrom` in code under test should be a compile-time\n * error. `MultiSpeakerTts` is still provided.\n */\nexport const layerWithoutIncremental = (\n script: MockSynthesizerScript,\n): {\n readonly layer: Layer.Layer<SpeechSynthesizer | MultiSpeakerTts>\n readonly recorder: Effect.Effect<MockSynthesizerRecorder>\n} => {\n const buffers = newBuffers()\n const live = Layer.merge(\n Layer.effect(SpeechSynthesizer, makeService(script, recordFor(buffers))),\n Layer.succeed(MultiSpeakerTts, undefined),\n )\n return { layer: live, recorder: recorderEffect(buffers) }\n}\n\n/**\n * Variant that omits the `MultiSpeakerTts` marker — simulates a\n * provider without multi-speaker dialogue support (e.g. OpenAI,\n * Inworld). Calls to `synthesizeDialogue` /\n * `streamSynthesizeDialogue` in code under test should be a\n * compile-time error.\n */\nexport const layerWithoutMultiSpeaker = (\n script: MockSynthesizerScript,\n): {\n readonly layer: Layer.Layer<SpeechSynthesizer | TtsIncrementalText>\n readonly recorder: Effect.Effect<MockSynthesizerRecorder>\n} => {\n const buffers = newBuffers()\n const live = Layer.merge(\n Layer.effect(SpeechSynthesizer, makeService(script, recordFor(buffers))),\n Layer.succeed(TtsIncrementalText, undefined),\n )\n return { layer: live, recorder: recorderEffect(buffers) }\n}\n"],"mappings":"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"}
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ToolResult } from "./
|
|
1
|
+
import { v as ToolCall } from "../Items-pXR5BAgW.mjs";
|
|
2
|
+
import { ToolResult } from "./ToolResult.mjs";
|
|
3
3
|
import { ToolEvent } from "./ToolEvent.mjs";
|
|
4
4
|
import { Data, Effect, Queue, Scope, Stream } from "effect";
|
|
5
5
|
import * as _$effect_Unify0 from "effect/Unify";
|
|
6
6
|
|
|
7
|
-
//#region src/tool/
|
|
8
|
-
declare namespace
|
|
9
|
-
export {
|
|
7
|
+
//#region src/tool/Approval.d.ts
|
|
8
|
+
declare namespace Approval_d_exports {
|
|
9
|
+
export { ApprovalDecision, ApprovalMapEntry, ToolCallPlan, Verdict, approvalRequested, approve, fromMap, fromQueue, reject, splitApprovalDecisions };
|
|
10
10
|
}
|
|
11
11
|
type ToolCallPlan = {
|
|
12
|
-
readonly approved: ReadonlyArray<
|
|
12
|
+
readonly approved: ReadonlyArray<ToolCall>;
|
|
13
13
|
readonly rejected: ReadonlyArray<ToolResult>;
|
|
14
14
|
};
|
|
15
|
-
type
|
|
15
|
+
type ApprovalDecision = Data.TaggedEnum<{
|
|
16
16
|
Approved: {
|
|
17
|
-
readonly call:
|
|
17
|
+
readonly call: ToolCall;
|
|
18
18
|
};
|
|
19
19
|
Rejected: {
|
|
20
20
|
readonly result: ToolResult;
|
|
21
21
|
};
|
|
22
22
|
}>;
|
|
23
|
-
declare const
|
|
23
|
+
declare const ApprovalDecision: {
|
|
24
24
|
readonly Approved: Data.TaggedEnum.ConstructorFrom<{
|
|
25
25
|
readonly _tag: "Approved";
|
|
26
|
-
readonly call:
|
|
26
|
+
readonly call: ToolCall;
|
|
27
27
|
}, "_tag">;
|
|
28
28
|
readonly Rejected: Data.TaggedEnum.ConstructorFrom<{
|
|
29
29
|
readonly _tag: "Rejected";
|
|
@@ -31,7 +31,7 @@ declare const ToolCallDecision: {
|
|
|
31
31
|
}, "_tag">;
|
|
32
32
|
readonly $is: <Tag extends "Approved" | "Rejected">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
33
33
|
readonly _tag: "Approved";
|
|
34
|
-
readonly call:
|
|
34
|
+
readonly call: ToolCall;
|
|
35
35
|
}, {
|
|
36
36
|
readonly _tag: Tag;
|
|
37
37
|
}> | Extract<{
|
|
@@ -44,7 +44,7 @@ declare const ToolCallDecision: {
|
|
|
44
44
|
<Cases extends {
|
|
45
45
|
readonly Approved: (args: {
|
|
46
46
|
readonly _tag: "Approved";
|
|
47
|
-
readonly call:
|
|
47
|
+
readonly call: ToolCall;
|
|
48
48
|
}) => any;
|
|
49
49
|
readonly Rejected: (args: {
|
|
50
50
|
readonly _tag: "Rejected";
|
|
@@ -52,7 +52,7 @@ declare const ToolCallDecision: {
|
|
|
52
52
|
}) => any;
|
|
53
53
|
}>(cases: Cases): (value: {
|
|
54
54
|
readonly _tag: "Approved";
|
|
55
|
-
readonly call:
|
|
55
|
+
readonly call: ToolCall;
|
|
56
56
|
} | {
|
|
57
57
|
readonly _tag: "Rejected";
|
|
58
58
|
readonly result: ToolResult;
|
|
@@ -60,7 +60,7 @@ declare const ToolCallDecision: {
|
|
|
60
60
|
<Cases extends {
|
|
61
61
|
readonly Approved: (args: {
|
|
62
62
|
readonly _tag: "Approved";
|
|
63
|
-
readonly call:
|
|
63
|
+
readonly call: ToolCall;
|
|
64
64
|
}) => any;
|
|
65
65
|
readonly Rejected: (args: {
|
|
66
66
|
readonly _tag: "Rejected";
|
|
@@ -68,17 +68,17 @@ declare const ToolCallDecision: {
|
|
|
68
68
|
}) => any;
|
|
69
69
|
}>(value: {
|
|
70
70
|
readonly _tag: "Approved";
|
|
71
|
-
readonly call:
|
|
71
|
+
readonly call: ToolCall;
|
|
72
72
|
} | {
|
|
73
73
|
readonly _tag: "Rejected";
|
|
74
74
|
readonly result: ToolResult;
|
|
75
75
|
}, cases: Cases): _$effect_Unify0.Unify<ReturnType<Cases["Approved" | "Rejected"]>>;
|
|
76
76
|
};
|
|
77
77
|
};
|
|
78
|
-
declare const approve: (call:
|
|
79
|
-
declare const reject: (result: ToolResult) =>
|
|
80
|
-
declare const
|
|
81
|
-
declare const approvalRequested: (call:
|
|
78
|
+
declare const approve: (call: ToolCall) => ApprovalDecision;
|
|
79
|
+
declare const reject: (result: ToolResult) => ApprovalDecision;
|
|
80
|
+
declare const splitApprovalDecisions: (decisions: ReadonlyArray<ApprovalDecision>) => ToolCallPlan;
|
|
81
|
+
declare const approvalRequested: (call: ToolCall) => ToolEvent;
|
|
82
82
|
type Verdict = {
|
|
83
83
|
readonly call_id: string;
|
|
84
84
|
readonly decision: "approve" | "deny";
|
|
@@ -87,12 +87,12 @@ type Verdict = {
|
|
|
87
87
|
/**
|
|
88
88
|
* Queue-backed approval planner. Safe calls are returned immediately in
|
|
89
89
|
* `approved`; gated calls emit `ApprovalRequested` events and later produce
|
|
90
|
-
* one `
|
|
90
|
+
* one `ApprovalDecision` when their matching verdict arrives.
|
|
91
91
|
*/
|
|
92
|
-
declare const
|
|
93
|
-
readonly approved: ReadonlyArray<
|
|
94
|
-
readonly decisions: Stream.Stream<
|
|
95
|
-
readonly
|
|
92
|
+
declare const fromQueue: (predicate: (call: ToolCall) => boolean, verdicts: Queue.Dequeue<Verdict>) => (calls: ReadonlyArray<ToolCall>) => Effect.Effect<{
|
|
93
|
+
readonly approved: ReadonlyArray<ToolCall>;
|
|
94
|
+
readonly decisions: Stream.Stream<ApprovalDecision>;
|
|
95
|
+
readonly approvalRequests: Stream.Stream<ToolEvent>;
|
|
96
96
|
}, never, Scope.Scope>;
|
|
97
97
|
type ApprovalMapEntry = {
|
|
98
98
|
readonly decision: "approve";
|
|
@@ -100,7 +100,7 @@ type ApprovalMapEntry = {
|
|
|
100
100
|
readonly decision: "deny";
|
|
101
101
|
readonly reason?: string;
|
|
102
102
|
};
|
|
103
|
-
declare const
|
|
103
|
+
declare const fromMap: (predicate: (call: ToolCall) => boolean, approvals: ReadonlyMap<string, ApprovalMapEntry>) => (calls: ReadonlyArray<ToolCall>) => ToolCallPlan;
|
|
104
104
|
//#endregion
|
|
105
|
-
export {
|
|
106
|
-
//# sourceMappingURL=
|
|
105
|
+
export { ApprovalDecision, ApprovalMapEntry, ToolCallPlan, Verdict, approvalRequested, approve, fromMap, fromQueue, reject, splitApprovalDecisions, Approval_d_exports as t };
|
|
106
|
+
//# sourceMappingURL=Approval.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Approval.d.mts","names":[],"sources":["../../src/tool/Approval.ts"],"mappings":";;;;;;;;;;KAYY,YAAA;EAAA,SACD,QAAA,EAAU,aAAA,CAAc,QAAA;EAAA,SACxB,QAAA,EAAU,aAAA,CAAc,UAAA;AAAA;AAAA,KAGvB,gBAAA,GAAmB,IAAA,CAAK,UAAA;EAClC,QAAA;IAAA,SAAqB,IAAA,EAAM,QAAA;EAAA;EAC3B,QAAA;IAAA,SAAqB,MAAA,EAAQ,UAAA;EAAA;AAAA;AAAA,cAGlB,gBAAA;EAAA;;mBAJgB,QAAA;EAAA;EAAA;;qBACE,UAAA;EAAA;EAAA;;mBADF,QAAA;EAAA;IAAA;;;qBACE,UAAA;EAAA;IAAA;;;;;;uBADF,QAAA;MAAA;MAAA;;yBACE,UAAA;MAAA;IAAA;;qBADF,QAAA;IAAA;MAAA;uBACE,UAAA;IAAA;;;;uBADF,QAAA;MAAA;MAAA;;yBACE,UAAA;MAAA;IAAA;;qBADF,QAAA;IAAA;MAAA;uBACE,UAAA;IAAA;;;cAKlB,OAAA,GAAW,IAAA,EAAM,QAAA,KAAW,gBAAA;AAAA,cAE5B,MAAA,GAAU,MAAA,EAAQ,UAAA,KAAa,gBAAA;AAAA,cAG/B,sBAAA,GAA0B,SAAA,EAAW,aAAA,CAAc,gBAAA,MAAoB,YAAA;AAAA,cASvE,iBAAA,GAAqB,IAAA,EAAM,QAAA,KAAW,SAAA;AAAA,KAWvC,OAAA;EAAA,SACD,OAAA;EAAA,SACA,QAAA;EAAA,SACA,MAAA;AAAA;;;;;;cAQE,SAAA,GACV,SAAA,GAAY,IAAA,EAAM,QAAA,cAAsB,QAAA,EAAU,KAAA,CAAM,OAAA,CAAQ,OAAA,OAE/D,KAAA,EAAO,aAAA,CAAc,QAAA,MACpB,MAAA,CAAO,MAAA;EAAA,SAEG,QAAA,EAAU,aAAA,CAAc,QAAA;EAAA,SACxB,SAAA,EAAW,MAAA,CAAO,MAAA,CAAO,gBAAA;EAAA,SACzB,gBAAA,EAAkB,MAAA,CAAO,MAAA,CAAO,SAAA;AAAA,UAG3C,KAAA,CAAM,KAAA;AAAA,KAkDE,gBAAA;EAAA,SACG,QAAA;AAAA;EAAA,SACA,QAAA;EAAA,SAA2B,MAAA;AAAA;AAAA,cAE7B,OAAA,GACV,SAAA,GAAY,IAAA,EAAM,QAAA,cAAsB,SAAA,EAAW,WAAA,SAAoB,gBAAA,OACvE,KAAA,EAAO,aAAA,CAAc,QAAA,MAAY,YAAA"}
|