@effect-uai/core 0.5.1 → 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 +12 -6
- package/dist/index.mjs +9 -3
- package/dist/language-model/LanguageModel.d.mts +29 -27
- package/dist/language-model/LanguageModel.d.mts.map +1 -1
- package/dist/language-model/LanguageModel.mjs +16 -25
- package/dist/language-model/LanguageModel.mjs.map +1 -1
- package/dist/language-model/LanguageModel.test.mjs +10 -81
- package/dist/language-model/LanguageModel.test.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/retry/Retry.d.mts +64 -0
- package/dist/retry/Retry.d.mts.map +1 -0
- package/dist/retry/Retry.mjs +69 -0
- package/dist/retry/Retry.mjs.map +1 -0
- package/dist/retry/Retry.test.mjs +142 -0
- package/dist/retry/Retry.test.mjs.map +1 -0
- 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 +14 -8
- 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/Approval.test.d.mts +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 +35 -7
- package/src/domain/Items.ts +9 -9
- package/src/domain/Turn.ts +16 -16
- package/src/index.ts +8 -2
- package/src/language-model/LanguageModel.test.ts +21 -102
- package/src/language-model/LanguageModel.ts +36 -59
- package/src/loop/Loop.test.ts +93 -91
- package/src/loop/Loop.ts +72 -170
- package/src/retry/Retry.test.ts +163 -0
- package/src/retry/Retry.ts +84 -0
- 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 +31 -25
- 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 → retry/Retry.test.d.mts} +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Data, Effect, type Schedule, Stream } from "effect"
|
|
2
|
+
import type * as AiError from "../domain/AiError.js"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Subset-aware retry helpers for `AiError`.
|
|
6
|
+
*
|
|
7
|
+
* Effect's own `Effect.retry` / `Stream.retry` apply the schedule to
|
|
8
|
+
* every failure. For AI calls that's the wrong policy — replaying a
|
|
9
|
+
* `ContentFiltered` or `InvalidRequest` won't change the outcome and
|
|
10
|
+
* just wastes quota. These helpers gate the schedule on the
|
|
11
|
+
* {@link Retryable} subset (rate limits, transport hiccups, timeouts);
|
|
12
|
+
* everything else propagates unchanged.
|
|
13
|
+
*
|
|
14
|
+
* Two carriers:
|
|
15
|
+
* - {@link stream} — for `Stream<A, AiError, R>` (e.g. `streamTurn`,
|
|
16
|
+
* `streamSynthesis`, `streamTranscriptionFrom`). Caveat: the entire
|
|
17
|
+
* stream re-runs on retry, so deltas before the failure replay on
|
|
18
|
+
* the next attempt.
|
|
19
|
+
* - {@link effect} — for `Effect<A, AiError, R>` (e.g. `turn`, `embed`,
|
|
20
|
+
* `embedMany`, `synthesize`, `transcribe`). At-most-once.
|
|
21
|
+
*
|
|
22
|
+
* Composes with `Stream.retry` / `Effect.retry` directly (no naming
|
|
23
|
+
* collision since these live under the `Retry` namespace).
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Wrapper around the retryable subset of `AiError`. Internal to the
|
|
28
|
+
* lift/retry/unlift dance — exported so callers can build their own
|
|
29
|
+
* variants if needed (e.g. a custom schedule that observes `cause`).
|
|
30
|
+
*/
|
|
31
|
+
export class Retryable extends Data.TaggedError("RetryableAi")<{
|
|
32
|
+
readonly cause: AiError.RateLimited | AiError.Unavailable | AiError.Timeout
|
|
33
|
+
}> {}
|
|
34
|
+
|
|
35
|
+
/** Type-narrowing predicate for the retryable subset. */
|
|
36
|
+
export const isRetryable = (
|
|
37
|
+
e: AiError.AiError,
|
|
38
|
+
): e is AiError.RateLimited | AiError.Unavailable | AiError.Timeout =>
|
|
39
|
+
e._tag === "RateLimited" || e._tag === "Unavailable" || e._tag === "Timeout"
|
|
40
|
+
|
|
41
|
+
// Lift events to Items, non-retryable failures to Terminal values
|
|
42
|
+
// (escape retry), retryable failures to wrapped errors (only thing
|
|
43
|
+
// retry sees).
|
|
44
|
+
type Lifted<A> =
|
|
45
|
+
| { readonly _tag: "Item"; readonly value: A }
|
|
46
|
+
| { readonly _tag: "Terminal"; readonly cause: AiError.AiError }
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Retry a `Stream<A, AiError, R>` on the retryable subset. Other
|
|
50
|
+
* failures bypass the schedule and propagate unchanged. The whole
|
|
51
|
+
* stream replays on each attempt — deltas emitted before the failure
|
|
52
|
+
* are re-emitted on the next try.
|
|
53
|
+
*/
|
|
54
|
+
export const stream =
|
|
55
|
+
<Out>(schedule: Schedule.Schedule<Out, Retryable>) =>
|
|
56
|
+
<A, R>(s: Stream.Stream<A, AiError.AiError, R>): Stream.Stream<A, AiError.AiError, R> =>
|
|
57
|
+
s.pipe(
|
|
58
|
+
Stream.map((value): Lifted<A> => ({ _tag: "Item", value })),
|
|
59
|
+
Stream.catchIf(
|
|
60
|
+
isRetryable,
|
|
61
|
+
(cause) => Stream.fail(new Retryable({ cause })),
|
|
62
|
+
(cause) => Stream.succeed<Lifted<A>>({ _tag: "Terminal", cause }),
|
|
63
|
+
),
|
|
64
|
+
Stream.retry(schedule),
|
|
65
|
+
Stream.catchTag("RetryableAi", (e) => Stream.fail<AiError.AiError>(e.cause)),
|
|
66
|
+
Stream.flatMap((item) =>
|
|
67
|
+
item._tag === "Item" ? Stream.succeed(item.value) : Stream.fail(item.cause),
|
|
68
|
+
),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Retry an `Effect<A, AiError, R>` on the retryable subset. Other
|
|
73
|
+
* failures bypass the schedule and propagate unchanged. At-most-once
|
|
74
|
+
* — the underlying call only re-runs if the previous attempt failed
|
|
75
|
+
* with a retryable tag.
|
|
76
|
+
*
|
|
77
|
+
* Skips the lift/unlift dance the Stream variant needs: `Effect.retry`
|
|
78
|
+
* exposes a `while:` predicate so the error channel stays a plain
|
|
79
|
+
* `AiError` throughout.
|
|
80
|
+
*/
|
|
81
|
+
export const effect =
|
|
82
|
+
<Out>(schedule: Schedule.Schedule<Out, AiError.AiError>) =>
|
|
83
|
+
<A, R>(eff: Effect.Effect<A, AiError.AiError, R>): Effect.Effect<A, AiError.AiError, R> =>
|
|
84
|
+
Effect.retry(eff, { schedule, while: isRetryable })
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Brand } from "effect"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A memory / data size, always represented internally as a number of
|
|
5
|
+
* bytes. Mirrors the `Duration` shape from `effect` — a branded value
|
|
6
|
+
* type with a wider `Input` union for ergonomic construction at call
|
|
7
|
+
* sites.
|
|
8
|
+
*
|
|
9
|
+
* `effect` itself has no byte-count primitive (only `Duration`), so we
|
|
10
|
+
* own this here rather than leak provider-specific shapes (Deno's
|
|
11
|
+
* template-literal `Memory`, Vercel's `mb`, Modal's `gpuMemory`, …)
|
|
12
|
+
* through the cross-provider `Sandbox` request types.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import * as Memory from "@effect-uai/core/Memory"
|
|
17
|
+
*
|
|
18
|
+
* Memory.gib(2) // 2 GiB → 2_147_483_648 bytes
|
|
19
|
+
* Memory.fromInputUnsafe("1.5 GiB") // human-string parsing
|
|
20
|
+
* Memory.fromInputUnsafe(1_280_000_000) // raw bytes
|
|
21
|
+
* Memory.toBytes(m) // unwrap
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
export type Memory = Brand.Branded<number, "Memory">
|
|
26
|
+
|
|
27
|
+
const make = Brand.nominal<Memory>()
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Input — what the public APIs accept.
|
|
31
|
+
//
|
|
32
|
+
// Supported string forms (case-insensitive on the unit, whitespace
|
|
33
|
+
// optional between number and unit):
|
|
34
|
+
//
|
|
35
|
+
// "<n>" bytes (no unit)
|
|
36
|
+
// "<n> B" bytes
|
|
37
|
+
// "<n> kB" 1_000 bytes (decimal)
|
|
38
|
+
// "<n> MB" 1_000_000 (decimal)
|
|
39
|
+
// "<n> GB" 1_000_000_000 (decimal)
|
|
40
|
+
// "<n> KiB" 1_024 (binary)
|
|
41
|
+
// "<n> MiB" 1_048_576 (binary)
|
|
42
|
+
// "<n> GiB" 1_073_741_824 (binary)
|
|
43
|
+
//
|
|
44
|
+
// Decimal vs binary follows the SI / IEC convention (`MB` = 10^6,
|
|
45
|
+
// `MiB` = 2^20). When in doubt, prefer the binary `*iB` units.
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
export type Input = number | Memory | string
|
|
49
|
+
|
|
50
|
+
const UNIT_BYTES: Record<string, number> = {
|
|
51
|
+
b: 1,
|
|
52
|
+
kb: 1_000,
|
|
53
|
+
mb: 1_000_000,
|
|
54
|
+
gb: 1_000_000_000,
|
|
55
|
+
tb: 1_000_000_000_000,
|
|
56
|
+
kib: 1_024,
|
|
57
|
+
mib: 1_024 * 1_024,
|
|
58
|
+
gib: 1_024 * 1_024 * 1_024,
|
|
59
|
+
tib: 1_024 * 1_024 * 1_024 * 1_024,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const PARSE_RE = /^\s*(\d+(?:\.\d+)?)\s*([a-zA-Z]*)\s*$/
|
|
63
|
+
|
|
64
|
+
const parseString = (s: string): number => {
|
|
65
|
+
const m = PARSE_RE.exec(s)
|
|
66
|
+
if (m === null) {
|
|
67
|
+
throw new Error(`Memory: cannot parse "${s}" — expected forms like "1024", "10 MB", "1GiB"`)
|
|
68
|
+
}
|
|
69
|
+
const value = Number(m[1])
|
|
70
|
+
const unit = (m[2] ?? "").toLowerCase()
|
|
71
|
+
if (unit === "") return value
|
|
72
|
+
const factor = UNIT_BYTES[unit]
|
|
73
|
+
if (factor === undefined) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Memory: unknown unit "${m[2]}" in "${s}" — supported: ${Object.keys(UNIT_BYTES).join(", ")}`,
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
return value * factor
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Coerce a {@link Input} to a {@link Memory}. Throws on malformed
|
|
83
|
+
* strings or non-finite numbers. Mirrors `Duration.fromInputUnsafe`.
|
|
84
|
+
*/
|
|
85
|
+
export const fromInputUnsafe = (input: Input): Memory => {
|
|
86
|
+
if (typeof input === "number") {
|
|
87
|
+
if (!Number.isFinite(input) || input < 0) {
|
|
88
|
+
throw new Error(`Memory: bytes must be a non-negative finite number, got ${input}`)
|
|
89
|
+
}
|
|
90
|
+
return make(Math.floor(input))
|
|
91
|
+
}
|
|
92
|
+
return make(Math.floor(parseString(input)))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Unwrap
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
/** Return the underlying byte count. */
|
|
100
|
+
export const toBytes = (m: Memory | Input): number =>
|
|
101
|
+
typeof m === "number" && (m as number) === Math.floor(m as number) && (m as number) >= 0
|
|
102
|
+
? (m as number)
|
|
103
|
+
: (fromInputUnsafe(m) as unknown as number)
|
|
104
|
+
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// Unit constructors — terse for use at call sites.
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
/** N bytes. */
|
|
110
|
+
export const bytes = (n: number): Memory => make(Math.floor(n))
|
|
111
|
+
|
|
112
|
+
/** N KiB (binary kibibytes, `n * 1024`). */
|
|
113
|
+
export const kib = (n: number): Memory => make(Math.floor(n * 1_024))
|
|
114
|
+
|
|
115
|
+
/** N MiB (binary mebibytes, `n * 1024^2`). */
|
|
116
|
+
export const mib = (n: number): Memory => make(Math.floor(n * 1_024 * 1_024))
|
|
117
|
+
|
|
118
|
+
/** N GiB (binary gibibytes, `n * 1024^3`). */
|
|
119
|
+
export const gib = (n: number): Memory => make(Math.floor(n * 1_024 * 1_024 * 1_024))
|
|
120
|
+
|
|
121
|
+
/** N kB (decimal kilobytes, `n * 1000`). */
|
|
122
|
+
export const kb = (n: number): Memory => make(Math.floor(n * 1_000))
|
|
123
|
+
|
|
124
|
+
/** N MB (decimal megabytes, `n * 1_000_000`). */
|
|
125
|
+
export const mb = (n: number): Memory => make(Math.floor(n * 1_000_000))
|
|
126
|
+
|
|
127
|
+
/** N GB (decimal gigabytes, `n * 1_000_000_000`). */
|
|
128
|
+
export const gb = (n: number): Memory => make(Math.floor(n * 1_000_000_000))
|