llm-mock-server 1.0.2 → 1.0.3

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.
@@ -1,6 +1,13 @@
1
1
  import type { ReplyObject, ReplyOptions } from "../../types.js";
2
2
  import type { SSEChunk } from "../types.js";
3
- import { splitText, genId, toolId, finishReason, MS_PER_SECOND, DEFAULT_USAGE } from "../serialize-helpers.js";
3
+ import {
4
+ splitText,
5
+ genId,
6
+ toolId,
7
+ finishReason,
8
+ MS_PER_SECOND,
9
+ DEFAULT_USAGE,
10
+ } from "../serialize-helpers.js";
4
11
 
5
12
  function buildUsage(usage: { input: number; output: number }) {
6
13
  return {
@@ -8,18 +15,29 @@ function buildUsage(usage: { input: number; output: number }) {
8
15
  completion_tokens: usage.output,
9
16
  total_tokens: usage.input + usage.output,
10
17
  prompt_tokens_details: { cached_tokens: 0, audio_tokens: 0 },
11
- completion_tokens_details: { reasoning_tokens: 0, audio_tokens: 0, accepted_prediction_tokens: 0, rejected_prediction_tokens: 0 },
18
+ completion_tokens_details: {
19
+ reasoning_tokens: 0,
20
+ audio_tokens: 0,
21
+ accepted_prediction_tokens: 0,
22
+ rejected_prediction_tokens: 0,
23
+ },
12
24
  };
13
25
  }
14
26
 
15
27
  function chunkEnvelope(
16
- id: string, created: number, model: string,
17
- delta: Record<string, unknown>, finish_reason: string | null = null,
28
+ id: string,
29
+ created: number,
30
+ model: string,
31
+ delta: Record<string, unknown>,
32
+ finish_reason: string | null = null,
18
33
  usage: Record<string, unknown> | null = null,
19
34
  ): SSEChunk {
20
35
  return {
21
36
  data: JSON.stringify({
22
- id, object: "chat.completion.chunk", created, model,
37
+ id,
38
+ object: "chat.completion.chunk",
39
+ created,
40
+ model,
23
41
  system_fingerprint: null,
24
42
  service_tier: "default",
25
43
  choices: [{ index: 0, delta, logprobs: null, finish_reason }],
@@ -28,7 +46,11 @@ function chunkEnvelope(
28
46
  };
29
47
  }
30
48
 
31
- export function serialize(reply: ReplyObject, model: string, options: ReplyOptions = {}): readonly SSEChunk[] {
49
+ export function serialize(
50
+ reply: ReplyObject,
51
+ model: string,
52
+ options: ReplyOptions = {},
53
+ ): readonly SSEChunk[] {
32
54
  const id = genId("chatcmpl");
33
55
  const created = Math.floor(Date.now() / MS_PER_SECOND);
34
56
  const usage = reply.usage ?? DEFAULT_USAGE;
@@ -41,10 +63,14 @@ export function serialize(reply: ReplyObject, model: string, options: ReplyOptio
41
63
 
42
64
  const toolChunks = (reply.tools ?? []).map((tool, i) =>
43
65
  chunkEnvelope(id, created, model, {
44
- tool_calls: [{
45
- index: i, id: toolId(tool, "call", i), type: "function",
46
- function: { name: tool.name, arguments: JSON.stringify(tool.args) },
47
- }],
66
+ tool_calls: [
67
+ {
68
+ index: i,
69
+ id: toolId(tool, "call", i),
70
+ type: "function",
71
+ function: { name: tool.name, arguments: JSON.stringify(tool.args) },
72
+ },
73
+ ],
48
74
  }),
49
75
  );
50
76
 
@@ -54,13 +80,22 @@ export function serialize(reply: ReplyObject, model: string, options: ReplyOptio
54
80
  chunkEnvelope(id, created, model, { role: "assistant" }),
55
81
  ...textChunks,
56
82
  ...toolChunks,
57
- chunkEnvelope(id, created, model, {}, finishReason(reply, "tool_calls", "stop")),
83
+ chunkEnvelope(
84
+ id,
85
+ created,
86
+ model,
87
+ {},
88
+ finishReason(reply, "tool_calls", "stop"),
89
+ ),
58
90
  chunkEnvelope(id, created, model, {}, null, usageChunk),
59
91
  { data: "[DONE]" },
60
92
  ];
61
93
  }
62
94
 
63
- export function serializeComplete(reply: ReplyObject, model: string): Record<string, unknown> {
95
+ export function serializeComplete(
96
+ reply: ReplyObject,
97
+ model: string,
98
+ ): Record<string, unknown> {
64
99
  const id = genId("chatcmpl");
65
100
  const created = Math.floor(Date.now() / MS_PER_SECOND);
66
101
  const usage = reply.usage ?? DEFAULT_USAGE;
@@ -70,21 +105,42 @@ export function serializeComplete(reply: ReplyObject, model: string): Record<str
70
105
  content: reply.text ?? null,
71
106
  ...(reply.tools?.length && {
72
107
  tool_calls: reply.tools.map((tool, i) => ({
73
- id: toolId(tool, "call", i), type: "function",
108
+ id: toolId(tool, "call", i),
109
+ type: "function",
74
110
  function: { name: tool.name, arguments: JSON.stringify(tool.args) },
75
111
  })),
76
112
  }),
77
113
  };
78
114
 
79
115
  return {
80
- id, object: "chat.completion", created, model,
116
+ id,
117
+ object: "chat.completion",
118
+ created,
119
+ model,
81
120
  system_fingerprint: null,
82
121
  service_tier: "default",
83
- choices: [{ index: 0, message, logprobs: null, finish_reason: finishReason(reply, "tool_calls", "stop") }],
122
+ choices: [
123
+ {
124
+ index: 0,
125
+ message,
126
+ logprobs: null,
127
+ finish_reason: finishReason(reply, "tool_calls", "stop"),
128
+ },
129
+ ],
84
130
  usage: buildUsage(usage),
85
131
  };
86
132
  }
87
133
 
88
- export function serializeError(error: { status: number; message: string; type?: string }): Record<string, unknown> {
89
- return { error: { message: error.message, type: error.type ?? "server_error", code: null } };
134
+ export function serializeError(error: {
135
+ status: number;
136
+ message: string;
137
+ type?: string;
138
+ }): Record<string, unknown> {
139
+ return {
140
+ error: {
141
+ message: error.message,
142
+ type: error.type ?? "server_error",
143
+ code: null,
144
+ },
145
+ };
90
146
  }
@@ -1,7 +1,8 @@
1
1
  import type { FormatName, Message, MockRequest, ToolDef } from "../types.js";
2
2
 
3
3
  function asRecord(body: unknown): Record<string, unknown> {
4
- if (typeof body === "object" && body !== null) return body as Record<string, unknown>;
4
+ if (typeof body === "object" && body !== null)
5
+ return body as Record<string, unknown>;
5
6
  return {};
6
7
  }
7
8
 
@@ -1,8 +1,14 @@
1
1
  import type { MockRequest, Message, ToolDef } from "../../types.js";
2
2
  import { buildMockRequest, type RequestMeta } from "../request-helpers.js";
3
- import { ResponsesRequestSchema, FunctionToolSchema, type ResponsesRequest } from "./schema.js";
3
+ import {
4
+ ResponsesRequestSchema,
5
+ FunctionToolSchema,
6
+ type ResponsesRequest,
7
+ } from "./schema.js";
4
8
 
5
- function extractInputContent(content: string | Record<string, unknown>[]): string {
9
+ function extractInputContent(
10
+ content: string | Record<string, unknown>[],
11
+ ): string {
6
12
  if (typeof content === "string") return content;
7
13
  return content
8
14
  .filter((b) => b["type"] === "input_text" || b["type"] === "text")
@@ -52,5 +58,13 @@ function parseTools(req: ResponsesRequest): readonly ToolDef[] | undefined {
52
58
 
53
59
  export function parseRequest(body: unknown, meta?: RequestMeta): MockRequest {
54
60
  const req = ResponsesRequestSchema.parse(body);
55
- return buildMockRequest("responses", req, parseInput(req), parseTools(req), "codex-mini", body, meta);
61
+ return buildMockRequest(
62
+ "responses",
63
+ req,
64
+ parseInput(req),
65
+ parseTools(req),
66
+ "codex-mini",
67
+ body,
68
+ meta,
69
+ );
56
70
  }
@@ -1,6 +1,10 @@
1
1
  import { z } from "zod";
2
2
 
3
- export { ResponsesRequestSchema, FunctionToolSchema, type ResponsesRequest } from "llm-schemas/openai/responses";
3
+ export {
4
+ ResponsesRequestSchema,
5
+ FunctionToolSchema,
6
+ type ResponsesRequest,
7
+ } from "llm-schemas/openai/responses";
4
8
 
5
9
  const OutputContentSchema = z.object({
6
10
  type: z.string(),
@@ -23,25 +27,31 @@ const OutputItemSchema = z.object({
23
27
  export const ResponsesEventSchema = z.object({
24
28
  type: z.string(),
25
29
  sequence_number: z.number().optional(),
26
- response: z.object({
27
- id: z.string(),
28
- object: z.string(),
29
- created_at: z.number(),
30
- model: z.string(),
31
- status: z.string(),
32
- output: z.array(OutputItemSchema),
33
- usage: z.object({
34
- input_tokens: z.number(),
35
- output_tokens: z.number(),
36
- total_tokens: z.number(),
37
- }).optional(),
38
- }).optional(),
30
+ response: z
31
+ .object({
32
+ id: z.string(),
33
+ object: z.string(),
34
+ created_at: z.number(),
35
+ model: z.string(),
36
+ status: z.string(),
37
+ output: z.array(OutputItemSchema),
38
+ usage: z
39
+ .object({
40
+ input_tokens: z.number(),
41
+ output_tokens: z.number(),
42
+ total_tokens: z.number(),
43
+ })
44
+ .optional(),
45
+ })
46
+ .optional(),
39
47
  item: OutputItemSchema.optional(),
40
- part: z.object({
41
- type: z.string(),
42
- text: z.string().optional(),
43
- annotations: z.array(z.unknown()).optional(),
44
- }).optional(),
48
+ part: z
49
+ .object({
50
+ type: z.string(),
51
+ text: z.string().optional(),
52
+ annotations: z.array(z.unknown()).optional(),
53
+ })
54
+ .optional(),
45
55
  delta: z.string().optional(),
46
56
  item_id: z.string().optional(),
47
57
  });
@@ -66,7 +76,11 @@ export type ResponsesComplete = z.infer<typeof ResponsesCompleteSchema>;
66
76
 
67
77
  export const ResponsesErrorSchema = z.object({
68
78
  type: z.literal("error"),
69
- error: z.object({ message: z.string(), type: z.string().optional(), code: z.string().optional() }),
79
+ error: z.object({
80
+ message: z.string(),
81
+ type: z.string().optional(),
82
+ code: z.string().optional(),
83
+ }),
70
84
  });
71
85
 
72
86
  export type ResponsesError = z.infer<typeof ResponsesErrorSchema>;
@@ -1,12 +1,26 @@
1
1
  import type { ReplyObject, ReplyOptions, ToolCall } from "../../types.js";
2
2
  import type { SSEChunk } from "../types.js";
3
- import { splitText, genId, toolId, shouldEmitText, MS_PER_SECOND, DEFAULT_USAGE } from "../serialize-helpers.js";
3
+ import {
4
+ splitText,
5
+ genId,
6
+ toolId,
7
+ shouldEmitText,
8
+ MS_PER_SECOND,
9
+ DEFAULT_USAGE,
10
+ } from "../serialize-helpers.js";
4
11
 
5
12
  function buildUsage(usage: { input: number; output: number }) {
6
- return { input_tokens: usage.input, output_tokens: usage.output, total_tokens: usage.input + usage.output };
13
+ return {
14
+ input_tokens: usage.input,
15
+ output_tokens: usage.output,
16
+ total_tokens: usage.input + usage.output,
17
+ };
7
18
  }
8
19
 
9
- interface StreamBlock { chunks: SSEChunk[]; outputItem: unknown }
20
+ interface StreamBlock {
21
+ chunks: SSEChunk[];
22
+ outputItem: unknown;
23
+ }
10
24
 
11
25
  const NO_ANNOTATIONS: readonly unknown[] = [];
12
26
 
@@ -14,43 +28,140 @@ type Chunk = (payload: Record<string, unknown>) => SSEChunk;
14
28
 
15
29
  function createChunk(): Chunk {
16
30
  let seq = 0;
17
- return (payload) => ({ data: JSON.stringify({ ...payload, sequence_number: seq++ }) });
31
+ return (payload) => ({
32
+ data: JSON.stringify({ ...payload, sequence_number: seq++ }),
33
+ });
18
34
  }
19
35
 
20
- function reasoningStreamBlock(c: Chunk, i: number, reasoning: string): StreamBlock {
36
+ function reasoningStreamBlock(
37
+ c: Chunk,
38
+ i: number,
39
+ reasoning: string,
40
+ ): StreamBlock {
21
41
  const itemId = `rs_${genId("rs")}`;
22
42
  const summaryPart = { type: "summary_text" as const, text: reasoning };
23
- const item = { type: "reasoning", id: itemId, status: "completed", summary: [summaryPart] };
43
+ const item = {
44
+ type: "reasoning",
45
+ id: itemId,
46
+ status: "completed",
47
+ summary: [summaryPart],
48
+ };
24
49
 
25
50
  return {
26
51
  outputItem: item,
27
52
  chunks: [
28
- c({ type: "response.output_item.added", output_index: i, item: { type: "reasoning", id: itemId, status: "in_progress", summary: [] } }),
29
- c({ type: "response.reasoning_summary_part.added", item_id: itemId, output_index: i, summary_index: 0, part: { type: "summary_text", text: "" } }),
30
- c({ type: "response.reasoning_summary_text.delta", item_id: itemId, output_index: i, summary_index: 0, delta: reasoning }),
31
- c({ type: "response.reasoning_summary_text.done", item_id: itemId, output_index: i, summary_index: 0, text: reasoning }),
32
- c({ type: "response.reasoning_summary_part.done", item_id: itemId, output_index: i, summary_index: 0, part: summaryPart }),
53
+ c({
54
+ type: "response.output_item.added",
55
+ output_index: i,
56
+ item: {
57
+ type: "reasoning",
58
+ id: itemId,
59
+ status: "in_progress",
60
+ summary: [],
61
+ },
62
+ }),
63
+ c({
64
+ type: "response.reasoning_summary_part.added",
65
+ item_id: itemId,
66
+ output_index: i,
67
+ summary_index: 0,
68
+ part: { type: "summary_text", text: "" },
69
+ }),
70
+ c({
71
+ type: "response.reasoning_summary_text.delta",
72
+ item_id: itemId,
73
+ output_index: i,
74
+ summary_index: 0,
75
+ delta: reasoning,
76
+ }),
77
+ c({
78
+ type: "response.reasoning_summary_text.done",
79
+ item_id: itemId,
80
+ output_index: i,
81
+ summary_index: 0,
82
+ text: reasoning,
83
+ }),
84
+ c({
85
+ type: "response.reasoning_summary_part.done",
86
+ item_id: itemId,
87
+ output_index: i,
88
+ summary_index: 0,
89
+ part: summaryPart,
90
+ }),
33
91
  c({ type: "response.output_item.done", output_index: i, item }),
34
92
  ],
35
93
  };
36
94
  }
37
95
 
38
- function textStreamBlock(c: Chunk, i: number, text: string, chunkSize: number): StreamBlock {
96
+ function textStreamBlock(
97
+ c: Chunk,
98
+ i: number,
99
+ text: string,
100
+ chunkSize: number,
101
+ ): StreamBlock {
39
102
  const itemId = `msg_${genId("msg")}`;
40
- const outputText = { type: "output_text" as const, text, annotations: NO_ANNOTATIONS };
41
- const outputItem = { type: "message", id: itemId, status: "completed", role: "assistant", content: [outputText] };
103
+ const outputText = {
104
+ type: "output_text" as const,
105
+ text,
106
+ annotations: NO_ANNOTATIONS,
107
+ };
108
+ const outputItem = {
109
+ type: "message",
110
+ id: itemId,
111
+ status: "completed",
112
+ role: "assistant",
113
+ content: [outputText],
114
+ };
42
115
 
43
116
  return {
44
117
  outputItem,
45
118
  chunks: [
46
- c({ type: "response.output_item.added", output_index: i, item: { type: "message", id: itemId, status: "in_progress", role: "assistant", content: [] } }),
47
- c({ type: "response.content_part.added", item_id: itemId, output_index: i, content_index: 0, part: { type: "output_text", text: "", annotations: [] } }),
119
+ c({
120
+ type: "response.output_item.added",
121
+ output_index: i,
122
+ item: {
123
+ type: "message",
124
+ id: itemId,
125
+ status: "in_progress",
126
+ role: "assistant",
127
+ content: [],
128
+ },
129
+ }),
130
+ c({
131
+ type: "response.content_part.added",
132
+ item_id: itemId,
133
+ output_index: i,
134
+ content_index: 0,
135
+ part: { type: "output_text", text: "", annotations: [] },
136
+ }),
48
137
  ...splitText(text, chunkSize).map((piece) =>
49
- c({ type: "response.output_text.delta", item_id: itemId, output_index: i, content_index: 0, delta: piece }),
138
+ c({
139
+ type: "response.output_text.delta",
140
+ item_id: itemId,
141
+ output_index: i,
142
+ content_index: 0,
143
+ delta: piece,
144
+ }),
50
145
  ),
51
- c({ type: "response.output_text.done", item_id: itemId, output_index: i, content_index: 0, text }),
52
- c({ type: "response.content_part.done", item_id: itemId, output_index: i, content_index: 0, part: outputText }),
53
- c({ type: "response.output_item.done", output_index: i, item: outputItem }),
146
+ c({
147
+ type: "response.output_text.done",
148
+ item_id: itemId,
149
+ output_index: i,
150
+ content_index: 0,
151
+ text,
152
+ }),
153
+ c({
154
+ type: "response.content_part.done",
155
+ item_id: itemId,
156
+ output_index: i,
157
+ content_index: 0,
158
+ part: outputText,
159
+ }),
160
+ c({
161
+ type: "response.output_item.done",
162
+ output_index: i,
163
+ item: outputItem,
164
+ }),
54
165
  ],
55
166
  };
56
167
  }
@@ -58,20 +169,49 @@ function textStreamBlock(c: Chunk, i: number, text: string, chunkSize: number):
58
169
  function toolStreamBlock(c: Chunk, i: number, tool: ToolCall): StreamBlock {
59
170
  const callId = toolId(tool, "call", i);
60
171
  const argsJson = JSON.stringify(tool.args);
61
- const outputItem = { type: "function_call", id: callId, status: "completed", name: tool.name, call_id: callId, arguments: argsJson };
172
+ const outputItem = {
173
+ type: "function_call",
174
+ id: callId,
175
+ status: "completed",
176
+ name: tool.name,
177
+ call_id: callId,
178
+ arguments: argsJson,
179
+ };
62
180
 
63
181
  return {
64
182
  outputItem,
65
183
  chunks: [
66
- c({ type: "response.output_item.added", output_index: i, item: { ...outputItem, status: "in_progress", arguments: "" } }),
67
- c({ type: "response.function_call_arguments.delta", item_id: callId, output_index: i, delta: argsJson }),
68
- c({ type: "response.function_call_arguments.done", item_id: callId, output_index: i, arguments: argsJson }),
69
- c({ type: "response.output_item.done", output_index: i, item: outputItem }),
184
+ c({
185
+ type: "response.output_item.added",
186
+ output_index: i,
187
+ item: { ...outputItem, status: "in_progress", arguments: "" },
188
+ }),
189
+ c({
190
+ type: "response.function_call_arguments.delta",
191
+ item_id: callId,
192
+ output_index: i,
193
+ delta: argsJson,
194
+ }),
195
+ c({
196
+ type: "response.function_call_arguments.done",
197
+ item_id: callId,
198
+ output_index: i,
199
+ arguments: argsJson,
200
+ }),
201
+ c({
202
+ type: "response.output_item.done",
203
+ output_index: i,
204
+ item: outputItem,
205
+ }),
70
206
  ],
71
207
  };
72
208
  }
73
209
 
74
- export function serialize(reply: ReplyObject, model: string, options: ReplyOptions = {}): readonly SSEChunk[] {
210
+ export function serialize(
211
+ reply: ReplyObject,
212
+ model: string,
213
+ options: ReplyOptions = {},
214
+ ): readonly SSEChunk[] {
75
215
  const id = genId("resp");
76
216
  const createdAt = Math.floor(Date.now() / MS_PER_SECOND);
77
217
  const usage = reply.usage ?? DEFAULT_USAGE;
@@ -80,13 +220,21 @@ export function serialize(reply: ReplyObject, model: string, options: ReplyOptio
80
220
 
81
221
  const baseResponse = { id, object: "response", created_at: createdAt, model };
82
222
  const header = [
83
- c({ type: "response.created", response: { ...baseResponse, status: "in_progress", output: [] } }),
84
- c({ type: "response.in_progress", response: { ...baseResponse, status: "in_progress", output: [] } }),
223
+ c({
224
+ type: "response.created",
225
+ response: { ...baseResponse, status: "in_progress", output: [] },
226
+ }),
227
+ c({
228
+ type: "response.in_progress",
229
+ response: { ...baseResponse, status: "in_progress", output: [] },
230
+ }),
85
231
  ];
86
232
 
87
233
  const blocks: StreamBlock[] = [
88
234
  ...(reply.reasoning ? [reasoningStreamBlock(c, i++, reply.reasoning)] : []),
89
- ...(shouldEmitText(reply) ? [textStreamBlock(c, i++, reply.text ?? "", options.chunkSize ?? 0)] : []),
235
+ ...(shouldEmitText(reply)
236
+ ? [textStreamBlock(c, i++, reply.text ?? "", options.chunkSize ?? 0)]
237
+ : []),
90
238
  ...(reply.tools ?? []).map((tool) => toolStreamBlock(c, i++, tool)),
91
239
  ];
92
240
 
@@ -98,36 +246,83 @@ export function serialize(reply: ReplyObject, model: string, options: ReplyOptio
98
246
  ...allChunks,
99
247
  c({
100
248
  type: "response.completed",
101
- response: { ...baseResponse, status: "completed", output,
102
- usage: buildUsage(usage) },
249
+ response: {
250
+ ...baseResponse,
251
+ status: "completed",
252
+ output,
253
+ usage: buildUsage(usage),
254
+ },
103
255
  }),
104
256
  ];
105
257
  }
106
258
 
107
- export function serializeComplete(reply: ReplyObject, model: string): Record<string, unknown> {
259
+ export function serializeComplete(
260
+ reply: ReplyObject,
261
+ model: string,
262
+ ): Record<string, unknown> {
108
263
  const id = genId("resp");
109
264
  const createdAt = Math.floor(Date.now() / MS_PER_SECOND);
110
265
  const usage = reply.usage ?? DEFAULT_USAGE;
111
266
 
112
267
  const output: unknown[] = [
113
268
  ...(reply.reasoning
114
- ? [{ type: "reasoning", id: `rs_${genId("rs")}`, status: "completed", summary: [{ type: "summary_text", text: reply.reasoning }] }]
269
+ ? [
270
+ {
271
+ type: "reasoning",
272
+ id: `rs_${genId("rs")}`,
273
+ status: "completed",
274
+ summary: [{ type: "summary_text", text: reply.reasoning }],
275
+ },
276
+ ]
115
277
  : []),
116
278
  ...(shouldEmitText(reply)
117
- ? [{ type: "message", id: `msg_${genId("msg")}`, status: "completed", role: "assistant", content: [{ type: "output_text", text: reply.text ?? "", annotations: [] }] }]
279
+ ? [
280
+ {
281
+ type: "message",
282
+ id: `msg_${genId("msg")}`,
283
+ status: "completed",
284
+ role: "assistant",
285
+ content: [
286
+ { type: "output_text", text: reply.text ?? "", annotations: [] },
287
+ ],
288
+ },
289
+ ]
118
290
  : []),
119
291
  ...(reply.tools ?? []).map((tool) => {
120
292
  const callId = toolId(tool, "call", 0);
121
- return { type: "function_call", id: callId, status: "completed", name: tool.name, call_id: callId, arguments: JSON.stringify(tool.args) };
293
+ return {
294
+ type: "function_call",
295
+ id: callId,
296
+ status: "completed",
297
+ name: tool.name,
298
+ call_id: callId,
299
+ arguments: JSON.stringify(tool.args),
300
+ };
122
301
  }),
123
302
  ];
124
303
 
125
304
  return {
126
- id, object: "response", created_at: createdAt, status: "completed", model, output,
305
+ id,
306
+ object: "response",
307
+ created_at: createdAt,
308
+ status: "completed",
309
+ model,
310
+ output,
127
311
  usage: buildUsage(usage),
128
312
  };
129
313
  }
130
314
 
131
- export function serializeError(error: { status: number; message: string; type?: string }): Record<string, unknown> {
132
- return { type: "error", error: { message: error.message, type: error.type ?? "server_error", code: error.type ?? "server_error" } };
315
+ export function serializeError(error: {
316
+ status: number;
317
+ message: string;
318
+ type?: string;
319
+ }): Record<string, unknown> {
320
+ return {
321
+ type: "error",
322
+ error: {
323
+ message: error.message,
324
+ type: error.type ?? "server_error",
325
+ code: error.type ?? "server_error",
326
+ },
327
+ };
133
328
  }
@@ -17,7 +17,11 @@ export function genId(prefix: string): string {
17
17
  return `${prefix}_${Date.now().toString(BASE_36)}`;
18
18
  }
19
19
 
20
- export function toolId(tool: { id?: string | undefined }, prefix: string, index: number): string {
20
+ export function toolId(
21
+ tool: { id?: string | undefined },
22
+ prefix: string,
23
+ index: number,
24
+ ): string {
21
25
  return tool.id ?? `${prefix}_${Date.now().toString(BASE_36)}_${index}`;
22
26
  }
23
27
 
@@ -25,6 +29,10 @@ export function shouldEmitText(reply: ReplyObject): boolean {
25
29
  return Boolean(reply.text) || (!reply.tools?.length && !reply.reasoning);
26
30
  }
27
31
 
28
- export function finishReason(reply: ReplyObject, onTools: string, onStop: string): string {
32
+ export function finishReason(
33
+ reply: ReplyObject,
34
+ onTools: string,
35
+ onStop: string,
36
+ ): string {
29
37
  return reply.tools?.length ? onTools : onStop;
30
38
  }
@@ -1,4 +1,9 @@
1
- import type { FormatName, MockRequest, ReplyObject, ReplyOptions } from "../types.js";
1
+ import type {
2
+ FormatName,
3
+ MockRequest,
4
+ ReplyObject,
5
+ ReplyOptions,
6
+ } from "../types.js";
2
7
  import type { RequestMeta } from "./request-helpers.js";
3
8
 
4
9
  export interface SSEChunk {
@@ -11,7 +16,15 @@ export interface Format {
11
16
  readonly route: string;
12
17
  parseRequest(body: unknown, meta?: RequestMeta): MockRequest;
13
18
  isStreaming(body: unknown): boolean;
14
- serialize(reply: ReplyObject, model: string, options?: ReplyOptions): readonly SSEChunk[];
19
+ serialize(
20
+ reply: ReplyObject,
21
+ model: string,
22
+ options?: ReplyOptions,
23
+ ): readonly SSEChunk[];
15
24
  serializeComplete(reply: ReplyObject, model: string): Record<string, unknown>;
16
- serializeError(error: { status: number; message: string; type?: string | undefined }): Record<string, unknown>;
25
+ serializeError(error: {
26
+ status: number;
27
+ message: string;
28
+ type?: string | undefined;
29
+ }): Record<string, unknown>;
17
30
  }