From 74a134eec04e7b4b8995dbb0aa8e3c092dd138b3 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 29 Dec 2024 17:59:45 -0500 Subject: [PATCH] Rename `chat.ts` to `chat-store.ts` --- web/src/lib/services/ChatService.ts | 296 +++++++++---------- web/src/lib/store/{chat.ts => chat-store.ts} | 6 +- 2 files changed, 151 insertions(+), 151 deletions(-) rename web/src/lib/store/{chat.ts => chat-store.ts} (97%) diff --git a/web/src/lib/services/ChatService.ts b/web/src/lib/services/ChatService.ts index 3766e8b3..a73c8a4d 100644 --- a/web/src/lib/services/ChatService.ts +++ b/web/src/lib/services/ChatService.ts @@ -1,168 +1,168 @@ import type { - ChatRequest, - StreamResponse, - ChatError as IChatError, - ChatPrompt + ChatRequest, + StreamResponse, + ChatError as IChatError, + ChatPrompt } from '$lib/types/interfaces/chat-interface'; import { get } from 'svelte/store'; import { modelConfig } from '$lib/store/model-store'; import { systemPrompt } from '$lib/store/pattern-store'; import { chatConfig } from '$lib/store/chat-config'; -import { messageStore } from '$lib/store/chat'; // Import messageStore +import { messageStore } from '$lib/store/chat-store'; // Import messageStore export class ChatError extends Error implements IChatError { - constructor( - message: string, - public readonly code: string = 'CHAT_ERROR', - public readonly details?: unknown - ) { - super(message); - this.name = 'ChatError'; - } + constructor( + message: string, + public readonly code: string = 'CHAT_ERROR', + public readonly details?: unknown + ) { + super(message); + this.name = 'ChatError'; + } } export class ChatService { - private async fetchStream(request: ChatRequest): Promise> { + private async fetchStream(request: ChatRequest): Promise> { + try { + const response = await fetch('/api/chat', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(request), + }); + + if (!response.ok) { + throw new ChatError( + `HTTP error! status: ${response.status}`, + 'HTTP_ERROR', + { status: response.status } + ); + } + + const reader = response.body?.getReader(); + if (!reader) { + throw new ChatError('Response body is null', 'NULL_RESPONSE'); + } + + return this.createMessageStream(reader); + } catch (error) { + if (error instanceof ChatError) { + throw error; + } + throw new ChatError( + 'Failed to fetch chat stream', + 'FETCH_ERROR', + error + ); + } + } + + private createMessageStream(reader: ReadableStreamDefaultReader): ReadableStream { + let buffer = ''; + + return new ReadableStream({ + async start(controller) { try { - const response = await fetch('/api/chat', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(request), - }); + while (true) { + const { done, value } = await reader.read(); + if (done) break; - if (!response.ok) { - throw new ChatError( - `HTTP error! status: ${response.status}`, - 'HTTP_ERROR', - { status: response.status } - ); + buffer += new TextDecoder().decode(value); + const messages = buffer + .split('\n\n') + .filter(msg => msg.startsWith('data: ')); + + if (messages.length > 1) { + buffer = messages.pop() || ''; + + for (const msg of messages) { + controller.enqueue(JSON.parse(msg.slice(6)) as StreamResponse); + } } + } - const reader = response.body?.getReader(); - if (!reader) { - throw new ChatError('Response body is null', 'NULL_RESPONSE'); - } - - return this.createMessageStream(reader); + if (buffer.startsWith('data: ')) { + controller.enqueue(JSON.parse(buffer.slice(6)) as StreamResponse); + } } catch (error) { - if (error instanceof ChatError) { - throw error; - } - throw new ChatError( - 'Failed to fetch chat stream', - 'FETCH_ERROR', - error - ); - } - } - - private createMessageStream(reader: ReadableStreamDefaultReader): ReadableStream { - let buffer = ''; - - return new ReadableStream({ - async start(controller) { - try { - while (true) { - const { done, value } = await reader.read(); - if (done) break; - - buffer += new TextDecoder().decode(value); - const messages = buffer - .split('\n\n') - .filter(msg => msg.startsWith('data: ')); - - if (messages.length > 1) { - buffer = messages.pop() || ''; - - for (const msg of messages) { - controller.enqueue(JSON.parse(msg.slice(6)) as StreamResponse); - } - } - } - - if (buffer.startsWith('data: ')) { - controller.enqueue(JSON.parse(buffer.slice(6)) as StreamResponse); - } - } catch (error) { - controller.error(new ChatError( - 'Error processing stream', - 'STREAM_PROCESSING_ERROR', - error - )); - } finally { - reader.releaseLock(); - controller.close(); - } - }, - - cancel() { - reader.cancel(); - } - }); - } - - private createChatPrompt(userInput: string, systemPromptText?: string): ChatPrompt { - const config = get(modelConfig); - return { - userInput, - systemPrompt: systemPromptText ?? get(systemPrompt), - model: config.model, - patternName: '' - }; - } - - public async createChatRequest(userInput: string, systemPromptText?: string): Promise { - const prompt = this.createChatPrompt(userInput, systemPromptText); - const config = get(chatConfig); - const messages = get(messageStore); - - return { - prompts: [prompt], - messages: messages, - ...config - }; - } - - public async streamChat(userInput: string, systemPromptText?: string): Promise> { - const request = await this.createChatRequest(userInput, systemPromptText); - return this.fetchStream(request); - } - - public async processStream( - stream: ReadableStream, - onContent: (content: string) => void, - onError: (error: Error) => void - ): Promise { - const reader = stream.getReader(); - let accumulatedContent = ''; - - try { - while (true) { - const { done, value } = await reader.read(); - - if (done) break; - - if (value.type === 'error') { - throw new ChatError(value.content, 'STREAM_CONTENT_ERROR'); - } - - accumulatedContent += value.content; - onContent(accumulatedContent); - } - } catch (error) { - if (error instanceof ChatError) { - onError(error); - } else { - onError(new ChatError( - 'Error processing stream content', - 'STREAM_PROCESSING_ERROR', - error - )); - } + controller.error(new ChatError( + 'Error processing stream', + 'STREAM_PROCESSING_ERROR', + error + )); } finally { - reader.releaseLock(); + reader.releaseLock(); + controller.close(); } + }, + + cancel() { + reader.cancel(); + } + }); + } + + private createChatPrompt(userInput: string, systemPromptText?: string): ChatPrompt { + const config = get(modelConfig); + return { + userInput, + systemPrompt: systemPromptText ?? get(systemPrompt), + model: config.model, + patternName: '' + }; + } + + public async createChatRequest(userInput: string, systemPromptText?: string): Promise { + const prompt = this.createChatPrompt(userInput, systemPromptText); + const config = get(chatConfig); + const messages = get(messageStore); + + return { + prompts: [prompt], + messages: messages, + ...config + }; + } + + public async streamChat(userInput: string, systemPromptText?: string): Promise> { + const request = await this.createChatRequest(userInput, systemPromptText); + return this.fetchStream(request); + } + + public async processStream( + stream: ReadableStream, + onContent: (content: string) => void, + onError: (error: Error) => void + ): Promise { + const reader = stream.getReader(); + let accumulatedContent = ''; + + try { + while (true) { + const { done, value } = await reader.read(); + + if (done) break; + + if (value.type === 'error') { + throw new ChatError(value.content, 'STREAM_CONTENT_ERROR'); + } + + accumulatedContent += value.content; + onContent(accumulatedContent); + } + } catch (error) { + if (error instanceof ChatError) { + onError(error); + } else { + onError(new ChatError( + 'Error processing stream content', + 'STREAM_PROCESSING_ERROR', + error + )); + } + } finally { + reader.releaseLock(); } + } } diff --git a/web/src/lib/store/chat.ts b/web/src/lib/store/chat-store.ts similarity index 97% rename from web/src/lib/store/chat.ts rename to web/src/lib/store/chat-store.ts index 4456bdea..27ad1e92 100644 --- a/web/src/lib/store/chat.ts +++ b/web/src/lib/store/chat-store.ts @@ -67,7 +67,7 @@ export const revertLastMessage = () => { messageStore.update(messages => messages.slice(0, -1)); }; -export async function sendMessage(userInput: string, systemPromptText?: string) { +export async function sendMessage(userInput: string, systemPromptText?: string, messageHistory?: string) { try { const $streaming = get(streamingStore); if ($streaming) { @@ -80,7 +80,7 @@ export async function sendMessage(userInput: string, systemPromptText?: string) // Add user message messageStore.update(messages => [...messages, { role: 'user', content: userInput }]); - const stream = await chatService.streamChat(userInput, systemPromptText); + const stream = await chatService.streamChat(userInput, systemPromptText, messageHistory); await chatService.processStream( stream, @@ -118,4 +118,4 @@ export async function sendMessage(userInput: string, systemPromptText?: string) } // Re-export types for convenience -export type { ChatState, Message }; \ No newline at end of file +export type { ChatState, Message };