Phase 1: SDK swap — the actual code diff
Both SDKs now accept the same `messages: [{role, content}]` array shape. The differences: method name, response structure, and how system prompts are passed. Here's the canonical diff for a simple completion.
```python # OpenAI (before) from openai import OpenAI client = OpenAI() resp = client.chat.completions.create( model="gpt-5.5", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Summarize: ..."} ], max_tokens=500, ) answer = resp.choices[0].message.content ```
```python # Anthropic (after) import anthropic client = anthropic.Anthropic() resp = client.messages.create( model="claude-sonnet-4-6", max_tokens=500, system="You are a helpful assistant.", messages=[ {"role": "user", "content": "Summarize: ..."} ], ) answer = resp.content[0].text ```
Three differences. **System prompt is its own field** on Claude (`system=`), not a message in the array. **`max_tokens` is required** on Claude (OpenAI defaults to model max). **Response shape** is `resp.content[0].text` (an array of content blocks) instead of `resp.choices[0].message.content`.
Streaming works the same way conceptually — both expose `with client.<x>.stream(...)` context managers. Anthropic's events are more structured (`content_block_start`, `content_block_delta`, `content_block_stop`) where OpenAI's stream is a flat sequence of completion chunks. Most production code just iterates the stream and concatenates `delta.text` (Claude) or `delta.content` (OpenAI) — both work.