Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,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 {
Expand Down
3 changes: 2 additions & 1 deletion ui/desktop/src/extensions.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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) {
Expand Down
14 changes: 10 additions & 4 deletions ui/desktop/src/hooks/useWhisper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
3 changes: 2 additions & 1 deletion ui/desktop/src/recipe/index.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -70,5 +71,5 @@ 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:');
}
11 changes: 9 additions & 2 deletions ui/desktop/src/sharedSessions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Message } from './types/message';
import { safeJsonParse } from './utils/jsonUtils';

export interface SharedSessionDetails {
share_token: string;
Expand Down Expand Up @@ -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}`);
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion ui/desktop/src/utils/askAI.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
}

Expand Down
11 changes: 10 additions & 1 deletion ui/desktop/src/utils/costDatabase.ts
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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(
Expand Down
6 changes: 5 additions & 1 deletion ui/desktop/src/utils/githubUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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}`);
Expand Down
13 changes: 13 additions & 0 deletions ui/desktop/src/utils/jsonUtils.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading