Skip to content

fix: strip markdown code fences from structured output before decoding#754

Merged
pushpak1300 merged 2 commits into
laravel:0.xfrom
vaishnavyogesh:fix/strip-structured-output-fences
Jun 30, 2026
Merged

fix: strip markdown code fences from structured output before decoding#754
pushpak1300 merged 2 commits into
laravel:0.xfrom
vaishnavyogesh:fix/strip-structured-output-fences

Conversation

@vaishnavyogesh

Copy link
Copy Markdown
Contributor

Strip markdown code fences from structured output before decoding

Problem

Some models return structured output JSON wrapped in a markdown fence (e.g. json\n{...}\n). Every gateway decoded with a bare json_decode($text, true) ?? [], so the wrapped payload silently became [].

Fix

  • New trait Laravel\Ai\Gateway\Concerns\DecodesStructuredOutput exposes decodeStructuredOutput(?string $text): array, which strips a wrapping ```json / ``` fence (entire-payload match only, with optional whitespace/newlines) and then decodes.
  • Trait is wired into every gateway parser that produces structured output: Xai, OpenAi, OpenRouter, Ollama, Mistral, Groq, Gemini, DeepSeek, Anthropic (fallback branch), and replacing Bedrock's private decodeStructuredOutput.

Safety

  • Regex is anchored to the start/end of the trimmed payload, so JSON strings containing triple-backtick substrings are not mangled.
  • Non-array decode results (scalars/strings) still return [], matching prior semantics.
  • Streaming structured output is already unsupported, so no streaming paths touched.

Tests

tests/Unit/Gateway/Concerns/DecodesStructuredOutputTest.php — 9 cases covering:

  • plain JSON, ```json fence, bare ``` fence, uppercase JSON
  • leading/trailing whitespace and newlines around the fence
  • invalid JSON, null/empty input
  • partial-fence safety (triple-backticks inside string values)
  • non-array scalar decodes

@pushpak1300

Copy link
Copy Markdown
Member

can you send me curl request to reproduce this with any provider ?

@vaishnavyogesh

vaishnavyogesh commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

can you send me curl request to reproduce this with any provider ?

curl --location 'https://api.fastrouter.ai/api/v1/chat/completions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer API_KEY' \
--data '{
    "model": "anthropic/claude-haiku-4.5",
    "messages": [
      {
        "role": "system",
        "content": "You are a helpful assistant that responds in JSON format. The weather in london is 20 degree celsius."
      },
      {
        "role": "user",
        "content": "What is the weather like in London?"
      }
    ],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "weather",
        "strict": true,
        "schema": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "City or location name"
            },
            "temperature": {
              "type": "number",
              "description": "Temperature in Celsius"
            },
            "conditions": {
              "type": "string",
              "description": "Weather conditions description"
            }
          },
          "required": ["location", "temperature", "conditions"],
          "additionalProperties": false
        }
      }
    }
}'

Response:

{
    "choices": [
        {
            "message": {
                "role": "assistant",
                "content": "```json\n{\n  \"location\": \"London\",\n  \"temperature\": 20,\n  \"conditions\": \"Clear\"\n}\n```",
                "annotations": null
            },
            "finish_reason": "stop",
            "index": 0
        }
    ],
    ...
 }

I'm using fastrouter provider behind openrouter gateway, if possible can you check if it's getting reproduced when prompting claude's haiku model directly through Anthropic gateway?

@pushpak1300 pushpak1300 merged commit 1b47f18 into laravel:0.x Jun 30, 2026
7 checks passed
@vaishnavyogesh

Copy link
Copy Markdown
Contributor Author

Hey @pushpak1300, were you able to reproduce it consistently?

@vaishnavyogesh vaishnavyogesh deleted the fix/strip-structured-output-fences branch July 1, 2026 08:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants