ui improvements - abort fix

This commit is contained in:
Andrew Pareles 2024-12-04 20:47:41 -08:00
parent 411167ee44
commit e1692758c6
9 changed files with 54 additions and 19 deletions

View file

@ -212,7 +212,7 @@ const RenderToken = ({ token, nested = false }: { token: Token | string, nested?
}
export const MarkdownRender = ({ string, nested = false }: { string: string, nested?: boolean }) => {
const tokens = marked.lexer(string); // https://marked.js.org/using_pro#renderer
const tokens = marked.lexer(string ?? '(empty)'); // https://marked.js.org/using_pro#renderer
return (
<>
{tokens.map((token, index) => (

View file

@ -1,4 +1,4 @@
import { Ollama } from 'ollama/browser';
import { Ollama, ErrorResponse } from 'ollama/browser';
import { parseMaxTokensStr } from '../../../registerConfig.js';
import { SendLLMMessageFnTypeInternal } from './_types.js';
@ -29,7 +29,15 @@ export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText,
})
// when error/fail
.catch(error => {
.catch((error) => {
if (typeof error === 'object') {
const e = error.error as ErrorResponse['error']
if (e) {
const name = error.name ?? 'Error'
onError({ error: `${name}: ${e}` })
return;
}
}
onError({ error })
})

View file

@ -44,15 +44,16 @@ export const sendLLMMessage = ({ messages, onText: onText_, onFinalMessage: onFi
}
const onError: OnError = ({ error }) => {
console.error('sendLLMMessage onError:', error)
if (_didAbort) return
console.error('sendLLMMessage onError:', error)
captureChatEvent(`${loggingName} - Error`, { error })
onError_({ error })
}
const onAbort = () => {
captureChatEvent(`${loggingName} - Abort`, { messageLengthSoFar: _fullTextSoFar.length })
_aborter?.()
try { _aborter?.() } // aborter sometimes automatically throws an error
catch (e) { }
_didAbort = true
}
abortRef_.current = onAbort
@ -90,8 +91,8 @@ export const sendLLMMessage = ({ messages, onText: onText_, onFinalMessage: onFi
catch (error) {
if (error instanceof Error) { onError({ error }) }
else { onError({ error: `Unexpected Error in sendLLMMessage: ${error}` }); }
; (_aborter as any)?.()
_didAbort = true
// ; (_aborter as any)?.()
// _didAbort = true
}

View file

@ -33,7 +33,7 @@ const getErrorDetails = (error: unknown) => {
}
// sometimes error is an object but not an Error
else if (typeof error === 'object') {
e = new Error(`The server didn't give a very useful error message. More details below.`, { cause: JSON.stringify(error) })
e = new Error(`More details below.`, { cause: JSON.stringify(error) })
}
else {

View file

@ -18,8 +18,9 @@ const Sidebar = () => {
const sidebarState = useSidebarState()
const { isHistoryOpen, currentTab: tab } = sidebarState
// className='@@void-scope'
return <div className='@@void-scope'>
<div className={`flex flex-col h-screen w-full px-2 py-2 overflow-y-auto`}>
<div className={`flex flex-col h-screen w-full px-2 py-2`}>
{/* <span onClick={() => {
const tabs = ['chat', 'settings', 'threadSelector']
@ -27,7 +28,7 @@ const Sidebar = () => {
sidebarStateService.setState({ currentTab: tabs[(index + 1) % tabs.length] as any })
}}>clickme {tab}</span> */}
<div className={`mb-2 h-[30vh] overflow-y-auto ${isHistoryOpen ? '' : 'hidden'}`}>
<div className={`mb-2 ${isHistoryOpen ? '' : 'hidden'}`}>
<SidebarThreadSelector />
</div>
@ -42,6 +43,7 @@ const Sidebar = () => {
</div>
</div>
}

View file

@ -21,7 +21,6 @@ import { LLMMessageServiceParams } from '../../../../../../../platform/void/comm
import { getCmdKey } from '../../../getCmdKey.js'
import { VSCodeDropdown } from '@vscode/webview-ui-toolkit/react';
import { InputBox } from './InputBox.js';
// read files from VSCode
const VSReadFile = async (modelService: IModelService, uri: URI): Promise<string | null> => {
@ -260,7 +259,7 @@ export const SidebarChat = () => {
sendLLMMessageService.abort(latestRequestIdRef.current)
// if messageStream was not empty, add it to the history
const llmContent = messageStream || '(null)'
const llmContent = messageStream || '(empty)'
const newHistoryElt: ChatMessage = { role: 'assistant', content: llmContent, displayContent: messageStream, }
threadsStateService.addMessageToCurrentThread(newHistoryElt)

View file

@ -23,10 +23,10 @@ export const SidebarThreadSelector = () => {
const { allThreads } = threadsState
// sorted by most recent to least recent
const sortedThreadIds = Object.keys(allThreads ?? {}).sort((threadId1, threadId2) => allThreads![threadId1].lastModified > allThreads![threadId2].lastModified ? 1 : -1)
const sortedThreadIds = Object.keys(allThreads ?? {}).sort((threadId1, threadId2) => allThreads![threadId1].lastModified > allThreads![threadId2].lastModified ? -1 : 1)
return (
<div className="flex flex-col gap-y-1">
<div className="flex flex-col gap-y-1 overflow-y-auto h-[30vh]">
{/* X button at top right */}
<div className="text-right">

View file

@ -11,7 +11,6 @@ import {
} from '../../../common/views.js';
import * as nls from '../../../../nls.js';
import * as dom from '../../../../base/browser/dom.js';
import { Codicon } from '../../../../base/common/codicons.js';
import { localize } from '../../../../nls.js';
@ -101,9 +100,7 @@ class VoidSidebarViewPane extends ViewPane {
protected override renderBody(parent: HTMLElement): void {
super.renderBody(parent);
const { root } = dom.h('div@root')
dom.append(parent, root);
parent.style.overflow = 'auto'
// gets set immediately
this.instantiationService.invokeFunction(accessor => {
@ -117,7 +114,7 @@ class VoidSidebarViewPane extends ViewPane {
sendLLMMessageService: accessor.get(ISendLLMMessageService),
contextViewService: accessor.get(IContextViewService),
}
mountFn(root, services);
mountFn(parent, services);
});
}

View file

@ -133,6 +133,8 @@ class ThreadHistoryService extends Disposable implements IThreadHistoryService {
}
switchToThread(threadId: string) {
// console.log('threadId', threadId)
// console.log('messages', this.state.allThreads[threadId].messages)
this._setState({ _currentThreadId: threadId }, true)
}
@ -194,3 +196,29 @@ class ThreadHistoryService extends Disposable implements IThreadHistoryService {
}
registerSingleton(IThreadHistoryService, ThreadHistoryService, InstantiationType.Eager);
// [
// {
// "role": "system",
// "content": "\nYou are a coding assistant. You are given a list of relevant files `files`, a selection that the user is making `selection`, and instructions to follow `instructions`.\n\nPlease edit the selected file following the user's instructions (or, if appropriate, answer their question instead).\n\nAll changes made to files must be outputted in unified diff format.\nUnified diff format instructions:\n1. Each diff must begin with ```@@ ... @@```.\n2. Each line must start with a `+` or `-` or ` ` symbol.\n3. Make diffs more than a few lines.\n4. Make high-level diffs rather than many one-line diffs.\n\nHere's an example of unified diff format:\n\n```\n@@ ... @@\n-def factorial(n):\n- if n == 0:\n- return 1\n- else:\n- return n * factorial(n-1)\n+def factorial(number):\n+ if number == 0:\n+ return 1\n+ else:\n+ return number * factorial(number-1)\n```\n\nPlease create high-level diffs where you group edits together if they are near each other, like in the above example. Another way to represent the above example is to make many small line edits. However, this is less preferred, because the edits are not high-level. The edits are close together and should be grouped:\n\n```\n@@ ... @@ # This is less preferred because edits are close together and should be grouped:\n-def factorial(n):\n+def factorial(number):\n- if n == 0:\n+ if number == 0:\n return 1\n else:\n- return n * factorial(n-1)\n+ return number * factorial(number-1)\n```\n\n# Example 1:\n\nFILES\nselected file `test.ts`:\n```\nx = 1\n\n{{selection}}\n\nz = 3\n```\n\nSELECTION\n```const y = 2```\n\nINSTRUCTIONS\n```y = 3```\n\nEXPECTED RESULT\n\nWe should change the selection from ```y = 2``` to ```y = 3```.\n```\n@@ ... @@\n-x = 1\n-\n-y = 2\n+x = 1\n+\n+y = 3\n```\n\n# Example 2:\n\nFILES\nselected file `Sidebar.tsx`:\n```\nimport React from 'react';\nimport styles from './Sidebar.module.css';\n\ninterface SidebarProps {\n items: { label: string; href: string }[];\n onItemSelect?: (label: string) => void;\n onExtraButtonClick?: () => void;\n}\n\nconst Sidebar: React.FC<SidebarProps> = ({ items, onItemSelect, onExtraButtonClick }) => {\n return (\n <div className={styles.sidebar}>\n <ul>\n {items.map((item, index) => (\n <li key={index}>\n {{selection}}\n className={styles.sidebarButton}\n onClick={() => onItemSelect?.(item.label)}\n >\n {item.label}\n </button>\n </li>\n ))}\n </ul>\n <button className={styles.extraButton} onClick={onExtraButtonClick}>\n Extra Action\n </button>\n </div>\n );\n};\n\nexport default Sidebar;\n```\n\nSELECTION\n``` <button```\n\nINSTRUCTIONS\n```make all the buttons like this into divs```\n\nEXPECTED OUTPUT\n\nWe should change all the buttons like the one selected into a div component. Here is the change:\n```\n@@ ... @@\n-<div className={styles.sidebar}>\n-<ul>\n- {items.map((item, index) => (\n-\t<li key={index}>\n-\t <button\n-\t\tclassName={styles.sidebarButton}\n-\t\tonClick={() => onItemSelect?.(item.label)}\n-\t >\n-\t\t{item.label}\n-\t </button>\n-\t</li>\n- ))}\n-</ul>\n-<button className={styles.extraButton} onClick={onExtraButtonClick}>\n- Extra Action\n-</button>\n-</div>\n+<div className={styles.sidebar}>\n+<ul>\n+ {items.map((item, index) => (\n+\t<li key={index}>\n+\t <div\n+\t\tclassName={styles.sidebarButton}\n+\t\tonClick={() => onItemSelect?.(item.label)}\n+\t >\n+\t\t{item.label}\n+\t </div>\n+\t</li>\n+ ))}\n+</ul>\n+<div className={styles.extraButton} onClick={onExtraButtonClick}>\n+ Extra Action\n+</div>\n+</div>\n```\n"
// },
// {
// "role": "user",
// "content": "test",
// "displayContent": "test",
// "selections": null
// },
// {
// "role": "assistant",
// "content": {
// "requestId": "49d4c9e6-5e53-4768-a77e-5c297223fa9c",
// "fullText": "I apologize, but I don't have enough context to provide a meaningful response based on just the word \"test\". If you have a specific question or topic you'd like me to assist with, please provide more details or context so I can better understand how to help you. I'm here to engage in conversation and provide information to the best of my abilities."
// },
// "displayContent": {
// "requestId": "49d4c9e6-5e53-4768-a77e-5c297223fa9c",
// "fullText": "I apologize, but I don't have enough context to provide a meaningful response based on just the word \"test\". If you have a specific question or topic you'd like me to assist with, please provide more details or context so I can better understand how to help you. I'm here to engage in conversation and provide information to the best of my abilities."
// }
// }
// ]