promptfoo 0.93.3 → 0.94.0

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.
Files changed (242) hide show
  1. package/dist/drizzle/0008_broad_boomer.sql +2 -0
  2. package/dist/drizzle/0009_strong_marten_broadcloak.sql +19 -0
  3. package/dist/drizzle/meta/0008_snapshot.json +732 -0
  4. package/dist/drizzle/meta/0009_snapshot.json +622 -0
  5. package/dist/drizzle/meta/_journal.json +14 -0
  6. package/dist/package.json +16 -19
  7. package/dist/src/app/assets/index-D0jdLrEN.js +769 -0
  8. package/dist/src/app/assets/{index.es-9tmWNumA.js → index.es-BExeKHIA.js} +1 -1
  9. package/dist/src/app/assets/{sync-BdPRE9yh.js → sync-B6HKT-pd.js} +1 -1
  10. package/dist/src/app/index.html +1 -1
  11. package/dist/src/{assertions.d.ts → assertions/index.d.ts} +4 -5
  12. package/dist/src/assertions/index.d.ts.map +1 -0
  13. package/dist/src/{assertions.js → assertions/index.js} +24 -50
  14. package/dist/src/assertions/index.js.map +1 -0
  15. package/dist/src/assertions/utils.d.ts +4 -0
  16. package/dist/src/assertions/utils.d.ts.map +1 -0
  17. package/dist/src/assertions/utils.js +39 -0
  18. package/dist/src/assertions/utils.js.map +1 -0
  19. package/dist/src/cache.d.ts +0 -1
  20. package/dist/src/cache.d.ts.map +1 -1
  21. package/dist/src/cache.js.map +1 -1
  22. package/dist/src/commands/config.d.ts.map +1 -1
  23. package/dist/src/commands/config.js +41 -8
  24. package/dist/src/commands/config.js.map +1 -1
  25. package/dist/src/commands/eval.d.ts.map +1 -1
  26. package/dist/src/commands/eval.js +37 -19
  27. package/dist/src/commands/eval.js.map +1 -1
  28. package/dist/src/commands/init.d.ts.map +1 -1
  29. package/dist/src/commands/init.js +3 -4
  30. package/dist/src/commands/init.js.map +1 -1
  31. package/dist/src/database/index.d.ts.map +1 -1
  32. package/dist/src/database/index.js +4 -1
  33. package/dist/src/database/index.js.map +1 -1
  34. package/dist/src/database/tables.d.ts +0 -114
  35. package/dist/src/database/tables.d.ts.map +1 -1
  36. package/dist/src/database/tables.js +7 -34
  37. package/dist/src/database/tables.js.map +1 -1
  38. package/dist/src/envars.d.ts +5 -3
  39. package/dist/src/envars.d.ts.map +1 -1
  40. package/dist/src/envars.js.map +1 -1
  41. package/dist/src/evaluator.d.ts.map +1 -1
  42. package/dist/src/evaluator.js +0 -5
  43. package/dist/src/evaluator.js.map +1 -1
  44. package/dist/src/fetch.d.ts +0 -1
  45. package/dist/src/fetch.d.ts.map +1 -1
  46. package/dist/src/fetch.js +3 -4
  47. package/dist/src/fetch.js.map +1 -1
  48. package/dist/src/googleSheets.d.ts.map +1 -1
  49. package/dist/src/googleSheets.js +13 -2
  50. package/dist/src/googleSheets.js.map +1 -1
  51. package/dist/src/integrations/portkey.d.ts.map +1 -1
  52. package/dist/src/integrations/portkey.js +1 -2
  53. package/dist/src/integrations/portkey.js.map +1 -1
  54. package/dist/src/models/eval.d.ts +1 -3
  55. package/dist/src/models/eval.d.ts.map +1 -1
  56. package/dist/src/models/eval.js +5 -21
  57. package/dist/src/models/eval.js.map +1 -1
  58. package/dist/src/models/evalResult.d.ts +2 -0
  59. package/dist/src/models/evalResult.d.ts.map +1 -1
  60. package/dist/src/models/evalResult.js +4 -1
  61. package/dist/src/models/evalResult.js.map +1 -1
  62. package/dist/src/onboarding.d.ts +1 -1
  63. package/dist/src/onboarding.d.ts.map +1 -1
  64. package/dist/src/onboarding.js +1 -2
  65. package/dist/src/onboarding.js.map +1 -1
  66. package/dist/src/prompts/processors/javascript.d.ts +1 -0
  67. package/dist/src/prompts/processors/javascript.d.ts.map +1 -1
  68. package/dist/src/prompts/processors/javascript.js +6 -2
  69. package/dist/src/prompts/processors/javascript.js.map +1 -1
  70. package/dist/src/prompts/processors/python.d.ts +2 -0
  71. package/dist/src/prompts/processors/python.d.ts.map +1 -1
  72. package/dist/src/prompts/processors/python.js +4 -2
  73. package/dist/src/prompts/processors/python.js.map +1 -1
  74. package/dist/src/providers/anthropic.js +1 -1
  75. package/dist/src/providers/anthropic.js.map +1 -1
  76. package/dist/src/providers/bedrock.d.ts +4 -0
  77. package/dist/src/providers/bedrock.d.ts.map +1 -1
  78. package/dist/src/providers/bedrock.js +12 -0
  79. package/dist/src/providers/bedrock.js.map +1 -1
  80. package/dist/src/providers/fal.d.ts.map +1 -1
  81. package/dist/src/providers/fal.js +1 -2
  82. package/dist/src/providers/fal.js.map +1 -1
  83. package/dist/src/providers/http.d.ts +6 -2
  84. package/dist/src/providers/http.d.ts.map +1 -1
  85. package/dist/src/providers/http.js +70 -29
  86. package/dist/src/providers/http.js.map +1 -1
  87. package/dist/src/providers/openai.d.ts.map +1 -1
  88. package/dist/src/providers/openai.js +39 -30
  89. package/dist/src/providers/openai.js.map +1 -1
  90. package/dist/src/providers/palm.d.ts.map +1 -1
  91. package/dist/src/providers/palm.js +3 -1
  92. package/dist/src/providers/palm.js.map +1 -1
  93. package/dist/src/providers/replicate.d.ts.map +1 -1
  94. package/dist/src/providers/replicate.js +2 -3
  95. package/dist/src/providers/replicate.js.map +1 -1
  96. package/dist/src/providers/togetherai.d.ts +7 -0
  97. package/dist/src/providers/togetherai.d.ts.map +1 -0
  98. package/dist/src/providers/togetherai.js +33 -0
  99. package/dist/src/providers/togetherai.js.map +1 -0
  100. package/dist/src/providers/vertex.d.ts.map +1 -1
  101. package/dist/src/providers/vertex.js +4 -4
  102. package/dist/src/providers/vertex.js.map +1 -1
  103. package/dist/src/providers/vertexUtil.d.ts +78 -1
  104. package/dist/src/providers/vertexUtil.d.ts.map +1 -1
  105. package/dist/src/providers/vertexUtil.js +57 -15
  106. package/dist/src/providers/vertexUtil.js.map +1 -1
  107. package/dist/src/providers/watsonx.d.ts +5 -4
  108. package/dist/src/providers/watsonx.d.ts.map +1 -1
  109. package/dist/src/providers/watsonx.js +48 -21
  110. package/dist/src/providers/watsonx.js.map +1 -1
  111. package/dist/src/providers/xai.d.ts +7 -0
  112. package/dist/src/providers/xai.d.ts.map +1 -0
  113. package/dist/src/providers/xai.js +23 -0
  114. package/dist/src/providers/xai.js.map +1 -0
  115. package/dist/src/providers.d.ts.map +1 -1
  116. package/dist/src/providers.js +26 -0
  117. package/dist/src/providers.js.map +1 -1
  118. package/dist/src/python/pythonUtils.js +2 -2
  119. package/dist/src/python/pythonUtils.js.map +1 -1
  120. package/dist/src/redteam/commands/init.d.ts.map +1 -1
  121. package/dist/src/redteam/commands/init.js +8 -3
  122. package/dist/src/redteam/commands/init.js.map +1 -1
  123. package/dist/src/redteam/constants.d.ts +25 -2
  124. package/dist/src/redteam/constants.d.ts.map +1 -1
  125. package/dist/src/redteam/constants.js +335 -3
  126. package/dist/src/redteam/constants.js.map +1 -1
  127. package/dist/src/server/apiSchemas.d.ts +59 -0
  128. package/dist/src/server/apiSchemas.d.ts.map +1 -0
  129. package/dist/src/server/apiSchemas.js +37 -0
  130. package/dist/src/server/apiSchemas.js.map +1 -0
  131. package/dist/src/server/routes/eval.d.ts +2 -0
  132. package/dist/src/server/routes/eval.d.ts.map +1 -0
  133. package/dist/src/server/routes/eval.js +160 -0
  134. package/dist/src/server/routes/eval.js.map +1 -0
  135. package/dist/src/server/routes/providers.d.ts.map +1 -1
  136. package/dist/src/server/routes/providers.js.map +1 -1
  137. package/dist/src/server/routes/redteam.d.ts +2 -0
  138. package/dist/src/server/routes/redteam.d.ts.map +1 -0
  139. package/dist/src/server/routes/redteam.js +43 -0
  140. package/dist/src/server/routes/redteam.js.map +1 -0
  141. package/dist/src/server/routes/user.d.ts +2 -0
  142. package/dist/src/server/routes/user.d.ts.map +1 -0
  143. package/dist/src/server/routes/user.js +60 -0
  144. package/dist/src/server/routes/user.js.map +1 -0
  145. package/dist/src/server/server.d.ts.map +1 -1
  146. package/dist/src/server/server.js +25 -141
  147. package/dist/src/server/server.js.map +1 -1
  148. package/dist/src/share.d.ts.map +1 -1
  149. package/dist/src/share.js.map +1 -1
  150. package/dist/src/telemetry.d.ts +21 -6
  151. package/dist/src/telemetry.d.ts.map +1 -1
  152. package/dist/src/telemetry.js +28 -11
  153. package/dist/src/telemetry.js.map +1 -1
  154. package/dist/src/testCases.d.ts.map +1 -1
  155. package/dist/src/testCases.js +9 -0
  156. package/dist/src/testCases.js.map +1 -1
  157. package/dist/src/types/index.d.ts.map +1 -1
  158. package/dist/src/types/index.js +1 -0
  159. package/dist/src/types/index.js.map +1 -1
  160. package/dist/src/types/prompts.d.ts +1 -0
  161. package/dist/src/types/prompts.d.ts.map +1 -1
  162. package/dist/src/util/config/default.d.ts +1 -0
  163. package/dist/src/util/config/default.d.ts.map +1 -1
  164. package/dist/src/util/config/default.js +4 -0
  165. package/dist/src/util/config/default.js.map +1 -1
  166. package/dist/src/util/index.d.ts +0 -1
  167. package/dist/src/util/index.d.ts.map +1 -1
  168. package/dist/src/util/index.js +1 -10
  169. package/dist/src/util/index.js.map +1 -1
  170. package/dist/src/validators/redteam.d.ts +6 -3
  171. package/dist/src/validators/redteam.d.ts.map +1 -1
  172. package/dist/src/validators/redteam.js +15 -14
  173. package/dist/src/validators/redteam.js.map +1 -1
  174. package/dist/test/assertions.test.js +3 -4
  175. package/dist/test/assertions.test.js.map +1 -1
  176. package/dist/test/assertions.utils.test.js +55 -5
  177. package/dist/test/assertions.utils.test.js.map +1 -1
  178. package/dist/test/cache.test.js +10 -16
  179. package/dist/test/cache.test.js.map +1 -1
  180. package/dist/test/commands/init.test.js +14 -11
  181. package/dist/test/commands/init.test.js.map +1 -1
  182. package/dist/test/evaluator.test.js +0 -1
  183. package/dist/test/evaluator.test.js.map +1 -1
  184. package/dist/test/evaluatorHelpers.test.js +0 -1
  185. package/dist/test/evaluatorHelpers.test.js.map +1 -1
  186. package/dist/test/models/eval.test.js +30 -1
  187. package/dist/test/models/eval.test.js.map +1 -1
  188. package/dist/test/onboarding.test.js +1 -1
  189. package/dist/test/prompts.processors.javascript.test.js +54 -1
  190. package/dist/test/prompts.processors.javascript.test.js.map +1 -1
  191. package/dist/test/prompts.processors.python.test.js +15 -0
  192. package/dist/test/prompts.processors.python.test.js.map +1 -1
  193. package/dist/test/prompts.processors.python.utils.test.js +2 -0
  194. package/dist/test/prompts.processors.python.utils.test.js.map +1 -1
  195. package/dist/test/prompts.test.js +2 -0
  196. package/dist/test/prompts.test.js.map +1 -1
  197. package/dist/test/providers.bedrock.test.js +58 -6
  198. package/dist/test/providers.bedrock.test.js.map +1 -1
  199. package/dist/test/providers.http.test.js +127 -31
  200. package/dist/test/providers.http.test.js.map +1 -1
  201. package/dist/test/providers.palm.test.d.ts +2 -0
  202. package/dist/test/providers.palm.test.d.ts.map +1 -0
  203. package/dist/test/providers.palm.test.js +131 -0
  204. package/dist/test/providers.palm.test.js.map +1 -0
  205. package/dist/test/providers.test.js +165 -73
  206. package/dist/test/providers.test.js.map +1 -1
  207. package/dist/test/providers.togetherai.test.d.ts +2 -0
  208. package/dist/test/providers.togetherai.test.d.ts.map +1 -0
  209. package/dist/test/providers.togetherai.test.js +53 -0
  210. package/dist/test/providers.togetherai.test.js.map +1 -0
  211. package/dist/test/providers.vertex.test.js +102 -1
  212. package/dist/test/providers.vertex.test.js.map +1 -1
  213. package/dist/test/providers.watsonx.test.js +15 -14
  214. package/dist/test/providers.watsonx.test.js.map +1 -1
  215. package/dist/test/providers.xai.test.d.ts +2 -0
  216. package/dist/test/providers.xai.test.d.ts.map +1 -0
  217. package/dist/test/providers.xai.test.js +47 -0
  218. package/dist/test/providers.xai.test.js.map +1 -0
  219. package/dist/test/rateLimit.test.js +18 -28
  220. package/dist/test/rateLimit.test.js.map +1 -1
  221. package/dist/test/redteam/validators.test.js +1 -0
  222. package/dist/test/redteam/validators.test.js.map +1 -1
  223. package/dist/test/server/providers.test.js +13 -1
  224. package/dist/test/server/providers.test.js.map +1 -1
  225. package/dist/test/testCases.test.js +8 -1
  226. package/dist/test/testCases.test.js.map +1 -1
  227. package/dist/test/util.config.load.test.d.ts +2 -0
  228. package/dist/test/util.config.load.test.d.ts.map +1 -0
  229. package/dist/test/{util.config.load.test.ts.js → util.config.load.test.js} +1 -26
  230. package/dist/test/util.config.load.test.js.map +1 -0
  231. package/dist/tsconfig.tsbuildinfo +1 -1
  232. package/package.json +16 -19
  233. package/dist/src/app/assets/index-x0TdnK0A.js +0 -767
  234. package/dist/src/assertions.d.ts.map +0 -1
  235. package/dist/src/assertions.js.map +0 -1
  236. package/dist/src/models/provider.d.ts +0 -11
  237. package/dist/src/models/provider.d.ts.map +0 -1
  238. package/dist/src/models/provider.js +0 -52
  239. package/dist/src/models/provider.js.map +0 -1
  240. package/dist/test/util.config.load.test.ts.d.ts +0 -2
  241. package/dist/test/util.config.load.test.ts.d.ts.map +0 -1
  242. package/dist/test/util.config.load.test.ts.js.map +0 -1
