anthropics / anthropics/anthropic-sdk-python

output_config.format cannot take the pydantic model that output_format deprecates, and the dict workaround silently skips transform_schema

Offen
#1,819 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
Vorherrschende Sprache
Python
Sterne
3.9k
Forks
853
Ø Merge
1 T. 11 Std.
Gemergte PRs (30 T.)
10

Beschreibung

## Summary

On `anthropic==0.121.0`, passing `output_format=` to the beta messages resource emits

> `DeprecationWarning: The 'output_format' parameter is deprecated. Please use 'output_config.format' instead.`
> — `resources/beta/messages/messages.py:4229`

but `output_config.format` **cannot accept a pydantic model**, which is the input form
`output_format=` exists for. Following the warning literally raises `TypeError`, and the
natural workaround (hand it `Model.model_json_schema()` as a dict) silently **skips
`transform_schema`** — so migrating changes the schema that goes on the wire.

## Repro

Executed as written against `anthropic==0.121.0` / `pydantic==2.13.4`, with
`httpx.Client.send` patched to capture the request body and return a 401 (no API key,
nothing leaves the machine).

```python
class Item(BaseModel):
code: str = Field(pattern=r'^[A-Z]{3}$', min_length=3)
```

**A — the deprecated form.** Warns, and `transform_schema` runs:

```json
{"type":"object","title":"Item",
"properties":{"code":{"type":"string","title":"Code",
"description":"{minLength: 3, pattern: ^[A-Z]{3}$}"}},
"additionalProperties":false,"required":["code"]}
```

**B — the migration the warning names.** `output_config={"format": Item}`:

```
TypeError: Object of type ModelMetaclass is not JSON serializable
```

**C — the workaround**, `output_config={"format": {"type":"json_schema","schema": Item.model_json_schema()}}`:

```json
{"properties":{"code":{"minLength":3,"pattern":"^[A-Z]{3}$","title":"Code","type":"string"}},
"required":["code"],"title":"Item","type":"object"}
```

C is **not** A. `additionalProperties: false` is gone, and `minLength`/`pattern` are no
longer folded into `description`. So a user who follows the deprecation notice stops
getting the normalization the SDK applies today, with no error and no warning.

## Cause

`transform_schema` runs only on the `elif` branch of

```python
if is_dict(output_format): # beta messages.py:1769, :3778
transformed_output_format = cast(...) # cast through, untransformed
elif is_given(output_format) and output_format is not None:
schema = adapted_type.json_schema()
transformed_output_format = JSONOutputFormatParam(schema=transform_schema(schema), ...)
```

`output_config` has no equivalent branch at all — its `format` is forwarded as given, so
every form of it is untransformed. Measured across `create` / `parse` / `stream` and both
the beta and non-beta resources: `output_config.format` never transforms.

Two smaller notes from the same measurement:

- The DeprecationWarning fires on the **beta** resource only. `messages.parse`,
`messages.stream` and `messages.count_tokens` on the non-beta resource accept
`output_format=` silently, so the two resources disagree about whether this parameter
is deprecated.
- `count_tokens(output_format=)` is also untransformed while
`count_tokens(output_format=)` is transformed, so a token count taken via the
dict form is counting a different document than the one `parse()` would send.

## Suggested shape of a fix

Either route `output_config.format` through the same branch (accept a type there and
transform it), or state in the deprecation message that `output_config.format` takes a
pre-built `json_schema` dict and applies no transform — the current wording implies a
drop-in rename, and it is not one.

## Test-design warning

A regression test that asserts only "the request was built" cannot see this — the
request builds fine in both A and C. It has to assert on the **emitted schema**. And a
fixture whose model carries no constrained fields is weaker than it looks: it will still
differ on `additionalProperties`, but nothing else, so the demotion half of the change
goes untested. Use a field with `pattern=`/`min_length=` as above.

## Scope

No API key was used, so this is entirely about the request the SDK builds; I am not
claiming anything about how the service treats either payload.

Beitragsleitfaden

Beitragsleitfaden öffnen

Rechercherichtung

Start in resources/beta/messages/messages.py at the referenced lines around 1769, 3778, and 4229, then compare output_config.format with output_format across create, parse, stream, and count_tokens in both resources. Use the constrained Pydantic model from the report and assert on the emitted schema; done means the migration path and its warning behavior are consistent without silently changing transformation.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python
Bereich
api, testing
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Ruhig
Klarheit
Klar beschrieben
Anfängerfreundlichkeit
58/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.