@@ -2,17 +2,15 @@ import {vi, type MockedFunction, beforeEach, expect, describe, it} from 'vitest'
2
2
import * as core from '../__fixtures__/core.js'
3
3
4
4
// eslint-disable-next-line @typescript-eslint/no-explicit-any
5
- const mockPost = vi . fn ( ) as MockedFunction < any >
6
- const mockPath = vi . fn ( ( ) => ( { post : mockPost } ) )
7
- const mockClient = vi . fn ( ( ) => ( { path : mockPath } ) )
8
-
9
- vi . mock ( '@azure-rest/ai-inference' , ( ) => ( {
10
- default : mockClient ,
11
- isUnexpected : vi . fn ( ( ) => false ) ,
5
+ const mockCreate = vi . fn ( ) as MockedFunction < any >
6
+ const mockCompletions = { create : mockCreate }
7
+ const mockChat = { completions : mockCompletions }
8
+ const mockOpenAIClient = vi . fn ( ( ) => ( {
9
+ chat : mockChat ,
12
10
} ) )
13
11
14
- vi . mock ( '@azure/core-auth ' , ( ) => ( {
15
- AzureKeyCredential : vi . fn ( ) ,
12
+ vi . mock ( 'openai ' , ( ) => ( {
13
+ default : mockOpenAIClient ,
16
14
} ) )
17
15
18
16
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -29,8 +27,8 @@ const {simpleInference, mcpInference} = await import('../src/inference.js')
29
27
describe ( 'inference.ts' , ( ) => {
30
28
const mockRequest = {
31
29
messages : [
32
- { role : 'system' , content : 'You are a test assistant' } ,
33
- { role : 'user' , content : 'Hello, AI!' } ,
30
+ { role : 'system' as const , content : 'You are a test assistant' } ,
31
+ { role : 'user' as const , content : 'Hello, AI!' } ,
34
32
] ,
35
33
modelName : 'gpt-4' ,
36
34
maxTokens : 100 ,
@@ -45,18 +43,16 @@ describe('inference.ts', () => {
45
43
describe ( 'simpleInference' , ( ) => {
46
44
it ( 'performs simple inference without tools' , async ( ) => {
47
45
const mockResponse = {
48
- body : {
49
- choices : [
50
- {
51
- message : {
52
- content : 'Hello, user!' ,
53
- } ,
46
+ choices : [
47
+ {
48
+ message : {
49
+ content : 'Hello, user!' ,
54
50
} ,
55
- ] ,
56
- } ,
51
+ } ,
52
+ ] ,
57
53
}
58
54
59
- mockPost . mockResolvedValue ( mockResponse )
55
+ mockCreate . mockResolvedValue ( mockResponse )
60
56
61
57
const result = await simpleInference ( mockRequest )
62
58
@@ -65,38 +61,34 @@ describe('inference.ts', () => {
65
61
expect ( core . info ) . toHaveBeenCalledWith ( 'Model response: Hello, user!' )
66
62
67
63
// Verify the request structure
68
- expect ( mockPost ) . toHaveBeenCalledWith ( {
69
- body : {
70
- messages : [
71
- {
72
- role : 'system' ,
73
- content : 'You are a test assistant' ,
74
- } ,
75
- {
76
- role : 'user' ,
77
- content : 'Hello, AI!' ,
78
- } ,
79
- ] ,
80
- max_tokens : 100 ,
81
- model : 'gpt-4' ,
82
- } ,
64
+ expect ( mockCreate ) . toHaveBeenCalledWith ( {
65
+ messages : [
66
+ {
67
+ role : 'system' ,
68
+ content : 'You are a test assistant' ,
69
+ } ,
70
+ {
71
+ role : 'user' ,
72
+ content : 'Hello, AI!' ,
73
+ } ,
74
+ ] ,
75
+ max_tokens : 100 ,
76
+ model : 'gpt-4' ,
83
77
} )
84
78
} )
85
79
86
80
it ( 'handles null response content' , async ( ) => {
87
81
const mockResponse = {
88
- body : {
89
- choices : [
90
- {
91
- message : {
92
- content : null ,
93
- } ,
82
+ choices : [
83
+ {
84
+ message : {
85
+ content : null ,
94
86
} ,
95
- ] ,
96
- } ,
87
+ } ,
88
+ ] ,
97
89
}
98
90
99
- mockPost . mockResolvedValue ( mockResponse )
91
+ mockCreate . mockResolvedValue ( mockResponse )
100
92
101
93
const result = await simpleInference ( mockRequest )
102
94
@@ -123,19 +115,17 @@ describe('inference.ts', () => {
123
115
124
116
it ( 'performs inference without tool calls' , async ( ) => {
125
117
const mockResponse = {
126
- body : {
127
- choices : [
128
- {
129
- message : {
130
- content : 'Hello, user!' ,
131
- tool_calls : null ,
132
- } ,
118
+ choices : [
119
+ {
120
+ message : {
121
+ content : 'Hello, user!' ,
122
+ tool_calls : null ,
133
123
} ,
134
- ] ,
135
- } ,
124
+ } ,
125
+ ] ,
136
126
}
137
127
138
- mockPost . mockResolvedValue ( mockResponse )
128
+ mockCreate . mockResolvedValue ( mockResponse )
139
129
140
130
const result = await mcpInference ( mockRequest , mockMcpClient )
141
131
@@ -146,12 +136,12 @@ describe('inference.ts', () => {
146
136
147
137
// The MCP inference loop will always add the assistant message, even when there are no tool calls
148
138
// So we don't check the exact messages, just that tools were included
149
- expect ( mockPost ) . toHaveBeenCalledTimes ( 1 )
139
+ expect ( mockCreate ) . toHaveBeenCalledTimes ( 1 )
150
140
// eslint-disable-next-line @typescript-eslint/no-explicit-any
151
- const callArgs = mockPost . mock . calls [ 0 ] [ 0 ] as any
152
- expect ( callArgs . body . tools ) . toEqual ( mockMcpClient . tools )
153
- expect ( callArgs . body . model ) . toBe ( 'gpt-4' )
154
- expect ( callArgs . body . max_tokens ) . toBe ( 100 )
141
+ const callArgs = mockCreate . mock . calls [ 0 ] [ 0 ] as any
142
+ expect ( callArgs . tools ) . toEqual ( mockMcpClient . tools )
143
+ expect ( callArgs . model ) . toBe ( 'gpt-4' )
144
+ expect ( callArgs . max_tokens ) . toBe ( 100 )
155
145
} )
156
146
157
147
it ( 'executes tool calls and continues conversation' , async ( ) => {
@@ -176,49 +166,45 @@ describe('inference.ts', () => {
176
166
177
167
// First response with tool calls
178
168
const firstResponse = {
179
- body : {
180
- choices : [
181
- {
182
- message : {
183
- content : 'I need to use a tool.' ,
184
- tool_calls : toolCalls ,
185
- } ,
169
+ choices : [
170
+ {
171
+ message : {
172
+ content : 'I need to use a tool.' ,
173
+ tool_calls : toolCalls ,
186
174
} ,
187
- ] ,
188
- } ,
175
+ } ,
176
+ ] ,
189
177
}
190
178
191
179
// Second response after tool execution
192
180
const secondResponse = {
193
- body : {
194
- choices : [
195
- {
196
- message : {
197
- content : 'Here is the final answer.' ,
198
- tool_calls : null ,
199
- } ,
181
+ choices : [
182
+ {
183
+ message : {
184
+ content : 'Here is the final answer.' ,
185
+ tool_calls : null ,
200
186
} ,
201
- ] ,
202
- } ,
187
+ } ,
188
+ ] ,
203
189
}
204
190
205
- mockPost . mockResolvedValueOnce ( firstResponse ) . mockResolvedValueOnce ( secondResponse )
191
+ mockCreate . mockResolvedValueOnce ( firstResponse ) . mockResolvedValueOnce ( secondResponse )
206
192
207
193
mockExecuteToolCalls . mockResolvedValue ( toolResults )
208
194
209
195
const result = await mcpInference ( mockRequest , mockMcpClient )
210
196
211
197
expect ( result ) . toBe ( 'Here is the final answer.' )
212
198
expect ( mockExecuteToolCalls ) . toHaveBeenCalledWith ( mockMcpClient . client , toolCalls )
213
- expect ( mockPost ) . toHaveBeenCalledTimes ( 2 )
199
+ expect ( mockCreate ) . toHaveBeenCalledTimes ( 2 )
214
200
215
201
// Verify the second call includes the conversation history
216
202
// eslint-disable-next-line @typescript-eslint/no-explicit-any
217
- const secondCall = mockPost . mock . calls [ 1 ] [ 0 ] as any
218
- expect ( secondCall . body . messages ) . toHaveLength ( 5 ) // system, user, assistant, tool, assistant
219
- expect ( secondCall . body . messages [ 2 ] . role ) . toBe ( 'assistant' )
220
- expect ( secondCall . body . messages [ 2 ] . tool_calls ) . toEqual ( toolCalls )
221
- expect ( secondCall . body . messages [ 3 ] ) . toEqual ( toolResults [ 0 ] )
203
+ const secondCall = mockCreate . mock . calls [ 1 ] [ 0 ] as any
204
+ expect ( secondCall . messages ) . toHaveLength ( 5 ) // system, user, assistant, tool, assistant
205
+ expect ( secondCall . messages [ 2 ] . role ) . toBe ( 'assistant' )
206
+ expect ( secondCall . messages [ 2 ] . tool_calls ) . toEqual ( toolCalls )
207
+ expect ( secondCall . messages [ 3 ] ) . toEqual ( toolResults [ 0 ] )
222
208
} )
223
209
224
210
it ( 'handles maximum iteration limit' , async ( ) => {
@@ -243,43 +229,39 @@ describe('inference.ts', () => {
243
229
244
230
// Always respond with tool calls to trigger infinite loop
245
231
const responseWithToolCalls = {
246
- body : {
247
- choices : [
248
- {
249
- message : {
250
- content : 'Using tool again.' ,
251
- tool_calls : toolCalls ,
252
- } ,
232
+ choices : [
233
+ {
234
+ message : {
235
+ content : 'Using tool again.' ,
236
+ tool_calls : toolCalls ,
253
237
} ,
254
- ] ,
255
- } ,
238
+ } ,
239
+ ] ,
256
240
}
257
241
258
- mockPost . mockResolvedValue ( responseWithToolCalls )
242
+ mockCreate . mockResolvedValue ( responseWithToolCalls )
259
243
mockExecuteToolCalls . mockResolvedValue ( toolResults )
260
244
261
245
const result = await mcpInference ( mockRequest , mockMcpClient )
262
246
263
- expect ( mockPost ) . toHaveBeenCalledTimes ( 5 ) // Max iterations reached
247
+ expect ( mockCreate ) . toHaveBeenCalledTimes ( 5 ) // Max iterations reached
264
248
expect ( core . warning ) . toHaveBeenCalledWith ( 'GitHub MCP inference loop exceeded maximum iterations (5)' )
265
249
expect ( result ) . toBe ( 'Using tool again.' ) // Last assistant message
266
250
} )
267
251
268
252
it ( 'handles empty tool calls array' , async ( ) => {
269
253
const mockResponse = {
270
- body : {
271
- choices : [
272
- {
273
- message : {
274
- content : 'Hello, user!' ,
275
- tool_calls : [ ] ,
276
- } ,
254
+ choices : [
255
+ {
256
+ message : {
257
+ content : 'Hello, user!' ,
258
+ tool_calls : [ ] ,
277
259
} ,
278
- ] ,
279
- } ,
260
+ } ,
261
+ ] ,
280
262
}
281
263
282
- mockPost . mockResolvedValue ( mockResponse )
264
+ mockCreate . mockResolvedValue ( mockResponse )
283
265
284
266
const result = await mcpInference ( mockRequest , mockMcpClient )
285
267
@@ -297,32 +279,28 @@ describe('inference.ts', () => {
297
279
]
298
280
299
281
const firstResponse = {
300
- body : {
301
- choices : [
302
- {
303
- message : {
304
- content : 'First message' ,
305
- tool_calls : toolCalls ,
306
- } ,
282
+ choices : [
283
+ {
284
+ message : {
285
+ content : 'First message' ,
286
+ tool_calls : toolCalls ,
307
287
} ,
308
- ] ,
309
- } ,
288
+ } ,
289
+ ] ,
310
290
}
311
291
312
292
const secondResponse = {
313
- body : {
314
- choices : [
315
- {
316
- message : {
317
- content : 'Second message' ,
318
- tool_calls : toolCalls ,
319
- } ,
293
+ choices : [
294
+ {
295
+ message : {
296
+ content : 'Second message' ,
297
+ tool_calls : toolCalls ,
320
298
} ,
321
- ] ,
322
- } ,
299
+ } ,
300
+ ] ,
323
301
}
324
302
325
- mockPost . mockResolvedValueOnce ( firstResponse ) . mockResolvedValue ( secondResponse )
303
+ mockCreate . mockResolvedValueOnce ( firstResponse ) . mockResolvedValue ( secondResponse )
326
304
327
305
mockExecuteToolCalls . mockResolvedValue ( [
328
306
{
0 commit comments