@@ -30,7 +30,6 @@ const chalk_1 = __importDefault(require("chalk"));
30
30
  const child_process_1 = __importDefault(require("child_process"));
31
31
  const dedent_1 = __importDefault(require("dedent"));
32
32
  const fs = __importStar(require("fs"));
33
- const node_fetch_1 = __importDefault(require("node-fetch"));
34
33
  const path = __importStar(require("path"));
35
34
  const stream_1 = __importDefault(require("stream"));
36
35
  const cache_1 = require("../src/cache");
@@ -68,7 +67,6 @@ jest.mock('fs', () => ({
68
67
  jest.mock('glob', () => ({
69
68
  globSync: jest.fn(),
70
69
  }));
71
- jest.mock('node-fetch', () => jest.fn());
72
70
  jest.mock('proxy-agent', () => ({
73
71
  ProxyAgent: jest.fn().mockImplementation(() => ({})),
74
72
  }));
@@ -93,6 +91,8 @@ jest.mock('../src/logger', () => ({
93
91
  info: jest.fn(),
94
92
  warn: jest.fn(),
95
93
  }));
94
+ const mockFetch = jest.mocked(jest.fn());
95
+ global.fetch = mockFetch;
96
96
  const defaultMockResponse = {
97
97
  status: 200,
98
98
  statusText: 'OK',
@@ -113,10 +113,10 @@ describe('call provider apis', () => {
113
113
  usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
114
114
  })),
115
115
  };
116
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
116
+ mockFetch.mockResolvedValue(mockResponse);
117
117
  const provider = new openai_1.OpenAiCompletionProvider('text-davinci-003');
118
118
  const result = await provider.callApi('Test prompt');
119
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
119
+ expect(mockFetch).toHaveBeenCalledTimes(1);
120
120
  expect(result.output).toBe('Test output');
121
121
  expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
122
122
  });
