@effect-uai/core 0.6.0 → 0.8.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-pXR5BAgW.d.mts → Items-7ljaVxH_.d.mts} +12 -12
- package/dist/{Items-pXR5BAgW.d.mts.map → Items-7ljaVxH_.d.mts.map} +1 -1
- package/dist/{Tool-DP-AwKOf.d.mts → Tool-BKiieFXk.d.mts} +2 -2
- package/dist/{Tool-DP-AwKOf.d.mts.map → Tool-BKiieFXk.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 +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 +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 +7 -4
- package/dist/index.mjs +5 -2
- package/dist/language-model/LanguageModel.d.mts +2 -2
- 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/speech-synthesizer/SpeechSynthesizer.d.mts +21 -36
- package/dist/speech-synthesizer/SpeechSynthesizer.d.mts.map +1 -1
- package/dist/speech-synthesizer/SpeechSynthesizer.mjs.map +1 -1
- package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs +3 -3
- 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 +1 -1
- package/dist/testing/MockSpeechSynthesizer.d.mts +1 -1
- package/dist/tool/Approval.d.mts +1 -1
- package/dist/tool/HistoryCheck.d.mts +1 -1
- package/dist/tool/Tool.d.mts +1 -1
- package/dist/tool/ToolResult.d.mts +1 -1
- package/dist/tool/Toolkit.d.mts +2 -2
- package/dist/tool/Toolkit.mjs +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/dist/web-search/WebSearch.d.mts +119 -0
- package/dist/web-search/WebSearch.d.mts.map +1 -0
- package/dist/web-search/WebSearch.mjs +42 -0
- package/dist/web-search/WebSearch.mjs.map +1 -0
- package/dist/web-search/WebSearchTool.d.mts +58 -0
- package/dist/web-search/WebSearchTool.d.mts.map +1 -0
- package/dist/web-search/WebSearchTool.mjs +69 -0
- package/dist/web-search/WebSearchTool.mjs.map +1 -0
- package/package.json +13 -1
- package/src/capabilities/Capabilities.ts +81 -0
- package/src/domain/Audio.ts +2 -1
- package/src/domain/Music.ts +142 -68
- package/src/domain/Transcript.ts +7 -1
- 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 +3 -0
- package/src/music-generator/MusicGenerator.test.ts +78 -53
- package/src/music-generator/MusicGenerator.ts +65 -24
- package/src/speech-synthesizer/SpeechSynthesizer.test.ts +3 -3
- package/src/speech-synthesizer/SpeechSynthesizer.ts +18 -33
- package/src/testing/MockMusicGenerator.ts +28 -18
- package/src/transcriber/Transcriber.test.ts +3 -3
- package/src/transcriber/Transcriber.ts +10 -7
- package/src/web-search/WebSearch.ts +150 -0
- package/src/web-search/WebSearchTool.ts +106 -0
- package/dist/Audio-BfCTGnH3.d.mts.map +0 -1
package/src/domain/Music.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Duration } from "effect"
|
|
2
|
+
import type { AudioBlob, AudioChunk, AudioFormat } from "./Audio.js"
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Prompt fragment with a relative weight. Native to Lyria RealTime
|
|
5
|
-
* (`{ text, weight }` pairs blended in the model).
|
|
6
|
-
* providers (
|
|
7
|
-
*
|
|
6
|
+
* (`{ text, weight }` pairs blended in the model) and Riffusion compose.
|
|
7
|
+
* Single-prompt providers (the majority) treat a 1-element array with
|
|
8
|
+
* `weight: 1` as a plain prompt.
|
|
8
9
|
*/
|
|
9
10
|
export type WeightedPrompt = {
|
|
10
11
|
readonly text: string
|
|
@@ -13,109 +14,182 @@ export type WeightedPrompt = {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
|
-
* Cross-provider music-generation request.
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* Cross-provider music-generation request. Trimmed to fields the
|
|
18
|
+
* majority of music providers honor structurally; provider-specific
|
|
19
|
+
* extras (Lyria `bpm` / `scale`, ElevenLabs `compositionPlan` /
|
|
20
|
+
* `signWithC2pa`, Suno `style` / `personaId`, MiniMax `lyricsOptimizer`,
|
|
21
|
+
* `instrumental` on providers that ship a wire toggle) live on each
|
|
22
|
+
* provider's typed request which extends this and narrows `model`.
|
|
23
|
+
*
|
|
24
|
+
* Music providers are one-shot text-to-audio, not conversational. No
|
|
25
|
+
* `messages[]` shape applies.
|
|
20
26
|
*/
|
|
21
27
|
export type CommonGenerateMusicRequest = {
|
|
22
28
|
/** Model identifier. Each provider narrows. */
|
|
23
29
|
readonly model: string
|
|
24
|
-
/** Single prompt string
|
|
25
|
-
readonly
|
|
30
|
+
/** Single prompt string. Weighted blends are provider-typed extras. */
|
|
31
|
+
readonly prompt: string
|
|
26
32
|
/**
|
|
27
|
-
* Lyrics text, optionally with section tags like `[Verse]` /
|
|
28
|
-
* `[Bridge]` / `[Outro]`.
|
|
29
|
-
*
|
|
33
|
+
* Lyrics text, optionally with section tags like `[Verse]` /
|
|
34
|
+
* `[Chorus]` / `[Bridge]` / `[Outro]`. Routed to a structured wire
|
|
35
|
+
* field on providers that have one (ElevenLabs `lines`, MiniMax
|
|
36
|
+
* `lyrics`, Mureka `lyrics`, Suno custom-mode `prompt`); embedded in
|
|
37
|
+
* the prompt with a warning on providers without one (Lyria 3,
|
|
38
|
+
* Riffusion).
|
|
30
39
|
*/
|
|
31
40
|
readonly lyrics?: string
|
|
32
|
-
/** Target duration
|
|
33
|
-
readonly
|
|
34
|
-
/** Beats per minute (60–200 typical). */
|
|
35
|
-
readonly bpm?: number
|
|
41
|
+
/** Target duration. Provider may treat as a hint or hard limit. */
|
|
42
|
+
readonly duration?: Duration.Duration
|
|
36
43
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
44
|
+
* Reproducibility seed. Tuning hint (bucket 3): honored where the
|
|
45
|
+
* provider takes one (Lyria 2 Vertex, Lyria RealTime, ElevenLabs
|
|
46
|
+
* with `compositionPlan`, Stable Audio, MusicGen); silently ignored
|
|
47
|
+
* elsewhere.
|
|
39
48
|
*/
|
|
40
|
-
readonly
|
|
41
|
-
/** Skip vocals / lyrics. */
|
|
42
|
-
readonly instrumental?: boolean
|
|
49
|
+
readonly seed?: number
|
|
43
50
|
/** Preferred output format. Provider may override. */
|
|
44
51
|
readonly outputFormat?: AudioFormat
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
/**
|
|
48
|
-
* Streamed-output request. Same shape as the sync request
|
|
55
|
+
* Streamed-output request. Same shape as the sync request: the
|
|
49
56
|
* streaming variant only differs in how the response is delivered.
|
|
50
57
|
*/
|
|
51
58
|
export type CommonStreamGenerateMusicRequest = CommonGenerateMusicRequest
|
|
52
59
|
|
|
53
60
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
61
|
+
* Playback-control vocabulary that converges across interactive-media
|
|
62
|
+
* protocols (Web Audio, MIDI sequencer control, music SDK surfaces).
|
|
63
|
+
* Provider-specific control actions (Lyria's `reset_context` and any
|
|
64
|
+
* future provider's extras) live on the provider-typed session-input
|
|
65
|
+
* union.
|
|
66
|
+
*/
|
|
67
|
+
export type MusicSessionControl = "play" | "pause" | "stop" | "reset"
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Bidirectional-session input on the cross-provider surface. Two
|
|
71
|
+
* load-bearing actions every interactive music session needs:
|
|
72
|
+
*
|
|
73
|
+
* - `prompts` — steer the active generation with a (single or
|
|
74
|
+
* weighted) prompt blend.
|
|
75
|
+
* - `control` — play / pause / stop / reset.
|
|
76
|
+
*
|
|
77
|
+
* Provider-typed services extend this union with their own `config`
|
|
78
|
+
* variant for model-specific knobs (Lyria RealTime exposes density /
|
|
79
|
+
* brightness / mute-stems / BPM / scale / etc. via its own
|
|
80
|
+
* `LyriaRealtimeSessionInput`).
|
|
57
81
|
*/
|
|
58
82
|
export type MusicSessionInput =
|
|
59
83
|
| { readonly _tag: "prompts"; readonly prompts: ReadonlyArray<WeightedPrompt> }
|
|
60
|
-
| {
|
|
61
|
-
readonly _tag: "config"
|
|
62
|
-
readonly config: {
|
|
63
|
-
readonly bpm?: number
|
|
64
|
-
readonly scale?: string
|
|
65
|
-
readonly density?: number
|
|
66
|
-
readonly brightness?: number
|
|
67
|
-
readonly guidance?: number
|
|
68
|
-
readonly temperature?: number
|
|
69
|
-
readonly topK?: number
|
|
70
|
-
readonly seed?: number
|
|
71
|
-
readonly muteBass?: boolean
|
|
72
|
-
readonly muteDrums?: boolean
|
|
73
|
-
readonly onlyBassAndDrums?: boolean
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
| { readonly _tag: "control"; readonly action: "play" | "pause" | "stop" | "reset_context" }
|
|
84
|
+
| { readonly _tag: "control"; readonly action: MusicSessionControl }
|
|
77
85
|
|
|
78
86
|
export const promptsInput = (prompts: ReadonlyArray<WeightedPrompt>): MusicSessionInput => ({
|
|
79
87
|
_tag: "prompts",
|
|
80
88
|
prompts,
|
|
81
89
|
})
|
|
82
90
|
|
|
83
|
-
export const
|
|
84
|
-
|
|
85
|
-
|
|
91
|
+
export const controlInput = (action: MusicSessionControl): MusicSessionInput => ({
|
|
92
|
+
_tag: "control",
|
|
93
|
+
action,
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Watermark kind on the response. SynthID is mandatory on Lyria; C2PA
|
|
98
|
+
* is opt-in on ElevenLabs. No provider exposes additional metadata
|
|
99
|
+
* about the watermark itself, so a bare literal-union is enough.
|
|
100
|
+
*/
|
|
101
|
+
export type Watermark = "synthid" | "c2pa" | (string & {})
|
|
86
102
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Labelled section with absolute timing. Populated when the provider
|
|
105
|
+
* returns structured section metadata (Lyria text part, ElevenLabs
|
|
106
|
+
* `/v1/music/detailed` with `with_timestamps`).
|
|
107
|
+
*/
|
|
108
|
+
export type MusicSection = {
|
|
109
|
+
readonly label: string
|
|
110
|
+
readonly startSeconds: number
|
|
111
|
+
readonly endSeconds: number
|
|
112
|
+
}
|
|
90
113
|
|
|
91
114
|
/**
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* - `songId` — Suno task id, ElevenLabs `song_id`, etc. Used for
|
|
96
|
-
* back-reference (re-download, stem export, follow-up edits).
|
|
97
|
-
* - `lyrics` — generated lyrics when the model returned them (Lyria
|
|
98
|
-
* text part, Mureka, Suno).
|
|
99
|
-
* - `sections` — structured section markers (Lyria optional JSON
|
|
100
|
-
* structure response).
|
|
101
|
-
* - `watermark` — presence marker (Lyria SynthID is always set).
|
|
115
|
+
* Single generated track. Composes `AudioBlob` rather than extending
|
|
116
|
+
* it — `result.audio` is the raw blob, `result.songId` / `lyrics` /
|
|
117
|
+
* `sections` / `watermark` are provider-side metadata when returned.
|
|
102
118
|
*/
|
|
103
|
-
export type MusicResult =
|
|
119
|
+
export type MusicResult = {
|
|
120
|
+
readonly audio: AudioBlob
|
|
121
|
+
/**
|
|
122
|
+
* Provider identifier, useful when routing through multiple
|
|
123
|
+
* providers in a single pipeline (`"lyria"`, `"elevenlabs-music"`,
|
|
124
|
+
* etc.).
|
|
125
|
+
*/
|
|
126
|
+
readonly provider?: string
|
|
127
|
+
/** Provider-side ID for back-reference (re-download, extension, stems). */
|
|
104
128
|
readonly songId?: string
|
|
129
|
+
/** Generated or transcribed lyrics when the provider returned them. */
|
|
105
130
|
readonly lyrics?: string
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}>
|
|
111
|
-
readonly watermark?: { readonly kind: string }
|
|
131
|
+
/** Structured section markers when the provider returned them. */
|
|
132
|
+
readonly sections?: ReadonlyArray<MusicSection>
|
|
133
|
+
/** Watermark presence indicator when the provider applied one. */
|
|
134
|
+
readonly watermark?: Watermark
|
|
112
135
|
}
|
|
113
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Result of a `generate` call. Suno and Mureka always return 2 tracks
|
|
139
|
+
* per request; every other provider returns 1. `primary` is the
|
|
140
|
+
* caller-friendly convenience; `variants` carries every track the
|
|
141
|
+
* provider produced (length ≥ 1; `primary === variants[0]`).
|
|
142
|
+
*/
|
|
143
|
+
export type GenerateResult = {
|
|
144
|
+
readonly primary: MusicResult
|
|
145
|
+
readonly variants: ReadonlyArray<MusicResult>
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* In-band event on the bidirectional `streamGenerationFrom` output
|
|
150
|
+
* stream. Audio chunks flow as `audio`; server-side warnings
|
|
151
|
+
* (`warning`) and prompt-filter rejections (`filteredPrompt`) flow
|
|
152
|
+
* alongside rather than going to a side-channel log.
|
|
153
|
+
*/
|
|
154
|
+
export type MusicStreamEvent =
|
|
155
|
+
| { readonly _tag: "audio"; readonly chunk: AudioChunk }
|
|
156
|
+
| { readonly _tag: "warning"; readonly message: string }
|
|
157
|
+
| { readonly _tag: "filteredPrompt"; readonly prompt: string; readonly reason: string }
|
|
158
|
+
|
|
114
159
|
export const isPromptsInput = (
|
|
115
160
|
i: MusicSessionInput,
|
|
116
161
|
): i is MusicSessionInput & { _tag: "prompts" } => i._tag === "prompts"
|
|
117
|
-
export const isConfigInput = (i: MusicSessionInput): i is MusicSessionInput & { _tag: "config" } =>
|
|
118
|
-
i._tag === "config"
|
|
119
162
|
export const isControlInput = (
|
|
120
163
|
i: MusicSessionInput,
|
|
121
164
|
): i is MusicSessionInput & { _tag: "control" } => i._tag === "control"
|
|
165
|
+
|
|
166
|
+
export const audioEvent = (chunk: AudioChunk): MusicStreamEvent => ({ _tag: "audio", chunk })
|
|
167
|
+
export const warningEvent = (message: string): MusicStreamEvent => ({
|
|
168
|
+
_tag: "warning",
|
|
169
|
+
message,
|
|
170
|
+
})
|
|
171
|
+
export const filteredPromptEvent = (prompt: string, reason: string): MusicStreamEvent => ({
|
|
172
|
+
_tag: "filteredPrompt",
|
|
173
|
+
prompt,
|
|
174
|
+
reason,
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
export const isAudioEvent = (e: MusicStreamEvent): e is MusicStreamEvent & { _tag: "audio" } =>
|
|
178
|
+
e._tag === "audio"
|
|
179
|
+
export const isWarningEvent = (e: MusicStreamEvent): e is MusicStreamEvent & { _tag: "warning" } =>
|
|
180
|
+
e._tag === "warning"
|
|
181
|
+
export const isFilteredPromptEvent = (
|
|
182
|
+
e: MusicStreamEvent,
|
|
183
|
+
): e is MusicStreamEvent & { _tag: "filteredPrompt" } => e._tag === "filteredPrompt"
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Build a single-variant `GenerateResult` from a `MusicResult`.
|
|
187
|
+
* Convenience for providers that always return exactly one track
|
|
188
|
+
* (Lyria, ElevenLabs, MiniMax, Stable Audio, MusicGen, Riffusion,
|
|
189
|
+
* Tencent). Providers that return multiple tracks (Suno, Mureka)
|
|
190
|
+
* construct the `GenerateResult` directly.
|
|
191
|
+
*/
|
|
192
|
+
export const singleVariant = (result: MusicResult): GenerateResult => ({
|
|
193
|
+
primary: result,
|
|
194
|
+
variants: [result],
|
|
195
|
+
})
|
package/src/domain/Transcript.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import type { Duration } from "effect"
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Per-word timing + metadata. `confidence` and `speakerId` are optional
|
|
3
5
|
* because providers vary widely in what they emit and when (some only on
|
|
4
6
|
* final, some only with diarization enabled, some not at all).
|
|
7
|
+
*
|
|
8
|
+
* `startSeconds` / `endSeconds` stay raw `number` offsets (not `Duration`)
|
|
9
|
+
* — they're positions in the audio, not durations.
|
|
5
10
|
*/
|
|
6
11
|
export type WordTimestamp = {
|
|
7
12
|
readonly text: string
|
|
@@ -20,7 +25,8 @@ export type WordTimestamp = {
|
|
|
20
25
|
export type TranscriptResult = {
|
|
21
26
|
readonly text: string
|
|
22
27
|
readonly languageCode?: string
|
|
23
|
-
|
|
28
|
+
/** Total audio duration. (Word offsets stay raw seconds on `WordTimestamp`.) */
|
|
29
|
+
readonly duration?: Duration.Duration
|
|
24
30
|
readonly words?: ReadonlyArray<WordTimestamp>
|
|
25
31
|
readonly raw?: unknown
|
|
26
32
|
}
|
|
@@ -107,6 +107,17 @@ export const isBinary = (e: Embedding): e is BinaryEmbedding => e._tag === "bina
|
|
|
107
107
|
export const isSparse = (e: Embedding): e is SparseEmbedding => e._tag === "sparse"
|
|
108
108
|
export const isMultivector = (e: Embedding): e is MultivectorEmbedding => e._tag === "multivector"
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Domain of `EmbeddingFor` and the response type parameter on
|
|
112
|
+
* `EmbedResponse` / `EmbedManyResponse`: every encoding a provider can
|
|
113
|
+
* *return*, including Jina's widened `sparse` / `multivector`. Distinct
|
|
114
|
+
* from `EmbedEncoding` (the cross-provider *request* set, which is dense
|
|
115
|
+
* quantization only). Providers whose typed request widens beyond the
|
|
116
|
+
* dense set (Jina) parameterize their responses with their own encoding
|
|
117
|
+
* union, which is a subset of this.
|
|
118
|
+
*/
|
|
119
|
+
export type ResponseEncoding = "float32" | "int8" | "binary" | "sparse" | "multivector"
|
|
120
|
+
|
|
110
121
|
/**
|
|
111
122
|
* Maps an `encoding` request field to the corresponding response embedding
|
|
112
123
|
* variant. `undefined` (no encoding requested) defaults to `Float32Embedding`,
|
|
@@ -27,7 +27,7 @@ describe("EmbedResponse<E> conditional narrowing", () => {
|
|
|
27
27
|
}>()
|
|
28
28
|
})
|
|
29
29
|
|
|
30
|
-
it("maps each
|
|
30
|
+
it("maps each encoding literal to the matching Embedding variant", () => {
|
|
31
31
|
expectTypeOf<EmbedResponse<"float32">["embedding"]>().toEqualTypeOf<Float32Embedding>()
|
|
32
32
|
expectTypeOf<EmbedResponse<"int8">["embedding"]>().toEqualTypeOf<Int8Embedding>()
|
|
33
33
|
expectTypeOf<EmbedResponse<"binary">["embedding"]>().toEqualTypeOf<BinaryEmbedding>()
|
|
@@ -69,13 +69,24 @@ describe("embed / embedMany free exports preserve E in their return type", () =>
|
|
|
69
69
|
>()
|
|
70
70
|
})
|
|
71
71
|
|
|
72
|
-
it("embedMany with encoding:
|
|
73
|
-
const result = embedMany({ inputs: ["x"], model: "m", encoding: "
|
|
72
|
+
it("embedMany with encoding: binary returns EmbedManyResponse<binary>", () => {
|
|
73
|
+
const result = embedMany({ inputs: ["x"], model: "m", encoding: "binary" })
|
|
74
74
|
expectTypeOf(result).toEqualTypeOf<
|
|
75
|
-
Effect.Effect<EmbedManyResponse<"
|
|
75
|
+
Effect.Effect<EmbedManyResponse<"binary">, AiError.AiError, EmbeddingModel>
|
|
76
76
|
>()
|
|
77
77
|
})
|
|
78
78
|
|
|
79
|
+
it("rejects non-dense encodings on the generic path (trimmed EmbedEncoding)", () => {
|
|
80
|
+
// `sparse` / `multivector` are Jina-only representation kinds, not part
|
|
81
|
+
// of the cross-provider request set. Requesting them on the generic
|
|
82
|
+
// helper is a compile error; they're reachable only via the typed
|
|
83
|
+
// `JinaEmbedding` tag.
|
|
84
|
+
// @ts-expect-error "multivector" is not an EmbedEncoding
|
|
85
|
+
embedMany({ inputs: ["x"], model: "m", encoding: "multivector" })
|
|
86
|
+
// @ts-expect-error "sparse" is not an EmbedEncoding
|
|
87
|
+
embed({ input: "x", model: "m", encoding: "sparse" })
|
|
88
|
+
})
|
|
89
|
+
|
|
79
90
|
it("when the request is widened to CommonEmbedRequest, the response is the open union", () => {
|
|
80
91
|
// Demonstrates the documented case: annotating the request type erases
|
|
81
92
|
// the literal `encoding` and the response falls back to `Embedding`.
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
import { Context, Effect } from "effect"
|
|
2
2
|
import * as AiError from "../domain/AiError.js"
|
|
3
|
-
import type { EmbeddingFor, EmbedInput, Usage } from "./Embedding.js"
|
|
3
|
+
import type { EmbeddingFor, EmbedInput, ResponseEncoding, Usage } from "./Embedding.js"
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Output representation requested from the provider.
|
|
6
|
+
* Output representation requested from the provider. Cross-provider dense
|
|
7
|
+
* quantization only, the same vector at different storage cost:
|
|
8
|
+
* - `float32`: universal default.
|
|
9
|
+
* - `int8`: ~4x smaller, minimal recall loss on most benchmarks. Supported
|
|
10
|
+
* by Cohere, Voyage, Jina (binary only), Mixedbread.
|
|
11
|
+
* - `binary`: ~32x smaller, meaningful recall loss but pairs well with a
|
|
12
|
+
* float32 reranker pass over a small candidate set.
|
|
7
13
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
14
|
+
* Non-dense representations (`sparse`, `multivector`) are a different vector
|
|
15
|
+
* *structure*, not a storage option, and are produced by Jina only. They
|
|
16
|
+
* live on `JinaEncoding`, not here (the same widening pattern `task` uses).
|
|
17
|
+
* The response `Embedding` union still carries all arms, see
|
|
18
|
+
* {@link ResponseEncoding}.
|
|
13
19
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* Each provider's typed request narrows this to its supported set at
|
|
21
|
-
* compile time (e.g. `JinaEmbedEncoding = "float32" | "binary" | "sparse" |
|
|
22
|
-
* "multivector"`). On the generic `EmbeddingModel` path, callers can
|
|
23
|
-
* pass any `EmbedEncoding` and the provider's API will reject mismatches at
|
|
24
|
-
* runtime.
|
|
20
|
+
* Each provider's typed request narrows this to its supported set. On the
|
|
21
|
+
* generic `EmbeddingModel` path, a provider that emits float32 only (OpenAI,
|
|
22
|
+
* Gemini) rejects a non-float32 encoding with `AiError.Unsupported`
|
|
23
|
+
* (bucket 1) via {@link assertEncoding}, rather than silently returning a
|
|
24
|
+
* mislabeled float32 vector.
|
|
25
25
|
*/
|
|
26
|
-
export type EmbedEncoding = "float32" | "int8" | "binary"
|
|
26
|
+
export type EmbedEncoding = "float32" | "int8" | "binary"
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Cross-provider single-embed request. Mirrors the shape of
|
|
@@ -80,13 +80,13 @@ export type CommonEmbedManyRequest = Omit<CommonEmbedRequest, "input"> & {
|
|
|
80
80
|
* narrowing. Defaults to `undefined` for back-compat with consumers that
|
|
81
81
|
* use the bare `EmbedResponse` name.
|
|
82
82
|
*/
|
|
83
|
-
export type EmbedResponse<E extends
|
|
83
|
+
export type EmbedResponse<E extends ResponseEncoding | undefined = undefined> = {
|
|
84
84
|
readonly embedding: EmbeddingFor<E>
|
|
85
85
|
readonly usage: Usage
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
/** Batch-embed response. Same encoding rule as {@link EmbedResponse}. */
|
|
89
|
-
export type EmbedManyResponse<E extends
|
|
89
|
+
export type EmbedManyResponse<E extends ResponseEncoding | undefined = undefined> = {
|
|
90
90
|
readonly embeddings: ReadonlyArray<EmbeddingFor<E>>
|
|
91
91
|
readonly usage: Usage
|
|
92
92
|
}
|
|
@@ -115,3 +115,30 @@ export const embedMany = <E extends EmbedEncoding | undefined = undefined>(
|
|
|
115
115
|
request: Omit<CommonEmbedManyRequest, "encoding"> & { readonly encoding?: E },
|
|
116
116
|
): Effect.Effect<EmbedManyResponse<E>, AiError.AiError, EmbeddingModel> =>
|
|
117
117
|
Effect.flatMap(EmbeddingModel.asEffect(), (m) => m.embedMany(request))
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Guard a requested `encoding` against a provider's supported set. Returns
|
|
121
|
+
* `void` when the encoding is unset or supported, and fails
|
|
122
|
+
* `AiError.Unsupported` (bucket 1) otherwise.
|
|
123
|
+
*
|
|
124
|
+
* Used by the generic-tag registration of providers that emit a strict
|
|
125
|
+
* subset of `EmbedEncoding`: OpenAI / Gemini pass `["float32"]`, Jina passes
|
|
126
|
+
* `["float32", "binary"]`. Without this guard, a non-supported encoding on
|
|
127
|
+
* the generic path silently returns a float32 vector mislabeled as the
|
|
128
|
+
* requested type (the `as`-cast lie), which breaks downstream storage and
|
|
129
|
+
* vector math.
|
|
130
|
+
*/
|
|
131
|
+
export const assertEncoding = (
|
|
132
|
+
encoding: EmbedEncoding | undefined,
|
|
133
|
+
supported: ReadonlyArray<EmbedEncoding>,
|
|
134
|
+
provider: string,
|
|
135
|
+
): Effect.Effect<void, AiError.AiError> =>
|
|
136
|
+
encoding === undefined || supported.includes(encoding)
|
|
137
|
+
? Effect.void
|
|
138
|
+
: Effect.fail(
|
|
139
|
+
new AiError.Unsupported({
|
|
140
|
+
provider,
|
|
141
|
+
capability: "encoding",
|
|
142
|
+
reason: `${provider} emits ${supported.join(" / ")} only; encoding="${encoding}" is unavailable.`,
|
|
143
|
+
}),
|
|
144
|
+
)
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * as AiError from "./domain/AiError.js"
|
|
2
2
|
export * as Audio from "./domain/Audio.js"
|
|
3
|
+
export * as Capabilities from "./capabilities/Capabilities.js"
|
|
3
4
|
export * as Image from "./domain/Image.js"
|
|
4
5
|
export * as Items from "./domain/Items.js"
|
|
5
6
|
export * as Media from "./domain/Media.js"
|
|
@@ -17,6 +18,8 @@ export * as SandboxImage from "./sandbox/SandboxImage.js"
|
|
|
17
18
|
export * as SandboxNetwork from "./sandbox/SandboxNetwork.js"
|
|
18
19
|
export * as SpeechSynthesizer from "./speech-synthesizer/SpeechSynthesizer.js"
|
|
19
20
|
export * as Transcriber from "./transcriber/Transcriber.js"
|
|
21
|
+
export * as WebSearch from "./web-search/WebSearch.js"
|
|
22
|
+
export * as WebSearchTool from "./web-search/WebSearchTool.js"
|
|
20
23
|
export * as Vector from "./math/Vector.js"
|
|
21
24
|
export * as Loop from "./loop/Loop.js"
|
|
22
25
|
export * as Retry from "./retry/Retry.js"
|