@effect-uai/mistral 0.11.0 → 0.12.1
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/dist/Mistral.d.mts +6 -5
- package/dist/Mistral.d.mts.map +1 -1
- package/dist/Mistral.mjs +1 -1
- package/dist/Mistral.mjs.map +1 -1
- package/dist/MistralRealtimeTranscriber.d.mts +1 -3
- package/dist/MistralRealtimeTranscriber.d.mts.map +1 -1
- package/dist/MistralRealtimeTranscriber.mjs +1 -1
- package/dist/MistralRealtimeTranscriber.mjs.map +1 -1
- package/dist/MistralSynthesizer.d.mts +1 -3
- package/dist/MistralSynthesizer.d.mts.map +1 -1
- package/dist/MistralSynthesizer.mjs +4 -2
- package/dist/MistralSynthesizer.mjs.map +1 -1
- package/dist/MistralTranscriber.d.mts +1 -3
- package/dist/MistralTranscriber.d.mts.map +1 -1
- package/dist/MistralTranscriber.mjs +3 -2
- package/dist/MistralTranscriber.mjs.map +1 -1
- package/dist/audioCodec.d.mts +0 -1
- package/dist/audioCodec.d.mts.map +1 -1
- package/dist/audioCodec.mjs.map +1 -1
- package/dist/codec.d.mts +5 -82
- package/dist/codec.d.mts.map +1 -1
- package/dist/codec.mjs +11 -161
- package/dist/codec.mjs.map +1 -1
- package/dist/codec.test.d.mts +1 -1
- package/dist/codec.test.mjs +1 -1
- package/dist/codec.test.mjs.map +1 -1
- package/dist/http.d.mts +0 -1
- package/dist/http.d.mts.map +1 -1
- package/dist/http.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/{magic-string.es-CaWgF-3p.mjs → magic-string.es-CUAnjKYC.mjs} +14 -13
- package/dist/{magic-string.es-CaWgF-3p.mjs.map → magic-string.es-CUAnjKYC.mjs.map} +1 -1
- package/dist/{models-B1wZuvz5.d.mts → models-gP7kSqeb.d.mts} +1 -1
- package/dist/models-gP7kSqeb.d.mts.map +1 -0
- package/dist/models.d.mts +1 -1
- package/dist/onHalt.test.d.mts +1 -1
- package/dist/onHalt.test.mjs +1 -1
- package/dist/onHalt.test.mjs.map +1 -1
- package/dist/{realtimeStt-BFcctqFC.d.mts → realtimeStt-BcthMc57.d.mts} +3 -3
- package/dist/realtimeStt-BcthMc57.d.mts.map +1 -0
- package/dist/realtimeStt.d.mts +1 -1
- package/dist/realtimeStt.mjs.map +1 -1
- package/dist/{test.DNmyFkvJ-CPKukVWS.mjs → test.DNmyFkvJ-N3QqMRwZ.mjs} +247 -222
- package/dist/test.DNmyFkvJ-N3QqMRwZ.mjs.map +1 -0
- package/package.json +8 -5
- package/src/Mistral.ts +4 -7
- package/src/MistralRealtimeTranscriber.ts +4 -8
- package/src/MistralSynthesizer.ts +8 -12
- package/src/MistralTranscriber.ts +9 -11
- package/src/audioCodec.ts +25 -20
- package/src/codec.ts +40 -232
- package/dist/models-B1wZuvz5.d.mts.map +0 -1
- package/dist/realtimeStt-BFcctqFC.d.mts.map +0 -1
- package/dist/test.DNmyFkvJ-CPKukVWS.mjs.map +0 -1
- /package/dist/{chunk-CfYAbeIz.mjs → rolldown-runtime-D7D4PA-g.mjs} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-uai/mistral",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Mistral provider for @effect-uai/core: chat-completions plus Voxtral speech-to-text and text-to-speech.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -58,16 +58,19 @@
|
|
|
58
58
|
"publishConfig": {
|
|
59
59
|
"access": "public"
|
|
60
60
|
},
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"@effect-uai/chat-completions": "0.12.1"
|
|
63
|
+
},
|
|
61
64
|
"devDependencies": {
|
|
62
65
|
"@types/ws": "^8.5.13",
|
|
63
|
-
"effect": "4.0.0-
|
|
64
|
-
"typescript": "^
|
|
66
|
+
"effect": "4.0.0-rc.111",
|
|
67
|
+
"typescript": "^7.0.2",
|
|
65
68
|
"ws": "^8.18.0",
|
|
66
|
-
"@effect-uai/core": "0.
|
|
69
|
+
"@effect-uai/core": "0.12.1"
|
|
67
70
|
},
|
|
68
71
|
"peerDependencies": {
|
|
69
72
|
"@effect-uai/core": ">=0.2.0 <1",
|
|
70
|
-
"effect": ">=4.0.0-
|
|
73
|
+
"effect": ">=4.0.0-rc.111 <5.0.0",
|
|
71
74
|
"ws": "^8.0.0"
|
|
72
75
|
},
|
|
73
76
|
"peerDependenciesMeta": {
|
package/src/Mistral.ts
CHANGED
|
@@ -182,13 +182,10 @@ export const layer = (
|
|
|
182
182
|
const typed = Layer.effect(Mistral, make(cfg))
|
|
183
183
|
const generic = Layer.effect(
|
|
184
184
|
LanguageModel,
|
|
185
|
-
Effect.map(
|
|
186
|
-
|
|
187
|
-
(
|
|
188
|
-
|
|
189
|
-
turn: (request) => s.turn(request as MistralRequest),
|
|
190
|
-
}),
|
|
191
|
-
),
|
|
185
|
+
Effect.map(make(cfg), (s): LanguageModelService => ({
|
|
186
|
+
streamTurn: (request) => s.streamTurn(request as MistralRequest),
|
|
187
|
+
turn: (request) => s.turn(request as MistralRequest),
|
|
188
|
+
})),
|
|
192
189
|
)
|
|
193
190
|
return Layer.merge(typed, generic)
|
|
194
191
|
}
|
|
@@ -47,14 +47,10 @@ export const layer = (
|
|
|
47
47
|
Layer.effect(MistralTranscriber, make(cfg)),
|
|
48
48
|
Layer.effect(
|
|
49
49
|
Transcriber,
|
|
50
|
-
Effect.map(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
s.transcribe(req as MistralTranscribeRequest),
|
|
55
|
-
streamTranscriptionFrom: s.streamTranscriptionFrom,
|
|
56
|
-
}),
|
|
57
|
-
),
|
|
50
|
+
Effect.map(make(cfg), (s): TranscriberService => ({
|
|
51
|
+
transcribe: (req: CommonTranscribeRequest) => s.transcribe(req as MistralTranscribeRequest),
|
|
52
|
+
streamTranscriptionFrom: s.streamTranscriptionFrom,
|
|
53
|
+
})),
|
|
58
54
|
),
|
|
59
55
|
Layer.succeed(SttStreaming, undefined),
|
|
60
56
|
)
|
|
@@ -268,18 +268,14 @@ export const layer = (
|
|
|
268
268
|
Layer.effect(MistralSynthesizer, make(cfg)),
|
|
269
269
|
Layer.effect(
|
|
270
270
|
SpeechSynthesizer,
|
|
271
|
-
Effect.map(
|
|
272
|
-
|
|
273
|
-
(
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
synthesizeDialogue: s.synthesizeDialogue,
|
|
280
|
-
streamSynthesizeDialogue: s.streamSynthesizeDialogue,
|
|
281
|
-
}),
|
|
282
|
-
),
|
|
271
|
+
Effect.map(make(cfg), (s): SpeechSynthesizerService => ({
|
|
272
|
+
synthesize: (req: CommonSynthesizeRequest) => s.synthesize(req as MistralSynthesizeRequest),
|
|
273
|
+
streamSynthesis: (req: CommonSynthesizeRequest) =>
|
|
274
|
+
s.streamSynthesis(req as MistralSynthesizeRequest),
|
|
275
|
+
streamSynthesisFrom: s.streamSynthesisFrom,
|
|
276
|
+
synthesizeDialogue: s.synthesizeDialogue,
|
|
277
|
+
streamSynthesizeDialogue: s.streamSynthesizeDialogue,
|
|
278
|
+
})),
|
|
283
279
|
),
|
|
284
280
|
Layer.succeed(TtsIncrementalText, undefined),
|
|
285
281
|
)
|
|
@@ -86,9 +86,11 @@ const buildFormData = (
|
|
|
86
86
|
const filePart: Part = isAudioUrl(request.audio)
|
|
87
87
|
? ["file_url", request.audio.url]
|
|
88
88
|
: yield* audioToBlob(request.audio).pipe(
|
|
89
|
-
Effect.map(
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
Effect.map((blob): Part => [
|
|
90
|
+
"file",
|
|
91
|
+
blob,
|
|
92
|
+
request.fileName ?? defaultFileName(blob.type),
|
|
93
|
+
]),
|
|
92
94
|
)
|
|
93
95
|
const parts: ReadonlyArray<Part> = [
|
|
94
96
|
filePart,
|
|
@@ -212,13 +214,9 @@ export const layer = (
|
|
|
212
214
|
Layer.effect(MistralTranscriber, make(cfg)),
|
|
213
215
|
Layer.effect(
|
|
214
216
|
Transcriber,
|
|
215
|
-
Effect.map(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
s.transcribe(req as MistralTranscribeRequest),
|
|
220
|
-
streamTranscriptionFrom: s.streamTranscriptionFrom,
|
|
221
|
-
}),
|
|
222
|
-
),
|
|
217
|
+
Effect.map(make(cfg), (s): TranscriberService => ({
|
|
218
|
+
transcribe: (req: CommonTranscribeRequest) => s.transcribe(req as MistralTranscribeRequest),
|
|
219
|
+
streamTranscriptionFrom: s.streamTranscriptionFrom,
|
|
220
|
+
})),
|
|
223
221
|
),
|
|
224
222
|
)
|
package/src/audioCodec.ts
CHANGED
|
@@ -99,25 +99,30 @@ export const containerToTtsFormat: (
|
|
|
99
99
|
*/
|
|
100
100
|
export const realizedTtsFormat: (rf: MistralTtsFormat) => AudioFormat =
|
|
101
101
|
Match.type<MistralTtsFormat>().pipe(
|
|
102
|
-
Match.when(
|
|
103
|
-
"mp3",
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
"
|
|
120
|
-
|
|
121
|
-
),
|
|
102
|
+
Match.when("mp3", (): AudioFormat => ({
|
|
103
|
+
container: "mp3",
|
|
104
|
+
encoding: "mp3",
|
|
105
|
+
sampleRate: 24000,
|
|
106
|
+
})),
|
|
107
|
+
Match.when("wav", (): AudioFormat => ({
|
|
108
|
+
container: "wav",
|
|
109
|
+
encoding: "pcm_s16le",
|
|
110
|
+
sampleRate: 24000,
|
|
111
|
+
})),
|
|
112
|
+
Match.when("pcm", (): AudioFormat => ({
|
|
113
|
+
container: "raw",
|
|
114
|
+
encoding: "pcm_f32le",
|
|
115
|
+
sampleRate: 24000,
|
|
116
|
+
})),
|
|
117
|
+
Match.when("flac", (): AudioFormat => ({
|
|
118
|
+
container: "flac",
|
|
119
|
+
encoding: "flac",
|
|
120
|
+
sampleRate: 24000,
|
|
121
|
+
})),
|
|
122
|
+
Match.when("opus", (): AudioFormat => ({
|
|
123
|
+
container: "opus",
|
|
124
|
+
encoding: "opus",
|
|
125
|
+
sampleRate: 24000,
|
|
126
|
+
})),
|
|
122
127
|
Match.exhaustive,
|
|
123
128
|
)
|
package/src/codec.ts
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
|
-
import { Array as Arr, Encoding, Match, Option
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import { Array as Arr, Effect, Encoding, Match, Option } from "effect"
|
|
2
|
+
import type { ContentBlock, HistoryItem, InputImage } from "@effect-uai/core/Items"
|
|
3
|
+
|
|
4
|
+
// The streaming decoder, tool encoding, and terminal-turn assembly are the
|
|
5
|
+
// generic OpenAI chat-completions codec: Mistral speaks that dialect on the
|
|
6
|
+
// wire, so it reuses them verbatim. Only request encoding diverges (below).
|
|
7
|
+
export {
|
|
8
|
+
type Accumulator,
|
|
9
|
+
type WireChunk,
|
|
10
|
+
accumulatorToTurn,
|
|
11
|
+
applyChunk,
|
|
12
|
+
emptyAccumulator,
|
|
13
|
+
responseFormatWire,
|
|
14
|
+
toolsWire,
|
|
15
|
+
} from "@effect-uai/chat-completions/codec"
|
|
16
|
+
import {
|
|
17
|
+
type WireChunk,
|
|
18
|
+
decodeChunk as decodeChunkShared,
|
|
19
|
+
} from "@effect-uai/chat-completions/codec"
|
|
13
20
|
|
|
14
21
|
// ---------------------------------------------------------------------------
|
|
15
22
|
// History → Mistral chat `messages`
|
|
16
23
|
//
|
|
17
|
-
//
|
|
18
|
-
// `
|
|
19
|
-
//
|
|
20
|
-
// `{ role: "tool", tool_call_id, content }` message. Our history is a flatter
|
|
21
|
-
// item list (assistant message, then separate `function_call` items), so we
|
|
22
|
-
// fold consecutive `function_call`s onto the preceding assistant message.
|
|
24
|
+
// Same shape as the generic codec, with one wire quirk: Mistral takes a bare
|
|
25
|
+
// `image_url` string, not OpenAI's `image_url: { url }` object. That is the
|
|
26
|
+
// reason this half stays local rather than importing `itemsToMessages`.
|
|
23
27
|
// ---------------------------------------------------------------------------
|
|
24
28
|
|
|
25
29
|
type WireMessage = {
|
|
@@ -42,6 +46,7 @@ const blockToPart = Match.type<ContentBlock>().pipe(
|
|
|
42
46
|
input_text: (b) => ({ type: "text", text: b.text }),
|
|
43
47
|
output_text: (b) => ({ type: "text", text: b.text }),
|
|
44
48
|
refusal: (b) => ({ type: "text", text: b.text }),
|
|
49
|
+
// Bare string, not `{ url }`: the Mistral divergence from OpenAI.
|
|
45
50
|
input_image: (b) => ({ type: "image_url", image_url: imageSourceToUrl(b.source) }),
|
|
46
51
|
}),
|
|
47
52
|
)
|
|
@@ -102,26 +107,9 @@ export const itemsToMessages = (items: ReadonlyArray<HistoryItem>): ReadonlyArra
|
|
|
102
107
|
Arr.reduce(items, [] as ReadonlyArray<WireMessage>, foldItem)
|
|
103
108
|
|
|
104
109
|
// ---------------------------------------------------------------------------
|
|
105
|
-
//
|
|
110
|
+
// tool_choice
|
|
106
111
|
// ---------------------------------------------------------------------------
|
|
107
112
|
|
|
108
|
-
export const toolsWire = (
|
|
109
|
-
descriptors: ReadonlyArray<ToolDescriptor>,
|
|
110
|
-
): Option.Option<ReadonlyArray<Record<string, unknown>>> =>
|
|
111
|
-
descriptors.length > 0
|
|
112
|
-
? Option.some(
|
|
113
|
-
descriptors.map((t) => ({
|
|
114
|
-
type: "function",
|
|
115
|
-
function: {
|
|
116
|
-
name: t.name,
|
|
117
|
-
description: t.description,
|
|
118
|
-
parameters: t.inputSchema,
|
|
119
|
-
...(t.strict !== undefined && { strict: t.strict }),
|
|
120
|
-
},
|
|
121
|
-
})),
|
|
122
|
-
)
|
|
123
|
-
: Option.none()
|
|
124
|
-
|
|
125
113
|
type ToolChoice =
|
|
126
114
|
| "auto"
|
|
127
115
|
| "required"
|
|
@@ -137,202 +125,22 @@ export const toolChoiceWire = (choice: ToolChoice): string | Record<string, unkn
|
|
|
137
125
|
Match.orElse((c) => ({ type: "function", function: { name: c.name } })),
|
|
138
126
|
)
|
|
139
127
|
|
|
140
|
-
export const responseFormatWire = (
|
|
141
|
-
structured: StructuredFormat<unknown>,
|
|
142
|
-
): Record<string, unknown> => ({
|
|
143
|
-
type: "json_schema",
|
|
144
|
-
json_schema: {
|
|
145
|
-
name: structured.name,
|
|
146
|
-
schema: structured.schema["~standard"].jsonSchema.input({ target: "draft-2020-12" }),
|
|
147
|
-
...(structured.strict !== undefined && { strict: structured.strict }),
|
|
148
|
-
},
|
|
149
|
-
})
|
|
150
|
-
|
|
151
128
|
// ---------------------------------------------------------------------------
|
|
152
|
-
// Streaming decode
|
|
129
|
+
// Streaming decode
|
|
153
130
|
// ---------------------------------------------------------------------------
|
|
154
131
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
const WireChoice = Schema.Struct({
|
|
173
|
-
index: Schema.optional(Schema.Number),
|
|
174
|
-
delta: Schema.optional(WireDelta),
|
|
175
|
-
finish_reason: Schema.optional(Schema.NullOr(Schema.String)),
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
const WireUsage = Schema.Struct({
|
|
179
|
-
prompt_tokens: Schema.optional(Schema.Number),
|
|
180
|
-
completion_tokens: Schema.optional(Schema.Number),
|
|
181
|
-
total_tokens: Schema.optional(Schema.Number),
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
export const WireChunk = Schema.Struct({
|
|
185
|
-
choices: Schema.optional(Schema.Array(WireChoice)),
|
|
186
|
-
usage: Schema.optional(Schema.NullOr(WireUsage)),
|
|
187
|
-
})
|
|
188
|
-
export type WireChunk = typeof WireChunk.Type
|
|
189
|
-
|
|
190
|
-
export const decodeChunk = Schema.decodeUnknownEffect(WireChunk)
|
|
191
|
-
|
|
192
|
-
// ---------------------------------------------------------------------------
|
|
193
|
-
// Accumulator
|
|
194
|
-
// ---------------------------------------------------------------------------
|
|
195
|
-
|
|
196
|
-
type ToolAcc = {
|
|
197
|
-
readonly call_id: string
|
|
198
|
-
readonly name: string
|
|
199
|
-
readonly arguments: string
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export type Accumulator = {
|
|
203
|
-
readonly text: string
|
|
204
|
-
/** Tool calls keyed by their wire `index` (defaults to position when absent). */
|
|
205
|
-
readonly tools: ReadonlyMap<number, ToolAcc>
|
|
206
|
-
readonly order: ReadonlyArray<number>
|
|
207
|
-
readonly usage: Usage
|
|
208
|
-
readonly finishReason: Option.Option<string>
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export const emptyAccumulator: Accumulator = {
|
|
212
|
-
text: "",
|
|
213
|
-
tools: new Map(),
|
|
214
|
-
order: [],
|
|
215
|
-
usage: {},
|
|
216
|
-
finishReason: Option.none(),
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/** State threaded through the fold plus the events the step produced. */
|
|
220
|
-
type Step = readonly [Accumulator, ReadonlyArray<TurnEvent>]
|
|
221
|
-
|
|
222
|
-
const usageFrom = (wire: typeof WireUsage.Type): Usage => ({
|
|
223
|
-
...(wire.prompt_tokens !== undefined && { input_tokens: wire.prompt_tokens }),
|
|
224
|
-
...(wire.completion_tokens !== undefined && { output_tokens: wire.completion_tokens }),
|
|
225
|
-
...(wire.total_tokens !== undefined && { total_tokens: wire.total_tokens }),
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
const reasonToStop = Match.type<string>().pipe(
|
|
229
|
-
Match.when("stop", () => "stop" as const),
|
|
230
|
-
Match.when("length", () => "max_tokens" as const),
|
|
231
|
-
Match.when("model_length", () => "max_tokens" as const),
|
|
232
|
-
Match.when("tool_calls", () => "tool_calls" as const),
|
|
233
|
-
Match.when("content_filter", () => "content_filter" as const),
|
|
234
|
-
Match.orElse(() => "stop" as const),
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
const stopReasonOf = (acc: Accumulator): StopReason =>
|
|
238
|
-
Option.match(acc.finishReason, {
|
|
239
|
-
onNone: () => (acc.tools.size > 0 ? "tool_calls" : "stop"),
|
|
240
|
-
onSome: reasonToStop,
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
const withTool = (acc: Accumulator, index: number, tool: ToolAcc, isNew: boolean): Accumulator => ({
|
|
244
|
-
...acc,
|
|
245
|
-
tools: new Map(acc.tools).set(index, tool),
|
|
246
|
-
order: isNew ? [...acc.order, index] : acc.order,
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
const applyContent = (acc: Accumulator, delta: typeof WireDelta.Type | undefined): Step => {
|
|
250
|
-
const content = delta?.content
|
|
251
|
-
return content !== undefined && content !== null && content.length > 0
|
|
252
|
-
? [{ ...acc, text: acc.text + content }, [TurnEvent.TextDelta({ text: content })]]
|
|
253
|
-
: [acc, []]
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const applyToolCall = (acc: Accumulator, tc: typeof WireToolCall.Type, position: number): Step => {
|
|
257
|
-
const index = tc.index ?? position
|
|
258
|
-
const argsDelta = tc.function?.arguments ?? ""
|
|
259
|
-
const existing = acc.tools.get(index)
|
|
260
|
-
if (existing === undefined) {
|
|
261
|
-
const call_id = tc.id ?? `call_${index}`
|
|
262
|
-
const name = tc.function?.name ?? ""
|
|
263
|
-
return [
|
|
264
|
-
withTool(acc, index, { call_id, name, arguments: argsDelta }, true),
|
|
265
|
-
[
|
|
266
|
-
TurnEvent.ToolCallStart({ call_id, name }),
|
|
267
|
-
...(argsDelta.length > 0
|
|
268
|
-
? [TurnEvent.ToolCallArgsDelta({ call_id, delta: argsDelta })]
|
|
269
|
-
: []),
|
|
270
|
-
],
|
|
271
|
-
]
|
|
272
|
-
}
|
|
273
|
-
return argsDelta.length > 0
|
|
274
|
-
? [
|
|
275
|
-
withTool(acc, index, { ...existing, arguments: existing.arguments + argsDelta }, false),
|
|
276
|
-
[TurnEvent.ToolCallArgsDelta({ call_id: existing.call_id, delta: argsDelta })],
|
|
277
|
-
]
|
|
278
|
-
: [acc, []]
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const chain = (step: Step, next: (acc: Accumulator) => Step): Step => {
|
|
282
|
-
const [acc, events] = step
|
|
283
|
-
const [acc2, more] = next(acc)
|
|
284
|
-
return [acc2, [...events, ...more]]
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
const applyChoice = (step: Step, choice: typeof WireChoice.Type): Step => {
|
|
288
|
-
const withContent = chain(step, (acc) => applyContent(acc, choice.delta))
|
|
289
|
-
const withTools = Arr.reduce(choice.delta?.tool_calls ?? [], withContent, (s: Step, tc, i) =>
|
|
290
|
-
chain(s, (acc) => applyToolCall(acc, tc, i)),
|
|
291
|
-
)
|
|
292
|
-
const reason = choice.finish_reason
|
|
293
|
-
return reason !== undefined && reason !== null
|
|
294
|
-
? [{ ...withTools[0], finishReason: Option.some(reason) }, withTools[1]]
|
|
295
|
-
: withTools
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Fold one decoded chunk into the accumulator, returning the next state and
|
|
300
|
-
* the `TurnEvent`s the chunk produced. Mirrors Anthropic's `deltasFromEvent`,
|
|
301
|
-
* but over chat-completions deltas.
|
|
302
|
-
*/
|
|
303
|
-
export const applyChunk = (acc: Accumulator, chunk: WireChunk): Step => {
|
|
304
|
-
const afterChoices = Arr.reduce(chunk.choices ?? [], [acc, []] as Step, applyChoice)
|
|
305
|
-
const usage = chunk.usage
|
|
306
|
-
return usage !== undefined && usage !== null
|
|
307
|
-
? chain(afterChoices, (a) => {
|
|
308
|
-
const u = usageFrom(usage)
|
|
309
|
-
return [{ ...a, usage: u }, [TurnEvent.UsageUpdate({ usage: u })]]
|
|
310
|
-
})
|
|
311
|
-
: afterChoices
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/** Assemble the terminal `Turn` from the accumulated state. */
|
|
315
|
-
export const accumulatorToTurn = (acc: Accumulator): Turn => {
|
|
316
|
-
const message: ReadonlyArray<HistoryItem> =
|
|
317
|
-
acc.text.length > 0
|
|
318
|
-
? [{ type: "message", role: "assistant", content: [{ type: "output_text", text: acc.text }] }]
|
|
319
|
-
: []
|
|
320
|
-
const toolCalls: ReadonlyArray<HistoryItem> = acc.order.flatMap((index) => {
|
|
321
|
-
const t = acc.tools.get(index)
|
|
322
|
-
return t === undefined
|
|
323
|
-
? []
|
|
324
|
-
: [
|
|
325
|
-
{
|
|
326
|
-
type: "function_call" as const,
|
|
327
|
-
call_id: t.call_id,
|
|
328
|
-
name: t.name,
|
|
329
|
-
arguments: t.arguments,
|
|
330
|
-
},
|
|
331
|
-
]
|
|
332
|
-
})
|
|
333
|
-
return {
|
|
334
|
-
items: [...message, ...toolCalls],
|
|
335
|
-
usage: acc.usage,
|
|
336
|
-
stop_reason: stopReasonOf(acc),
|
|
337
|
-
}
|
|
338
|
-
}
|
|
132
|
+
// Mistral emits `model_length` when it truncates on the context window; the
|
|
133
|
+
// shared decoder only knows OpenAI's `length`. Rewrite it before folding so the
|
|
134
|
+
// shared accumulator maps it to `max_tokens` rather than the `stop` fallback.
|
|
135
|
+
const normalizeChunk = (chunk: WireChunk): WireChunk =>
|
|
136
|
+
chunk.choices === undefined
|
|
137
|
+
? chunk
|
|
138
|
+
: {
|
|
139
|
+
...chunk,
|
|
140
|
+
choices: chunk.choices.map((c) =>
|
|
141
|
+
c.finish_reason === "model_length" ? { ...c, finish_reason: "length" } : c,
|
|
142
|
+
),
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const decodeChunk = (u: unknown): Effect.Effect<WireChunk, unknown> =>
|
|
146
|
+
decodeChunkShared(u).pipe(Effect.map(normalizeChunk))
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"models-B1wZuvz5.d.mts","names":[],"sources":["../src/models.ts"],"mappings":";;AAWA;;;;;AAmBA;;;;;KAnBY,YAAA;;KAmBA,sBAAA;;KAOA,oBAAA;AAMZ;AAAA,KAAY,eAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"realtimeStt-BFcctqFC.d.mts","names":[],"sources":["../src/realtimeStt.ts"],"mappings":";;;;;;KA8CY,MAAA;EAAA,SACD,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SACjB,OAAA,WA4J+B;EAAA,SA1J/B,oBAAA,GAAuB,QAAA,CAAS,QAAA;EA2J9B;;;;;EAAA,SArJF,gBAAA,GAAmB,QAAA,CAAS,QAAA;AAAA;AAAA,cAiJ1B,mBAAA,GACV,GAAA,EAAK,MAAA,YAEJ,OAAA,EAAS,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,CAAA,EAAG,CAAA,GACtC,OAAA,EAAS,6BAAA,KACR,MAAA,CAAO,MAAA,CAAO,eAAA,EAAiB,OAAA,CAAQ,OAAA,GAAU,CAAA,EAAG,CAAA"}
|