@@ -129,10 +129,10 @@ describe('call provider apis', () => {
129
129
  })),
130
130
  ok: true,
131
131
  };
132
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
132
+ mockFetch.mockResolvedValue(mockResponse);
133
133
  const provider = new openai_1.OpenAiChatCompletionProvider('gpt-4o-mini');
134
134
  const result = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt' }]));
135
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
135
+ expect(mockFetch).toHaveBeenCalledTimes(1);
136
136
  expect(result.output).toBe('Test output');
137
137
  expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
138
138
  });
@@ -145,14 +145,14 @@ describe('call provider apis', () => {
145
145
  })),
146
146
  ok: true,
147
147
  };
148
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
148
+ mockFetch.mockResolvedValue(mockResponse);
149
149
  const provider = new openai_1.OpenAiChatCompletionProvider('gpt-4o-mini');
150
150
  const result = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt 2' }]));
151
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
151
+ expect(mockFetch).toHaveBeenCalledTimes(1);
152
152
  expect(result.output).toBe('Test output 2');
153
153
  expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
154
154
  const result2 = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt 2' }]));
155
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
155
+ expect(mockFetch).toHaveBeenCalledTimes(1);
156
156
  expect(result2.output).toBe('Test output 2');
157
157
  expect(result2.tokenUsage).toEqual({ total: 10, cached: 10 });
