llm-mock-server 1.0.1 → 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.
- package/.claude/skills/desloppify/SKILL.md +308 -0
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000801.json +242 -0
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000905.json +248 -0
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000917.json +248 -0
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000950.json +311 -0
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/claude_launch_prompt.md +17 -0
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/review_result.json +255 -0
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/review_result.template.json +22 -0
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/reviewer_instructions.md +20 -0
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/session.json +20 -0
- package/.desloppify/query.json +284 -0
- package/.desloppify/review_packet_blind.json +1303 -0
- package/.desloppify/review_packets/holistic_packet_20260315_000339.json +1471 -0
- package/.desloppify/state-typescript.json +5114 -0
- package/.desloppify/state-typescript.json.bak +5108 -0
- package/.editorconfig +12 -0
- package/.github/workflows/test.yml +3 -0
- package/.oxfmtrc.json +9 -0
- package/dist/cli.js +5 -2
- package/dist/cli.js.map +1 -1
- package/dist/formats/anthropic/index.js +1 -1
- package/dist/formats/anthropic/index.js.map +1 -1
- package/dist/formats/anthropic/parse.d.ts +1 -1
- package/dist/formats/anthropic/parse.d.ts.map +1 -1
- package/dist/formats/anthropic/parse.js +1 -1
- package/dist/formats/anthropic/parse.js.map +1 -1
- package/dist/formats/anthropic/serialize.d.ts +2 -2
- package/dist/formats/anthropic/serialize.d.ts.map +1 -1
- package/dist/formats/anthropic/serialize.js +6 -3
- package/dist/formats/anthropic/serialize.js.map +1 -1
- package/dist/formats/openai/index.js +1 -1
- package/dist/formats/openai/index.js.map +1 -1
- package/dist/formats/openai/parse.d.ts +1 -1
- package/dist/formats/openai/parse.d.ts.map +1 -1
- package/dist/formats/openai/parse.js +1 -1
- package/dist/formats/openai/parse.js.map +1 -1
- package/dist/formats/openai/serialize.d.ts +2 -2
- package/dist/formats/openai/serialize.d.ts.map +1 -1
- package/dist/formats/openai/serialize.js +12 -15
- package/dist/formats/openai/serialize.js.map +1 -1
- package/dist/formats/request-helpers.d.ts +13 -0
- package/dist/formats/request-helpers.d.ts.map +1 -0
- package/dist/formats/request-helpers.js +28 -0
- package/dist/formats/request-helpers.js.map +1 -0
- package/dist/formats/responses/index.js +1 -1
- package/dist/formats/responses/index.js.map +1 -1
- package/dist/formats/responses/parse.d.ts +1 -1
- package/dist/formats/responses/parse.d.ts.map +1 -1
- package/dist/formats/responses/parse.js +1 -1
- package/dist/formats/responses/parse.js.map +1 -1
- package/dist/formats/responses/schema.d.ts +1 -20
- package/dist/formats/responses/schema.d.ts.map +1 -1
- package/dist/formats/responses/schema.js.map +1 -1
- package/dist/formats/responses/serialize.d.ts +2 -2
- package/dist/formats/responses/serialize.d.ts.map +1 -1
- package/dist/formats/responses/serialize.js +6 -3
- package/dist/formats/responses/serialize.js.map +1 -1
- package/dist/formats/serialize-helpers.d.ts +14 -0
- package/dist/formats/serialize-helpers.d.ts.map +1 -0
- package/dist/formats/serialize-helpers.js +25 -0
- package/dist/formats/serialize-helpers.js.map +1 -0
- package/dist/formats/types.d.ts +3 -3
- package/dist/formats/types.d.ts.map +1 -1
- package/dist/loader.d.ts +3 -2
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +6 -9
- package/dist/loader.js.map +1 -1
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +17 -23
- package/dist/logger.js.map +1 -1
- package/dist/mock-server.d.ts.map +1 -1
- package/dist/mock-server.js +8 -15
- package/dist/mock-server.js.map +1 -1
- package/dist/route-handler.d.ts +2 -1
- package/dist/route-handler.d.ts.map +1 -1
- package/dist/rule-engine.d.ts +12 -1
- package/dist/rule-engine.d.ts.map +1 -1
- package/dist/rule-engine.js +14 -0
- package/dist/rule-engine.js.map +1 -1
- package/dist/types/reply.d.ts +6 -10
- package/dist/types/reply.d.ts.map +1 -1
- package/dist/types/request.d.ts +7 -11
- package/dist/types/request.d.ts.map +1 -1
- package/dist/types/rule.d.ts +3 -10
- package/dist/types/rule.d.ts.map +1 -1
- package/dist/types.d.ts +3 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -2
- package/scorecard.png +0 -0
- package/src/cli-validators.ts +12 -4
- package/src/cli.ts +27 -7
- package/src/formats/anthropic/index.ts +1 -1
- package/src/formats/anthropic/parse.ts +25 -6
- package/src/formats/anthropic/schema.ts +16 -8
- package/src/formats/anthropic/serialize.ts +116 -28
- package/src/formats/openai/index.ts +1 -1
- package/src/formats/openai/parse.ts +13 -3
- package/src/formats/openai/schema.ts +43 -30
- package/src/formats/openai/serialize.ts +84 -30
- package/src/formats/{parse-helpers.ts → request-helpers.ts} +4 -32
- package/src/formats/responses/index.ts +1 -1
- package/src/formats/responses/parse.ts +18 -4
- package/src/formats/responses/schema.ts +34 -22
- package/src/formats/responses/serialize.ts +237 -38
- package/src/formats/serialize-helpers.ts +38 -0
- package/src/formats/types.ts +18 -5
- package/src/index.ts +3 -1
- package/src/loader.ts +43 -20
- package/src/logger.ts +31 -19
- package/src/mock-server.ts +38 -21
- package/src/route-handler.ts +50 -15
- package/src/rule-engine.ts +64 -11
- package/src/types/reply.ts +12 -12
- package/src/types/request.ts +7 -11
- package/src/types/rule.ts +3 -10
- package/src/types.ts +23 -4
- package/test/cli-validators.test.ts +16 -4
- package/test/formats/anthropic.test.ts +84 -23
- package/test/formats/openai.test.ts +85 -24
- package/test/formats/parse-helpers.test.ts +315 -0
- package/test/formats/responses.test.ts +99 -34
- package/test/helpers/make-req.ts +18 -0
- package/test/history.test.ts +361 -0
- package/test/loader.test.ts +44 -45
- package/test/logger.test.ts +344 -0
- package/test/mock-server.test.ts +77 -23
- package/test/rule-engine.test.ts +57 -41
- package/src/types/index.ts +0 -4
|
@@ -1,15 +1,43 @@
|
|
|
1
1
|
import type { ReplyObject, ReplyOptions } from "../../types.js";
|
|
2
2
|
import type { SSEChunk } from "../types.js";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
splitText,
|
|
5
|
+
genId,
|
|
6
|
+
toolId,
|
|
7
|
+
finishReason,
|
|
8
|
+
MS_PER_SECOND,
|
|
9
|
+
DEFAULT_USAGE,
|
|
10
|
+
} from "../serialize-helpers.js";
|
|
11
|
+
|
|
12
|
+
function buildUsage(usage: { input: number; output: number }) {
|
|
13
|
+
return {
|
|
14
|
+
prompt_tokens: usage.input,
|
|
15
|
+
completion_tokens: usage.output,
|
|
16
|
+
total_tokens: usage.input + usage.output,
|
|
17
|
+
prompt_tokens_details: { cached_tokens: 0, audio_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
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
4
26
|
|
|
5
27
|
function chunkEnvelope(
|
|
6
|
-
id: string,
|
|
7
|
-
|
|
28
|
+
id: string,
|
|
29
|
+
created: number,
|
|
30
|
+
model: string,
|
|
31
|
+
delta: Record<string, unknown>,
|
|
32
|
+
finish_reason: string | null = null,
|
|
8
33
|
usage: Record<string, unknown> | null = null,
|
|
9
34
|
): SSEChunk {
|
|
10
35
|
return {
|
|
11
36
|
data: JSON.stringify({
|
|
12
|
-
id,
|
|
37
|
+
id,
|
|
38
|
+
object: "chat.completion.chunk",
|
|
39
|
+
created,
|
|
40
|
+
model,
|
|
13
41
|
system_fingerprint: null,
|
|
14
42
|
service_tier: "default",
|
|
15
43
|
choices: [{ index: 0, delta, logprobs: null, finish_reason }],
|
|
@@ -18,7 +46,11 @@ function chunkEnvelope(
|
|
|
18
46
|
};
|
|
19
47
|
}
|
|
20
48
|
|
|
21
|
-
export function serialize(
|
|
49
|
+
export function serialize(
|
|
50
|
+
reply: ReplyObject,
|
|
51
|
+
model: string,
|
|
52
|
+
options: ReplyOptions = {},
|
|
53
|
+
): readonly SSEChunk[] {
|
|
22
54
|
const id = genId("chatcmpl");
|
|
23
55
|
const created = Math.floor(Date.now() / MS_PER_SECOND);
|
|
24
56
|
const usage = reply.usage ?? DEFAULT_USAGE;
|
|
@@ -31,32 +63,39 @@ export function serialize(reply: ReplyObject, model: string, options: ReplyOptio
|
|
|
31
63
|
|
|
32
64
|
const toolChunks = (reply.tools ?? []).map((tool, i) =>
|
|
33
65
|
chunkEnvelope(id, created, model, {
|
|
34
|
-
tool_calls: [
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
+
],
|
|
38
74
|
}),
|
|
39
75
|
);
|
|
40
76
|
|
|
41
|
-
const usageChunk =
|
|
42
|
-
prompt_tokens: usage.input,
|
|
43
|
-
completion_tokens: usage.output,
|
|
44
|
-
total_tokens: usage.input + usage.output,
|
|
45
|
-
prompt_tokens_details: { cached_tokens: 0, audio_tokens: 0 },
|
|
46
|
-
completion_tokens_details: { reasoning_tokens: 0, audio_tokens: 0, accepted_prediction_tokens: 0, rejected_prediction_tokens: 0 },
|
|
47
|
-
};
|
|
77
|
+
const usageChunk = buildUsage(usage);
|
|
48
78
|
|
|
49
79
|
return [
|
|
50
80
|
chunkEnvelope(id, created, model, { role: "assistant" }),
|
|
51
81
|
...textChunks,
|
|
52
82
|
...toolChunks,
|
|
53
|
-
chunkEnvelope(
|
|
83
|
+
chunkEnvelope(
|
|
84
|
+
id,
|
|
85
|
+
created,
|
|
86
|
+
model,
|
|
87
|
+
{},
|
|
88
|
+
finishReason(reply, "tool_calls", "stop"),
|
|
89
|
+
),
|
|
54
90
|
chunkEnvelope(id, created, model, {}, null, usageChunk),
|
|
55
91
|
{ data: "[DONE]" },
|
|
56
92
|
];
|
|
57
93
|
}
|
|
58
94
|
|
|
59
|
-
export function serializeComplete(
|
|
95
|
+
export function serializeComplete(
|
|
96
|
+
reply: ReplyObject,
|
|
97
|
+
model: string,
|
|
98
|
+
): Record<string, unknown> {
|
|
60
99
|
const id = genId("chatcmpl");
|
|
61
100
|
const created = Math.floor(Date.now() / MS_PER_SECOND);
|
|
62
101
|
const usage = reply.usage ?? DEFAULT_USAGE;
|
|
@@ -66,27 +105,42 @@ export function serializeComplete(reply: ReplyObject, model: string): unknown {
|
|
|
66
105
|
content: reply.text ?? null,
|
|
67
106
|
...(reply.tools?.length && {
|
|
68
107
|
tool_calls: reply.tools.map((tool, i) => ({
|
|
69
|
-
id: toolId(tool, "call", i),
|
|
108
|
+
id: toolId(tool, "call", i),
|
|
109
|
+
type: "function",
|
|
70
110
|
function: { name: tool.name, arguments: JSON.stringify(tool.args) },
|
|
71
111
|
})),
|
|
72
112
|
}),
|
|
73
113
|
};
|
|
74
114
|
|
|
75
115
|
return {
|
|
76
|
-
id,
|
|
116
|
+
id,
|
|
117
|
+
object: "chat.completion",
|
|
118
|
+
created,
|
|
119
|
+
model,
|
|
77
120
|
system_fingerprint: null,
|
|
78
121
|
service_tier: "default",
|
|
79
|
-
choices: [
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
122
|
+
choices: [
|
|
123
|
+
{
|
|
124
|
+
index: 0,
|
|
125
|
+
message,
|
|
126
|
+
logprobs: null,
|
|
127
|
+
finish_reason: finishReason(reply, "tool_calls", "stop"),
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
usage: buildUsage(usage),
|
|
87
131
|
};
|
|
88
132
|
}
|
|
89
133
|
|
|
90
|
-
export function serializeError(error: {
|
|
91
|
-
|
|
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
|
+
};
|
|
92
146
|
}
|
|
@@ -1,39 +1,11 @@
|
|
|
1
|
-
import type { FormatName, Message, MockRequest,
|
|
2
|
-
|
|
3
|
-
export const MS_PER_SECOND = 1000;
|
|
4
|
-
const BASE_36 = 36;
|
|
5
|
-
export const DEFAULT_USAGE = { input: 10, output: 5 } as const;
|
|
1
|
+
import type { FormatName, Message, MockRequest, ToolDef } from "../types.js";
|
|
6
2
|
|
|
7
3
|
function asRecord(body: unknown): Record<string, unknown> {
|
|
8
|
-
if (typeof body === "object" && body !== null)
|
|
4
|
+
if (typeof body === "object" && body !== null)
|
|
5
|
+
return body as Record<string, unknown>;
|
|
9
6
|
return {};
|
|
10
7
|
}
|
|
11
8
|
|
|
12
|
-
export function splitText(text: string, chunkSize: number): string[] {
|
|
13
|
-
if (chunkSize <= 0 || text.length <= chunkSize) return [text];
|
|
14
|
-
const chunks: string[] = [];
|
|
15
|
-
for (let i = 0; i < text.length; i += chunkSize) {
|
|
16
|
-
chunks.push(text.slice(i, i + chunkSize));
|
|
17
|
-
}
|
|
18
|
-
return chunks;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function genId(prefix: string): string {
|
|
22
|
-
return `${prefix}_${Date.now().toString(BASE_36)}`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function toolId(tool: { id?: string | undefined }, prefix: string, index: number): string {
|
|
26
|
-
return tool.id ?? `${prefix}_${Date.now().toString(BASE_36)}_${index}`;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function shouldEmitText(reply: ReplyObject): boolean {
|
|
30
|
-
return Boolean(reply.text) || (!reply.tools?.length && !reply.reasoning);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function finishReason(reply: ReplyObject, onTools: string, onStop: string): string {
|
|
34
|
-
return reply.tools?.length ? onTools : onStop;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
9
|
export function isStreaming(body: unknown): boolean {
|
|
38
10
|
return asRecord(body)["stream"] !== false;
|
|
39
11
|
}
|
|
@@ -45,7 +17,7 @@ export interface RequestMeta {
|
|
|
45
17
|
|
|
46
18
|
const EMPTY_META: RequestMeta = { headers: {}, path: "" };
|
|
47
19
|
|
|
48
|
-
|
|
20
|
+
interface ParsedBody {
|
|
49
21
|
readonly model?: string | undefined;
|
|
50
22
|
readonly stream?: boolean | undefined;
|
|
51
23
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Format } from "../types.js";
|
|
2
|
-
import { isStreaming } from "../
|
|
2
|
+
import { isStreaming } from "../request-helpers.js";
|
|
3
3
|
import { parseRequest } from "./parse.js";
|
|
4
4
|
import { serialize, serializeComplete, serializeError } from "./serialize.js";
|
|
5
5
|
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import type { MockRequest, Message, ToolDef } from "../../types.js";
|
|
2
|
-
import { buildMockRequest, type RequestMeta } from "../
|
|
3
|
-
import {
|
|
2
|
+
import { buildMockRequest, type RequestMeta } from "../request-helpers.js";
|
|
3
|
+
import {
|
|
4
|
+
ResponsesRequestSchema,
|
|
5
|
+
FunctionToolSchema,
|
|
6
|
+
type ResponsesRequest,
|
|
7
|
+
} from "./schema.js";
|
|
4
8
|
|
|
5
|
-
function extractInputContent(
|
|
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(
|
|
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 {
|
|
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(),
|
|
@@ -20,30 +24,34 @@ const OutputItemSchema = z.object({
|
|
|
20
24
|
summary: z.array(z.object({ type: z.string(), text: z.string() })).optional(),
|
|
21
25
|
});
|
|
22
26
|
|
|
23
|
-
export type ResponsesOutputItem = z.infer<typeof OutputItemSchema>;
|
|
24
|
-
|
|
25
27
|
export const ResponsesEventSchema = z.object({
|
|
26
28
|
type: z.string(),
|
|
27
29
|
sequence_number: z.number().optional(),
|
|
28
|
-
response: z
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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(),
|
|
41
47
|
item: OutputItemSchema.optional(),
|
|
42
|
-
part: z
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
part: z
|
|
49
|
+
.object({
|
|
50
|
+
type: z.string(),
|
|
51
|
+
text: z.string().optional(),
|
|
52
|
+
annotations: z.array(z.unknown()).optional(),
|
|
53
|
+
})
|
|
54
|
+
.optional(),
|
|
47
55
|
delta: z.string().optional(),
|
|
48
56
|
item_id: z.string().optional(),
|
|
49
57
|
});
|
|
@@ -68,7 +76,11 @@ export type ResponsesComplete = z.infer<typeof ResponsesCompleteSchema>;
|
|
|
68
76
|
|
|
69
77
|
export const ResponsesErrorSchema = z.object({
|
|
70
78
|
type: z.literal("error"),
|
|
71
|
-
error: z.object({
|
|
79
|
+
error: z.object({
|
|
80
|
+
message: z.string(),
|
|
81
|
+
type: z.string().optional(),
|
|
82
|
+
code: z.string().optional(),
|
|
83
|
+
}),
|
|
72
84
|
});
|
|
73
85
|
|
|
74
86
|
export type ResponsesError = z.infer<typeof ResponsesErrorSchema>;
|