@copilotkit/aimock 1.15.0 → 1.15.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/CHANGELOG.md +26 -16
- package/dist/bedrock-converse.cjs +133 -25
- package/dist/bedrock-converse.cjs.map +1 -1
- package/dist/bedrock-converse.d.cts.map +1 -1
- package/dist/bedrock-converse.d.ts.map +1 -1
- package/dist/bedrock-converse.js +135 -27
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +262 -48
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.d.cts.map +1 -1
- package/dist/bedrock.d.ts.map +1 -1
- package/dist/bedrock.js +263 -50
- package/dist/bedrock.js.map +1 -1
- package/dist/chaos.cjs +9 -35
- package/dist/chaos.cjs.map +1 -1
- package/dist/chaos.d.cts +2 -17
- package/dist/chaos.d.cts.map +1 -1
- package/dist/chaos.d.ts +2 -17
- package/dist/chaos.d.ts.map +1 -1
- package/dist/chaos.js +10 -35
- package/dist/chaos.js.map +1 -1
- package/dist/cohere.cjs +289 -33
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.d.cts +9 -0
- package/dist/cohere.d.cts.map +1 -1
- package/dist/cohere.d.ts +9 -0
- package/dist/cohere.d.ts.map +1 -1
- package/dist/cohere.js +290 -34
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/embeddings.cjs +22 -4
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts +2 -2
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts +2 -2
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +22 -4
- package/dist/embeddings.js.map +1 -1
- package/dist/gemini.cjs +48 -45
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +48 -45
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +9 -0
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +9 -0
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +21 -3
- package/dist/images.cjs.map +1 -1
- package/dist/images.js +21 -3
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/jest.cjs +10 -3
- package/dist/jest.cjs.map +1 -1
- package/dist/jest.js +10 -3
- package/dist/jest.js.map +1 -1
- package/dist/journal.cjs +1 -1
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +1 -1
- package/dist/journal.js.map +1 -1
- package/dist/messages.cjs +5 -4
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.js +5 -4
- package/dist/messages.js.map +1 -1
- package/dist/ollama.cjs +129 -8
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.d.cts.map +1 -1
- package/dist/ollama.d.ts.map +1 -1
- package/dist/ollama.js +130 -9
- package/dist/ollama.js.map +1 -1
- package/dist/recorder.cjs +234 -69
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts +5 -50
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts +5 -50
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +234 -69
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +12 -3
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts +2 -1
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts +2 -1
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +12 -4
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +13 -6
- package/dist/router.cjs.map +1 -1
- package/dist/router.js +13 -6
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +150 -94
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +152 -96
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +21 -3
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.js +21 -3
- package/dist/speech.js.map +1 -1
- package/dist/transcription.cjs +10 -6
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.d.cts.map +1 -1
- package/dist/transcription.d.ts.map +1 -1
- package/dist/transcription.js +10 -6
- package/dist/transcription.js.map +1 -1
- package/dist/types.d.cts +1 -16
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +1 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/video.cjs +66 -10
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts +16 -3
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts +16 -3
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +66 -11
- package/dist/video.js.map +1 -1
- package/dist/vitest.cjs +10 -3
- package/dist/vitest.cjs.map +1 -1
- package/dist/vitest.js +10 -3
- package/dist/vitest.js.map +1 -1
- package/package.json +1 -1
package/dist/video.js
CHANGED
|
@@ -5,6 +5,49 @@ import { applyChaos } from "./chaos.js";
|
|
|
5
5
|
import { proxyAndRecord } from "./recorder.js";
|
|
6
6
|
|
|
7
7
|
//#region src/video.ts
|
|
8
|
+
const VIDEO_STATE_MAX_ENTRIES = 1e4;
|
|
9
|
+
const VIDEO_STATE_TTL_MS = 36e5;
|
|
10
|
+
/**
|
|
11
|
+
* A Map wrapper for video state that enforces a maximum size and per-entry TTL.
|
|
12
|
+
* Entries older than VIDEO_STATE_TTL_MS are lazily evicted on `get`.
|
|
13
|
+
* When the map exceeds VIDEO_STATE_MAX_ENTRIES on `set`, the oldest entries
|
|
14
|
+
* are removed to stay within bounds.
|
|
15
|
+
*/
|
|
16
|
+
var VideoStateMap = class {
|
|
17
|
+
entries = /* @__PURE__ */ new Map();
|
|
18
|
+
get(key) {
|
|
19
|
+
const entry = this.entries.get(key);
|
|
20
|
+
if (!entry) return void 0;
|
|
21
|
+
if (Date.now() - entry.createdAt > VIDEO_STATE_TTL_MS) {
|
|
22
|
+
this.entries.delete(key);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
return entry.video;
|
|
26
|
+
}
|
|
27
|
+
set(key, video) {
|
|
28
|
+
this.entries.set(key, {
|
|
29
|
+
video,
|
|
30
|
+
createdAt: Date.now()
|
|
31
|
+
});
|
|
32
|
+
if (this.entries.size > VIDEO_STATE_MAX_ENTRIES) {
|
|
33
|
+
const excess = this.entries.size - VIDEO_STATE_MAX_ENTRIES;
|
|
34
|
+
const iter = this.entries.keys();
|
|
35
|
+
for (let i = 0; i < excess; i++) {
|
|
36
|
+
const next = iter.next();
|
|
37
|
+
if (!next.done) this.entries.delete(next.value);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
delete(key) {
|
|
42
|
+
return this.entries.delete(key);
|
|
43
|
+
}
|
|
44
|
+
clear() {
|
|
45
|
+
this.entries.clear();
|
|
46
|
+
}
|
|
47
|
+
get size() {
|
|
48
|
+
return this.entries.size;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
8
51
|
async function handleVideoCreate(req, res, raw, fixtures, journal, defaults, setCorsHeaders, videoStates) {
|
|
9
52
|
setCorsHeaders(res);
|
|
10
53
|
const path = req.url ?? "/v1/videos";
|
|
@@ -30,6 +73,23 @@ async function handleVideoCreate(req, res, raw, fixtures, journal, defaults, set
|
|
|
30
73
|
} }));
|
|
31
74
|
return;
|
|
32
75
|
}
|
|
76
|
+
if (!videoReq.prompt) {
|
|
77
|
+
journal.add({
|
|
78
|
+
method,
|
|
79
|
+
path,
|
|
80
|
+
headers: flattenHeaders(req.headers),
|
|
81
|
+
body: null,
|
|
82
|
+
response: {
|
|
83
|
+
status: 400,
|
|
84
|
+
fixture: null
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
writeErrorResponse(res, 400, JSON.stringify({ error: {
|
|
88
|
+
message: "Missing required parameter: 'prompt'",
|
|
89
|
+
type: "invalid_request_error"
|
|
90
|
+
} }));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
33
93
|
const syntheticReq = {
|
|
34
94
|
model: videoReq.model ?? "sora-2",
|
|
35
95
|
messages: [{
|
|
@@ -46,10 +106,10 @@ async function handleVideoCreate(req, res, raw, fixtures, journal, defaults, set
|
|
|
46
106
|
path,
|
|
47
107
|
headers: flattenHeaders(req.headers),
|
|
48
108
|
body: syntheticReq
|
|
49
|
-
},
|
|
109
|
+
}, defaults.registry, defaults.logger)) return;
|
|
50
110
|
if (!fixture) {
|
|
51
111
|
if (defaults.record) {
|
|
52
|
-
if (await proxyAndRecord(req, res, syntheticReq, "openai", req.url ?? "/v1/videos", fixtures, defaults, raw)
|
|
112
|
+
if (await proxyAndRecord(req, res, syntheticReq, "openai", req.url ?? "/v1/videos", fixtures, defaults, raw)) {
|
|
53
113
|
journal.add({
|
|
54
114
|
method,
|
|
55
115
|
path,
|
|
@@ -57,7 +117,8 @@ async function handleVideoCreate(req, res, raw, fixtures, journal, defaults, set
|
|
|
57
117
|
body: syntheticReq,
|
|
58
118
|
response: {
|
|
59
119
|
status: res.statusCode ?? 200,
|
|
60
|
-
fixture: null
|
|
120
|
+
fixture: null,
|
|
121
|
+
source: "proxy"
|
|
61
122
|
}
|
|
62
123
|
});
|
|
63
124
|
return;
|
|
@@ -146,16 +207,10 @@ async function handleVideoCreate(req, res, raw, fixtures, journal, defaults, set
|
|
|
146
207
|
}));
|
|
147
208
|
}
|
|
148
209
|
}
|
|
149
|
-
function handleVideoStatus(req, res, videoId, journal,
|
|
210
|
+
function handleVideoStatus(req, res, videoId, journal, setCorsHeaders, videoStates) {
|
|
150
211
|
setCorsHeaders(res);
|
|
151
212
|
const path = req.url ?? `/v1/videos/${videoId}`;
|
|
152
213
|
const method = req.method ?? "GET";
|
|
153
|
-
if (applyChaos(res, null, defaults.chaos, req.headers, journal, {
|
|
154
|
-
method,
|
|
155
|
-
path,
|
|
156
|
-
headers: flattenHeaders(req.headers),
|
|
157
|
-
body: null
|
|
158
|
-
}, "fixture", defaults.registry, defaults.logger)) return;
|
|
159
214
|
const stateKey = `${getTestId(req)}:${videoId}`;
|
|
160
215
|
const video = videoStates.get(stateKey);
|
|
161
216
|
if (!video) {
|
|
@@ -197,5 +252,5 @@ function handleVideoStatus(req, res, videoId, journal, defaults, setCorsHeaders,
|
|
|
197
252
|
}
|
|
198
253
|
|
|
199
254
|
//#endregion
|
|
200
|
-
export { handleVideoCreate, handleVideoStatus };
|
|
255
|
+
export { VideoStateMap, handleVideoCreate, handleVideoStatus };
|
|
201
256
|
//# sourceMappingURL=video.js.map
|
package/dist/video.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video.js","names":[],"sources":["../src/video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults, VideoResponse } from \"./types.js\";\nimport { isVideoResponse, isErrorResponse, flattenHeaders, getTestId } from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\ninterface VideoRequest {\n model?: string;\n prompt: string;\n [key: string]: unknown;\n}\n\n/** Stored video state for GET status checks. Key: `${testId}:${videoId}` */\nexport type VideoStateMap = Map<string, VideoResponse[\"video\"]>;\n\nexport async function handleVideoCreate(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n videoStates: VideoStateMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? \"/v1/videos\";\n const method = req.method ?? \"POST\";\n\n let videoReq: VideoRequest;\n try {\n videoReq = JSON.parse(raw) as VideoRequest;\n } catch {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: { message: \"Malformed JSON\", type: \"invalid_request_error\", code: \"invalid_json\" },\n }),\n );\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model: videoReq.model ?? \"sora-2\",\n messages: [{ role: \"user\", content: videoReq.prompt }],\n _endpointType: \"video\",\n };\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n syntheticReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n syntheticReq,\n \"openai\",\n req.url ?? \"/v1/videos\",\n fixtures,\n defaults,\n raw,\n );\n if (outcome !== \"not_configured\") {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: { message: strictMessage, type: \"invalid_request_error\", code: \"no_fixture_match\" },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n if (!isVideoResponse(response)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: { message: \"Fixture response is not a video type\", type: \"server_error\" },\n }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const video = response.video;\n const created_at = Math.floor(Date.now() / 1000);\n\n // Store for GET status checks\n const stateKey = `${testId}:${video.id}`;\n videoStates.set(stateKey, video);\n\n if (video.status === \"completed\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id: video.id, status: video.status, url: video.url, created_at }));\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id: video.id, status: video.status, created_at }));\n }\n}\n\nexport function handleVideoStatus(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n videoId: string,\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n videoStates: VideoStateMap,\n): void {\n setCorsHeaders(res);\n const path = req.url ?? `/v1/videos/${videoId}`;\n const method = req.method ?? \"GET\";\n\n if (\n applyChaos(\n res,\n null,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: null },\n \"fixture\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n const testId = getTestId(req);\n const stateKey = `${testId}:${videoId}`;\n const video = videoStates.get(stateKey);\n\n if (!video) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 404, fixture: null },\n });\n writeErrorResponse(\n res,\n 404,\n JSON.stringify({ error: { message: `Video ${videoId} not found`, type: \"not_found\" } }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 200, fixture: null },\n });\n\n const created_at = Math.floor(Date.now() / 1000);\n const body: Record<string, unknown> = {\n id: video.id,\n status: video.status,\n created_at,\n };\n if (video.url) body.url = video.url;\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"video.js","names":[],"sources":["../src/video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults, VideoResponse } from \"./types.js\";\nimport { isVideoResponse, isErrorResponse, flattenHeaders, getTestId } from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\ninterface VideoRequest {\n model?: string;\n prompt: string;\n [key: string]: unknown;\n}\n\n// ─── VideoStateMap with TTL and size bound ────────────────────────────────\n\nconst VIDEO_STATE_MAX_ENTRIES = 10_000;\nconst VIDEO_STATE_TTL_MS = 3_600_000; // 1 hour\n\ninterface VideoStateEntry {\n video: VideoResponse[\"video\"];\n createdAt: number;\n}\n\n/**\n * A Map wrapper for video state that enforces a maximum size and per-entry TTL.\n * Entries older than VIDEO_STATE_TTL_MS are lazily evicted on `get`.\n * When the map exceeds VIDEO_STATE_MAX_ENTRIES on `set`, the oldest entries\n * are removed to stay within bounds.\n */\nexport class VideoStateMap {\n private readonly entries = new Map<string, VideoStateEntry>();\n\n get(key: string): VideoResponse[\"video\"] | undefined {\n const entry = this.entries.get(key);\n if (!entry) return undefined;\n if (Date.now() - entry.createdAt > VIDEO_STATE_TTL_MS) {\n this.entries.delete(key);\n return undefined;\n }\n return entry.video;\n }\n\n set(key: string, video: VideoResponse[\"video\"]): void {\n this.entries.set(key, { video, createdAt: Date.now() });\n // Evict oldest entries if over capacity\n if (this.entries.size > VIDEO_STATE_MAX_ENTRIES) {\n const excess = this.entries.size - VIDEO_STATE_MAX_ENTRIES;\n const iter = this.entries.keys();\n for (let i = 0; i < excess; i++) {\n const next = iter.next();\n if (!next.done) this.entries.delete(next.value);\n }\n }\n }\n\n delete(key: string): boolean {\n return this.entries.delete(key);\n }\n\n clear(): void {\n this.entries.clear();\n }\n\n get size(): number {\n return this.entries.size;\n }\n}\n\nexport async function handleVideoCreate(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n videoStates: VideoStateMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? \"/v1/videos\";\n const method = req.method ?? \"POST\";\n\n let videoReq: VideoRequest;\n try {\n videoReq = JSON.parse(raw) as VideoRequest;\n } catch {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: { message: \"Malformed JSON\", type: \"invalid_request_error\", code: \"invalid_json\" },\n }),\n );\n return;\n }\n\n if (!videoReq.prompt) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: { message: \"Missing required parameter: 'prompt'\", type: \"invalid_request_error\" },\n }),\n );\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model: videoReq.model ?? \"sora-2\",\n messages: [{ role: \"user\", content: videoReq.prompt }],\n _endpointType: \"video\",\n };\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n syntheticReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n syntheticReq,\n \"openai\",\n req.url ?? \"/v1/videos\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: { message: strictMessage, type: \"invalid_request_error\", code: \"no_fixture_match\" },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n if (!isVideoResponse(response)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: { message: \"Fixture response is not a video type\", type: \"server_error\" },\n }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const video = response.video;\n const created_at = Math.floor(Date.now() / 1000);\n\n // Store for GET status checks\n const stateKey = `${testId}:${video.id}`;\n videoStates.set(stateKey, video);\n\n if (video.status === \"completed\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id: video.id, status: video.status, url: video.url, created_at }));\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id: video.id, status: video.status, created_at }));\n }\n}\n\nexport function handleVideoStatus(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n videoId: string,\n journal: Journal,\n setCorsHeaders: (res: http.ServerResponse) => void,\n videoStates: VideoStateMap,\n): void {\n setCorsHeaders(res);\n const path = req.url ?? `/v1/videos/${videoId}`;\n const method = req.method ?? \"GET\";\n\n const testId = getTestId(req);\n const stateKey = `${testId}:${videoId}`;\n const video = videoStates.get(stateKey);\n\n if (!video) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 404, fixture: null },\n });\n writeErrorResponse(\n res,\n 404,\n JSON.stringify({ error: { message: `Video ${videoId} not found`, type: \"not_found\" } }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 200, fixture: null },\n });\n\n const created_at = Math.floor(Date.now() / 1000);\n const body: Record<string, unknown> = {\n id: video.id,\n status: video.status,\n created_at,\n };\n if (video.url) body.url = video.url;\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n}\n"],"mappings":";;;;;;;AAiBA,MAAM,0BAA0B;AAChC,MAAM,qBAAqB;;;;;;;AAa3B,IAAa,gBAAb,MAA2B;CACzB,AAAiB,0BAAU,IAAI,KAA8B;CAE7D,IAAI,KAAiD;EACnD,MAAM,QAAQ,KAAK,QAAQ,IAAI,IAAI;AACnC,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,KAAK,KAAK,GAAG,MAAM,YAAY,oBAAoB;AACrD,QAAK,QAAQ,OAAO,IAAI;AACxB;;AAEF,SAAO,MAAM;;CAGf,IAAI,KAAa,OAAqC;AACpD,OAAK,QAAQ,IAAI,KAAK;GAAE;GAAO,WAAW,KAAK,KAAK;GAAE,CAAC;AAEvD,MAAI,KAAK,QAAQ,OAAO,yBAAyB;GAC/C,MAAM,SAAS,KAAK,QAAQ,OAAO;GACnC,MAAM,OAAO,KAAK,QAAQ,MAAM;AAChC,QAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK;IAC/B,MAAM,OAAO,KAAK,MAAM;AACxB,QAAI,CAAC,KAAK,KAAM,MAAK,QAAQ,OAAO,KAAK,MAAM;;;;CAKrD,OAAO,KAAsB;AAC3B,SAAO,KAAK,QAAQ,OAAO,IAAI;;CAGjC,QAAc;AACZ,OAAK,QAAQ,OAAO;;CAGtB,IAAI,OAAe;AACjB,SAAO,KAAK,QAAQ;;;AAIxB,eAAsB,kBACpB,KACA,KACA,KACA,UACA,SACA,UACA,gBACA,aACe;AACf,gBAAe,IAAI;CACnB,MAAM,OAAO,IAAI,OAAO;CACxB,MAAM,SAAS,IAAI,UAAU;CAE7B,IAAI;AACJ,KAAI;AACF,aAAW,KAAK,MAAM,IAAI;SACpB;AACN,UAAQ,IAAI;GACV;GACA;GACA,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK,SAAS;IAAM;GACzC,CAAC;AACF,qBACE,KACA,KACA,KAAK,UAAU,EACb,OAAO;GAAE,SAAS;GAAkB,MAAM;GAAyB,MAAM;GAAgB,EAC1F,CAAC,CACH;AACD;;AAGF,KAAI,CAAC,SAAS,QAAQ;AACpB,UAAQ,IAAI;GACV;GACA;GACA,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK,SAAS;IAAM;GACzC,CAAC;AACF,qBACE,KACA,KACA,KAAK,UAAU,EACb,OAAO;GAAE,SAAS;GAAwC,MAAM;GAAyB,EAC1F,CAAC,CACH;AACD;;CAGF,MAAM,eAAsC;EAC1C,OAAO,SAAS,SAAS;EACzB,UAAU,CAAC;GAAE,MAAM;GAAQ,SAAS,SAAS;GAAQ,CAAC;EACtD,eAAe;EAChB;CAED,MAAM,SAAS,UAAU,IAAI;CAC7B,MAAM,UAAU,aACd,UACA,cACA,QAAQ,6BAA6B,OAAO,EAC5C,SAAS,iBACV;AAED,KAAI,QACF,SAAQ,2BAA2B,SAAS,UAAU,OAAO;AAG/D,KACE,WACE,KACA,SACA,SAAS,OACT,IAAI,SACJ,SACA;EAAE;EAAQ;EAAM,SAAS,eAAe,IAAI,QAAQ;EAAE,MAAM;EAAc,EAC1E,SAAS,UACT,SAAS,OACV,CAED;AAEF,KAAI,CAAC,SAAS;AACZ,MAAI,SAAS,QAWX;OAVgB,MAAM,eACpB,KACA,KACA,cACA,UACA,IAAI,OAAO,cACX,UACA,UACA,IACD,EACY;AACX,YAAQ,IAAI;KACV;KACA;KACA,SAAS,eAAe,IAAI,QAAQ;KACpC,MAAM;KACN,UAAU;MAAE,QAAQ,IAAI,cAAc;MAAK,SAAS;MAAM,QAAQ;MAAS;KAC5E,CAAC;AACF;;;EAIJ,MAAM,eAAe,SAAS,SAAS,MAAM;EAC7C,MAAM,gBAAgB,SAAS,SAC3B,oCACA;AACJ,UAAQ,IAAI;GACV;GACA;GACA,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IAAE,QAAQ;IAAc,SAAS;IAAM;GAClD,CAAC;AACF,qBACE,KACA,cACA,KAAK,UAAU,EACb,OAAO;GAAE,SAAS;GAAe,MAAM;GAAyB,MAAM;GAAoB,EAC3F,CAAC,CACH;AACD;;CAGF,MAAM,WAAW,QAAQ;AAEzB,KAAI,gBAAgB,SAAS,EAAE;EAC7B,MAAM,SAAS,SAAS,UAAU;AAClC,UAAQ,IAAI;GACV;GACA;GACA,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IAAE;IAAQ;IAAS;GAC9B,CAAC;AACF,qBAAmB,KAAK,QAAQ,KAAK,UAAU,SAAS,CAAC;AACzD;;AAGF,KAAI,CAAC,gBAAgB,SAAS,EAAE;AAC9B,UAAQ,IAAI;GACV;GACA;GACA,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK;IAAS;GACnC,CAAC;AACF,qBACE,KACA,KACA,KAAK,UAAU,EACb,OAAO;GAAE,SAAS;GAAwC,MAAM;GAAgB,EACjF,CAAC,CACH;AACD;;AAGF,SAAQ,IAAI;EACV;EACA;EACA,SAAS,eAAe,IAAI,QAAQ;EACpC,MAAM;EACN,UAAU;GAAE,QAAQ;GAAK;GAAS;EACnC,CAAC;CAEF,MAAM,QAAQ,SAAS;CACvB,MAAM,aAAa,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;CAGhD,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM;AACpC,aAAY,IAAI,UAAU,MAAM;AAEhC,KAAI,MAAM,WAAW,aAAa;AAChC,MAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,MAAI,IAAI,KAAK,UAAU;GAAE,IAAI,MAAM;GAAI,QAAQ,MAAM;GAAQ,KAAK,MAAM;GAAK;GAAY,CAAC,CAAC;QACtF;AACL,MAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,MAAI,IAAI,KAAK,UAAU;GAAE,IAAI,MAAM;GAAI,QAAQ,MAAM;GAAQ;GAAY,CAAC,CAAC;;;AAI/E,SAAgB,kBACd,KACA,KACA,SACA,SACA,gBACA,aACM;AACN,gBAAe,IAAI;CACnB,MAAM,OAAO,IAAI,OAAO,cAAc;CACtC,MAAM,SAAS,IAAI,UAAU;CAG7B,MAAM,WAAW,GADF,UAAU,IAAI,CACF,GAAG;CAC9B,MAAM,QAAQ,YAAY,IAAI,SAAS;AAEvC,KAAI,CAAC,OAAO;AACV,UAAQ,IAAI;GACV;GACA;GACA,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK,SAAS;IAAM;GACzC,CAAC;AACF,qBACE,KACA,KACA,KAAK,UAAU,EAAE,OAAO;GAAE,SAAS,SAAS,QAAQ;GAAa,MAAM;GAAa,EAAE,CAAC,CACxF;AACD;;AAGF,SAAQ,IAAI;EACV;EACA;EACA,SAAS,eAAe,IAAI,QAAQ;EACpC,MAAM;EACN,UAAU;GAAE,QAAQ;GAAK,SAAS;GAAM;EACzC,CAAC;CAEF,MAAM,aAAa,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;CAChD,MAAM,OAAgC;EACpC,IAAI,MAAM;EACV,QAAQ,MAAM;EACd;EACD;AACD,KAAI,MAAM,IAAK,MAAK,MAAM,MAAM;AAEhC,KAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,KAAI,IAAI,KAAK,UAAU,KAAK,CAAC"}
|
package/dist/vitest.cjs
CHANGED
|
@@ -30,6 +30,8 @@ let vitest = require("vitest");
|
|
|
30
30
|
*/
|
|
31
31
|
function useAimock(options = {}) {
|
|
32
32
|
let handle = null;
|
|
33
|
+
let origOpenaiUrl;
|
|
34
|
+
let origAnthropicUrl;
|
|
33
35
|
(0, vitest.beforeAll)(async () => {
|
|
34
36
|
const { fixtures: fixturePath, patchEnv, ...serverOpts } = options;
|
|
35
37
|
const llm = new require_llmock.LLMock(serverOpts);
|
|
@@ -39,6 +41,8 @@ function useAimock(options = {}) {
|
|
|
39
41
|
}
|
|
40
42
|
const url = await llm.start();
|
|
41
43
|
if (patchEnv !== false) {
|
|
44
|
+
origOpenaiUrl = process.env.OPENAI_BASE_URL;
|
|
45
|
+
origAnthropicUrl = process.env.ANTHROPIC_BASE_URL;
|
|
42
46
|
process.env.OPENAI_BASE_URL = `${url}/v1`;
|
|
43
47
|
process.env.ANTHROPIC_BASE_URL = `${url}/v1`;
|
|
44
48
|
}
|
|
@@ -53,8 +57,10 @@ function useAimock(options = {}) {
|
|
|
53
57
|
(0, vitest.afterAll)(async () => {
|
|
54
58
|
if (handle) {
|
|
55
59
|
if (options.patchEnv !== false) {
|
|
56
|
-
|
|
57
|
-
delete process.env.
|
|
60
|
+
if (origOpenaiUrl !== void 0) process.env.OPENAI_BASE_URL = origOpenaiUrl;
|
|
61
|
+
else delete process.env.OPENAI_BASE_URL;
|
|
62
|
+
if (origAnthropicUrl !== void 0) process.env.ANTHROPIC_BASE_URL = origAnthropicUrl;
|
|
63
|
+
else delete process.env.ANTHROPIC_BASE_URL;
|
|
58
64
|
}
|
|
59
65
|
await handle.llm.stop();
|
|
60
66
|
handle = null;
|
|
@@ -69,7 +75,8 @@ function loadFixtures(fixturePath) {
|
|
|
69
75
|
try {
|
|
70
76
|
if ((0, node_fs.statSync)(fixturePath).isDirectory()) return require_fixture_loader.loadFixturesFromDir(fixturePath);
|
|
71
77
|
return require_fixture_loader.loadFixtureFile(fixturePath);
|
|
72
|
-
} catch {
|
|
78
|
+
} catch (err) {
|
|
79
|
+
console.warn(`[aimock] Failed to load fixtures from ${fixturePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
73
80
|
return [];
|
|
74
81
|
}
|
|
75
82
|
}
|
package/dist/vitest.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitest.cjs","names":["LLMock","loadFixturesFromDir","loadFixtureFile"],"sources":["../src/vitest.ts"],"sourcesContent":["/**\n * Vitest integration for aimock.\n *\n * Usage:\n * import { useAimock } from \"@copilotkit/aimock/vitest\";\n *\n * const mock = useAimock({ fixtures: \"./fixtures\" });\n *\n * it(\"responds\", async () => {\n * const res = await fetch(`${mock().url}/v1/chat/completions`, { ... });\n * });\n */\n\nimport { beforeAll, afterAll, beforeEach } from \"vitest\";\nimport { LLMock } from \"./llmock.js\";\nimport { loadFixtureFile, loadFixturesFromDir } from \"./fixture-loader.js\";\nimport type { Fixture, MockServerOptions } from \"./types.js\";\nimport { statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nexport interface UseAimockOptions extends MockServerOptions {\n /** Path to fixture file or directory. Loaded automatically on start. */\n fixtures?: string;\n /** If true, sets process.env.OPENAI_BASE_URL to the mock URL + /v1. */\n patchEnv?: boolean;\n}\n\nexport interface AimockHandle {\n /** The LLMock instance. */\n readonly llm: LLMock;\n /** The server URL (e.g., http://127.0.0.1:4010). */\n readonly url: string;\n}\n\n/**\n * Start an aimock server for the duration of the test suite.\n *\n * - `beforeAll`: starts the server and optionally loads fixtures\n * - `beforeEach`: resets fixture match counts (not fixtures themselves)\n * - `afterAll`: stops the server\n *\n * Returns a getter function — call it inside tests to access the handle.\n */\nexport function useAimock(options: UseAimockOptions = {}): () => AimockHandle {\n let handle: AimockHandle | null = null;\n\n beforeAll(async () => {\n const { fixtures: fixturePath, patchEnv, ...serverOpts } = options;\n const llm = new LLMock(serverOpts);\n\n if (fixturePath) {\n const resolved = resolve(fixturePath);\n const loadedFixtures = loadFixtures(resolved);\n for (const f of loadedFixtures) {\n llm.addFixture(f);\n }\n }\n\n const url = await llm.start();\n\n if (patchEnv !== false) {\n process.env.OPENAI_BASE_URL = `${url}/v1`;\n process.env.ANTHROPIC_BASE_URL = `${url}/v1`;\n }\n\n handle = { llm, url };\n });\n\n beforeEach(() => {\n if (handle) {\n handle.llm.resetMatchCounts();\n }\n });\n\n afterAll(async () => {\n if (handle) {\n if (options.patchEnv !== false) {\n delete process.env.OPENAI_BASE_URL;\n delete process.env.ANTHROPIC_BASE_URL;\n }\n await handle.llm.stop();\n handle = null;\n }\n });\n\n return () => {\n if (!handle) {\n throw new Error(\"useAimock(): server not started — are you calling this inside a test?\");\n }\n return handle;\n };\n}\n\nfunction loadFixtures(fixturePath: string): Fixture[] {\n try {\n const stat = statSync(fixturePath);\n if (stat.isDirectory()) {\n return loadFixturesFromDir(fixturePath);\n }\n return loadFixtureFile(fixturePath);\n } catch {\n return [];\n }\n}\n\nexport { LLMock } from \"./llmock.js\";\nexport type { MockServerOptions, Fixture } from \"./types.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,SAAgB,UAAU,UAA4B,EAAE,EAAsB;CAC5E,IAAI,SAA8B;
|
|
1
|
+
{"version":3,"file":"vitest.cjs","names":["LLMock","loadFixturesFromDir","loadFixtureFile"],"sources":["../src/vitest.ts"],"sourcesContent":["/**\n * Vitest integration for aimock.\n *\n * Usage:\n * import { useAimock } from \"@copilotkit/aimock/vitest\";\n *\n * const mock = useAimock({ fixtures: \"./fixtures\" });\n *\n * it(\"responds\", async () => {\n * const res = await fetch(`${mock().url}/v1/chat/completions`, { ... });\n * });\n */\n\nimport { beforeAll, afterAll, beforeEach } from \"vitest\";\nimport { LLMock } from \"./llmock.js\";\nimport { loadFixtureFile, loadFixturesFromDir } from \"./fixture-loader.js\";\nimport type { Fixture, MockServerOptions } from \"./types.js\";\nimport { statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nexport interface UseAimockOptions extends MockServerOptions {\n /** Path to fixture file or directory. Loaded automatically on start. */\n fixtures?: string;\n /** If true, sets process.env.OPENAI_BASE_URL to the mock URL + /v1. */\n patchEnv?: boolean;\n}\n\nexport interface AimockHandle {\n /** The LLMock instance. */\n readonly llm: LLMock;\n /** The server URL (e.g., http://127.0.0.1:4010). */\n readonly url: string;\n}\n\n/**\n * Start an aimock server for the duration of the test suite.\n *\n * - `beforeAll`: starts the server and optionally loads fixtures\n * - `beforeEach`: resets fixture match counts (not fixtures themselves)\n * - `afterAll`: stops the server\n *\n * Returns a getter function — call it inside tests to access the handle.\n */\nexport function useAimock(options: UseAimockOptions = {}): () => AimockHandle {\n let handle: AimockHandle | null = null;\n let origOpenaiUrl: string | undefined;\n let origAnthropicUrl: string | undefined;\n\n beforeAll(async () => {\n const { fixtures: fixturePath, patchEnv, ...serverOpts } = options;\n const llm = new LLMock(serverOpts);\n\n if (fixturePath) {\n const resolved = resolve(fixturePath);\n const loadedFixtures = loadFixtures(resolved);\n for (const f of loadedFixtures) {\n llm.addFixture(f);\n }\n }\n\n const url = await llm.start();\n\n if (patchEnv !== false) {\n origOpenaiUrl = process.env.OPENAI_BASE_URL;\n origAnthropicUrl = process.env.ANTHROPIC_BASE_URL;\n process.env.OPENAI_BASE_URL = `${url}/v1`;\n process.env.ANTHROPIC_BASE_URL = `${url}/v1`;\n }\n\n handle = { llm, url };\n });\n\n beforeEach(() => {\n if (handle) {\n handle.llm.resetMatchCounts();\n }\n });\n\n afterAll(async () => {\n if (handle) {\n if (options.patchEnv !== false) {\n if (origOpenaiUrl !== undefined) process.env.OPENAI_BASE_URL = origOpenaiUrl;\n else delete process.env.OPENAI_BASE_URL;\n if (origAnthropicUrl !== undefined) process.env.ANTHROPIC_BASE_URL = origAnthropicUrl;\n else delete process.env.ANTHROPIC_BASE_URL;\n }\n await handle.llm.stop();\n handle = null;\n }\n });\n\n return () => {\n if (!handle) {\n throw new Error(\"useAimock(): server not started — are you calling this inside a test?\");\n }\n return handle;\n };\n}\n\nfunction loadFixtures(fixturePath: string): Fixture[] {\n try {\n const stat = statSync(fixturePath);\n if (stat.isDirectory()) {\n return loadFixturesFromDir(fixturePath);\n }\n return loadFixtureFile(fixturePath);\n } catch (err) {\n console.warn(\n `[aimock] Failed to load fixtures from ${fixturePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n return [];\n }\n}\n\nexport { LLMock } from \"./llmock.js\";\nexport type { MockServerOptions, Fixture } from \"./types.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,SAAgB,UAAU,UAA4B,EAAE,EAAsB;CAC5E,IAAI,SAA8B;CAClC,IAAI;CACJ,IAAI;AAEJ,uBAAU,YAAY;EACpB,MAAM,EAAE,UAAU,aAAa,UAAU,GAAG,eAAe;EAC3D,MAAM,MAAM,IAAIA,sBAAO,WAAW;AAElC,MAAI,aAAa;GAEf,MAAM,iBAAiB,oCADE,YAAY,CACQ;AAC7C,QAAK,MAAM,KAAK,eACd,KAAI,WAAW,EAAE;;EAIrB,MAAM,MAAM,MAAM,IAAI,OAAO;AAE7B,MAAI,aAAa,OAAO;AACtB,mBAAgB,QAAQ,IAAI;AAC5B,sBAAmB,QAAQ,IAAI;AAC/B,WAAQ,IAAI,kBAAkB,GAAG,IAAI;AACrC,WAAQ,IAAI,qBAAqB,GAAG,IAAI;;AAG1C,WAAS;GAAE;GAAK;GAAK;GACrB;AAEF,8BAAiB;AACf,MAAI,OACF,QAAO,IAAI,kBAAkB;GAE/B;AAEF,sBAAS,YAAY;AACnB,MAAI,QAAQ;AACV,OAAI,QAAQ,aAAa,OAAO;AAC9B,QAAI,kBAAkB,OAAW,SAAQ,IAAI,kBAAkB;QAC1D,QAAO,QAAQ,IAAI;AACxB,QAAI,qBAAqB,OAAW,SAAQ,IAAI,qBAAqB;QAChE,QAAO,QAAQ,IAAI;;AAE1B,SAAM,OAAO,IAAI,MAAM;AACvB,YAAS;;GAEX;AAEF,cAAa;AACX,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,wEAAwE;AAE1F,SAAO;;;AAIX,SAAS,aAAa,aAAgC;AACpD,KAAI;AAEF,4BADsB,YAAY,CACzB,aAAa,CACpB,QAAOC,2CAAoB,YAAY;AAEzC,SAAOC,uCAAgB,YAAY;UAC5B,KAAK;AACZ,UAAQ,KACN,yCAAyC,YAAY,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAC1G;AACD,SAAO,EAAE"}
|
package/dist/vitest.js
CHANGED
|
@@ -28,6 +28,8 @@ import { afterAll, beforeAll, beforeEach } from "vitest";
|
|
|
28
28
|
*/
|
|
29
29
|
function useAimock(options = {}) {
|
|
30
30
|
let handle = null;
|
|
31
|
+
let origOpenaiUrl;
|
|
32
|
+
let origAnthropicUrl;
|
|
31
33
|
beforeAll(async () => {
|
|
32
34
|
const { fixtures: fixturePath, patchEnv, ...serverOpts } = options;
|
|
33
35
|
const llm = new LLMock(serverOpts);
|
|
@@ -37,6 +39,8 @@ function useAimock(options = {}) {
|
|
|
37
39
|
}
|
|
38
40
|
const url = await llm.start();
|
|
39
41
|
if (patchEnv !== false) {
|
|
42
|
+
origOpenaiUrl = process.env.OPENAI_BASE_URL;
|
|
43
|
+
origAnthropicUrl = process.env.ANTHROPIC_BASE_URL;
|
|
40
44
|
process.env.OPENAI_BASE_URL = `${url}/v1`;
|
|
41
45
|
process.env.ANTHROPIC_BASE_URL = `${url}/v1`;
|
|
42
46
|
}
|
|
@@ -51,8 +55,10 @@ function useAimock(options = {}) {
|
|
|
51
55
|
afterAll(async () => {
|
|
52
56
|
if (handle) {
|
|
53
57
|
if (options.patchEnv !== false) {
|
|
54
|
-
|
|
55
|
-
delete process.env.
|
|
58
|
+
if (origOpenaiUrl !== void 0) process.env.OPENAI_BASE_URL = origOpenaiUrl;
|
|
59
|
+
else delete process.env.OPENAI_BASE_URL;
|
|
60
|
+
if (origAnthropicUrl !== void 0) process.env.ANTHROPIC_BASE_URL = origAnthropicUrl;
|
|
61
|
+
else delete process.env.ANTHROPIC_BASE_URL;
|
|
56
62
|
}
|
|
57
63
|
await handle.llm.stop();
|
|
58
64
|
handle = null;
|
|
@@ -67,7 +73,8 @@ function loadFixtures(fixturePath) {
|
|
|
67
73
|
try {
|
|
68
74
|
if (statSync(fixturePath).isDirectory()) return loadFixturesFromDir(fixturePath);
|
|
69
75
|
return loadFixtureFile(fixturePath);
|
|
70
|
-
} catch {
|
|
76
|
+
} catch (err) {
|
|
77
|
+
console.warn(`[aimock] Failed to load fixtures from ${fixturePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
71
78
|
return [];
|
|
72
79
|
}
|
|
73
80
|
}
|
package/dist/vitest.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitest.js","names":[],"sources":["../src/vitest.ts"],"sourcesContent":["/**\n * Vitest integration for aimock.\n *\n * Usage:\n * import { useAimock } from \"@copilotkit/aimock/vitest\";\n *\n * const mock = useAimock({ fixtures: \"./fixtures\" });\n *\n * it(\"responds\", async () => {\n * const res = await fetch(`${mock().url}/v1/chat/completions`, { ... });\n * });\n */\n\nimport { beforeAll, afterAll, beforeEach } from \"vitest\";\nimport { LLMock } from \"./llmock.js\";\nimport { loadFixtureFile, loadFixturesFromDir } from \"./fixture-loader.js\";\nimport type { Fixture, MockServerOptions } from \"./types.js\";\nimport { statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nexport interface UseAimockOptions extends MockServerOptions {\n /** Path to fixture file or directory. Loaded automatically on start. */\n fixtures?: string;\n /** If true, sets process.env.OPENAI_BASE_URL to the mock URL + /v1. */\n patchEnv?: boolean;\n}\n\nexport interface AimockHandle {\n /** The LLMock instance. */\n readonly llm: LLMock;\n /** The server URL (e.g., http://127.0.0.1:4010). */\n readonly url: string;\n}\n\n/**\n * Start an aimock server for the duration of the test suite.\n *\n * - `beforeAll`: starts the server and optionally loads fixtures\n * - `beforeEach`: resets fixture match counts (not fixtures themselves)\n * - `afterAll`: stops the server\n *\n * Returns a getter function — call it inside tests to access the handle.\n */\nexport function useAimock(options: UseAimockOptions = {}): () => AimockHandle {\n let handle: AimockHandle | null = null;\n\n beforeAll(async () => {\n const { fixtures: fixturePath, patchEnv, ...serverOpts } = options;\n const llm = new LLMock(serverOpts);\n\n if (fixturePath) {\n const resolved = resolve(fixturePath);\n const loadedFixtures = loadFixtures(resolved);\n for (const f of loadedFixtures) {\n llm.addFixture(f);\n }\n }\n\n const url = await llm.start();\n\n if (patchEnv !== false) {\n process.env.OPENAI_BASE_URL = `${url}/v1`;\n process.env.ANTHROPIC_BASE_URL = `${url}/v1`;\n }\n\n handle = { llm, url };\n });\n\n beforeEach(() => {\n if (handle) {\n handle.llm.resetMatchCounts();\n }\n });\n\n afterAll(async () => {\n if (handle) {\n if (options.patchEnv !== false) {\n delete process.env.OPENAI_BASE_URL;\n delete process.env.ANTHROPIC_BASE_URL;\n }\n await handle.llm.stop();\n handle = null;\n }\n });\n\n return () => {\n if (!handle) {\n throw new Error(\"useAimock(): server not started — are you calling this inside a test?\");\n }\n return handle;\n };\n}\n\nfunction loadFixtures(fixturePath: string): Fixture[] {\n try {\n const stat = statSync(fixturePath);\n if (stat.isDirectory()) {\n return loadFixturesFromDir(fixturePath);\n }\n return loadFixtureFile(fixturePath);\n } catch {\n return [];\n }\n}\n\nexport { LLMock } from \"./llmock.js\";\nexport type { MockServerOptions, Fixture } from \"./types.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,SAAgB,UAAU,UAA4B,EAAE,EAAsB;CAC5E,IAAI,SAA8B;
|
|
1
|
+
{"version":3,"file":"vitest.js","names":[],"sources":["../src/vitest.ts"],"sourcesContent":["/**\n * Vitest integration for aimock.\n *\n * Usage:\n * import { useAimock } from \"@copilotkit/aimock/vitest\";\n *\n * const mock = useAimock({ fixtures: \"./fixtures\" });\n *\n * it(\"responds\", async () => {\n * const res = await fetch(`${mock().url}/v1/chat/completions`, { ... });\n * });\n */\n\nimport { beforeAll, afterAll, beforeEach } from \"vitest\";\nimport { LLMock } from \"./llmock.js\";\nimport { loadFixtureFile, loadFixturesFromDir } from \"./fixture-loader.js\";\nimport type { Fixture, MockServerOptions } from \"./types.js\";\nimport { statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nexport interface UseAimockOptions extends MockServerOptions {\n /** Path to fixture file or directory. Loaded automatically on start. */\n fixtures?: string;\n /** If true, sets process.env.OPENAI_BASE_URL to the mock URL + /v1. */\n patchEnv?: boolean;\n}\n\nexport interface AimockHandle {\n /** The LLMock instance. */\n readonly llm: LLMock;\n /** The server URL (e.g., http://127.0.0.1:4010). */\n readonly url: string;\n}\n\n/**\n * Start an aimock server for the duration of the test suite.\n *\n * - `beforeAll`: starts the server and optionally loads fixtures\n * - `beforeEach`: resets fixture match counts (not fixtures themselves)\n * - `afterAll`: stops the server\n *\n * Returns a getter function — call it inside tests to access the handle.\n */\nexport function useAimock(options: UseAimockOptions = {}): () => AimockHandle {\n let handle: AimockHandle | null = null;\n let origOpenaiUrl: string | undefined;\n let origAnthropicUrl: string | undefined;\n\n beforeAll(async () => {\n const { fixtures: fixturePath, patchEnv, ...serverOpts } = options;\n const llm = new LLMock(serverOpts);\n\n if (fixturePath) {\n const resolved = resolve(fixturePath);\n const loadedFixtures = loadFixtures(resolved);\n for (const f of loadedFixtures) {\n llm.addFixture(f);\n }\n }\n\n const url = await llm.start();\n\n if (patchEnv !== false) {\n origOpenaiUrl = process.env.OPENAI_BASE_URL;\n origAnthropicUrl = process.env.ANTHROPIC_BASE_URL;\n process.env.OPENAI_BASE_URL = `${url}/v1`;\n process.env.ANTHROPIC_BASE_URL = `${url}/v1`;\n }\n\n handle = { llm, url };\n });\n\n beforeEach(() => {\n if (handle) {\n handle.llm.resetMatchCounts();\n }\n });\n\n afterAll(async () => {\n if (handle) {\n if (options.patchEnv !== false) {\n if (origOpenaiUrl !== undefined) process.env.OPENAI_BASE_URL = origOpenaiUrl;\n else delete process.env.OPENAI_BASE_URL;\n if (origAnthropicUrl !== undefined) process.env.ANTHROPIC_BASE_URL = origAnthropicUrl;\n else delete process.env.ANTHROPIC_BASE_URL;\n }\n await handle.llm.stop();\n handle = null;\n }\n });\n\n return () => {\n if (!handle) {\n throw new Error(\"useAimock(): server not started — are you calling this inside a test?\");\n }\n return handle;\n };\n}\n\nfunction loadFixtures(fixturePath: string): Fixture[] {\n try {\n const stat = statSync(fixturePath);\n if (stat.isDirectory()) {\n return loadFixturesFromDir(fixturePath);\n }\n return loadFixtureFile(fixturePath);\n } catch (err) {\n console.warn(\n `[aimock] Failed to load fixtures from ${fixturePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n return [];\n }\n}\n\nexport { LLMock } from \"./llmock.js\";\nexport type { MockServerOptions, Fixture } from \"./types.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,SAAgB,UAAU,UAA4B,EAAE,EAAsB;CAC5E,IAAI,SAA8B;CAClC,IAAI;CACJ,IAAI;AAEJ,WAAU,YAAY;EACpB,MAAM,EAAE,UAAU,aAAa,UAAU,GAAG,eAAe;EAC3D,MAAM,MAAM,IAAI,OAAO,WAAW;AAElC,MAAI,aAAa;GAEf,MAAM,iBAAiB,aADN,QAAQ,YAAY,CACQ;AAC7C,QAAK,MAAM,KAAK,eACd,KAAI,WAAW,EAAE;;EAIrB,MAAM,MAAM,MAAM,IAAI,OAAO;AAE7B,MAAI,aAAa,OAAO;AACtB,mBAAgB,QAAQ,IAAI;AAC5B,sBAAmB,QAAQ,IAAI;AAC/B,WAAQ,IAAI,kBAAkB,GAAG,IAAI;AACrC,WAAQ,IAAI,qBAAqB,GAAG,IAAI;;AAG1C,WAAS;GAAE;GAAK;GAAK;GACrB;AAEF,kBAAiB;AACf,MAAI,OACF,QAAO,IAAI,kBAAkB;GAE/B;AAEF,UAAS,YAAY;AACnB,MAAI,QAAQ;AACV,OAAI,QAAQ,aAAa,OAAO;AAC9B,QAAI,kBAAkB,OAAW,SAAQ,IAAI,kBAAkB;QAC1D,QAAO,QAAQ,IAAI;AACxB,QAAI,qBAAqB,OAAW,SAAQ,IAAI,qBAAqB;QAChE,QAAO,QAAQ,IAAI;;AAE1B,SAAM,OAAO,IAAI,MAAM;AACvB,YAAS;;GAEX;AAEF,cAAa;AACX,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,wEAAwE;AAE1F,SAAO;;;AAIX,SAAS,aAAa,aAAgC;AACpD,KAAI;AAEF,MADa,SAAS,YAAY,CACzB,aAAa,CACpB,QAAO,oBAAoB,YAAY;AAEzC,SAAO,gBAAgB,YAAY;UAC5B,KAAK;AACZ,UAAQ,KACN,yCAAyC,YAAY,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAC1G;AACD,SAAO,EAAE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@copilotkit/aimock",
|
|
3
|
-
"version": "1.15.
|
|
3
|
+
"version": "1.15.1",
|
|
4
4
|
"description": "Mock infrastructure for AI application testing — LLM APIs, image generation, text-to-speech, transcription, video generation, MCP tools, A2A agents, AG-UI event streams, vector databases, search, rerank, and moderation. One package, one port, zero dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|