openai-api-mock 0.1.31 → 0.1.33
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/LICENSE.md +21 -21
- package/README.md +173 -147
- package/dist/index.cjs +2267 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +2267 -0
- package/dist/index.js.map +1 -0
- package/index.d.ts +45 -0
- package/package.json +60 -53
- package/.github/workflows/test.yml +0 -33
- package/__tests__/custom_endpoint.test.js +0 -47
- package/__tests__/index.test.js +0 -225
- package/__tests__/mock_delay.test.js +0 -29
- package/jest.config.js +0 -15
- package/renovate.json +0 -4
- package/src/chat.js +0 -16
- package/src/chat.stream.js +0 -33
- package/src/image.js +0 -24
- package/src/index.js +0 -123
- package/src/utils/fakeDataGenerators.js +0 -107
- package/src/utils/responseGenerators.js +0 -112
- package/vite.config.js +0 -37
package/src/index.js
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import nock from 'nock';
|
|
2
|
-
import { getChatResponce } from './chat.js';
|
|
3
|
-
import { createChatStream } from './chat.stream.js';
|
|
4
|
-
import { getImageResponce } from './image.js';
|
|
5
|
-
|
|
6
|
-
const OPEN_AI_BASE_URL = 'https://api.openai.com';
|
|
7
|
-
const CHAT_COMPLETIONS_ENDPOINT = '/v1/chat/completions';
|
|
8
|
-
const IMAGE_GENERATIONS_ENDPOINT = '/v1/images/generations';
|
|
9
|
-
const customScopes = []; // Track custom scopes
|
|
10
|
-
/**
|
|
11
|
-
* Mock OpenAI API responses
|
|
12
|
-
* @param {boolean} force - Force mocking regardless of environment
|
|
13
|
-
* @param {Object} options - Additional configuration options
|
|
14
|
-
* @param {boolean} options.includeErrors - Whether to include error scenarios in mocking
|
|
15
|
-
* @param {number} options.latency - Artificial latency in ms to simulate network delay
|
|
16
|
-
* @param {boolean} options.logRequests - Whether to log incoming requests
|
|
17
|
-
* @returns {Object} An object with control methods for the mocks
|
|
18
|
-
*/
|
|
19
|
-
export function mockOpenAIResponse(force = false, options = {}) {
|
|
20
|
-
const {
|
|
21
|
-
includeErrors = false,
|
|
22
|
-
latency = 0,
|
|
23
|
-
logRequests = false
|
|
24
|
-
} = options;
|
|
25
|
-
|
|
26
|
-
const env = process.env.NODE_ENV || 'development';
|
|
27
|
-
|
|
28
|
-
// Only proceed if in development or forced
|
|
29
|
-
if (env !== 'development' && !force) {
|
|
30
|
-
return { isActive: false, stopMocking };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Mock chat completions endpoint
|
|
34
|
-
const chatScope = nock(OPEN_AI_BASE_URL)
|
|
35
|
-
.post(CHAT_COMPLETIONS_ENDPOINT)
|
|
36
|
-
.delay(latency) // Add delay to the interceptor
|
|
37
|
-
.reply(function (uri, requestBody) {
|
|
38
|
-
if (logRequests) {
|
|
39
|
-
console.log(`[openai-api-mock] Chat request:`, JSON.stringify(requestBody, null, 2));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
// Validate minimal required fields
|
|
44
|
-
if (!requestBody.model || !requestBody.messages || !Array.isArray(requestBody.messages)) {
|
|
45
|
-
return [400, { error: { message: 'Invalid request. Missing required fields.' } }];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Simulate random errors if enabled
|
|
49
|
-
if (includeErrors && Math.random() < 0.05) {
|
|
50
|
-
return [429, { error: { message: 'Rate limit exceeded' } }];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const isSteaming = requestBody.stream === true;
|
|
54
|
-
|
|
55
|
-
if (isSteaming) {
|
|
56
|
-
const stream = createChatStream(requestBody);
|
|
57
|
-
return [200, stream];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return [200, getChatResponce(requestBody)];
|
|
61
|
-
} catch (error) {
|
|
62
|
-
console.error('[openai-api-mock] Error processing chat request:', error);
|
|
63
|
-
return [500, { error: { message: 'Internal server error in mock' } }];
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// Mock image generations endpoint
|
|
68
|
-
const imageScope = nock(OPEN_AI_BASE_URL)
|
|
69
|
-
.post(IMAGE_GENERATIONS_ENDPOINT)
|
|
70
|
-
.delay(latency) // Add delay to the interceptor
|
|
71
|
-
.reply(function (uri, requestBody) {
|
|
72
|
-
if (logRequests) {
|
|
73
|
-
console.log(`[openai-api-mock] Image request:`, JSON.stringify(requestBody, null, 2));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
// Validate minimal required fields
|
|
78
|
-
if (!requestBody.prompt) {
|
|
79
|
-
return [400, { error: { message: 'Invalid request. Missing prompt.' } }];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Simulate random errors if enabled
|
|
83
|
-
if (includeErrors && Math.random() < 0.05) {
|
|
84
|
-
return [400, { error: { message: 'Your request was rejected as a result of our safety system.' } }];
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return [200, getImageResponce(requestBody)];
|
|
88
|
-
} catch (error) {
|
|
89
|
-
console.error('[openai-api-mock] Error processing image request:', error);
|
|
90
|
-
return [500, { error: { message: 'Internal server error in mock' } }];
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// Enable other network connections
|
|
95
|
-
nock.enableNetConnect(host => host !== "api.openai.com");
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return {
|
|
100
|
-
isActive: true,
|
|
101
|
-
stopMocking,
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Adds a custom endpoint mock
|
|
105
|
-
* @param {string} method - HTTP method (e.g., 'GET', 'POST')
|
|
106
|
-
* @param {string} path - Endpoint path (e.g., '/v1/custom')
|
|
107
|
-
* @param {Function} handler - Function returning [statusCode, responseBody]
|
|
108
|
-
*/
|
|
109
|
-
addCustomEndpoint(method, path, handler) {
|
|
110
|
-
const methodLower = method.toLowerCase();
|
|
111
|
-
const scope = nock(OPEN_AI_BASE_URL)
|
|
112
|
-
[methodLower](path)
|
|
113
|
-
.reply(handler)
|
|
114
|
-
.persist(); // Keep the mock active indefinitely
|
|
115
|
-
customScopes.push(scope);
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function stopMocking() {
|
|
121
|
-
nock.cleanAll();
|
|
122
|
-
customScopes.forEach(scope => scope.persist(false)); // Disable persistence
|
|
123
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { faker } from '@faker-js/faker';
|
|
2
|
-
|
|
3
|
-
function generateToolCallArguments(requestBody) {
|
|
4
|
-
const { parameters } = requestBody.tools[0].function;
|
|
5
|
-
const argumentsObject = {};
|
|
6
|
-
|
|
7
|
-
Object.entries(parameters.properties).forEach(([paramName, paramDetails]) => {
|
|
8
|
-
argumentsObject[paramName] = generateFakeData(
|
|
9
|
-
paramDetails.type,
|
|
10
|
-
paramDetails,
|
|
11
|
-
paramName
|
|
12
|
-
);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
return JSON.stringify(argumentsObject, null, 2);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function generateFunctionCallArguments(requestBody) {
|
|
19
|
-
const { parameters } = requestBody.functions[0];
|
|
20
|
-
const argumentsObject = {};
|
|
21
|
-
|
|
22
|
-
Object.entries(parameters.properties).forEach(([paramName, paramDetails]) => {
|
|
23
|
-
argumentsObject[paramName] = generateFakeData(
|
|
24
|
-
paramDetails.type,
|
|
25
|
-
paramDetails,
|
|
26
|
-
paramName
|
|
27
|
-
);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
return JSON.stringify(argumentsObject, null, 2);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function generateFakeData(type, properties, name) {
|
|
34
|
-
switch (type) {
|
|
35
|
-
case "string":
|
|
36
|
-
return generateFakeStringData(name);
|
|
37
|
-
case "number":
|
|
38
|
-
return faker.number.int({ max: 100 });
|
|
39
|
-
case "array":
|
|
40
|
-
return generateFakeArray(properties);
|
|
41
|
-
case "object":
|
|
42
|
-
return generateFakeObject(properties);
|
|
43
|
-
case "boolean":
|
|
44
|
-
return faker.datatype.boolean();
|
|
45
|
-
default:
|
|
46
|
-
return faker.lorem.words(1);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function generateFakeArray(properties) {
|
|
51
|
-
const arrayItemsType = properties.items.type;
|
|
52
|
-
return Array.from({ length: 5 }, () => {
|
|
53
|
-
if (arrayItemsType === "string") {
|
|
54
|
-
return faker.lorem.words(1);
|
|
55
|
-
} else if (arrayItemsType === "object") {
|
|
56
|
-
const itemProperties = properties.items.properties;
|
|
57
|
-
return generateFakeData("object", { properties: itemProperties }, "item");
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function generateFakeObject(properties) {
|
|
63
|
-
const itemObject = {};
|
|
64
|
-
Object.entries(properties.properties).forEach(([itemName, itemDetails]) => {
|
|
65
|
-
itemObject[itemName] = generateFakeData(itemDetails.type, itemDetails, itemName);
|
|
66
|
-
});
|
|
67
|
-
return itemObject;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function generateFakeStringData(name) {
|
|
71
|
-
if (name === "name") {
|
|
72
|
-
return faker.person.fullName();
|
|
73
|
-
} else if (name === "email") {
|
|
74
|
-
return faker.internet.email();
|
|
75
|
-
} else if (name === "price") {
|
|
76
|
-
return faker.commerce.price();
|
|
77
|
-
} else if (name === "company") {
|
|
78
|
-
return faker.company.name();
|
|
79
|
-
} else if (name === "phone") {
|
|
80
|
-
return faker.phone.number();
|
|
81
|
-
} else if (name === "address") {
|
|
82
|
-
return faker.location.streetAddress();
|
|
83
|
-
} else if (name === "date") {
|
|
84
|
-
return faker.date.past();
|
|
85
|
-
} else if (name === "jobTitle") {
|
|
86
|
-
return faker.person.jobTitle();
|
|
87
|
-
} else if (name === "creditCardNumber") {
|
|
88
|
-
return faker.finance.creditCardNumber();
|
|
89
|
-
} else if (name === "currencyCode") {
|
|
90
|
-
return faker.finance.currencyCode();
|
|
91
|
-
} else if (name === "productName") {
|
|
92
|
-
return faker.commerce.productName();
|
|
93
|
-
} else if (name === "uuid") {
|
|
94
|
-
return faker.string.uuid();
|
|
95
|
-
} else {
|
|
96
|
-
return faker.lorem.words(5);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export {
|
|
101
|
-
generateToolCallArguments,
|
|
102
|
-
generateFunctionCallArguments,
|
|
103
|
-
generateFakeData,
|
|
104
|
-
generateFakeArray,
|
|
105
|
-
generateFakeObject,
|
|
106
|
-
generateFakeStringData,
|
|
107
|
-
};
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { faker } from '@faker-js/faker';
|
|
2
|
-
import { generateFunctionCallArguments, generateToolCallArguments } from './fakeDataGenerators.js'
|
|
3
|
-
|
|
4
|
-
function createDefaultResponse(created) {
|
|
5
|
-
return {
|
|
6
|
-
choices: [
|
|
7
|
-
{
|
|
8
|
-
finish_reason: "stop",
|
|
9
|
-
index: 0,
|
|
10
|
-
message: {
|
|
11
|
-
content: faker.lorem.words(5),
|
|
12
|
-
role: "assistant",
|
|
13
|
-
},
|
|
14
|
-
logprobs: null,
|
|
15
|
-
},
|
|
16
|
-
],
|
|
17
|
-
created: created,
|
|
18
|
-
id: `chatcmpl-${faker.string.alphanumeric(30)}`,
|
|
19
|
-
model: `gpt-3.5-mock`,
|
|
20
|
-
object: "chat.completion",
|
|
21
|
-
usage: {
|
|
22
|
-
completion_tokens: 17,
|
|
23
|
-
prompt_tokens: 57,
|
|
24
|
-
total_tokens: 74,
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function createFunctionCallingResponse(requestBody, created) {
|
|
30
|
-
const isTool = Boolean(requestBody.tools);
|
|
31
|
-
const functionOrToolCallObject = isTool
|
|
32
|
-
? [createToolCallObject(requestBody)]
|
|
33
|
-
: createFunctionCallObject(requestBody);
|
|
34
|
-
const functionOrToolCall = isTool ? "tool_calls" : "function_call";
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
id: `chatcmpl-${faker.string.alphanumeric(30)}`,
|
|
38
|
-
object: "chat.completion",
|
|
39
|
-
created: created,
|
|
40
|
-
model: "gpt-3.5-mock",
|
|
41
|
-
choices: [
|
|
42
|
-
{
|
|
43
|
-
index: 0,
|
|
44
|
-
message: {
|
|
45
|
-
role: "assistant",
|
|
46
|
-
content: null,
|
|
47
|
-
[functionOrToolCall]: functionOrToolCallObject,
|
|
48
|
-
},
|
|
49
|
-
finish_reason: functionOrToolCall,
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
|
-
usage: {
|
|
53
|
-
prompt_tokens: 81,
|
|
54
|
-
completion_tokens: 19,
|
|
55
|
-
total_tokens: 100,
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function createToolCallObject(requestBody) {
|
|
61
|
-
return {
|
|
62
|
-
id: `call-${faker.string.alphanumeric(30)}`,
|
|
63
|
-
type: "function",
|
|
64
|
-
function: {
|
|
65
|
-
name: `${requestBody.tools[0].function.name}`,
|
|
66
|
-
arguments: `${generateToolCallArguments(requestBody)}`,
|
|
67
|
-
},
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function createFunctionCallObject(requestBody) {
|
|
72
|
-
return {
|
|
73
|
-
name: `${requestBody.functions[0].name}`,
|
|
74
|
-
arguments: `${generateFunctionCallArguments(requestBody)}`,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function getSteamChatObject() {
|
|
79
|
-
const created = Math.floor(Date.now() / 1000);
|
|
80
|
-
|
|
81
|
-
let lorem = faker.lorem.paragraph()
|
|
82
|
-
const loremArray = lorem.split(" ");
|
|
83
|
-
|
|
84
|
-
let ob = {
|
|
85
|
-
id: `chatcmpl-${faker.string.alphanumeric(30)}`,
|
|
86
|
-
object: 'chat.completion.chunk',
|
|
87
|
-
created: created,
|
|
88
|
-
model: "gpt-3.5-mock",
|
|
89
|
-
system_fingerprint: null,
|
|
90
|
-
choices: [
|
|
91
|
-
{
|
|
92
|
-
index: 0,
|
|
93
|
-
delta: {
|
|
94
|
-
content: loremArray[0]
|
|
95
|
-
},
|
|
96
|
-
logprobs: null,
|
|
97
|
-
finish_reason: null
|
|
98
|
-
}
|
|
99
|
-
]
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return JSON.stringify(ob);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
export {
|
|
107
|
-
createDefaultResponse,
|
|
108
|
-
createFunctionCallingResponse,
|
|
109
|
-
createToolCallObject,
|
|
110
|
-
createFunctionCallObject,
|
|
111
|
-
getSteamChatObject,
|
|
112
|
-
};
|
package/vite.config.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vite';
|
|
2
|
-
import { builtinModules } from 'module';
|
|
3
|
-
|
|
4
|
-
// Get all Node.js built-in modules
|
|
5
|
-
const builtins = builtinModules.filter(m => !m.startsWith('_'));
|
|
6
|
-
|
|
7
|
-
export default defineConfig({
|
|
8
|
-
build: {
|
|
9
|
-
lib: {
|
|
10
|
-
entry: './src/index.js',
|
|
11
|
-
name: 'openai-api-mock',
|
|
12
|
-
formats: ['es', 'cjs'],
|
|
13
|
-
fileName: (format) => `index.${format === 'es' ? 'js' : 'cjs'}`
|
|
14
|
-
},
|
|
15
|
-
rollupOptions: {
|
|
16
|
-
external: [
|
|
17
|
-
...builtins.map(m => `node:${m}`),
|
|
18
|
-
...builtins,
|
|
19
|
-
'nock',
|
|
20
|
-
'node-fetch',
|
|
21
|
-
'openai'
|
|
22
|
-
],
|
|
23
|
-
output: {
|
|
24
|
-
exports: "auto",
|
|
25
|
-
// Provide global variables to use in UMD build
|
|
26
|
-
globals: {
|
|
27
|
-
nock: 'nock',
|
|
28
|
-
'node-fetch': 'fetch',
|
|
29
|
-
openai: 'OpenAI'
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
target: 'node18',
|
|
34
|
-
sourcemap: true,
|
|
35
|
-
minify: false
|
|
36
|
-
}
|
|
37
|
-
});
|