---
name: instructor-v1-api
source: https://app.decimal.ai/s/instructor-v1-api@1/SKILL.md
source_sha256: c8f4fbc2b0f1
---

# Instructor (1.0) API idiom

## Contract

Enforces the CURRENT (1.0) Instructor public API whenever you write Python that uses the `instructor`
library to get structured, Pydantic-validated output from an LLM. The pre-1.0 idiom
(`instructor.patch(client)`, hand-parsed JSON, hand-rolled retries) is deprecated; emit the 1.0 form
below. Apply to any request to "use instructor" / "extract structured data with instructor."

## Rules

1. **Create the client with a `from_` factory — never `patch()`, never a raw client.** Use
   `instructor.from_provider("openai/gpt-4o-mini")` (the unified form: `"<provider>/<model>"`), or a
   provider factory `instructor.from_openai(...)` / `instructor.from_anthropic(...)` around the provider
   client. The pre-1.0 `instructor.patch(client)` is deprecated. A raw, unwrapped `OpenAI()` /
   `Anthropic()` client does NOT accept `response_model` — it must be wrapped first.

2. **Pass the target Pydantic class through `response_model=`.** That keyword carries the class:
   `response_model=User`. It is NOT `response_format=` (that is OpenAI's own JSON-mode flag), NOT
   `output_model=`, NOT `schema=`, NOT `pydantic_model=`.

3. **The target is a `pydantic.BaseModel` subclass, and the call returns the validated instance.** The
   create call returns an already-parsed, already-validated model object — read fields straight off it
   (`result.name`, `result.address.city`). Do NOT `json.loads(response.choices[0].message.content)` or
   index into the raw completion; Instructor has already parsed and validated it for you.

4. **Get automatic re-asking with `max_retries=`, not a hand-written loop.** On a Pydantic
   `ValidationError`, Instructor re-sends the error to the model and asks again, up to `max_retries`
   times (default 3): `response_model=Event, max_retries=3`. Never wrap the call in your own
   `for _ in range(3): try/except` loop — `max_retries=` is the built-in mechanism and feeds the
   validation error back to the model.

5. **Stream a single growing object with `create_partial(...)`.** For one object whose fields fill in
   progressively, call `client.chat.completions.create_partial(...)` (or `client.messages.create_partial`
   / `client.create_partial`) and iterate the returned generator — each item is a more-complete instance.
   Do NOT set `stream=True` and stitch JSON chunks together yourself.

6. **Stream many objects with `create_iterable(...)`.** For a sequence of objects delivered one at a
   time, call `create_iterable(...)` and iterate it (equivalently, set `response_model=Iterable[Item]`).
   Do NOT parse one combined blob and split it by hand.

## Worked examples

Client creation (BEFORE = deprecated pre-1.0 → AFTER = 1.0):
```python
# BEFORE
import instructor, openai
client = instructor.patch(openai.OpenAI())      # deprecated monkey-patch
# AFTER
import instructor
client = instructor.from_provider("openai/gpt-4o-mini")
# or, wrapping an existing client:
client = instructor.from_openai(openai.OpenAI())
```

Extraction + reading fields (BEFORE = raw SDK + manual parse → AFTER = 1.0):
```python
# BEFORE
raw = openai_client.chat.completions.create(model=..., messages=msgs)
data = json.loads(raw.choices[0].message.content)   # manual, unvalidated
name = data["name"]
# AFTER
obj = client.chat.completions.create(response_model=User, messages=msgs)
name = obj.name                                     # already a validated User
```

Automatic retries (BEFORE = hand-rolled loop → AFTER = 1.0):
```python
# BEFORE
for _ in range(3):
    try: obj = extract(); break
    except ValidationError: continue
# AFTER
obj = client.chat.completions.create(response_model=Event, max_retries=3, messages=msgs)
```

Streaming a single partial object vs a sequence:
```python
# one growing object
for partial in client.chat.completions.create_partial(response_model=Report, messages=msgs):
    print(partial.title)          # fills in as it streams
# many objects, one at a time
for item in client.chat.completions.create_iterable(response_model=Task, messages=msgs):
    handle(item)
```

## Edge cases

- **Local / OpenAI-compatible servers (Ollama, vLLM, LM Studio):** still wrap the OpenAI client —
  `instructor.from_openai(OpenAI(base_url="http://localhost:11434/v1", api_key="ollama"))`. Never
  `patch()`; never pass the raw client to a `response_model=` call.
- **`from_provider` string:** the argument is `"<provider>/<model>"`, e.g. `"anthropic/claude-3-5-sonnet-latest"`;
  `from_provider` dispatches to the right provider factory for you.
- **Iterable via `response_model`:** `response_model=Iterable[Task]` is the equivalent of `create_iterable`;
  both stream a sequence — pick one, don't hand-split a combined response.
- **Get the raw completion too:** use `create_with_completion(...)`, which returns
  `(parsed_model, raw_completion)` — still never `json.loads` the parsed side.
- **Async:** `instructor.from_provider(..., async_client=True)` then `await client...create(...)` /
  `async for` the partial/iterable stream.

## Do / Don't

- DO build the client with `from_provider(...)` / `from_openai(...)` / `from_anthropic(...)`.
  DON'T `instructor.patch(client)` or pass a raw unwrapped client to a `response_model=` call.
- DO pass the class as `response_model=Model`. DON'T use `response_format=`, `output_model=`, or `schema=`.
- DO read fields off the returned object (`obj.field`). DON'T `json.loads(response.choices[0].message.content)`.
- DO enable retries with `max_retries=N`. DON'T write your own try/except retry loop.
- DO stream one object with `create_partial(...)`. DON'T `stream=True` + manual chunk assembly.
- DO stream many with `create_iterable(...)` / `response_model=Iterable[...]`. DON'T split a combined blob.

## Common mistakes

- Monkey-patching with `instructor.patch(OpenAI())` — deprecated; use a `from_` factory.
- Passing a raw `OpenAI()` / `Anthropic()` client and expecting `response_model=` to work — it must be wrapped.
- Using `response_format=` (OpenAI's JSON flag) instead of Instructor's `response_model=`.
- Calling `json.loads(response.choices[0].message.content)` — the create call already returns a parsed instance.
- Wrapping the call in a manual `for/try/except` retry loop instead of `max_retries=`.
- Setting `stream=True` and assembling JSON by hand instead of `create_partial` / `create_iterable`.

## Quick checklist

- [ ] Client built with `from_provider` / `from_openai` / `from_anthropic` — not `patch()` or a raw client.
- [ ] Pydantic class passed via `response_model=` (not `response_format=` / `schema=`).
- [ ] Target is a `pydantic.BaseModel`; fields read directly off the returned instance (no `json.loads`).
- [ ] Automatic re-ask via `max_retries=` — no hand-rolled retry loop.
- [ ] Single growing object streamed with `create_partial`; sequence with `create_iterable` / `Iterable[...]`.
