Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Internal server error: Tool error: Error converting serde json to string. (Run ID: codestoryai_aide_issue_1331_1a9ce545) #1332

Open
wants to merge 1 commit into
base: cs-main
Choose a base branch
from
Open
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
16 changes: 14 additions & 2 deletions extensions/codestory/src/chatState/convertStreamToMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,24 @@ export const reportProcUpdateToChat = (
}
};

function safeJSONParse(text: string): any {
try {
text = text.replace(/^\uFEFF/, '').trim();
return JSON.parse(text);
} catch (error) {
console.error('Failed to parse JSON:', error);
console.error('Raw content:', text);
throw error;
}
}

export const readJsonFile = (filePath: string): any => {
const jsonString = fs.readFileSync(filePath, 'utf-8');
return JSON.parse(jsonString);
return safeJSONParse(jsonString);
};



// const randomInt = (min: number, max: number) =>
// Math.floor(Math.random() * (max - min + 1)) + min;

Expand Down Expand Up @@ -843,4 +855,4 @@ class DocumentManager {
}
return index + 2;
}
}
}
90 changes: 50 additions & 40 deletions extensions/codestory/src/sidecar/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@ import { ConversationMessage, EditFileResponse, getSideCarModelConfiguration, Id
import { sidecarUsesAgentReasoning } from '../utilities/agentConfiguration';
import { readAideRulesContent } from '../utilities/aideRules';

function safeJSONParse(text: string): any {
try {
// Remove any potential BOM characters and whitespace
text = text.replace(/^\uFEFF/, '').trim();
// Handle double-encoded JSON if needed
if (text.startsWith('"') && text.endsWith('"')) {
text = JSON.parse(text);
}
return JSON.parse(text);
} catch (error) {
console.error('Failed to parse JSON:', error);
console.error('Raw content:', text);
throw error;
}
}

export enum CompletionStopReason {
/**
* Used to signal to the completion processing code that we're still streaming.
Expand Down Expand Up @@ -153,15 +169,15 @@ export class SideCarClient {
const url = baseUrl.toString();
const asyncIterableResponse = await callServerEventStreamingBufferedGET(url);
for await (const line of asyncIterableResponse) {
const lineParts = line.split('data:{');
for (const lineSinglePart of lineParts) {
const lineSinglePartTrimmed = lineSinglePart.trim();
if (lineSinglePartTrimmed === '') {
continue;
}
const finalString = '{' + lineSinglePartTrimmed;
const syncUpdate = JSON.parse(finalString) as SyncUpdate;
if (!line.includes('data:')) continue;

try {
const jsonStr = line.substring(line.indexOf('data:') + 5).trim();
const syncUpdate = safeJSONParse(jsonStr) as SyncUpdate;
yield syncUpdate;
} catch (parseError) {
console.error('Failed to parse sync update:', parseError);
continue;
}
}
}
Expand Down Expand Up @@ -243,21 +259,15 @@ export class SideCarClient {
};
const asyncIterableResponse = await callServerEventStreamingBufferedPOST(url, body);
for await (const line of asyncIterableResponse) {
const lineParts = line.split('data:{');
for (const lineSinglePart of lineParts) {
const lineSinglePartTrimmed = lineSinglePart.trim();
if (lineSinglePartTrimmed === '') {
continue;
}
try {
// Attempt to parse JSON and yield
const editFileResponse = JSON.parse('{' + lineSinglePartTrimmed) as EditFileResponse;
yield editFileResponse;
} catch (parseError) {
// Log problematic content and error details
console.error(`Failed to parse JSON. Raw content: '{${lineSinglePartTrimmed}'`);
console.error('Parsing error details:', parseError);
}
if (!line.includes('data:')) continue;

try {
const jsonStr = line.substring(line.indexOf('data:') + 5).trim();
const editFileResponse = safeJSONParse(jsonStr) as EditFileResponse;
yield editFileResponse;
} catch (parseError) {
console.error('Failed to parse edit file response:', parseError);
continue;
}
}
}
Expand Down Expand Up @@ -671,14 +681,13 @@ export class SideCarClient {
stopReason: CompletionStopReason.RequestAborted,
};
}
const lineParts = line.split('data:"{');
for (const lineSinglePart of lineParts) {
const lineSinglePartTrimmed = lineSinglePart.trim();
if (lineSinglePartTrimmed === '') {
continue;
}
const finalString = '{' + lineSinglePartTrimmed.slice(0, -1);
const editFileResponse = JSON.parse(JSON.parse(`"${finalString}"`)) as CompletionResponse;
if (!line.includes('data:')) continue;

try {
const jsonStr = line.substring(line.indexOf('data:') + 5).trim();
// Handle double-encoded JSON from completion endpoint
const decodedStr = JSON.parse(jsonStr);
const editFileResponse = safeJSONParse(decodedStr) as CompletionResponse;
// take the first provided completion here
if (editFileResponse.completions.length > 0) {
finalAnswer = editFileResponse.completions[0].insertText;
Expand Down Expand Up @@ -1182,14 +1191,15 @@ export class SideCarClient {

const asyncIterableResponse = callServerEventStreamingBufferedPOST(url, body);
for await (const line of asyncIterableResponse) {
const lineParts = line.split('data:{');
for (const lineSinglePart of lineParts) {
const lineSinglePartTrimmed = lineSinglePart.trim();
if (lineSinglePartTrimmed === '') {
continue;
}
const conversationMessage = JSON.parse('{' + lineSinglePartTrimmed) as SideCarAgentEvent;
yield conversationMessage;
if (!line.includes('data:')) continue;

try {
const jsonStr = line.substring(line.indexOf('data:') + 5).trim();
const message = safeJSONParse(jsonStr) as SideCarAgentEvent;
yield message;
} catch (parseError) {
console.error('Failed to parse user feedback event:', parseError);
continue;
}
}
}
Expand Down Expand Up @@ -2227,4 +2237,4 @@ function getCurrentActiveWindow(): {
end_line: endPosition.line + 1,
language: activeWindow.document.languageId,
};
}
}
84 changes: 49 additions & 35 deletions extensions/codestory/src/sidecar/ssestream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,40 +30,54 @@ export async function* callServerEvent(url: string): AsyncIterableIterator<strin
}

class BufferedStream {
private _buffer: string[];

constructor() {
this._buffer = [];
}

public transform(chunk: string): string[] {
const finalAnswer: string[] = [];

for (let i = 0, len = chunk.length; i < len; ++i) {

// axum sends \n\n as the separator between events
// log when we have a hit for this
let isEventSeparator = false;
if (i !== 0) {
isEventSeparator = chunk[i] === '\n' && chunk[i - 1] === '\n';
}

// Keep buffering unless we've hit the end of an event
if (!isEventSeparator) {
this._buffer.push(chunk[i]);
continue;
}

const event = this._buffer.join('');

if (event) {
finalAnswer.push(event);
}

this._buffer = [];
}
return finalAnswer;
}
private _buffer: string[];
private _currentEvent: string[];

constructor() {
this._buffer = [];
this._currentEvent = [];
}

public transform(chunk: string): string[] {
const finalAnswer: string[] = [];

for (let i = 0, len = chunk.length; i < len; ++i) {
// Handle line endings
if (chunk[i] === '\n') {
const line = this._buffer.join('');
this._buffer = [];

// Skip empty lines
if (!line.trim()) {
// Empty line marks end of event
if (this._currentEvent.length > 0) {
const event = this._currentEvent.join('\n');
if (event.includes('data:')) {
finalAnswer.push(event);
}
this._currentEvent = [];
}
continue;
}

this._currentEvent.push(line);
continue;
}

this._buffer.push(chunk[i]);
}

// Handle any remaining buffer at the end of chunk
if (this._buffer.length > 0) {
const line = this._buffer.join('');
if (line.trim()) {
this._currentEvent.push(line);
}
this._buffer = [];
}

return finalAnswer;
}
}

export async function* callServerEventStreamingBufferedGET(url: string): AsyncIterableIterator<string> {
Expand Down Expand Up @@ -138,4 +152,4 @@ export async function* callServerEventStreamingBufferedPOST(url: string, body: a
} finally {
reader.releaseLock();
}
}
}
4 changes: 2 additions & 2 deletions extensions/codestory/src/storage/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const saveCodeStoryStorageObjectToStorage = async (
await ensureDirectoryExists(pathForStorage);
const codeStoryStorageString = JSON.stringify(codeStoryStorage);
fs.writeFileSync(pathForStorage, codeStoryStorageString);
return JSON.parse(codeStoryStorageString) as CodeStoryStorage;
return parseCodeStoryStorage(codeStoryStorageString);
};

export const loadOrSaveToStorage = async (
Expand All @@ -99,4 +99,4 @@ export const loadOrSaveToStorage = async (
return storage;
}
return saveCodeStoryStorageToStorage(globalStorageUri, workingDirectory);
};
};
16 changes: 14 additions & 2 deletions extensions/codestory/src/utilities/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,21 @@
import * as path from 'path';
import * as fs from 'fs';

// Helper function to safely parse JSON
function safeJSONParse(text: string): any {
try {
text = text.replace(/^\uFEFF/, '').trim();
return JSON.parse(text);
} catch (error) {
console.error('Failed to parse JSON:', error);
console.error('Raw content:', text);
throw error;
}
}

// Read and return data from sample.json file in working directory
export const readJSONFromFile = () => {
const filePath = path.join(__dirname, '../../sample.json');
const fileData = fs.readFileSync(filePath, 'utf-8');
return JSON.parse(fileData);
};
return safeJSONParse(fileData);
};
18 changes: 15 additions & 3 deletions extensions/codestory/src/utilities/workspaceContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,22 @@ export class ProjectContext {
this.contentIndicators.set(fileName, indicatorFunction);
}

function safeJSONParse(text: string): any {
try {
text = text.replace(/^\uFEFF/, '').trim();
return JSON.parse(text);
} catch (error) {
console.error('Failed to parse JSON:', error);
console.error('Raw content:', text);
throw error;
}
}

collectPackageJsonIndicators(fileContent: string): string[] {
const labels = [];
const parsedContent = JSON.parse(fileContent);
const dependencies = parsedContent.dependencies;
try {
const parsedContent = safeJSONParse(fileContent);
const dependencies = parsedContent.dependencies;
const devDependencies = parsedContent.devDependencies;
if (dependencies) {
if (dependencies['@angular/core']) {
Expand All @@ -122,4 +134,4 @@ export class ProjectContext {
}
return labels;
}
}
}