Skip to content

Fix xAI driver compatibility with OpenAI-compatible endpoints#964

Open
meirdick wants to merge 1 commit intoprism-php:mainfrom
meirdick:fix/xai-workers-ai-compat
Open

Fix xAI driver compatibility with OpenAI-compatible endpoints#964
meirdick wants to merge 1 commit intoprism-php:mainfrom
meirdick:fix/xai-workers-ai-compat

Conversation

@meirdick
Copy link

Problem

The xAI driver doesn't work with OpenAI-compatible /chat/completions endpoints like Cloudflare Workers AI's /compat endpoint. Two issues:

1. User messages always use array content format

MessageMap serializes all user messages as:

{"role": "user", "content": [{"type": "text", "text": "Hello"}]}

Some OpenAI-compatible endpoints only accept string content:

{"role": "user", "content": "Hello"}

The array format causes the model to receive garbled input and respond with nonsense, or reject the request with: "Type mismatch of '/messages/0/content', 'array' not in 'string'".

2. Structured handler crashes when content is returned as object

The structured handler expects choices.0.message.content to be a string (per the OpenAI spec), but some providers return it as a parsed object/array:

// Expected (xAI/OpenAI):
{"content": "{\"title\":\"Inception\"}", "parsed": {"title": "Inception"}}

// Actual (Workers AI):
{"content": {"title": "Inception"}}

This crashes on new AssistantMessage($content) with: TypeError: Argument #1 ($content) must be of type string, array given.

Fix

  1. MessageMap: Use plain string content when no images are present, array format only when images exist. Both formats are valid per the OpenAI spec.

  2. Structured handler: Normalize array content to a JSON string via json_encode(), and use it as the parsed data when no parsed field is present.

Both changes are backward-compatible with xAI's native API — verified by all existing tests passing.

Testing

  • All 49 existing xAI tests pass
  • Added new test: it('handles content returned as object instead of string') covering the Workers AI response format
  • Updated MessageMapTest to expect string content for text-only messages

Context

Relates to #520. The xAI driver is commonly used with Cloudflare Workers AI (via the /compat endpoint) because it sends to /chat/completions rather than /responses. These two fixes make that combination work reliably for text generation and structured output.

Two changes to improve compatibility with OpenAI-compatible providers
(e.g. Cloudflare Workers AI) that use the xAI driver:

1. MessageMap: Use plain string content for text-only user messages,
   array content format only when images are present. Some /chat/completions
   endpoints only accept string content and reject the array format with
   errors like "Type mismatch of '/messages/0/content', 'array' not in 'string'".

2. Structured handler: Handle content returned as a parsed object/array
   instead of a JSON string. Some providers return structured output
   directly as an object in the content field rather than a JSON-encoded
   string with a separate parsed field. The handler now normalizes this
   by json_encoding array content and using it as the parsed data when
   no parsed field is present.

Both changes are backward-compatible with xAI's native API.
@meirdick
Copy link
Author

Note: The Text handler (Text.php:78, :155) also reads choices.0.message.content and passes it to AssistantMessage. In testing, text generation responses from OpenAI-compatible endpoints always return content as a string, so this isn't currently an issue. But if a provider ever returns array content for non-structured responses, it would hit the same TypeError. Happy to add the same is_array() guard there if you'd like — kept the PR minimal for now.

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.

1 participant