Repository: fmaclen/hollama
Pull request #110 (2024-07-21 22:34:49)

feat: add retry button to error messages
- All errors can now be retried
- Adds a hint to explain the behavior in #103


Patch:
diff --git a/src/routes/sessions/[id]/+page.svelte b/src/routes/sessions/[id]/+page.svelte
index 24e5be3..8f2580b 100644
--- a/src/routes/sessions/[id]/+page.svelte
+++ b/src/routes/sessions/[id]/+page.svelte
@@ -192,11 +192,11 @@
 	}
 
 	function handleError(error: Error) {
-		resetPrompt();
-
 		let content: string;
 		if (error.message === 'Failed to fetch') {
 			content = `Couldn't connect to Ollama. Is the [server running](/settings)?`;
+		} else if (error.name === 'SyntaxError') {
+			content = `Sorry, this session is _likely_ exceeding the context window of \`${session.model}\`\n\`\`\`\n${error.name}: ${error.message}\n\`\`\``;
 		} else {
 			content = `Sorry, something went wrong.\n\`\`\`\n${error}\n\`\`\``;
 		}
@@ -240,7 +240,11 @@
 
 				{#each session.messages as message, i (session.id + i)}
 					{#key message.role}
-						<Article {message} retryIndex={message.role === 'ai' ? i : undefined} {handleRetry} />
+						<Article
+							{message}
+							retryIndex={['ai', 'system'].includes(message.role) ? i : undefined}
+							{handleRetry}
+						/>
 					{/key}
 				{/each}
 


Test patch:
diff --git a/tests/session.test.ts b/tests/session.test.ts
index 7672dc0..b12f401 100644
--- a/tests/session.test.ts
+++ b/tests/session.test.ts
@@ -373,9 +373,7 @@ test.describe('Session', () => {
 		await expect(promptEditor).not.toHaveClass(/ prompt-editor--fullscreen/);
 	});
 
-	test('handles errors when generating completion response', async ({ page }) => {
-		const runButton = page.locator('button', { hasText: 'Run' });
-
+	test('handles errors when generating completion response and retries', async ({ page }) => {
 		await page.goto('/');
 		await page.getByText('Sessions', { exact: true }).click();
 		await page.getByTestId('new-session').click();
@@ -391,11 +389,24 @@ test.describe('Session', () => {
 		});
 		await page.getByLabel('Model').selectOption(MOCK_API_TAGS_RESPONSE.models[0].name);
 		await promptTextarea.fill('Who would win in a fight between Emma Watson and Jessica Alba?');
-		await runButton.click();
+		await page.locator('button', { hasText: 'Run' }).click();
 		await expect(page.locator('article nav', { hasText: 'System' })).toHaveCount(1);
 		await expect(page.getByText("Couldn't connect to Ollama. Is the server running?")).toBeVisible();
 		await expect(page.locator('code', { hasText: 'Ollama says: Not so fast!' })).not.toBeVisible();
-		await expect(promptTextarea).toHaveValue('Who would win in a fight between Emma Watson and Jessica Alba?');
+		await expect(promptTextarea).not.toHaveValue('Who would win in a fight between Emma Watson and Jessica Alba?');
+
+		// Mock an incomplete JSON response
+		await page.route('**/generate', async (route) => {
+			await route.fulfill({
+				status: 200,
+				contentType: 'application/json',
+				body: "{ incomplete"
+			});
+		});
+		await page.getByTitle('Retry').click();
+		await expect(page.locator('article nav', { hasText: 'System' })).toHaveCount(1);
+		await expect(page.getByText(`Sorry, this session is likely exceeding the context window of ${MOCK_API_TAGS_RESPONSE.models[0].name}`)).toBeVisible();
+		await expect(page.locator('code', { hasText: 'SyntaxError' })).toBeVisible();
 
 		// Mock a 500 error response
 		await page.route('**/generate', async (route) => {
@@ -405,11 +416,11 @@ test.describe('Session', () => {
 				body: JSON.stringify({ error: 'Ollama says: Not so fast!' })
 			});
 		});
-		await runButton.click();
-		await expect(page.locator('article nav', { hasText: 'System' })).toHaveCount(2);
+		await page.getByTitle('Retry').click();
+		await expect(page.locator('article nav', { hasText: 'System' })).toHaveCount(1);
 		await expect(page.getByText('Sorry, something went wrong.')).toBeVisible();
 		await expect(page.locator('code', { hasText: 'Ollama says: Not so fast!' })).toBeVisible();
-		await expect(promptTextarea).toHaveValue('Who would win in a fight between Emma Watson and Jessica Alba?');
+		await expect(promptTextarea).not.toHaveValue('Who would win in a fight between Emma Watson and Jessica Alba?');
 	});
 
 	test('ai completion can be retried', async ({ page }) => {