diff --git a/app/components/chat/APIKeyManager.tsx b/app/components/chat/APIKeyManager.tsx index 5acbbf12..92263363 100644 --- a/app/components/chat/APIKeyManager.tsx +++ b/app/components/chat/APIKeyManager.tsx @@ -2,7 +2,6 @@ import React, { useState, useEffect, useCallback } from 'react'; import { IconButton } from '~/components/ui/IconButton'; import type { ProviderInfo } from '~/types/model'; import Cookies from 'js-cookie'; -import { providerBaseUrlEnvKeys } from '~/utils/constants'; interface APIKeyManagerProps { provider: ProviderInfo; @@ -93,18 +92,16 @@ export const APIKeyManager: React.FC = ({ provider, apiKey, {provider?.name} API Key: {!isEditing && (
- {isEnvKeySet ? ( - <> -
- - Set via {providerBaseUrlEnvKeys[provider.name].apiTokenKey} environment variable - - - ) : apiKey ? ( + {apiKey ? ( <>
Set via UI + ) : isEnvKeySet ? ( + <> +
+ Set via environment variable + ) : ( <>
@@ -117,7 +114,7 @@ export const APIKeyManager: React.FC = ({ provider, apiKey,
- {isEditing && !isEnvKeySet ? ( + {isEditing ? (
= ({ provider, apiKey,
) : ( <> - {!isEnvKeySet && ( + { setIsEditing(true)} title="Edit API Key" @@ -153,8 +150,8 @@ export const APIKeyManager: React.FC = ({ provider, apiKey, >
- )} - {provider?.getApiKeyLink && !isEnvKeySet && ( + } + {provider?.getApiKeyLink && !apiKey && ( window.open(provider?.getApiKeyLink)} title="Get API Key" diff --git a/app/components/chat/Chat.client.tsx b/app/components/chat/Chat.client.tsx index 135dc8fc..c56a4ee1 100644 --- a/app/components/chat/Chat.client.tsx +++ b/app/components/chat/Chat.client.tsx @@ -137,35 +137,36 @@ export const ChatImpl = memo( const [apiKeys, setApiKeys] = useState>({}); - const { messages, isLoading, input, handleInputChange, setInput, stop, append, setMessages, reload } = useChat({ - api: '/api/chat', - body: { - apiKeys, - files, - promptId, - contextOptimization: contextOptimizationEnabled, - }, - sendExtraMessageFields: true, - onError: (error) => { - logger.error('Request failed\n\n', error); - toast.error( - 'There was an error processing your request: ' + (error.message ? error.message : 'No details were returned'), - ); - }, - onFinish: (message, response) => { - const usage = response.usage; + const { messages, isLoading, input, handleInputChange, setInput, stop, append, setMessages, reload, error } = + useChat({ + api: '/api/chat', + body: { + apiKeys, + files, + promptId, + contextOptimization: contextOptimizationEnabled, + }, + sendExtraMessageFields: true, + onError: (e) => { + logger.error('Request failed\n\n', e, error); + toast.error( + 'There was an error processing your request: ' + (e.message ? e.message : 'No details were returned'), + ); + }, + onFinish: (message, response) => { + const usage = response.usage; - if (usage) { - console.log('Token usage:', usage); + if (usage) { + console.log('Token usage:', usage); - // You can now use the usage data as needed - } + // You can now use the usage data as needed + } - logger.debug('Finished streaming'); - }, - initialMessages, - initialInput: Cookies.get(PROMPT_COOKIE_KEY) || '', - }); + logger.debug('Finished streaming'); + }, + initialMessages, + initialInput: Cookies.get(PROMPT_COOKIE_KEY) || '', + }); useEffect(() => { const prompt = searchParams.get('prompt'); @@ -263,6 +264,10 @@ export const ChatImpl = memo( */ await workbenchStore.saveAllFiles(); + if (error != null) { + setMessages(messages.slice(0, -1)); + } + const fileModifications = workbenchStore.getFileModifcations(); chatStore.setKey('aborted', false); diff --git a/app/lib/.server/llm/stream-text.ts b/app/lib/.server/llm/stream-text.ts index d8f41b59..32aebdf7 100644 --- a/app/lib/.server/llm/stream-text.ts +++ b/app/lib/.server/llm/stream-text.ts @@ -226,7 +226,7 @@ export async function streamText(props: { logger.info(`Sending llm call to ${provider.name} with model ${modelDetails.name}`); - return _streamText({ + return await _streamText({ model: provider.getModelInstance({ model: currentModel, serverEnv, diff --git a/app/routes/api.chat.ts b/app/routes/api.chat.ts index 8c15bf27..5c8c298b 100644 --- a/app/routes/api.chat.ts +++ b/app/routes/api.chat.ts @@ -122,6 +122,8 @@ async function chatAction({ context, request }: ActionFunctionArgs) { return; }, }; + const totalMessageContent = messages.reduce((acc, message) => acc + message.content, ''); + logger.debug(`Total message length: ${totalMessageContent.split(' ').length}, words`); const result = await streamText({ messages, @@ -134,13 +136,27 @@ async function chatAction({ context, request }: ActionFunctionArgs) { contextOptimization, }); + (async () => { + for await (const part of result.fullStream) { + if (part.type === 'error') { + const error: any = part.error; + logger.error(`${error}`); + + return; + } + } + })(); + stream.switchSource(result.toDataStream()); + // return createrespo return new Response(stream.readable, { status: 200, headers: { - contentType: 'text/event-stream', - connection: 'keep-alive', + 'Content-Type': 'text/event-stream; charset=utf-8', + Connection: 'keep-alive', + 'Cache-Control': 'no-cache', + 'Text-Encoding': 'chunked', }, }); } catch (error: any) { diff --git a/app/routes/api.check-env-key.ts b/app/routes/api.check-env-key.ts index 7def79f1..14d21236 100644 --- a/app/routes/api.check-env-key.ts +++ b/app/routes/api.check-env-key.ts @@ -1,4 +1,4 @@ -import type { LoaderFunction } from '@remix-run/node'; +import type { LoaderFunction } from '@remix-run/cloudflare'; import { providerBaseUrlEnvKeys } from '~/utils/constants'; export const loader: LoaderFunction = async ({ context, request }) => { diff --git a/app/routes/api.enhancer.ts b/app/routes/api.enhancer.ts index 2b8fee83..5d16ac25 100644 --- a/app/routes/api.enhancer.ts +++ b/app/routes/api.enhancer.ts @@ -107,7 +107,10 @@ async function enhancerAction({ context, request }: ActionFunctionArgs) { return new Response(result.textStream, { status: 200, headers: { - 'Content-Type': 'text/plain; charset=utf-8', + 'Content-Type': 'text/event-stream', + Connection: 'keep-alive', + 'Cache-Control': 'no-cache', + 'Text-Encoding': 'chunked', }, }); } catch (error: unknown) {