mirror of
https://github.com/aljazceru/goose.git
synced 2026-02-20 22:14:21 +01:00
Catch json errors a little better (#3437)
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
@@ -85,7 +85,7 @@ export default function AppSettingsSection({ scrollToSection }: AppSettingsSecti
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
await response.json(); // Consume the response
|
||||
await response.json();
|
||||
setPricingStatus('success');
|
||||
setLastFetchTime(new Date());
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getApiUrl, getSecretKey } from './config';
|
||||
import { toast } from 'react-toastify';
|
||||
import { safeJsonParse } from './utils/jsonUtils';
|
||||
|
||||
import builtInExtensionsData from './built-in-extensions.json';
|
||||
import { toastError, toastLoading, toastSuccess } from './toasts';
|
||||
@@ -181,7 +182,7 @@ export async function removeExtension(name: string, silent: boolean = false): Pr
|
||||
body: JSON.stringify(sanitizeName(name)),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
const data = await safeJsonParse<{ error: boolean; message: string }>(response);
|
||||
|
||||
if (!data.error) {
|
||||
if (!silent) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState, useRef, useCallback, useEffect } from 'react';
|
||||
import { useConfig } from '../components/ConfigContext';
|
||||
import { getApiUrl, getSecretKey } from '../config';
|
||||
import { useDictationSettings } from './useDictationSettings';
|
||||
import { safeJsonParse } from '../utils/jsonUtils';
|
||||
|
||||
interface UseWhisperOptions {
|
||||
onTranscription?: (text: string) => void;
|
||||
@@ -151,13 +152,18 @@ export const useWhisper = ({ onTranscription, onError, onSizeWarning }: UseWhisp
|
||||
} else if (response.status === 402) {
|
||||
throw new Error('API quota exceeded. Please check your account limits.');
|
||||
}
|
||||
const errorData = await response
|
||||
.json()
|
||||
.catch(() => ({ error: { message: 'Transcription failed' } }));
|
||||
const errorData = await safeJsonParse<{
|
||||
error: { message: string };
|
||||
}>(response, 'Failed to parse error response').catch(() => ({
|
||||
error: { message: 'Transcription failed' },
|
||||
}));
|
||||
throw new Error(errorData.error?.message || 'Transcription failed');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const data = await safeJsonParse<{ text: string }>(
|
||||
response,
|
||||
'Failed to parse transcription response'
|
||||
);
|
||||
if (data.text) {
|
||||
onTranscription?.(data.text);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Message } from '../types/message';
|
||||
import { getApiUrl } from '../config';
|
||||
import { FullExtensionConfig } from '../extensions';
|
||||
import { safeJsonParse } from '../utils/jsonUtils';
|
||||
|
||||
export interface Parameter {
|
||||
key: string;
|
||||
@@ -70,7 +71,7 @@ export async function createRecipe(request: CreateRecipeRequest): Promise<Create
|
||||
throw new Error(`Failed to create recipe: ${response.statusText} (${errorText})`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
return safeJsonParse<CreateRecipeResponse>(response, 'Server failed to create recipe:');
|
||||
}
|
||||
|
||||
export interface EncodeRecipeRequest {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Message } from './types/message';
|
||||
import { safeJsonParse } from './utils/jsonUtils';
|
||||
|
||||
export interface SharedSessionDetails {
|
||||
share_token: string;
|
||||
@@ -35,7 +36,10 @@ export async function fetchSharedSessionDetails(
|
||||
throw new Error(`Failed to fetch shared session: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const data = await safeJsonParse<SharedSessionDetails>(
|
||||
response,
|
||||
'Failed to parse shared session'
|
||||
);
|
||||
|
||||
if (baseUrl != data.base_url) {
|
||||
throw new Error(`Base URL mismatch for shared session: ${baseUrl} != ${data.base_url}`);
|
||||
@@ -98,7 +102,10 @@ export async function createSharedSession(
|
||||
throw new Error(`Failed to create shared session: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const data = await safeJsonParse<{ share_token: string }>(
|
||||
response,
|
||||
'Failed to parse shared session response'
|
||||
);
|
||||
return data.share_token;
|
||||
} catch (error) {
|
||||
console.error('Error creating shared session:', error);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getApiUrl, getSecretKey } from '../config';
|
||||
import { safeJsonParse } from './jsonUtils';
|
||||
|
||||
const getQuestionClassifierPrompt = (messageContent: string): string => `
|
||||
You are a simple classifier that takes content and decides if it is asking for input
|
||||
@@ -167,7 +168,7 @@ export async function ask(prompt: string): Promise<string> {
|
||||
throw new Error('Failed to get response');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const data = await safeJsonParse<{ response: string }>(response, 'Failed to get AI response');
|
||||
return data.response;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Import the proper type from ConfigContext
|
||||
import { getApiUrl, getSecretKey } from '../config';
|
||||
import { safeJsonParse } from './jsonUtils';
|
||||
|
||||
export interface ModelCostInfo {
|
||||
input_token_cost: number; // Cost per token for input (in USD)
|
||||
@@ -47,7 +48,15 @@ async function fetchPricingForModel(
|
||||
throw new Error(`API request failed with status ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const data = await safeJsonParse<{
|
||||
pricing: Array<{
|
||||
provider: string;
|
||||
model: string;
|
||||
input_token_cost: number;
|
||||
output_token_cost: number;
|
||||
currency: string;
|
||||
}>;
|
||||
}>(response, 'Failed to parse pricing data');
|
||||
|
||||
// Find the specific model pricing using the lookup provider/model
|
||||
const pricing = data.pricing?.find(
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import log from './logger';
|
||||
import { safeJsonParse } from './jsonUtils';
|
||||
|
||||
interface GitHubRelease {
|
||||
tag_name: string;
|
||||
@@ -53,7 +54,10 @@ export class GitHubUpdater {
|
||||
throw new Error(`GitHub API returned ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const release: GitHubRelease = await response.json();
|
||||
const release: GitHubRelease = await safeJsonParse<GitHubRelease>(
|
||||
response,
|
||||
'Failed to get GitHub release information'
|
||||
);
|
||||
log.info(`GitHubUpdater: Found release: ${release.tag_name} (${release.name})`);
|
||||
log.info(`GitHubUpdater: Release published at: ${release.published_at}`);
|
||||
log.info(`GitHubUpdater: Release assets count: ${release.assets.length}`);
|
||||
|
||||
13
ui/desktop/src/utils/jsonUtils.ts
Normal file
13
ui/desktop/src/utils/jsonUtils.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export async function safeJsonParse<T>(
|
||||
response: Response,
|
||||
errorMessage: string = 'Failed to parse server response'
|
||||
): Promise<T> {
|
||||
try {
|
||||
return (await response.json()) as T;
|
||||
} catch (error) {
|
||||
if (error instanceof SyntaxError) {
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user