Skip to content

feat: first-class structured output support in AiClient - #4

Merged
roboshyim merged 1 commit into
mainfrom
feat/structured-output
Jul 21, 2026
Merged

feat: first-class structured output support in AiClient#4
roboshyim merged 1 commit into
mainfrom
feat/structured-output

Conversation

@roboshyim

Copy link
Copy Markdown
Collaborator

What

Exposes a clean, typed, provider-agnostic structured-output API on the AiClient facade (P0 roadmap item #4). The ChatRequest value object and Capability::StructuredOutput already existed — this wires them into a usable public API so features (property suggestion, SEO JSON, review summaries) and external plugin devs can request validated JSON without hand-rolling schema plumbing.

Public API

$response = $aiClient->chatStructured($messages, $schema, $options);
if ($response->isValid()) {
    $data = $response->data;          // decoded array
    $title = $response->get('title'); // convenience accessor
} else {
    $error = $response->error;        // why it failed
}
$raw = $response->raw; // underlying ChatResponse (usage, finishReason)

Implementation

  • AiClient::chatStructured(array $messages, array $schema, array $options = []): StructuredResponse — builds a ChatRequest with responseSchema, routes via ModelResolver to a StructuredOutput-capable provider, parses the result.
  • StructuredResponse (src/Response/) — typed wrapper: ->data, ->raw, ->error, ->isValid(), ->get(key, default).
  • StructuredOutputParser (src/Client/) — robust extraction: strips ```json markdown fences, pulls the first valid JSON object/array from prose, graceful failure (returns invalid response, never fatals).

Tests

tests/Unit/Client/StructuredOutputTest.php (286 lines): schema passthrough to ChatRequest, clean JSON, fenced JSON, invalid JSON → invalid response, facade routing. Suite green in container (Shopware 6.7.2.2 / PHP 8.3).

Complements the Anthropic provider PR (#3), which implements the tool-use side of structured output.

Expose chatStructured() on the AiClient facade so features can request
validated JSON without hand-rolling schema plumbing:

- AiClient::chatStructured(messages, schema, options) builds a
  ChatRequest with responseSchema + Capability::StructuredOutput, routes
  through the normal provider resolution, and returns a typed
  StructuredResponse (data, raw ChatResponse, isValid/error state).
- StructuredOutputParser strips markdown fences and extracts the first
  balanced JSON object/array from free text; failures yield an invalid
  StructuredResponse instead of throwing.
- Minimal structural validation of the decoded payload against the
  schema's top-level type (no new dependencies).
- OpenAI already maps responseSchema to response_format json_schema
  (strict); Gemini maps to responseMimeType + responseSchema.
@roboshyim
roboshyim force-pushed the feat/structured-output branch from f82e3ee to 5fc4c34 Compare July 21, 2026 16:45
@roboshyim
roboshyim merged commit 819af10 into main Jul 21, 2026
2 checks passed
@roboshyim
roboshyim deleted the feat/structured-output branch July 21, 2026 17:00
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