diff --git a/README.md b/README.md index d27ae91..02450d1 100755 --- a/README.md +++ b/README.md @@ -113,6 +113,8 @@ properties: - headers: `object (optional)` - the headers to include in the request. - body: `object (optional)` - the body of the request. +- fakeCharactersPerSecond: `number (optional)` - the number of + characters to display per second. If this is unused the hook will display the messages as they come in. #### method diff --git a/package.json b/package.json index c105e7c..514b1d7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@magicul/react-chat-stream", "description": "A React hook that lets you easily integrate your custom ChatGPT-like chat in React.", - "version": "0.3.1", + "version": "0.4.0", "main": "dist/index.js", "types": "dist/index.d.ts", "homepage": "https://github.com/XD2Sketch/react-chat-stream#readme", diff --git a/src/hooks/useChatStream.ts b/src/hooks/useChatStream.ts index 8ac8d1a..5e85936 100644 --- a/src/hooks/useChatStream.ts +++ b/src/hooks/useChatStream.ts @@ -37,13 +37,27 @@ const useChatStream = (input: UseChatStreamInput) => { }; const fetchAndUpdateAIResponse = async (message: string) => { + const charactersPerSecond = input.options.fakeCharactersPerSecond; const stream = await getStream(message, input.options, input.method); const initialMessage = addMessage({ content: '', role: 'bot' }); let response = ''; - for await (const message of decodeStreamToJson(stream)) { - appendMessageToChat(message); - response += message; + for await (const chunk of decodeStreamToJson(stream)) { + if (!charactersPerSecond) { + appendMessageToChat(chunk); + response += chunk; + continue; + } + + // Stream characters one by one based on the characters per second that is set. + for (const char of chunk) { + appendMessageToChat(char); + response += char; + + if (charactersPerSecond > 0) { + await new Promise(resolve => setTimeout(resolve, 1000 / charactersPerSecond)); + } + } } return { ...initialMessage, content: response }; diff --git a/src/types.ts b/src/types.ts index 4d98fa2..25f5ea8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,12 +10,13 @@ export type UseChatStreamChatMessage = { id: string, } -export type UseChatStreamHttpOptions = { +export type UseChatStreamOptions = { url: string; method: HttpMethod; query?: Record; headers?: HeadersInit; body?: Record; + fakeCharactersPerSecond?: number; } export type UseChatStreamEventHandlers = { @@ -28,9 +29,9 @@ export type UseChatStreamInputMethod = { } export type UseChatStreamInput = { - options: UseChatStreamHttpOptions, + options: UseChatStreamOptions, method: UseChatStreamInputMethod, - handlers: UseChatStreamEventHandlers + handlers: UseChatStreamEventHandlers, }; -export type UseChatStreamResult = ReturnType; \ No newline at end of file +export type UseChatStreamResult = ReturnType; diff --git a/src/utils/streams.ts b/src/utils/streams.ts index b6b0376..153f8ca 100644 --- a/src/utils/streams.ts +++ b/src/utils/streams.ts @@ -1,17 +1,17 @@ -import { UseChatStreamHttpOptions, UseChatStreamInputMethod } from '../types'; +import { UseChatStreamOptions, UseChatStreamInputMethod } from '../types'; const DEFAULT_HEADERS = { 'Content-Type': 'application/json', }; -const mergeInputInOptions = (input: string, options: UseChatStreamHttpOptions, method: UseChatStreamInputMethod) => { +const mergeInputInOptions = (input: string, options: UseChatStreamOptions, method: UseChatStreamInputMethod) => { options.query = options.query ?? {}; (options[method.type] as Record)[method.key] = input; return options; }; -export const getStream = async (input: string, options: UseChatStreamHttpOptions, method: UseChatStreamInputMethod) => { +export const getStream = async (input: string, options: UseChatStreamOptions, method: UseChatStreamInputMethod) => { options = mergeInputInOptions(input, options, method); const params = '?' + new URLSearchParams(options.query).toString();