openai-api-mock 0.1.7 → 0.1.8

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 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 : "OPENAI_API_KEY"});
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.7",
3
+ "version": "0.1.8",
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
+ ```json
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
- function_call: createFunctionCallObject(requestBody),
54
+ [functionOrToolCall]: functionOrToolCallObject,
51
55
  },
52
- finish_reason: 'function_call',
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':