anthropics / anthropics/anthropic-sdk-php

Bedrock: body round-trip flattens empty JSON objects ({} -> []), so a no-argument tool_use gets a 400

Abierto
#70 0 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
PHP
Estrellas
180
Forks
54
Merge medio
12 h 17 min
PR fusionados (30 d)
6

Descripción

**Environment**

- `anthropic-ai/sdk` v0.42.0 (latest; the code is unchanged on `main`)
- PHP 8.3, Bedrock transport (`Anthropic\Bedrock`), EU inference profile for Claude Sonnet

**Summary**

`RequestTransformer::decodedBody()` decodes the request body with `json_decode($body, true)` ([src/Core/RequestTransformer.php#L98](https://github.com/anthropics/anthropic-sdk-php/blob/main/src/Core/RequestTransformer.php#L98)). A PHP associative array cannot represent an empty JSON object. When `serialize()` re-encodes the body ([#L162](https://github.com/anthropics/anthropic-sdk-php/blob/main/src/Core/RequestTransformer.php#L162)), every `{}` in the request becomes `[]`.

The direct Anthropic API path does not show this: the transformer re-encodes only when the body was modified (`bodyDirty`). But `BedrockMiddleware` always rewrites the body (it moves `model` into the URL and injects `anthropic_version`), so on Bedrock every request is re-encoded and every empty object flattens.

**Impact**

A multi-turn conversation that replays an assistant `tool_use` block with an empty `input` (any tool that takes no arguments) is rejected by Bedrock with a 400 whose validation error points at `messages.N.content.M.tool_use.input`. The same request succeeds on `api.anthropic.com`.

The same flattening hits every other empty object in the body, for example a no-parameter tool's `input_schema.properties: {}` or an empty `metadata: {}`.

This makes the failure intermittent and hard to diagnose in an agent loop: simple turns work, and only a turn whose history contains a no-argument tool call fails.

**Repro**

The core defect in isolation:

```php
$body = '{"messages":[{"role":"assistant","content":[{"type":"tool_use","id":"toolu_1","name":"list_stores","input":{}}]}]}';
echo json_encode(json_decode($body, true));
// ...,"input":[]}]}]} — Bedrock rejects this
```

Full path: send a Messages request through the Bedrock client with a conversation history that contains an assistant `tool_use` block whose `input` is `{}`. Bedrock returns a 400; the direct API accepts the identical request.

**Suggested fix**

Decode without `assoc` and keep nested values as `stdClass`:

```php
$decoded = (array) json_decode((string) $stream, false);
```

The transformer reads and writes only top-level params (`model`, `anthropic_version`, `anthropic_beta`, `stream`), so the top-level array cast keeps the current API, and nested `stdClass` values re-encode as `{}` correctly. (`BedrockMiddleware`'s `base64_encode(json_encode($r->getBody(), ...))` for streaming also re-encodes nested objects correctly then.)

**Workaround we use**

A PSR-18 decorator on the Bedrock transporter restores the structural `"input":[]` back to `"input":{}` and then re-signs the request with SigV4 — the middleware signs before the transporter runs, so any body change after it must re-sign or AWS rejects the payload hash.

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.