| 1 | import { describe, expect, it, vi } from 'vitest' |
| 2 | import { |
| 3 | CompatibleChatOpenAIResponses, |
| 4 | OPENAI_RESPONSES_FORMAT_ERROR_CODE, |
| 5 | isOpenAIResponsesFormatError |
| 6 | } from '../../src/main/agent-runtime/model' |
| 7 | |
| 8 | describe('CompatibleChatOpenAIResponses', () => { |
| 9 | it('passes through non-stream Responses API payloads with output arrays', async () => { |
| 10 | const model = new CompatibleChatOpenAIResponses({ |
| 11 | model: 'gpt-5.1', |
| 12 | apiKey: 'secret' |
| 13 | }) |
| 14 | const payload = { id: 'resp_1', output: [] } |
| 15 | vi.spyOn(Object.getPrototypeOf(CompatibleChatOpenAIResponses.prototype), 'completionWithRetry') |
| 16 | .mockResolvedValueOnce(payload) |
| 17 | |
| 18 | await expect( |
| 19 | model.completionWithRetry({ model: 'gpt-5.1', input: 'OK', stream: false }) |
| 20 | ).resolves.toBe(payload) |
| 21 | }) |
| 22 | |
| 23 | it('throws a stable error when non-stream payloads are missing output arrays', async () => { |
| 24 | const model = new CompatibleChatOpenAIResponses({ |
| 25 | model: 'gpt-5.1', |
| 26 | apiKey: 'secret' |
| 27 | }) |
| 28 | vi.spyOn(Object.getPrototypeOf(CompatibleChatOpenAIResponses.prototype), 'completionWithRetry') |
| 29 | .mockResolvedValueOnce({ id: 'chatcmpl_1', choices: [] }) |
| 30 | |
| 31 | await expect( |
| 32 | model.completionWithRetry({ model: 'gpt-5.1', input: 'OK', stream: false }) |
| 33 | ).rejects.toMatchObject({ |
| 34 | name: OPENAI_RESPONSES_FORMAT_ERROR_CODE |
| 35 | }) |
| 36 | }) |
| 37 | |
| 38 | it('skips payload validation for streams', async () => { |
| 39 | const model = new CompatibleChatOpenAIResponses({ |
| 40 | model: 'gpt-5.1', |
| 41 | apiKey: 'secret' |
| 42 | }) |
| 43 | const stream = (async function* () { |
| 44 | yield { type: 'response.created' } |
| 45 | })() |
| 46 | vi.spyOn(Object.getPrototypeOf(CompatibleChatOpenAIResponses.prototype), 'completionWithRetry') |
| 47 | .mockResolvedValueOnce(stream) |
| 48 | |
| 49 | await expect( |
| 50 | model.completionWithRetry({ model: 'gpt-5.1', input: 'OK', stream: true }) |
| 51 | ).resolves.toBe(stream) |
| 52 | }) |
| 53 | }) |
| 54 | |
| 55 | describe('isOpenAIResponsesFormatError', () => { |
| 56 | it('matches current and older V8 undefined map errors', () => { |
| 57 | expect( |
| 58 | isOpenAIResponsesFormatError( |
| 59 | new TypeError("Cannot read properties of undefined (reading 'map')") |
| 60 | ) |
| 61 | ).toBe(true) |
| 62 | expect(isOpenAIResponsesFormatError(new TypeError("Cannot read property 'map' of undefined"))).toBe( |
| 63 | true |
| 64 | ) |
| 65 | }) |
| 66 | }) |
| 67 |