Skip to content
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
18 changes: 15 additions & 3 deletions src/lib/models/providers/ollama/ollamaLLM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ import crypto from 'crypto';
import { Message } from '@/lib/types';
import { repairJson } from '@toolsycc/json-repair';

/**
* Strip markdown code block wrappers from LLM responses.
* Some models (e.g. Claude via OpenAI-compatible proxies) wrap JSON output in
* markdown code blocks (```json ... ```), which causes JSON.parse to fail.
*/
function stripCodeBlockWrappers(text: string): string {
return text
.replace(/^```(?:json)?\s*\n?/i, '')
.replace(/\n?```\s*$/i, '');
}

type OllamaConfig = {
baseURL: string;
model: string;
Expand Down Expand Up @@ -208,9 +219,10 @@ class OllamaLLM extends BaseLLM<OllamaConfig> {
try {
return input.schema.parse(
JSON.parse(
repairJson(response.message.content, {
extractJson: true,
}) as string,
repairJson(
stripCodeBlockWrappers(response.message.content),
{ extractJson: true },
) as string,
),
) as T;
} catch (err) {
Expand Down
20 changes: 16 additions & 4 deletions src/lib/models/providers/openai/openaiLLM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ import {
import { Message } from '@/lib/types';
import { repairJson } from '@toolsycc/json-repair';

/**
* Strip markdown code block wrappers from LLM responses.
* Some models (e.g. Claude via OpenAI-compatible proxies) wrap JSON output in
* markdown code blocks (```json ... ```), which causes JSON.parse to fail.
*/
function stripCodeBlockWrappers(text: string): string {
return text
.replace(/^```(?:json)?\s*\n?/i, '')
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Code-block stripping misses leading whitespace/newline before opening fence, so markdown-wrapped JSON can still fail to parse.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/lib/models/providers/openai/openaiLLM.ts, line 30:

<comment>Code-block stripping misses leading whitespace/newline before opening fence, so markdown-wrapped JSON can still fail to parse.</comment>

<file context>
@@ -20,6 +20,17 @@ import {
+ */
+function stripCodeBlockWrappers(text: string): string {
+  return text
+    .replace(/^```(?:json)?\s*\n?/i, '')
+    .replace(/\n?```\s*$/i, '');
+}
</file context>
Suggested change
.replace(/^```(?:json)?\s*\n?/i, '')
.replace(/^\s*```(?:json)?\s*\n?/i, '')
Fix with Cubic

.replace(/\n?```\s*$/i, '');
}

type OpenAIConfig = {
apiKey: string;
model: string;
Expand Down Expand Up @@ -216,9 +227,10 @@ class OpenAILLM extends BaseLLM<OpenAIConfig> {
try {
return input.schema.parse(
JSON.parse(
repairJson(response.choices[0].message.content!, {
extractJson: true,
}) as string,
repairJson(
stripCodeBlockWrappers(response.choices[0].message.content!),
{ extractJson: true },
) as string,
),
) as T;
} catch (err) {
Expand Down Expand Up @@ -263,7 +275,7 @@ class OpenAILLM extends BaseLLM<OpenAIConfig> {
}
} else if (chunk.type === 'response.output_text.done' && chunk.text) {
try {
yield parse(chunk.text) as T;
yield parse(stripCodeBlockWrappers(chunk.text)) as T;
} catch (err) {
throw new Error(`Error parsing response from OpenAI: ${err}`);
}
Expand Down