@effect-uai/core 0.5.2 → 0.7.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/{Audio-BfCTGnH3.d.mts → Audio-DPlTsNdz.d.mts} +3 -2
- package/dist/Audio-DPlTsNdz.d.mts.map +1 -0
- package/dist/{Items-BH8xUkoR.d.mts → Items-D8UaXQx_.d.mts} +15 -15
- package/dist/Items-D8UaXQx_.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-CiFXYjmR.d.mts} +11 -11
- package/dist/{Tool-87ViKCCO.d.mts.map → Tool-CiFXYjmR.d.mts.map} +1 -1
- package/dist/capabilities/Capabilities.d.mts +78 -0
- package/dist/capabilities/Capabilities.d.mts.map +1 -0
- package/dist/capabilities/Capabilities.mjs +65 -0
- package/dist/capabilities/Capabilities.mjs.map +1 -0
- package/dist/domain/Audio.d.mts +1 -1
- package/dist/domain/Audio.mjs.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/Music.d.mts +131 -72
- package/dist/domain/Music.d.mts.map +1 -1
- package/dist/domain/Music.mjs +36 -9
- package/dist/domain/Music.mjs.map +1 -1
- package/dist/domain/Transcript.d.mts +8 -2
- package/dist/domain/Transcript.d.mts.map +1 -1
- package/dist/domain/Transcript.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/embedding-model/Embedding.d.mts +12 -2
- package/dist/embedding-model/Embedding.d.mts.map +1 -1
- package/dist/embedding-model/Embedding.mjs.map +1 -1
- package/dist/embedding-model/EmbeddingModel.d.mts +36 -23
- package/dist/embedding-model/EmbeddingModel.d.mts.map +1 -1
- package/dist/embedding-model/EmbeddingModel.mjs +20 -1
- package/dist/embedding-model/EmbeddingModel.mjs.map +1 -1
- package/dist/embedding-model/EmbeddingModel.test.mjs +15 -3
- package/dist/embedding-model/EmbeddingModel.test.mjs.map +1 -1
- package/dist/index.d.mts +13 -7
- package/dist/index.mjs +9 -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/music-generator/MusicGenerator.d.mts +48 -25
- package/dist/music-generator/MusicGenerator.d.mts.map +1 -1
- package/dist/music-generator/MusicGenerator.mjs +17 -9
- package/dist/music-generator/MusicGenerator.mjs.map +1 -1
- package/dist/music-generator/MusicGenerator.test.mjs +81 -56
- package/dist/music-generator/MusicGenerator.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 +84 -3
- 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 +59 -5
- package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs.map +1 -1
- package/dist/testing/MockMusicGenerator.d.mts +12 -7
- package/dist/testing/MockMusicGenerator.d.mts.map +1 -1
- package/dist/testing/MockMusicGenerator.mjs +4 -3
- package/dist/testing/MockMusicGenerator.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 +27 -12
- 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/dist/transcriber/Transcriber.d.mts +11 -10
- package/dist/transcriber/Transcriber.d.mts.map +1 -1
- package/dist/transcriber/Transcriber.mjs.map +1 -1
- package/dist/transcriber/Transcriber.test.mjs +3 -3
- package/dist/transcriber/Transcriber.test.mjs.map +1 -1
- package/package.json +35 -7
- package/src/capabilities/Capabilities.ts +81 -0
- package/src/domain/Audio.ts +2 -1
- package/src/domain/Items.ts +9 -9
- package/src/domain/Music.ts +142 -68
- package/src/domain/Transcript.ts +7 -1
- package/src/domain/Turn.ts +16 -16
- package/src/embedding-model/Embedding.ts +11 -0
- package/src/embedding-model/EmbeddingModel.test.ts +15 -4
- package/src/embedding-model/EmbeddingModel.ts +48 -21
- package/src/index.ts +8 -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/music-generator/MusicGenerator.test.ts +78 -53
- package/src/music-generator/MusicGenerator.ts +65 -24
- 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 +92 -3
- package/src/speech-synthesizer/SpeechSynthesizer.ts +109 -0
- package/src/testing/MockMusicGenerator.ts +28 -18
- 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/src/transcriber/Transcriber.test.ts +3 -3
- package/src/transcriber/Transcriber.ts +10 -7
- package/dist/Audio-BfCTGnH3.d.mts.map +0 -1
- 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
package/src/tool/Toolkit.test.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Context, Effect, Layer, Schema, Stream } from "effect"
|
|
2
2
|
import { describe, expect, expectTypeOf, it } from "vitest"
|
|
3
|
-
import type {
|
|
3
|
+
import type { ToolCall } from "../domain/Items.js"
|
|
4
4
|
import { isOutput } from "./ToolEvent.js"
|
|
5
|
-
import {
|
|
5
|
+
import { isOk } from "./ToolResult.js"
|
|
6
6
|
import * as Tool from "./Tool.js"
|
|
7
7
|
import * as Toolkit from "./Toolkit.js"
|
|
8
8
|
|
|
9
|
-
describe("
|
|
9
|
+
describe("Tool.toDescriptors", () => {
|
|
10
10
|
const GetWeatherInput = Schema.Struct({ city: Schema.String })
|
|
11
11
|
|
|
12
12
|
const getWeather = Tool.make({
|
|
@@ -17,7 +17,7 @@ describe("Toolkit.toDescriptors", () => {
|
|
|
17
17
|
})
|
|
18
18
|
|
|
19
19
|
it("renders the input schema as a JSON Schema document", () => {
|
|
20
|
-
const [desc] =
|
|
20
|
+
const [desc] = Tool.toDescriptors([getWeather])
|
|
21
21
|
expect(desc?.name).toBe("get_weather")
|
|
22
22
|
expect(desc?.description).toBe("Look up the current temperature for a city.")
|
|
23
23
|
expect(desc?.inputSchema).toMatchObject({
|
|
@@ -41,16 +41,16 @@ describe("Toolkit.toDescriptors", () => {
|
|
|
41
41
|
inputSchema: Tool.fromEffectSchema(GetWeatherInput),
|
|
42
42
|
run: () => Effect.succeed({}),
|
|
43
43
|
})
|
|
44
|
-
const [s, l] =
|
|
44
|
+
const [s, l] = Tool.toDescriptors([strictTool, looseTool])
|
|
45
45
|
expect(s?.strict).toBe(true)
|
|
46
46
|
expect(l).not.toHaveProperty("strict")
|
|
47
47
|
})
|
|
48
48
|
})
|
|
49
49
|
|
|
50
|
-
describe("Toolkit.
|
|
50
|
+
describe("Toolkit.run - tools with R requirements", () => {
|
|
51
51
|
// Two distinct services, modelling the "typed per-tool context" use case
|
|
52
52
|
// (cf. AI SDK 7's `toolsContext`). In Effect each tool declares its R, the
|
|
53
|
-
// compiler enforces it, and `
|
|
53
|
+
// compiler enforces it, and `run` surfaces the union for the caller
|
|
54
54
|
// to provide via Layer.
|
|
55
55
|
type WeatherApiKeyShape = { readonly key: string }
|
|
56
56
|
class WeatherApiKey extends Context.Service<WeatherApiKey, WeatherApiKeyShape>()(
|
|
@@ -84,7 +84,7 @@ describe("Toolkit.executeAll - tools with R requirements", () => {
|
|
|
84
84
|
}),
|
|
85
85
|
})
|
|
86
86
|
|
|
87
|
-
const call = (name: string, id: string):
|
|
87
|
+
const call = (name: string, id: string): ToolCall => ({
|
|
88
88
|
type: "function_call",
|
|
89
89
|
call_id: id,
|
|
90
90
|
name,
|
|
@@ -92,7 +92,7 @@ describe("Toolkit.executeAll - tools with R requirements", () => {
|
|
|
92
92
|
})
|
|
93
93
|
|
|
94
94
|
it("propagates each tool's R into the resulting Stream's requirements", () => {
|
|
95
|
-
const stream = Toolkit.
|
|
95
|
+
const stream = Toolkit.run([getWeather, getCoords], [])
|
|
96
96
|
expectTypeOf(stream).toEqualTypeOf<
|
|
97
97
|
Stream.Stream<import("./ToolEvent.js").ToolEvent, never, WeatherApiKey | GeoApiKey>
|
|
98
98
|
>()
|
|
@@ -104,7 +104,7 @@ describe("Toolkit.executeAll - tools with R requirements", () => {
|
|
|
104
104
|
Layer.succeed(GeoApiKey, { key: "geo-456" }),
|
|
105
105
|
)
|
|
106
106
|
|
|
107
|
-
const program = Toolkit.
|
|
107
|
+
const program = Toolkit.run(
|
|
108
108
|
[getWeather, getCoords],
|
|
109
109
|
[call("get_weather", "c1"), call("get_coords", "c2")],
|
|
110
110
|
).pipe(Stream.runCollect, Effect.provide(layer))
|
|
@@ -115,11 +115,11 @@ describe("Toolkit.executeAll - tools with R requirements", () => {
|
|
|
115
115
|
|
|
116
116
|
const w = byCall.get("c1")
|
|
117
117
|
const g = byCall.get("c2")
|
|
118
|
-
expect(w !== undefined &&
|
|
118
|
+
expect(w !== undefined && isOk(w) && w.value).toEqual({
|
|
119
119
|
source: "weather",
|
|
120
120
|
key: "weather-123",
|
|
121
121
|
})
|
|
122
|
-
expect(g !== undefined &&
|
|
122
|
+
expect(g !== undefined && isOk(g) && g.value).toEqual({
|
|
123
123
|
source: "geo",
|
|
124
124
|
key: "geo-456",
|
|
125
125
|
})
|
|
@@ -132,7 +132,7 @@ describe("Toolkit.executeAll - tools with R requirements", () => {
|
|
|
132
132
|
inputSchema: Tool.fromEffectSchema(Empty),
|
|
133
133
|
run: () => Effect.succeed(0),
|
|
134
134
|
})
|
|
135
|
-
const stream = Toolkit.
|
|
135
|
+
const stream = Toolkit.run([plain], [])
|
|
136
136
|
expectTypeOf(stream).toEqualTypeOf<
|
|
137
137
|
Stream.Stream<import("./ToolEvent.js").ToolEvent, never, never>
|
|
138
138
|
>()
|
package/src/tool/Toolkit.ts
CHANGED
|
@@ -1,66 +1,31 @@
|
|
|
1
1
|
import { Array as Arr, Effect, Function, Ref, Schema, Stream } from "effect"
|
|
2
2
|
import * as Loop from "../loop/Loop.js"
|
|
3
|
-
import type {
|
|
3
|
+
import type { ToolCall, HistoryItem } from "../domain/Items.js"
|
|
4
|
+
import { appendToHistory, type Turn } from "../domain/Turn.js"
|
|
4
5
|
import {
|
|
5
|
-
type
|
|
6
|
+
type AnyTool,
|
|
6
7
|
type AnyPlainTool,
|
|
7
8
|
type AnyStreamingTool,
|
|
8
9
|
isStreamingTool,
|
|
9
10
|
type StreamingTool,
|
|
10
11
|
type Tool,
|
|
11
|
-
type ToolDescriptor,
|
|
12
12
|
} from "./Tool.js"
|
|
13
|
-
import { ToolResult, executionError,
|
|
13
|
+
import { ToolResult, executionError, failed, toToolCallOutput } from "./ToolResult.js"
|
|
14
14
|
import { ToolEvent } from "./ToolEvent.js"
|
|
15
15
|
import { isOutput } from "./ToolEvent.js"
|
|
16
16
|
|
|
17
|
-
export type AnyTool = Tool<string, any, any, any>
|
|
18
|
-
|
|
19
|
-
export type Toolkit<Tools extends ReadonlyArray<AnyTool>> = {
|
|
20
|
-
readonly tools: Tools
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type ToolsR<Tools extends ReadonlyArray<AnyTool>> =
|
|
24
|
-
Tools[number] extends Tool<any, any, any, infer R> ? R : never
|
|
25
|
-
|
|
26
17
|
/**
|
|
27
18
|
* Union of every tool's `R` requirements in a mixed plain + streaming array.
|
|
28
|
-
* Used by `
|
|
29
|
-
*
|
|
19
|
+
* Used by `run` to surface the services tools need at the recipe level, so
|
|
20
|
+
* the loop's stream type carries them through to `Effect.provide`.
|
|
30
21
|
*/
|
|
31
|
-
export type ToolKindR<Tools extends ReadonlyArray<
|
|
22
|
+
export type ToolKindR<Tools extends ReadonlyArray<AnyTool<any>>> =
|
|
32
23
|
Tools[number] extends StreamingTool<any, any, any, any, infer R>
|
|
33
24
|
? R
|
|
34
25
|
: Tools[number] extends Tool<any, any, any, infer R>
|
|
35
26
|
? R
|
|
36
27
|
: never
|
|
37
28
|
|
|
38
|
-
export const make = <const Tools extends ReadonlyArray<AnyTool>>(tools: Tools): Toolkit<Tools> => ({
|
|
39
|
-
tools,
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Render every tool in a toolkit to a provider-agnostic descriptor.
|
|
44
|
-
* `inputSchema` is the JSON Schema document produced by the tool's
|
|
45
|
-
* Standard Schema converter (draft 2020-12).
|
|
46
|
-
*/
|
|
47
|
-
export const toDescriptors = <Tools extends ReadonlyArray<AnyTool>>(
|
|
48
|
-
toolkit: Toolkit<Tools>,
|
|
49
|
-
): ReadonlyArray<ToolDescriptor> =>
|
|
50
|
-
toolkit.tools.map((tool) => {
|
|
51
|
-
const inputSchema = tool.inputSchema["~standard"].jsonSchema.input({
|
|
52
|
-
target: "draft-2020-12",
|
|
53
|
-
})
|
|
54
|
-
return tool.strict !== undefined
|
|
55
|
-
? {
|
|
56
|
-
name: tool.name,
|
|
57
|
-
description: tool.description,
|
|
58
|
-
inputSchema,
|
|
59
|
-
strict: tool.strict,
|
|
60
|
-
}
|
|
61
|
-
: { name: tool.name, description: tool.description, inputSchema }
|
|
62
|
-
})
|
|
63
|
-
|
|
64
29
|
// ---------------------------------------------------------------------------
|
|
65
30
|
// Tool executor. Streams `ToolEvent`s in real time and dispatches streaming
|
|
66
31
|
// and plain tools uniformly. Policy stays outside this module: callers pass
|
|
@@ -72,9 +37,9 @@ export type ExecuteOptions = {
|
|
|
72
37
|
}
|
|
73
38
|
|
|
74
39
|
/** Execute every provided call. Approval/rejection policy belongs upstream. */
|
|
75
|
-
export const
|
|
40
|
+
export const run = <Tools extends ReadonlyArray<AnyTool<any>>>(
|
|
76
41
|
tools: Tools,
|
|
77
|
-
calls: ReadonlyArray<
|
|
42
|
+
calls: ReadonlyArray<ToolCall>,
|
|
78
43
|
options?: ExecuteOptions,
|
|
79
44
|
): Stream.Stream<ToolEvent, never, ToolKindR<Tools>> =>
|
|
80
45
|
Stream.fromIterable(calls).pipe(
|
|
@@ -83,16 +48,16 @@ export const executeAll = <Tools extends ReadonlyArray<AnyKindTool<any>>>(
|
|
|
83
48
|
}),
|
|
84
49
|
)
|
|
85
50
|
|
|
86
|
-
const
|
|
87
|
-
ToolResult.
|
|
51
|
+
const okResult = (call: ToolCall, tool: string, value: unknown): ToolResult =>
|
|
52
|
+
ToolResult.Ok({
|
|
88
53
|
call_id: call.call_id,
|
|
89
54
|
tool,
|
|
90
55
|
value,
|
|
91
56
|
})
|
|
92
57
|
|
|
93
58
|
const runOne = <R>(
|
|
94
|
-
tools: ReadonlyArray<
|
|
95
|
-
call:
|
|
59
|
+
tools: ReadonlyArray<AnyTool<R>>,
|
|
60
|
+
call: ToolCall,
|
|
96
61
|
): Stream.Stream<ToolEvent, never, R> => {
|
|
97
62
|
const tool = tools.find((t) => t.name === call.name)
|
|
98
63
|
if (tool === undefined) {
|
|
@@ -100,7 +65,7 @@ const runOne = <R>(
|
|
|
100
65
|
// still execute. LLMs hallucinate tool names; MCP tools come and go.
|
|
101
66
|
return Stream.succeed(
|
|
102
67
|
ToolEvent.Output({
|
|
103
|
-
result:
|
|
68
|
+
result: failed(call, "unknown_tool", `No tool registered with name "${call.name}"`),
|
|
104
69
|
}),
|
|
105
70
|
)
|
|
106
71
|
}
|
|
@@ -110,10 +75,7 @@ const runOne = <R>(
|
|
|
110
75
|
|
|
111
76
|
const parseJsonUnknown = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Unknown))
|
|
112
77
|
|
|
113
|
-
const runPlain = <R>(
|
|
114
|
-
tool: AnyPlainTool<R>,
|
|
115
|
-
call: FunctionCall,
|
|
116
|
-
): Stream.Stream<ToolEvent, never, R> =>
|
|
78
|
+
const runPlain = <R>(tool: AnyPlainTool<R>, call: ToolCall): Stream.Stream<ToolEvent, never, R> =>
|
|
117
79
|
Stream.fromEffect(
|
|
118
80
|
Effect.gen(function* () {
|
|
119
81
|
const parsed = yield* parseJsonUnknown(call.arguments).pipe(
|
|
@@ -127,7 +89,7 @@ const runPlain = <R>(
|
|
|
127
89
|
return executionError(call, "Tool input failed schema validation")
|
|
128
90
|
}
|
|
129
91
|
const output = yield* tool.run(validated.value)
|
|
130
|
-
return
|
|
92
|
+
return okResult(call, tool.name, output)
|
|
131
93
|
}).pipe(
|
|
132
94
|
Effect.catchCause(() => Effect.succeed(executionError(call, "Tool execution failed"))),
|
|
133
95
|
Effect.map((result) => ToolEvent.Output({ result })),
|
|
@@ -136,7 +98,7 @@ const runPlain = <R>(
|
|
|
136
98
|
|
|
137
99
|
const runStreaming = <R>(
|
|
138
100
|
tool: AnyStreamingTool<R>,
|
|
139
|
-
call:
|
|
101
|
+
call: ToolCall,
|
|
140
102
|
): Stream.Stream<ToolEvent, never, R> =>
|
|
141
103
|
Stream.unwrap(
|
|
142
104
|
Effect.gen(function* () {
|
|
@@ -156,13 +118,13 @@ const runStreaming = <R>(
|
|
|
156
118
|
}
|
|
157
119
|
|
|
158
120
|
// Real-time: tap each event into a Ref as it flows; emit one
|
|
159
|
-
//
|
|
121
|
+
// Progress per event; then concat one synthetic Output element
|
|
160
122
|
// built from the accumulated Ref via `finalize`.
|
|
161
123
|
const ref = yield* Ref.make<Array<unknown>>([])
|
|
162
|
-
const
|
|
124
|
+
const progress = tool.run(validated.value).pipe(
|
|
163
125
|
Stream.tap((event) => Ref.update(ref, Arr.append(event))),
|
|
164
126
|
Stream.map((data) =>
|
|
165
|
-
ToolEvent.
|
|
127
|
+
ToolEvent.Progress({
|
|
166
128
|
call_id: call.call_id,
|
|
167
129
|
tool: tool.name,
|
|
168
130
|
data,
|
|
@@ -173,12 +135,12 @@ const runStreaming = <R>(
|
|
|
173
135
|
Ref.get(ref).pipe(
|
|
174
136
|
Effect.map((events) =>
|
|
175
137
|
ToolEvent.Output({
|
|
176
|
-
result:
|
|
138
|
+
result: okResult(call, tool.name, tool.finalize(events)),
|
|
177
139
|
}),
|
|
178
140
|
),
|
|
179
141
|
),
|
|
180
142
|
)
|
|
181
|
-
return
|
|
143
|
+
return progress.pipe(Stream.concat(output))
|
|
182
144
|
}),
|
|
183
145
|
).pipe(
|
|
184
146
|
Stream.catchCause(() =>
|
|
@@ -191,35 +153,97 @@ const runStreaming = <R>(
|
|
|
191
153
|
)
|
|
192
154
|
|
|
193
155
|
// ---------------------------------------------------------------------------
|
|
194
|
-
// `
|
|
195
|
-
// shape. Drains the stream to the consumer in real-time, taps every
|
|
156
|
+
// `continueWithResults` - bridge from a `Stream<ToolEvent>` to the loop's
|
|
157
|
+
// emit shape. Drains the stream to the consumer in real-time, taps every
|
|
196
158
|
// `Output` into an internal Ref, and at end-of-stream emits
|
|
197
159
|
// `Loop.next(build(results))`. Recipe never sees the Ref.
|
|
198
160
|
//
|
|
199
|
-
// Dual: data-first `
|
|
200
|
-
// `stream.pipe(
|
|
161
|
+
// Dual: data-first `continueWithResults(stream, build)` and data-last
|
|
162
|
+
// `stream.pipe(continueWithResults(build))` both work.
|
|
201
163
|
// ---------------------------------------------------------------------------
|
|
202
164
|
|
|
203
|
-
|
|
165
|
+
/**
|
|
166
|
+
* Append a completed turn plus tool results to a state's history, converting
|
|
167
|
+
* the results to wire-format `ToolCallOutput`s. Curried so it slots directly
|
|
168
|
+
* into `continueWithResults`:
|
|
169
|
+
*
|
|
170
|
+
* Toolkit.run(tools, calls).pipe(
|
|
171
|
+
* Toolkit.continueWithResults(Toolkit.appendToolResults(state, turn)),
|
|
172
|
+
* )
|
|
173
|
+
*
|
|
174
|
+
* Equivalent to `appendToHistory(state, turn, results.map(toToolCallOutput))`
|
|
175
|
+
* — the helper just hides the wire-conversion step.
|
|
176
|
+
*/
|
|
177
|
+
export const appendToolResults =
|
|
178
|
+
<S extends { readonly history: ReadonlyArray<HistoryItem> }>(state: S, turn: Turn) =>
|
|
179
|
+
(results: ReadonlyArray<ToolResult>): S =>
|
|
180
|
+
appendToHistory(state, turn, results.map(toToolCallOutput))
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Drain a `Stream<ToolEvent>` and return the accumulated `ToolResult`s
|
|
184
|
+
* from every `Output` event. One-shot — the type is `Effect<results>`,
|
|
185
|
+
* not a one-element stream — so it composes naturally with `Effect.map`
|
|
186
|
+
* to build state.
|
|
187
|
+
*
|
|
188
|
+
* Counterpart to `continueWithResults`: `continueWithResults` bundles
|
|
189
|
+
* drain + emit Next into one bridge; this one is the right arm of a
|
|
190
|
+
* broadcast/fork-and-merge for callers that want to vary the left arm
|
|
191
|
+
* (e.g., filter/tap ToolEvents before forwarding).
|
|
192
|
+
*/
|
|
193
|
+
export const collectResults = <E, R>(
|
|
194
|
+
stream: Stream.Stream<ToolEvent, E, R>,
|
|
195
|
+
): Effect.Effect<ReadonlyArray<ToolResult>, E, R> =>
|
|
196
|
+
stream.pipe(
|
|
197
|
+
Stream.filter(isOutput),
|
|
198
|
+
Stream.map((e) => e.result),
|
|
199
|
+
Stream.runCollect,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Bridge from a `Stream<ToolEvent>` to the loop's emit shape. Forwards
|
|
204
|
+
* every ToolEvent downstream as a `Loop.value` and at end-of-stream emits
|
|
205
|
+
* one `Loop.next(build(results))` carrying the accumulated `ToolResult`s
|
|
206
|
+
* from terminal `Output` events.
|
|
207
|
+
*
|
|
208
|
+
* Conceptually a broadcast fork (one arm passes events through, the other
|
|
209
|
+
* drains them into a state). Implemented as a single-pass Ref tap + concat
|
|
210
|
+
* for zero buffering — the broadcast version is observationally equivalent
|
|
211
|
+
* but holds events in a PubSub until both arms drain. The public primitives
|
|
212
|
+
* (`Loop.value`, `collectResults`, `Loop.next`) compose the same pattern
|
|
213
|
+
* when you need to vary an arm.
|
|
214
|
+
*
|
|
215
|
+
* Dual: data-first `continueWithResults(stream, build)` and data-last
|
|
216
|
+
* `stream.pipe(continueWithResults(build))` both work.
|
|
217
|
+
*/
|
|
218
|
+
export const continueWithResults: {
|
|
204
219
|
<S>(
|
|
205
220
|
build: (results: ReadonlyArray<ToolResult>) => S,
|
|
206
221
|
): <R>(
|
|
207
222
|
stream: Stream.Stream<ToolEvent, never, R>,
|
|
208
|
-
) => Stream.Stream<Loop.
|
|
223
|
+
) => Stream.Stream<Loop.Step<ToolEvent, S>, never, R>
|
|
209
224
|
<S, R>(
|
|
210
225
|
stream: Stream.Stream<ToolEvent, never, R>,
|
|
211
226
|
build: (results: ReadonlyArray<ToolResult>) => S,
|
|
212
|
-
): Stream.Stream<Loop.
|
|
227
|
+
): Stream.Stream<Loop.Step<ToolEvent, S>, never, R>
|
|
213
228
|
} = Function.dual(
|
|
214
229
|
2,
|
|
215
230
|
<S, R>(
|
|
216
231
|
stream: Stream.Stream<ToolEvent, never, R>,
|
|
217
232
|
build: (results: ReadonlyArray<ToolResult>) => S,
|
|
218
|
-
): Stream.Stream<Loop.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
233
|
+
): Stream.Stream<Loop.Step<ToolEvent, S>, never, R> =>
|
|
234
|
+
Stream.unwrap(
|
|
235
|
+
Effect.gen(function* () {
|
|
236
|
+
const ref = yield* Ref.make<ReadonlyArray<ToolResult>>([])
|
|
237
|
+
const tapped = stream.pipe(
|
|
238
|
+
Stream.tap((e) =>
|
|
239
|
+
isOutput(e) ? Ref.update(ref, (acc) => Arr.append(acc, e.result)) : Effect.void,
|
|
240
|
+
),
|
|
241
|
+
Stream.map(Loop.value),
|
|
242
|
+
)
|
|
243
|
+
const continuation = Stream.unwrap(
|
|
244
|
+
Ref.get(ref).pipe(Effect.map((acc) => Loop.next(build(acc)))),
|
|
245
|
+
)
|
|
246
|
+
return tapped.pipe(Stream.concat(continuation))
|
|
247
|
+
}),
|
|
224
248
|
),
|
|
225
249
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect, Stream } from "effect"
|
|
1
|
+
import { Duration, Effect, Stream } from "effect"
|
|
2
2
|
import { describe, expect, expectTypeOf, it } from "vitest"
|
|
3
3
|
import type * as AiError from "../domain/AiError.js"
|
|
4
4
|
import type { TranscriptEvent, TranscriptResult } from "../domain/Transcript.js"
|
|
@@ -8,7 +8,7 @@ import * as Transcriber from "./Transcriber.js"
|
|
|
8
8
|
describe("Transcriber.transcribe", () => {
|
|
9
9
|
it("returns the scripted TranscriptResult", async () => {
|
|
10
10
|
const mock = MockTranscriber.layer({
|
|
11
|
-
transcripts: [{ text: "hello world",
|
|
11
|
+
transcripts: [{ text: "hello world", duration: Duration.seconds(1.23) }],
|
|
12
12
|
})
|
|
13
13
|
const program = Transcriber.transcribe({
|
|
14
14
|
audio: { _tag: "bytes", bytes: new Uint8Array([0]), mimeType: "audio/wav" },
|
|
@@ -16,7 +16,7 @@ describe("Transcriber.transcribe", () => {
|
|
|
16
16
|
})
|
|
17
17
|
const result = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)))
|
|
18
18
|
expect(result.text).toBe("hello world")
|
|
19
|
-
expect(result.
|
|
19
|
+
expect(result.duration).toStrictEqual(Duration.seconds(1.23))
|
|
20
20
|
})
|
|
21
21
|
|
|
22
22
|
it("records each transcribe call", async () => {
|
|
@@ -15,12 +15,13 @@ export type CommonTranscribeRequest = {
|
|
|
15
15
|
readonly model: string
|
|
16
16
|
/** ISO-639-1 / BCP-47. Omit for autodetection (where supported). */
|
|
17
17
|
readonly language?: string
|
|
18
|
-
/**
|
|
19
|
-
*
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
/** Free-form context / style hint (Whisper-style prose). OpenAI honors
|
|
19
|
+
* it; providers without a prompt field `warnDropped`. */
|
|
20
|
+
readonly prompt?: string
|
|
21
|
+
/** Vocabulary biasing — discrete terms to boost (names, jargon). Maps to
|
|
22
|
+
* Deepgram `keyterm`, ElevenLabs `keyterms`, Google `adaptation`,
|
|
23
|
+
* Inworld `prompts`; others `warnDropped`. */
|
|
24
|
+
readonly biasingTerms?: ReadonlyArray<string>
|
|
24
25
|
readonly diarization?: boolean
|
|
25
26
|
readonly wordTimestamps?: boolean
|
|
26
27
|
}
|
|
@@ -28,7 +29,9 @@ export type CommonTranscribeRequest = {
|
|
|
28
29
|
/**
|
|
29
30
|
* Streaming-transcription request. `inputFormat` declares what the
|
|
30
31
|
* bytes in the input stream will look like — providers reject
|
|
31
|
-
*
|
|
32
|
+
* formats they can't ingest at stream startup with
|
|
33
|
+
* `AiError.Unsupported` (a per-Layer capability gap, not a wire-shape
|
|
34
|
+
* mismatch).
|
|
32
35
|
*/
|
|
33
36
|
export type CommonStreamTranscribeRequest = Omit<CommonTranscribeRequest, "audio"> & {
|
|
34
37
|
readonly inputFormat: AudioFormat
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Audio-BfCTGnH3.d.mts","names":[],"sources":["../src/domain/Audio.ts"],"mappings":";;;;;;;;;;;;;;KAUY,aAAA;;;;;AAAZ;;;;;KA0BY,WAAA,GACR,QAAA,CAAS,aAAA,IACT,WAAA,CAAY,aAAA,IACZ,UAAA,CAAW,aAAA;AAAA,cAEF,UAAA,GAAc,CAAA,EAAG,WAAA,KAAc,CAAA,IAAK,QAAA,CAAS,aAAA;AAAA,cAC7C,aAAA,GAAiB,CAAA,EAAG,WAAA,KAAc,CAAA,IAAK,WAAA,CAAY,aAAA;AAAA,cAEnD,YAAA,GAAgB,CAAA,EAAG,WAAA,KAAc,CAAA,IAAK,UAAA,CAAW,aAAA;;;;;;;KAQlD,WAAA;EAAA,SACD,SAAA;EAAA,SACA,QAAA;EAAA,SAUA,UAAA,wDA1BK;EAAA,SA4BL,OAAA;EAAA,SACA,QAAA;AAAA;;AA1BX;;;;KAkCY,UAAA;EAAA,SACD,KAAA,EAAO,UAAA;AAAA;;;;;KAON,SAAA;EAAA,SACD,MAAA,EAAQ,WAAA;EAAA,SACR,KAAA,EAAO,UAAA;EAAA,SACP,eAAA;AAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Items-BH8xUkoR.d.mts","names":[],"sources":["../src/domain/Items.ts"],"mappings":"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"}
|