158
158
  });
@@ -165,15 +165,15 @@ describe('call provider apis', () => {
165
165
  })),
166
166
  ok: true,
167
167
  };
168
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
168
+ mockFetch.mockResolvedValue(mockResponse);
169
169
  const provider = new openai_1.OpenAiChatCompletionProvider('gpt-4o-mini');
170
170
  const result = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt' }]));
171
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
171
+ expect(mockFetch).toHaveBeenCalledTimes(1);
172
172
  expect(result.output).toBe('Test output');
173
173
  expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
174
174
  (0, cache_1.disableCache)();
175
175
  const result2 = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt' }]));
176
- expect(node_fetch_1.default).toHaveBeenCalledTimes(2);
176
+ expect(mockFetch).toHaveBeenCalledTimes(2);
177
177
  expect(result2.output).toBe('Test output');
178
178
  expect(result2.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
179
179
  (0, cache_1.enableCache)();
@@ -186,7 +186,7 @@ describe('call provider apis', () => {
186
186
  const provider = new openai_1.OpenAiChatCompletionProvider('gpt-4o-mini', { config });
187
187
  const prompt = 'Test prompt';
188
188
  await provider.callApi(prompt);
189
- expect(node_fetch_1.default).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
189
+ expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
190
190
  body: expect.stringMatching(`temperature\":3.1415926`),
191
191
  }));
192
192
  expect(provider.config.temperature).toBe(config.temperature);
@@ -201,7 +201,7 @@ describe('call provider apis', () => {
201
201
  })),
202
202
  ok: true,
203
203
  };
204
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
204
+ mockFetch.mockResolvedValue(mockResponse);
205
205
  const provider = new openai_1.OpenAiChatCompletionProvider('gpt-4o-mini', {
206
206
  config: {
207
207
  response_format: {
@@ -223,7 +223,7 @@ describe('call provider apis', () => {
223
223
  },
224
224
  });
225
225
  const result = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Get me a person' }]));
226
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
226
+ expect(mockFetch).toHaveBeenCalledTimes(1);
227
227
  expect(result.output).toEqual({ name: 'John', age: 30 });
228
228
  expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
229
229
  });
@@ -236,10 +236,10 @@ describe('call provider apis', () => {
236
236
  })),
237
237
  ok: true,
238
238
  };
239
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
239
+ mockFetch.mockResolvedValue(mockResponse);
240
240
  const provider = new openai_1.OpenAiChatCompletionProvider('gpt-4o-mini');
241
241
  const result = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Generate inappropriate content' }]));
242
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
242
+ expect(mockFetch).toHaveBeenCalledTimes(1);
243
243
  expect(result.error).toBe('Model refused to generate a response: Content policy violation');
244
244
  expect(result.tokenUsage).toEqual({ total: 5, prompt: 5, completion: 0 });
245
245
  });
@@ -266,7 +266,7 @@ describe('call provider apis', () => {
266
266
  })),
267
267
  ok: true,
268
268
  };
269
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
269
+ mockFetch.mockResolvedValue(mockResponse);
270
270
  const mockWeatherFunction = jest.fn().mockResolvedValue('Sunny, 25°C');
