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
- Langage dominant
- Python
- Étoiles
- 3.9k
- Forks
- 853
- Merge moyen
- 1 j 11 h
- PR mergées (30 j)
- 10
Description
## 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.
Guide de contribution
Ouvrir le guide de contribution
Piste de recherche
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.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- python
- Domaine
- api, testing
- Type d'issue
- Bug
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Activité
- Calme
- Clarté
- Clairement spécifiée
- Accessibilité débutants
- 58/100