import ReactDOMServer from 'react-dom/server'; import { OpenAIStream, ReactResponseRow, StreamData, experimental_StreamingReactResponse, } from '.'; import { openaiChatCompletionChunks, openaiFunctionCallChunks, } from '../tests/snapshots/openai-chat'; import { DEFAULT_TEST_URL, createMockServer } from '../tests/utils/mock-server'; const FUNCTION_CALL_TEST_URL = DEFAULT_TEST_URL + 'mock-func-call'; const server = createMockServer([ { url: DEFAULT_TEST_URL, chunks: openaiChatCompletionChunks, formatChunk: chunk => `data: ${JSON.stringify(chunk)}\n\n`, suffix: 'data: [DONE]', }, { url: FUNCTION_CALL_TEST_URL, chunks: openaiFunctionCallChunks, formatChunk: chunk => `data: ${JSON.stringify(chunk)}\n\n`, suffix: 'data: [DONE]', }, ]); beforeAll(() => { server.listen(); }); afterEach(() => { server.resetHandlers(); }); afterAll(() => { server.close(); }); async function extractReactRowContents(response: Promise) { let current: ReactResponseRow | null = await response; const rows: { ui: string | JSX.Element | JSX.Element[] | null | undefined; content: string; }[] = []; while (current != null) { let ui = await current.ui; if (ui != null && typeof ui !== 'string' && !Array.isArray(ui)) { ui = ReactDOMServer.renderToStaticMarkup(ui); } rows.push({ ui: ui, content: current.content, }); current = await current.next; } return rows; } describe('without ui', () => { it('should stream text response as React rows', async () => { const stream = OpenAIStream(await fetch(DEFAULT_TEST_URL)); const response = new experimental_StreamingReactResponse( stream, {}, ) as Promise; const rows = await extractReactRowContents(response); expect(rows).toEqual([ { ui: 'Hello', content: 'Hello' }, { ui: 'Hello,', content: 'Hello,' }, { ui: 'Hello, world', content: 'Hello, world' }, { ui: 'Hello, world.', content: 'Hello, world.' }, { ui: 'Hello, world.', content: 'Hello, world.' }, ]); }); it('should stream text response as React rows from data stream', async () => { const data = new StreamData(); const stream = OpenAIStream(await fetch(DEFAULT_TEST_URL), { onFinal() { data.close(); }, }); const response = new experimental_StreamingReactResponse(stream, { data, }) as Promise; const rows = await extractReactRowContents(response); expect(rows).toEqual([ { ui: 'Hello', content: 'Hello' }, { ui: 'Hello,', content: 'Hello,' }, { ui: 'Hello, world', content: 'Hello, world' }, { ui: 'Hello, world.', content: 'Hello, world.' }, { ui: 'Hello, world.', content: 'Hello, world.' }, ]); }); }); describe('with ui: sync jsx for content', () => { it('should stream React response as React rows', async () => { const stream = OpenAIStream(await fetch(DEFAULT_TEST_URL)); const response = new experimental_StreamingReactResponse(stream, { ui: ({ content }) => {content}, }) as Promise; const rows = await extractReactRowContents(response); expect(rows).toEqual([ { ui: 'Hello', content: 'Hello' }, { ui: 'Hello,', content: 'Hello,' }, { ui: 'Hello, world', content: 'Hello, world' }, { ui: 'Hello, world.', content: 'Hello, world.' }, { ui: 'Hello, world.', content: 'Hello, world.' }, ]); }); it('should stream React response as React rows from data stream', async () => { const data = new StreamData(); const stream = OpenAIStream(await fetch(DEFAULT_TEST_URL), { onFinal() { data.close(); }, }); const response = new experimental_StreamingReactResponse(stream, { data, ui: ({ content }) => {content}, }) as Promise; const rows = await extractReactRowContents(response); expect(rows).toEqual([ { ui: 'Hello', content: 'Hello' }, { ui: 'Hello,', content: 'Hello,' }, { ui: 'Hello, world', content: 'Hello, world' }, { ui: 'Hello, world.', content: 'Hello, world.' }, { ui: 'Hello, world.', content: 'Hello, world.' }, ]); }); }); describe('with ui: async sync jsx for content', () => { it('should stream React response as React rows', async () => { const stream = OpenAIStream(await fetch(DEFAULT_TEST_URL)); const response = new experimental_StreamingReactResponse(stream, { ui: async ({ content }) => Promise.resolve({content}), }) as Promise; const rows = await extractReactRowContents(response); expect(rows).toEqual([ { ui: 'Hello', content: 'Hello' }, { ui: 'Hello,', content: 'Hello,' }, { ui: 'Hello, world', content: 'Hello, world' }, { ui: 'Hello, world.', content: 'Hello, world.' }, { ui: 'Hello, world.', content: 'Hello, world.' }, ]); }); it('should stream React response as React rows from data stream', async () => { const data = new StreamData(); const stream = OpenAIStream(await fetch(DEFAULT_TEST_URL), { onFinal() { data.close(); }, }); const response = new experimental_StreamingReactResponse(stream, { data, ui: async ({ content }) => Promise.resolve({content}), }) as Promise; const rows = await extractReactRowContents(response); expect(rows).toEqual([ { ui: 'Hello', content: 'Hello' }, { ui: 'Hello,', content: 'Hello,' }, { ui: 'Hello, world', content: 'Hello, world' }, { ui: 'Hello, world.', content: 'Hello, world.' }, { ui: 'Hello, world.', content: 'Hello, world.' }, ]); }); }); describe('with ui: sync jsx for content and data', () => { it('should stream React response as React rows from data stream when data is appended', async () => { const data = new StreamData(); const stream = OpenAIStream(await fetch(FUNCTION_CALL_TEST_URL), { onFinal() { data.close(); }, async experimental_onFunctionCall({ name }) { data.append({ fn: name }); return undefined; }, }); const response = new experimental_StreamingReactResponse(stream, { data, ui: ({ content, data }) => { if (data != null) { return
{JSON.stringify(data)}
; } return {content}; }, }) as Promise; const rows = await extractReactRowContents(response); expect(rows).toStrictEqual([ { ui: '
[{"fn":"get_current_weather"}]
', content: '', }, { ui: '
[{"fn":"get_current_weather"}]
', content: '', }, { ui: '
[{"fn":"get_current_weather"}]
', content: '', }, ]); }); }); describe('with ui: async jsx for content and data', () => { it('should stream React response as React rows from data stream when data is appended', async () => { const data = new StreamData(); const stream = OpenAIStream(await fetch(FUNCTION_CALL_TEST_URL), { onFinal() { data.close(); }, async experimental_onFunctionCall({ name }) { data.append({ fn: name }); return undefined; }, }); const response = new experimental_StreamingReactResponse(stream, { data, ui: async ({ content, data }) => { if (data != null) { return Promise.resolve(
{JSON.stringify(data)}
); } return Promise.resolve({content}); }, }) as Promise; const rows = await extractReactRowContents(response); expect(rows).toStrictEqual([ { ui: '
[{"fn":"get_current_weather"}]
', content: '', }, { ui: '
[{"fn":"get_current_weather"}]
', content: '', }, { ui: '
[{"fn":"get_current_weather"}]
', content: '', }, ]); }); });