271
271
  const provider = new openai_1.OpenAiChatCompletionProvider('gpt-4o-mini', {
272
272
  config: {
@@ -292,7 +292,7 @@ describe('call provider apis', () => {
292
292
  },
293
293
  });
294
294
  const result = await provider.callApi(JSON.stringify([{ role: 'user', content: "What's the weather in New York?" }]));
295
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
295
+ expect(mockFetch).toHaveBeenCalledTimes(1);
296
296
  expect(mockWeatherFunction).toHaveBeenCalledWith('{"location":"New York"}');
297
297
  expect(result.output).toBe('Sunny, 25°C');
298
298
  expect(result.tokenUsage).toEqual({ total: 15, prompt: 10, completion: 5 });
@@ -305,10 +305,10 @@ describe('call provider apis', () => {
305
305
  usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
306
306
  })),
307
307
  };
308
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
308
+ mockFetch.mockResolvedValue(mockResponse);
309
309
  const provider = new azureopenai_1.AzureOpenAiCompletionProvider('text-davinci-003');
310
310
  const result = await provider.callApi('Test prompt');
311
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
311
+ expect(mockFetch).toHaveBeenCalledTimes(1);
312
312
  expect(result.output).toBe('Test output');
313
313
  expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
314
314
  });
@@ -320,10 +320,10 @@ describe('call provider apis', () => {
320
320
  usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
321
321
  })),
322
322
  };
323
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
323
+ mockFetch.mockResolvedValue(mockResponse);
324
324
  const provider = new azureopenai_1.AzureOpenAiChatCompletionProvider('gpt-4o-mini');
325
325
  const result = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt' }]));
326
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
326
+ expect(mockFetch).toHaveBeenCalledTimes(1);
327
327
  expect(result.output).toBe('Test output');
328
328
  expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
329
329
  });
@@ -348,7 +348,7 @@ describe('call provider apis', () => {
348
348
  usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
349
349
  })),
350
350
  };
351
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
351
+ mockFetch.mockResolvedValue(mockResponse);
352
352
  const provider = new azureopenai_1.AzureOpenAiChatCompletionProvider('gpt-4o-mini', {
353
353
  config: { dataSources },
354
354
  });
@@ -356,7 +356,7 @@ describe('call provider apis', () => {
356
356
  { role: 'system', content: 'System prompt' },
357
357
  { role: 'user', content: 'Test prompt' },
358
358
  ]));
359
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
359
+ expect(mockFetch).toHaveBeenCalledTimes(1);
360
360
  expect(result.output).toBe('Test response');
361
361
  expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
362
362
  });
@@ -369,10 +369,10 @@ describe('call provider apis', () => {
369
369
  usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
370
370
  })),
371
371
  };
372
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
372
+ mockFetch.mockResolvedValue(mockResponse);
373
373
  const provider = new azureopenai_1.AzureOpenAiChatCompletionProvider('gpt-4o-mini');
374
374
  const result = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt' }]));
375
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
375
+ expect(mockFetch).toHaveBeenCalledTimes(1);
376
376
  expect(result.output).toBe('Test output');
377
377
  expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
378
378
  (0, cache_1.enableCache)();
@@ -384,10 +384,10 @@ describe('call provider apis', () => {
384
384
  content: 'Test output',
385
385
  })),
386
386
  };
387
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
387
+ mockFetch.mockResolvedValue(mockResponse);
388
388
  const provider = new llama_1.LlamaProvider('llama.cpp');
389
389
  const result = await provider.callApi('Test prompt');
390
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
390
+ expect(mockFetch).toHaveBeenCalledTimes(1);
391
391
  expect(result.output).toBe('Test output');
392
392
  });
393
393
  it('OllamaCompletionProvider callApi', async () => {
@@ -404,10 +404,10 @@ describe('call provider apis', () => {
404
404
  {"model":"llama2:13b","created_at":"2023-08-08T21:50:35.117166Z","response":" blue","done":false}
405
405
  {"model":"llama2:13b","created_at":"2023-08-08T21:50:41.695299Z","done":true,"context":[1,29871,1,13,9314],"total_duration":10411943458,"load_duration":458333,"sample_count":217,"sample_duration":154566000,"prompt_eval_count":11,"prompt_eval_duration":3334582000,"eval_count":216,"eval_duration":6905134000}`),
406
406
  };
407
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
407
+ mockFetch.mockResolvedValue(mockResponse);
408
408
  const provider = new ollama_1.OllamaCompletionProvider('llama');
409
409
  const result = await provider.callApi('Test prompt');
410
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
410
+ expect(mockFetch).toHaveBeenCalledTimes(1);
411
411
  expect(result.output).toBe('Great question! The sky appears blue');
412
412
  });
413
413
  it('OllamaChatProvider callApi', async () => {
@@ -422,10 +422,10 @@ describe('call provider apis', () => {
422
422
  {"model":"orca-mini","created_at":"2023-12-16T01:46:19.324309782Z","message":{"role":"assistant","content":".","images":null},"done":false}
423
423
  {"model":"orca-mini","created_at":"2023-12-16T01:46:19.337165395Z","done":true,"total_duration":1486443841,"load_duration":1280794143,"prompt_eval_count":35,"prompt_eval_duration":142384000,"eval_count":6,"eval_duration":61912000}`),
424
424
  };
425
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
425
+ mockFetch.mockResolvedValue(mockResponse);
426
426
  const provider = new ollama_1.OllamaChatProvider('llama');
427
427
  const result = await provider.callApi('Test prompt');
428
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
428
+ expect(mockFetch).toHaveBeenCalledTimes(1);
429
429
  expect(result.output).toBe(' Because of Rayleigh scattering.');
430
430
  });
431
431
  it('WebhookProvider callApi', async () => {
@@ -435,10 +435,10 @@ describe('call provider apis', () => {
435
435
  output: 'Test output',
436
436
  })),
437
437
  };
438
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
438
+ mockFetch.mockResolvedValue(mockResponse);
439
439
  const provider = new webhook_1.WebhookProvider('http://example.com/webhook');
440
440
  const result = await provider.callApi('Test prompt');
