openai-api-mock 0.1.7 → 0.1.9
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/main.test.js +62 -2
- package/package.json +4 -2
- package/readme.md +36 -0
- package/src/chat.js +28 -3
package/main.test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { mockOpenAIResponse, stopMocking } = require('./main');
|
|
2
2
|
|
|
3
3
|
const OpenAI = require('openai');
|
|
4
|
-
const openai = new OpenAI({apiKey
|
|
4
|
+
const openai = new OpenAI({ apiKey: "OPENAI_API_KEY" });
|
|
5
5
|
|
|
6
6
|
describe('Mock OpenAI Chat & Image generation API', () => {
|
|
7
7
|
beforeEach(() => {
|
|
@@ -19,7 +19,6 @@ describe('Mock OpenAI Chat & Image generation API', () => {
|
|
|
19
19
|
{ role: 'user', content: "Suggest at least 5 recipes" },
|
|
20
20
|
]
|
|
21
21
|
});
|
|
22
|
-
|
|
23
22
|
expect(response.choices[0]).toHaveProperty('finish_reason', 'stop');
|
|
24
23
|
expect(response.model).toEqual('gpt-3.5-mock');
|
|
25
24
|
} catch (error) {
|
|
@@ -90,6 +89,67 @@ describe('Mock OpenAI Chat & Image generation API', () => {
|
|
|
90
89
|
});
|
|
91
90
|
|
|
92
91
|
|
|
92
|
+
it('should mock the chat completion with function call (tools)', async () => {
|
|
93
|
+
const response = await openai.chat.completions.create({
|
|
94
|
+
model: "gpt-3.5",
|
|
95
|
+
messages: [
|
|
96
|
+
{ role: 'system', content: "You'r an expert chef" },
|
|
97
|
+
{ role: 'user', content: "Suggest at least 5 recipes" },
|
|
98
|
+
],
|
|
99
|
+
tools: [
|
|
100
|
+
{
|
|
101
|
+
type: "function",
|
|
102
|
+
function: {
|
|
103
|
+
name: 'get_recipes',
|
|
104
|
+
description: 'Suggest at least 5 recipes based on the ingredients you have',
|
|
105
|
+
parameters: {
|
|
106
|
+
type: "object",
|
|
107
|
+
properties: {
|
|
108
|
+
recipes: {
|
|
109
|
+
type: "array",
|
|
110
|
+
description: "The recipes that can be made with the ingredients",
|
|
111
|
+
items: {
|
|
112
|
+
type: 'object',
|
|
113
|
+
properties: {
|
|
114
|
+
name: {
|
|
115
|
+
type: 'string',
|
|
116
|
+
description: 'The name of the recipe',
|
|
117
|
+
},
|
|
118
|
+
ingredients: {
|
|
119
|
+
type: 'array',
|
|
120
|
+
description: 'The ingredients needed for the recipe',
|
|
121
|
+
items: {
|
|
122
|
+
type: 'string',
|
|
123
|
+
description: 'The name of the ingredient',
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
instructions: {
|
|
127
|
+
type: 'string',
|
|
128
|
+
description: 'Detailed instructions on how to make the recipe',
|
|
129
|
+
},
|
|
130
|
+
serving: {
|
|
131
|
+
type: 'string',
|
|
132
|
+
description: 'The number of people the recipe serves',
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
required: ["recipes"],
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
}],
|
|
143
|
+
tool_choice: { name: 'get_recipes' },
|
|
144
|
+
});
|
|
145
|
+
expect(response.model).toEqual('gpt-3.5-mock');
|
|
146
|
+
expect(response.choices[0]).toHaveProperty('finish_reason', 'tool_calls');
|
|
147
|
+
expect(response.choices[0].message.tool_calls[0].function).toHaveProperty('name', 'get_recipes');
|
|
148
|
+
expect(response.choices[0].message.tool_calls[0].function).toHaveProperty('arguments');
|
|
149
|
+
expect(JSON.parse(response.choices[0].message.tool_calls[0].function.arguments)).toHaveProperty('recipes');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
|
|
93
153
|
it('should mock image generation', async () => {
|
|
94
154
|
try {
|
|
95
155
|
const response = await openai.images.generate({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openai-api-mock",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "A Node.js module for mocking OpenAI API responses in a development environment",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"mock",
|
|
12
12
|
"api",
|
|
13
13
|
"development",
|
|
14
|
-
"testing"
|
|
14
|
+
"testing",
|
|
15
|
+
"chatgpt",
|
|
16
|
+
"interceptor"
|
|
15
17
|
],
|
|
16
18
|
"scripts": {
|
|
17
19
|
"test": "jest"
|
package/readme.md
CHANGED
|
@@ -32,6 +32,42 @@ mockOpenAIResponse(force = true);
|
|
|
32
32
|
The force parameter is a boolean that determines whether the mock response should be used regardless of the environment.
|
|
33
33
|
If force is true, the mock response will be used regardless of the environment. If force is false or not provided, the mock response will only be used if the <code>NODE_ENV</code> environment variable is set to 'development'.
|
|
34
34
|
|
|
35
|
+
### Example responces
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
// Call the mockOpenAIResponse function once to set up the mock
|
|
39
|
+
mockOpenAIResponse()
|
|
40
|
+
|
|
41
|
+
// Now, when you call the OpenAI API, it will return a mock response
|
|
42
|
+
const response = await openai.chat.completions.create({
|
|
43
|
+
model: "gpt-3.5",
|
|
44
|
+
messages: [
|
|
45
|
+
{ role: 'system', content: "You'r an expert chef" },
|
|
46
|
+
{ role: 'user', content: "Suggest at least 5 recipes" },
|
|
47
|
+
]
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
In this example, the `response` constant will contain mock data, simulating a response from the OpenAI API:
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
{
|
|
54
|
+
choices: [
|
|
55
|
+
{
|
|
56
|
+
finish_reason: 'stop',
|
|
57
|
+
index: 0,
|
|
58
|
+
message: [Object],
|
|
59
|
+
logprobs: null
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
created: 1707040459,
|
|
63
|
+
id: 'chatcmpl-tggOnwW8Lp2XiwQ8dmHHAcNYJ8CfzR',
|
|
64
|
+
model: 'gpt-3.5-mock',
|
|
65
|
+
object: 'chat.completion',
|
|
66
|
+
usage: { completion_tokens: 17, prompt_tokens: 57, total_tokens: 74 }
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
|
|
35
71
|
## Intercepted URLs
|
|
36
72
|
|
|
37
73
|
This module uses the `nock` library to intercept HTTP calls to the following OpenAI API endpoints:
|
package/src/chat.js
CHANGED
|
@@ -3,7 +3,7 @@ const { faker } = require('@faker-js/faker');
|
|
|
3
3
|
function getChatResponce(requestBody) {
|
|
4
4
|
const created = Math.floor(Date.now() / 1000);
|
|
5
5
|
|
|
6
|
-
if (!requestBody.functions) {
|
|
6
|
+
if (!requestBody.functions && !requestBody.tools) {
|
|
7
7
|
return createDefaultResponse(created);
|
|
8
8
|
}
|
|
9
9
|
|
|
@@ -36,6 +36,10 @@ function createDefaultResponse(created) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
function createFunctionCallingResponse(requestBody, created) {
|
|
39
|
+
const isTool = Boolean(requestBody.tools);
|
|
40
|
+
const functionOrToolCallObject = isTool ? [createToolCallObject(requestBody)] : createFunctionCallObject(requestBody);
|
|
41
|
+
const functionOrToolCall = isTool ? 'tool_calls' : 'function_call';
|
|
42
|
+
|
|
39
43
|
const functionCallingResponse = {
|
|
40
44
|
id: `chatcmpl-${faker.string.alphanumeric(30)}`,
|
|
41
45
|
object: 'chat.completion',
|
|
@@ -47,9 +51,9 @@ function createFunctionCallingResponse(requestBody, created) {
|
|
|
47
51
|
message: {
|
|
48
52
|
role: 'assistant',
|
|
49
53
|
content: null,
|
|
50
|
-
|
|
54
|
+
[functionOrToolCall]: functionOrToolCallObject,
|
|
51
55
|
},
|
|
52
|
-
finish_reason:
|
|
56
|
+
finish_reason: functionOrToolCall,
|
|
53
57
|
},
|
|
54
58
|
],
|
|
55
59
|
usage: {
|
|
@@ -62,6 +66,16 @@ function createFunctionCallingResponse(requestBody, created) {
|
|
|
62
66
|
return functionCallingResponse;
|
|
63
67
|
}
|
|
64
68
|
|
|
69
|
+
function createToolCallObject(requestBody) {
|
|
70
|
+
return {
|
|
71
|
+
id: `call-${faker.string.alphanumeric(30)}`,
|
|
72
|
+
type: "function",
|
|
73
|
+
"function": {
|
|
74
|
+
name: `${requestBody.tools[0].function.name}`,
|
|
75
|
+
arguments: `${generateToolCallArguments(requestBody)}`
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
65
79
|
function createFunctionCallObject(requestBody) {
|
|
66
80
|
return {
|
|
67
81
|
name: `${requestBody.functions[0].name}`,
|
|
@@ -69,6 +83,17 @@ function createFunctionCallObject(requestBody) {
|
|
|
69
83
|
};
|
|
70
84
|
}
|
|
71
85
|
|
|
86
|
+
function generateToolCallArguments(requestBody) {
|
|
87
|
+
const { parameters } = requestBody.tools[0].function;
|
|
88
|
+
const argumentsObject = {};
|
|
89
|
+
|
|
90
|
+
Object.entries(parameters.properties).forEach(([paramName, paramDetails]) => {
|
|
91
|
+
argumentsObject[paramName] = generateFakeData(paramDetails.type, paramDetails);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return JSON.stringify(argumentsObject, null, 2);
|
|
95
|
+
}
|
|
96
|
+
|
|
72
97
|
function generateFakeData(type, properties) {
|
|
73
98
|
switch (type) {
|
|
74
99
|
case 'string':
|