441
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
441
+ expect(mockFetch).toHaveBeenCalledTimes(1);
442
442
  expect(result.output).toBe('Test output');
443
443
  });
444
444
  describe.each([
@@ -450,10 +450,10 @@ describe('call provider apis', () => {
450
450
  ...defaultMockResponse,
451
451
  text: jest.fn().mockResolvedValue(JSON.stringify(mockedData)),
452
452
  };
453
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
453
+ mockFetch.mockResolvedValue(mockResponse);
454
454
  const provider = new huggingface_1.HuggingfaceTextGenerationProvider('gpt2');
455
455
  const result = await provider.callApi('Test prompt');
456
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
456
+ expect(mockFetch).toHaveBeenCalledTimes(1);
457
457
  expect(result.output).toBe('Test output');
458
458
  });
459
459
  });
@@ -462,10 +462,10 @@ describe('call provider apis', () => {
462
462
  ...defaultMockResponse,
463
463
  text: jest.fn().mockResolvedValue(JSON.stringify([0.1, 0.2, 0.3, 0.4, 0.5])),
464
464
  };
465
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
465
+ mockFetch.mockResolvedValue(mockResponse);
466
466
  const provider = new huggingface_1.HuggingfaceFeatureExtractionProvider('distilbert-base-uncased');
467
467
  const result = await provider.callEmbeddingApi('Test text');
468
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
468
+ expect(mockFetch).toHaveBeenCalledTimes(1);
469
469
  expect(result.embedding).toEqual([0.1, 0.2, 0.3, 0.4, 0.5]);
470
470
  });
471
471
  it('HuggingfaceTextClassificationProvider callClassificationApi', async () => {
@@ -485,10 +485,10 @@ describe('call provider apis', () => {
485
485
  ...defaultMockResponse,
486
486
  text: jest.fn().mockResolvedValue(JSON.stringify(mockClassification)),
487
487
  };
488
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
488
+ mockFetch.mockResolvedValue(mockResponse);
489
489
  const provider = new huggingface_1.HuggingfaceTextClassificationProvider('foo');
490
490
  const result = await provider.callClassificationApi('Test text');
491
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
491
+ expect(mockFetch).toHaveBeenCalledTimes(1);
492
492
  expect(result.classification).toEqual({
493
493
  nothate: 0.9,
494
494
  hate: 0.1,
@@ -498,7 +498,6 @@ describe('call provider apis', () => {
498
498
  beforeAll(() => {
499
499
  (0, cache_1.enableCache)();
500
500
  });
501
- const fetchMock = jest.mocked(node_fetch_1.default);
502
501
  const cloudflareMinimumConfig = {
503
502
  accountId: 'testAccountId',
504
503
  apiKey: 'testApiKey',
@@ -530,13 +529,13 @@ describe('call provider apis', () => {
530
529
  text: jest.fn().mockResolvedValue(JSON.stringify(responsePayload)),
531
530
  ok: true,
532
531
  };
533
- fetchMock.mockResolvedValue(mockResponse);
532
+ mockFetch.mockResolvedValue(mockResponse);
534
533
  const result = await provider.callApi(PROMPT);
535
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
534
+ expect(mockFetch).toHaveBeenCalledTimes(1);
536
535
  expect(result.output).toBe(responsePayload.result.response);
537
536
  expect(result.tokenUsage).toEqual(tokenUsageDefaultResponse);
538
537
  const resultFromCache = await provider.callApi(PROMPT);
539
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
538
+ expect(mockFetch).toHaveBeenCalledTimes(1);
540
539
  expect(resultFromCache.output).toBe(responsePayload.result.response);
541
540
  expect(resultFromCache.tokenUsage).toEqual(tokenUsageDefaultResponse);
542
541
  });
@@ -560,13 +559,13 @@ describe('call provider apis', () => {
560
559
  text: jest.fn().mockResolvedValue(JSON.stringify(responsePayload)),
561
560
  ok: true,
562
561
  };
563
- fetchMock.mockResolvedValue(mockResponse);
562
+ mockFetch.mockResolvedValue(mockResponse);
564
563
  const result = await provider.callApi(PROMPT);
565
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
564
+ expect(mockFetch).toHaveBeenCalledTimes(1);
566
565
  expect(result.output).toBe(responsePayload.result.response);
567
566
  expect(result.tokenUsage).toEqual(tokenUsageDefaultResponse);
568
567
  const resultFromCache = await provider.callApi(PROMPT);
569
- expect(node_fetch_1.default).toHaveBeenCalledTimes(2);
568
+ expect(mockFetch).toHaveBeenCalledTimes(2);
570
569
  expect(resultFromCache.output).toBe(responsePayload.result.response);
571
570
  expect(resultFromCache.tokenUsage).toEqual(tokenUsageDefaultResponse);
572
571
  }
@@ -589,7 +588,7 @@ describe('call provider apis', () => {
589
588
  text: jest.fn().mockResolvedValue(JSON.stringify(responsePayload)),
590
589
  ok: true,
591
590
  };
592
- fetchMock.mockResolvedValue(mockResponse);
591
+ mockFetch.mockResolvedValue(mockResponse);
593
592
  const result = await provider.callApi(PROMPT);
594
593
  expect(result.error).toContain(JSON.stringify(responsePayload.errors));
595
594
  });
@@ -625,19 +624,14 @@ describe('call provider apis', () => {
625
624
  text: jest.fn().mockResolvedValue(JSON.stringify(responsePayload)),
626
625
  ok: true,
627
626
  };
628
- fetchMock.mockResolvedValue(mockResponse);
627
+ mockFetch.mockResolvedValue(mockResponse);
629
628
  await cfProvider.callApi(PROMPT);
630
- expect(fetchMock).toHaveBeenCalledTimes(1);
631
- expect(fetchMock.mock.calls).toHaveLength(1);
632
- const [url, options] = fetchMock.mock.calls[0] ?? [undefined, {}];
633
- const { body, headers, method } = options;
634
- expect(url).toContain(cloudflareChatConfig.accountId);
635
- expect(method).toBe('POST');
636
- expect(headers && 'Authorization' in headers ? headers['Authorization'] : '').toContain(cloudflareChatConfig.apiKey);
637
- const hydratedBody = typeof body === 'string' ? JSON.parse(body) : body;
638
- expect(hydratedBody.prompt).toBe(PROMPT);
629
+ expect(mockFetch).toHaveBeenCalledTimes(1);
630
+ expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
631
+ body: expect.stringMatching(`"prompt":"${PROMPT}"`),
632
+ }));
639
633
  const { accountId: _accountId, apiKey: _apiKey, ...passThroughConfig } = cloudflareChatConfig;
640
- const { prompt: _prompt, ...bodyWithoutPrompt } = hydratedBody;
634
+ const { prompt: _prompt, ...bodyWithoutPrompt } = JSON.parse(jest.mocked(mockFetch).mock.calls[0][1].body);
641
635
  expect(bodyWithoutPrompt).toEqual(passThroughConfig);
642
636
  });
643
637
  });
@@ -659,9 +653,9 @@ describe('call provider apis', () => {
659
653
  text: jest.fn().mockResolvedValue(JSON.stringify(responsePayload)),
660
654
  ok: true,
661
655
  };
662
- fetchMock.mockResolvedValue(mockResponse);
656
+ mockFetch.mockResolvedValue(mockResponse);
663
657
  const result = await provider.callApi('Test chat prompt');
664
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
658
+ expect(mockFetch).toHaveBeenCalledTimes(1);
665
659
  expect(result.output).toBe(responsePayload.result.response);
666
660
  expect(result.tokenUsage).toEqual(tokenUsageDefaultResponse);
667
661
  });
@@ -685,9 +679,9 @@ describe('call provider apis', () => {
685
679
  text: jest.fn().mockResolvedValue(JSON.stringify(responsePayload)),
686
680
  ok: true,
687
681
  };
688
- fetchMock.mockResolvedValue(mockResponse);
682
+ mockFetch.mockResolvedValue(mockResponse);
689
683
  const result = await provider.callEmbeddingApi('Create embeddings from this');
690
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
684
+ expect(mockFetch).toHaveBeenCalledTimes(1);
691
685
  expect(result.embedding).toEqual(responsePayload.result.data[0]);
692
686
  expect(result.tokenUsage).toEqual(tokenUsageDefaultResponse);
693
687
  });
@@ -756,7 +750,7 @@ describe('call provider apis', () => {
756
750
  })),
757
751
  ok: true,
758
752
  };
759
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
753
+ mockFetch.mockResolvedValue(mockResponse);
760
754
  const provider = new openai_1.OpenAiChatCompletionProvider('gpt-4o-mini', {
761
755
  config: {
762
756
  tools: [
@@ -785,7 +779,7 @@ describe('call provider apis', () => {
785
779
  },
786
780
  });
787
781
  const result = await provider.callApi('Add 5 and 6');
788
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
782
+ expect(mockFetch).toHaveBeenCalledTimes(1);
789
783
  expect(result.output).toBe('11');
790
784
  expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
791
785
  });
@@ -818,7 +812,7 @@ describe('call provider apis', () => {
818
812
  })),
819
813
  ok: true,
820
814
  };
821
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
815
+ mockFetch.mockResolvedValue(mockResponse);
822
816
  const provider = new openai_1.OpenAiChatCompletionProvider('gpt-4o-mini', {
823
817
  config: {
824
818
  tools: [
@@ -866,7 +860,7 @@ describe('call provider apis', () => {
866
860
  },
867
861
  });
868
862
  const result = await provider.callApi('Add 5 and 6, then multiply 2 and 3');
869
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
863
+ expect(mockFetch).toHaveBeenCalledTimes(1);
870
864
  expect(result.output).toBe('11\n6');
871
865
  expect(result.tokenUsage).toEqual({ total: 15, prompt: 7, completion: 8 });
872
866
  });
@@ -889,7 +883,7 @@ describe('call provider apis', () => {
889
883
  })),
890
884
  ok: true,
891
885
  };
892
- jest.mocked(node_fetch_1.default).mockResolvedValue(mockResponse);
886
+ mockFetch.mockResolvedValue(mockResponse);
893
887
  const provider = new openai_1.OpenAiChatCompletionProvider('gpt-4o-mini', {
894
888
  config: {
895
889
  tools: [
@@ -913,17 +907,97 @@ describe('call provider apis', () => {
913
907
  },
914
908
  });
915
909
  const result = await provider.callApi('Call the error function');
916
- expect(node_fetch_1.default).toHaveBeenCalledTimes(1);
910
+ expect(mockFetch).toHaveBeenCalledTimes(1);
917
911
  expect(result.output).toEqual({ arguments: '{}', name: 'errorFunction' });
918
912
  expect(result.tokenUsage).toEqual({ total: 5, prompt: 2, completion: 3 });
919
913
  });
920
914
  });
915
+ describe('OpenAiChatCompletionProvider response_format', () => {
916
+ it('should prioritize response_format from prompt config over provider config', async () => {
917
+ const providerResponseFormat = {
918
+ type: 'json_object',
919
+ };
920
+ const promptResponseFormat = {
921
+ type: 'json_schema',
922
+ json_schema: {
923
+ name: 'test_schema',
924
+ strict: true,
925
+ schema: {
926
+ type: 'object',
927
+ properties: { key2: { type: 'string' } },
928
+ additionalProperties: false,
929
+ },
930
+ },
931
+ };
932
+ const provider = new openai_1.OpenAiChatCompletionProvider('gpt-4o-mini', {
933
+ config: {
934
+ response_format: providerResponseFormat,
935
+ },
936
+ });
937
+ const mockResponse = {
938
+ ...defaultMockResponse,
939
+ text: jest.fn().mockResolvedValue(JSON.stringify({
940
+ choices: [{ message: { content: '{"key2": "value2"}' } }],
941
+ usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
942
+ })),
943
+ };
944
+ jest.mocked(fetch).mockResolvedValue(mockResponse);
945
+ const result = await provider.callApi('Test prompt', {
946
+ vars: {},
947
+ prompt: {
948
+ raw: 'Test prompt',
949
+ label: 'Test prompt',
950
+ config: {
951
+ response_format: promptResponseFormat,
952
+ },
953
+ },
954
+ });
955
+ expect(fetch).toHaveBeenCalledTimes(1);
956
+ const fetchArgs = jest.mocked(fetch).mock.calls[0];
957
+ const requestBody = JSON.parse(fetchArgs[1]?.body);
958
+ expect(requestBody.response_format).toEqual(promptResponseFormat);
959
+ expect(result.output).toEqual({ key2: 'value2' });
960
+ expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
961
+ });
962
+ it('should use provider config response_format when prompt config is not provided', async () => {
963
+ const providerResponseFormat = {
964
+ type: 'json_object',
965
+ };
966
+ const provider = new openai_1.OpenAiChatCompletionProvider('gpt-4o-mini', {
967
+ config: {
968
+ response_format: providerResponseFormat,
969
+ },
970
+ });
971
+ const mockResponse = {
972
+ ...defaultMockResponse,
973
+ text: jest.fn().mockResolvedValue(JSON.stringify({
974
+ choices: [{ message: { content: '{"key1": "value1"}' } }],
975
+ usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
976
+ })),
977
+ };
978
+ jest.mocked(fetch).mockResolvedValue(mockResponse);
979
+ const result = await provider.callApi('Test prompt', {
980
+ vars: {},
981
+ prompt: {
982
+ raw: 'Test prompt',
983
+ label: 'Test prompt',
984
+ },
985
+ });
986
+ expect(fetch).toHaveBeenCalledTimes(1);
987
+ const fetchArgs = jest.mocked(fetch).mock.calls[0];
988
+ const requestBody = JSON.parse(fetchArgs[1]?.body);
989
+ expect(requestBody.response_format).toEqual(providerResponseFormat);
990
+ expect(result.output).toBe('{"key1": "value1"}');
991
+ expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
992
+ });
993
+ });
921
994
  });
922
995
  describe('loadApiProvider', () => {
923
996
  it('loadApiProvider with yaml filepath', async () => {
924
- const mockYamlContent = `id: 'openai:gpt-4'
925
- config:
926
- key: 'value'`;
997
+ const mockYamlContent = (0, dedent_1.default) `
998
+ id: 'openai:gpt-4'
999
+ config:
1000
+ key: 'value'`;
927
1001
  jest.mocked(fs.readFileSync).mockReturnValueOnce(mockYamlContent);
928
1002
  const provider = await (0, providers_1.loadApiProvider)('file://path/to/mock-provider-file.yaml');
929
1003
  expect(provider.id()).toBe('openai:gpt-4');
@@ -1040,6 +1114,17 @@ config:
1040
1114
  // Intentionally openai, because it's just a wrapper around openai
1041
1115
  expect(provider.id()).toBe('mistralai/mistral-medium');
1042
1116
  });
1117
+ it('loadApiProvider with github', async () => {
1118
+ const provider = await (0, providers_1.loadApiProvider)('github:gpt-4o-mini');
1119
+ expect(provider).toBeInstanceOf(openai_1.OpenAiChatCompletionProvider);
1120
+ // Intentionally openai, because it's just a wrapper around openai
1121
+ expect(provider.id()).toBe('gpt-4o-mini');
1122
+ });
1123
+ it('loadApiProvider with togetherai', async () => {
1124
+ const provider = await (0, providers_1.loadApiProvider)('togetherai:chat:meta/meta-llama/Meta-Llama-3-8B-Instruct');
1125
+ expect(provider).toBeInstanceOf(openai_1.OpenAiChatCompletionProvider);
1126
+ expect(provider.id()).toBe('meta/meta-llama/Meta-Llama-3-8B-Instruct');
1127
+ });
1043
1128
  it('loadApiProvider with voyage', async () => {
1044
1129
  const provider = await (0, providers_1.loadApiProvider)('voyage:voyage-2');
1045
1130
  expect(provider).toBeInstanceOf(voyage_1.VoyageEmbeddingProvider);
@@ -1287,5 +1372,12 @@ config:
1287
1372
  const provider = await (0, providers_1.loadApiProvider)('openai:chat:gpt-4o', { options: providerOptions });
1288
1373
  expect(provider.label).toBe('foo');
1289
1374
  });
1375
+ it('loadApiProvider with xai', async () => {
1376
+ const provider = await (0, providers_1.loadApiProvider)('xai:grok-2');
1377
+ expect(provider).toBeInstanceOf(openai_1.OpenAiChatCompletionProvider);
1378
+ expect(provider.id()).toBe('grok-2');
1379
+ expect(provider.config.apiBaseUrl).toBe('https://api.x.ai/v1');
1380
+ expect(provider.config.apiKeyEnvar).toBe('XAI_API_KEY');
1381
+ });
1290
1382
  });
1291
1383
  //# sourceMappingURL=providers.test.js.map