chore: import upstream snapshot with attribution
Ruff / Ruff (push) Has been cancelled
Test / Core Tests (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.10) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.11) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.12) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.13) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.9) (push) Has been cancelled
Test / Full Coverage (Python 3.11) (push) Has been cancelled
Test / Core Provider Tests (OpenAI) (push) Has been cancelled
Test / Core Provider Tests (Anthropic) (push) Has been cancelled
Test / Core Provider Tests (Google) (push) Has been cancelled
Test / Core Provider Tests (Other) (push) Has been cancelled
Test / Anthropic Tests (push) Has been cancelled
Test / Gemini Tests (push) Has been cancelled
Test / Google GenAI Tests (push) Has been cancelled
Test / Vertex AI Tests (push) Has been cancelled
Test / OpenAI Tests (push) Has been cancelled
Test / Writer Tests (push) Has been cancelled
Test / Auto Client Tests (push) Has been cancelled
ty / type-check (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:36:38 +08:00
commit 97e91a83f3
978 changed files with 159975 additions and 0 deletions
+385
View File
@@ -0,0 +1,385 @@
# Field Validation
This guide covers how to add validation to fields when extracting structured data with Instructor. Field validation ensures that your extracted data meets specific criteria and constraints.
## Why Field Validation Matters
Field validation helps you:
1. Ensure data quality and consistency
2. Enforce business rules
3. Prevent errors in downstream processing
4. Provide clear feedback for invalid data
Instructor uses Pydantic's validation system, which is applied automatically during extraction.
## Basic Field Constraints
You can add basic constraints to fields using Pydantic's `Field` function:
```python
from pydantic import BaseModel, Field
import instructor
from openai import OpenAI
client = instructor.from_provider("openai/gpt-5-nano")
class User(BaseModel):
name: str = Field(..., min_length=2, max_length=50)
age: int = Field(..., ge=0, le=120) # greater than or equal to 0, less than or equal to 120
email: str = Field(..., pattern=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
# Extract with validation
response = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": "I'm John Smith, 35 years old, with email john@example.com"}
],
response_model=User
)
```
Common Field constraints include:
| Constraint | Description | Example |
|------------|-------------|---------|
| `min_length` | Minimum string length | `min_length=2` |
| `max_length` | Maximum string length | `max_length=50` |
| `pattern` | Regex pattern to match | `pattern=r'^[0-9]+$'` |
| `gt` | Greater than | `gt=0` (for numbers) |
| `ge` | Greater than or equal | `ge=18` |
| `lt` | Less than | `lt=100` |
| `le` | Less than or equal | `le=120` |
| `min_items` | Minimum list items | `min_items=1` |
| `max_items` | Maximum list items | `max_items=10` |
For more information on field definitions, see the [Fields](../../concepts/fields.md) concepts page.
## Validation with Field Validators
For more complex validation logic, use Pydantic's `field_validator` decorator:
```python
from pydantic import BaseModel, Field, field_validator
import instructor
from openai import OpenAI
import re
client = instructor.from_provider("openai/gpt-5-nano")
class Product(BaseModel):
name: str
sku: str
price: float
@field_validator('name')
@classmethod
def validate_name(cls, v):
if len(v.strip()) < 3:
raise ValueError("Product name must be at least 3 characters")
return v.strip()
@field_validator('sku')
@classmethod
def validate_sku(cls, v):
if not re.match(r'^[A-Z]{3}-\d{4}$', v):
raise ValueError("SKU must be in format XXX-0000")
return v
@field_validator('price')
@classmethod
def validate_price(cls, v):
if v <= 0:
raise ValueError("Price must be greater than zero")
if v > 10000:
raise ValueError("Price exceeds maximum allowed value")
return v
# Extract validated data
response = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": "Product: Wireless Headphones, SKU: ABC-1234, Price: $79.99"}
],
response_model=Product
)
```
Field validators can:
- Perform complex validation logic
- Clean and normalize data
- Transform values
- Check values against external data sources
For more on custom validators, see the [Custom Validators](../validation/custom_validators.md) guide.
## Model-level Validation
Sometimes validation needs to check relationships between fields. For this, use `model_validator`:
```python
from pydantic import BaseModel, Field, model_validator
import instructor
from openai import OpenAI
from datetime import date
client = instructor.from_provider("openai/gpt-5-nano")
class DateRange(BaseModel):
start_date: date
end_date: date
@model_validator(mode='after')
def validate_date_range(self):
if self.end_date < self.start_date:
raise ValueError("End date must be after start date")
return self
```
## Validation in Nested Structures
You can apply validation at any level in nested structures:
```python
from pydantic import BaseModel, Field, field_validator
import instructor
from openai import OpenAI
from typing import List
client = instructor.from_provider("openai/gpt-5-nano")
class Address(BaseModel):
street: str
city: str
state: str
zip_code: str
@field_validator('state')
@classmethod
def validate_state(cls, v):
valid_states = {"CA", "NY", "TX", "FL"} # Example: just a few states
if v not in valid_states:
raise ValueError(f"State must be one of: {', '.join(valid_states)}")
return v
@field_validator('zip_code')
@classmethod
def validate_zip(cls, v):
if not v.isdigit() or len(v) != 5:
raise ValueError("ZIP code must be 5 digits")
return v
class Person(BaseModel):
name: str
addresses: List[Address] # Nested structure with validation
```
For more on nested structures, see the [Nested Structure](nested_structure.md) guide.
## List Item Validation
You can validate items in a list:
```python
from typing import List
from pydantic import BaseModel, Field, field_validator
import instructor
from openai import OpenAI
client = instructor.from_provider("openai/gpt-5-nano")
class TagList(BaseModel):
tags: List[str] = Field(..., min_items=1, max_items=5)
@field_validator('tags')
@classmethod
def validate_tags(cls, tags):
# Convert all tags to lowercase
tags = [tag.lower() for tag in tags]
# Check for minimum length of each tag
for tag in tags:
if len(tag) < 2:
raise ValueError("Each tag must be at least 2 characters")
# Check for duplicates
if len(tags) != len(set(tags)):
raise ValueError("Tags must be unique")
return tags
```
For more on lists, see the [List Extraction](list_extraction.md) guide.
## Using Enumerations for Validation
Enums provide a way to validate fields against a predefined set of values:
```python
from enum import Enum
from pydantic import BaseModel
import instructor
from openai import OpenAI
client = instructor.from_provider("openai/gpt-5-nano")
class Status(str, Enum):
PENDING = "pending"
APPROVED = "approved"
REJECTED = "rejected"
class Priority(str, Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
class Task(BaseModel):
title: str
description: str
status: Status # Must be one of the enum values
priority: Priority # Must be one of the enum values
# Extract with enum validation
response = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": "Task: Update website, Description: Refresh content on homepage, Status: pending, Priority: high"}
],
response_model=Task
)
```
For more information on enums, see the [Enums](../../concepts/enums.md) concepts page.
## Custom Error Messages
You can customize validation error messages for better feedback:
```python
from pydantic import BaseModel, Field
import instructor
from openai import OpenAI
client = instructor.from_provider("openai/gpt-5-nano")
class CreditCard(BaseModel):
number: str = Field(
...,
pattern=r'^\d{16}$',
json_schema_extra={"error_msg": "Credit card number must be exactly 16 digits"}
)
expiry_month: int = Field(
...,
ge=1,
le=12,
json_schema_extra={"error_msg": "Expiry month must be between 1 and 12"}
)
expiry_year: int = Field(
...,
ge=2023,
le=2030,
json_schema_extra={"error_msg": "Expiry year must be between 2023 and 2030"}
)
cvv: str = Field(
...,
pattern=r'^\d{3,4}$',
json_schema_extra={"error_msg": "CVV must be 3 or 4 digits"}
)
```
## Handling Validation Failures
When validation fails, Instructor will:
1. Capture the validation error
2. Add the error message to the context
3. Retry the request with this feedback (if retries are enabled)
To control retry behavior:
```python
client = instructor.from_provider(
"openai/gpt-4o",
max_retries=2, # Number of retries after the initial attempt
throw_error=True # Whether to raise an exception on validation failure
)
```
For more on retries, see the [Retry Mechanisms](../validation/retry_mechanisms.md) guide.
## Real-world Example: Form Data Validation
Here's a more complete example validating form inputs:
```python
from pydantic import BaseModel, Field, field_validator, model_validator
import instructor
import re
from datetime import date, datetime
from typing import Optional
client = instructor.from_provider("openai/gpt-5-nano")
class RegistrationForm(BaseModel):
username: str = Field(..., min_length=3, max_length=20)
email: str
password: str
confirm_password: str
birth_date: date
@field_validator('username')
@classmethod
def validate_username(cls, v):
if not re.match(r'^[a-zA-Z0-9_]+$', v):
raise ValueError("Username can only contain letters, numbers, and underscores")
return v
@field_validator('email')
@classmethod
def validate_email(cls, v):
if not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', v):
raise ValueError("Invalid email format")
return v
@field_validator('password')
@classmethod
def validate_password(cls, v):
if len(v) < 8:
raise ValueError("Password must be at least 8 characters")
if not re.search(r'[A-Z]', v):
raise ValueError("Password must contain at least one uppercase letter")
if not re.search(r'[a-z]', v):
raise ValueError("Password must contain at least one lowercase letter")
if not re.search(r'[0-9]', v):
raise ValueError("Password must contain at least one number")
return v
@field_validator('birth_date')
@classmethod
def validate_age(cls, v):
today = date.today()
age = today.year - v.year - ((today.month, today.day) < (v.month, v.day))
if age < 18:
raise ValueError("You must be at least 18 years old to register")
return v
@model_validator(mode='after')
def passwords_match(self):
if self.password != self.confirm_password:
raise ValueError("Passwords do not match")
return self
```
## Related Resources
- [Validation Basics](../validation/basics.md) - Core validation concepts
- [Custom Validators](../validation/custom_validators.md) - Creating custom validation logic
- [Field-level Validation](../validation/field_level_validation.md) - Advanced field validation
- [Retry Mechanisms](../validation/retry_mechanisms.md) - Handling validation failures
- [Fields](../../concepts/fields.md) - Understanding field definitions
- [Enums](../../concepts/enums.md) - Using enumeration types
## Next Steps
- Learn about [Optional Fields](optional_fields.md) for handling missing data
- Explore [Custom Validators](../validation/custom_validators.md) for complex validation
- Check out [Nested Structure](nested_structure.md) for complex data relationships
+291
View File
@@ -0,0 +1,291 @@
---
title: List Extraction from LLMs Tutorial
description: Master extracting multiple structured objects from language models using Instructor with type-safe list validation.
---
# List Extraction Tutorial: Extract Multiple Objects from LLMs
Master the art of extracting lists and arrays from LLMs in this comprehensive tutorial. Learn how to use Instructor to extract multiple structured objects from language models like GPT-4, Claude, and Gemini with type-safe validation.
## Basic List Extraction
To extract a list of items, you define a model for a single item and then use Python's typing system to specify you want a list of that type:
```python
from typing import List
from pydantic import BaseModel, Field
import instructor
# Initialize the client
client = instructor.from_provider("openai/gpt-5-nano")
# Define a single item model
class Person(BaseModel):
name: str = Field(..., description="The person's full name")
age: int = Field(..., description="The person's age in years")
# Define a wrapper model for the list
class PeopleList(BaseModel):
people: List[Person] = Field(..., description="List of people mentioned in the text")
# Extract the list
response = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": """
Here's information about some people:
- John Smith is 35 years old
- Mary Johnson is 28 years old
- Robert Davis is 42 years old
"""}
],
response_model=PeopleList
)
# Access the extracted data
for i, person in enumerate(response.people):
print(f"Person {i+1}: {person.name}, {person.age} years old")
```
This example shows how to:
1. Define a model for a single item (`Person`)
2. Create a wrapper model that contains a list of items (`PeopleList`)
3. Access each item in the list through the response
## Direct List Extraction
You can also extract a list directly without a wrapper model:
```python
from typing import List
from pydantic import BaseModel, Field
import instructor
client = instructor.from_provider("openai/gpt-5-nano")
class Book(BaseModel):
title: str
author: str
publication_year: int
# Extract a list directly
books = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": """
Classic novels:
1. To Kill a Mockingbird by Harper Lee (1960)
2. 1984 by George Orwell (1949)
3. The Great Gatsby by F. Scott Fitzgerald (1925)
"""}
],
response_model=List[Book] # Direct list extraction
)
# Access the extracted data
for book in books:
print(f"{book.title} by {book.author} ({book.publication_year})")
```
## Nested Lists
You can extract nested lists by combining list types:
```python
from typing import List
from pydantic import BaseModel, Field
import instructor
client = instructor.from_provider("openai/gpt-5-nano")
class Author(BaseModel):
name: str
nationality: str
class Book(BaseModel):
title: str
authors: List[Author] # Nested list of authors
publication_year: int
# Extract data with nested lists
books = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": """
Book 1: "Good Omens" (1990)
Authors: Terry Pratchett (British), Neil Gaiman (British)
Book 2: "The Talisman" (1984)
Authors: Stephen King (American), Peter Straub (American)
"""}
],
response_model=List[Book]
)
# Access the nested data
for book in books:
author_names = ", ".join([author.name for author in book.authors])
print(f"{book.title} ({book.publication_year}) by {author_names}")
```
## Using Streaming with Lists
You can stream list extraction results using Instructor's streaming capabilities:
```python
from typing import List
import instructor
from pydantic import BaseModel, Field
client = instructor.from_provider("openai/gpt-5-nano")
class Task(BaseModel):
description: str
priority: str
deadline: str
# Stream a list of tasks
for task in client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": "Generate a list of 5 sample tasks for a project manager"}
],
response_model=List[Task],
stream=True
):
print(f"Received task: {task.description} (Priority: {task.priority}, Deadline: {task.deadline})")
```
For more information on streaming, see the [Streaming Basics](../streaming/basics.md) and [Streaming Lists](../streaming/lists.md) guides.
## List Validation
You can add validation for both individual items and the entire list:
```python
from typing import List
from pydantic import BaseModel, Field, field_validator, model_validator
import instructor
client = instructor.from_provider("openai/gpt-5-nano")
class Product(BaseModel):
name: str
price: float
@field_validator('price')
@classmethod
def validate_price(cls, v):
if v <= 0:
raise ValueError("Price must be greater than zero")
return v
class ProductList(BaseModel):
products: List[Product] = Field(..., min_items=1)
@model_validator(mode='after')
def validate_unique_names(self):
names = [p.name for p in self.products]
if len(names) != len(set(names)):
raise ValueError("All product names must be unique")
return self
# Extract list with validation
response = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": "List of products: Headphones ($50), Speakers ($80), Earbuds ($30)"}
],
response_model=ProductList
)
```
For more on validation, see [Field Validation](./field_validation.md) and [Validation Basics](../validation/basics.md).
## List Constraints
You can add constraints to lists using Pydantic's Field:
```python
from typing import List
from pydantic import BaseModel, Field
import instructor
client = instructor.from_provider("openai/gpt-5-nano")
class Ingredient(BaseModel):
name: str
amount: str
class Recipe(BaseModel):
title: str
ingredients: List[Ingredient] = Field(
...,
min_items=2, # Minimum 2 ingredients
max_items=10, # Maximum 10 ingredients
description="List of ingredients needed for the recipe"
)
steps: List[str] = Field(
...,
min_items=1,
description="Step-by-step instructions to prepare the recipe"
)
```
## Real-world Example: Task Extraction
Here's a more complete example for extracting a list of tasks from a meeting transcript:
```python
from typing import List, Optional
from pydantic import BaseModel, Field
import instructor
from datetime import date
client = instructor.from_provider("openai/gpt-5-nano")
class Assignee(BaseModel):
name: str
email: Optional[str] = None
class ActionItem(BaseModel):
description: str = Field(..., description="The task that needs to be completed")
assignee: Assignee = Field(..., description="The person responsible for the task")
due_date: Optional[date] = Field(None, description="The deadline for the task")
priority: str = Field(..., description="Priority level: Low, Medium, or High")
# Extract action items from meeting notes
action_items = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": """
Meeting Notes - Project Kickoff
Date: 2023-05-15
Attendees: John (john@example.com), Sarah (sarah@example.com), Mike
Discussion points:
1. John will prepare the project timeline by next Friday. This is high priority.
2. Sarah needs to contact the client for requirements clarification by Wednesday. Medium priority.
3. Mike is responsible for setting up the development environment. Due by tomorrow, high priority.
"""}
],
response_model=List[ActionItem]
)
# Process the extracted action items
for item in action_items:
due_str = item.due_date.isoformat() if item.due_date else "Not specified"
print(f"Task: {item.description}")
print(f"Assignee: {item.assignee.name} ({item.assignee.email or 'No email'})")
print(f"Due: {due_str}, Priority: {item.priority}")
print("---")
```
For a more detailed example, see the [Action Items Extraction](../../examples/action_items.md) example.
## Related Resources
- [Simple Object Extraction](./simple_object.md) - Extracting single objects
- [Nested Structure](./nested_structure.md) - Working with complex nested data
- [Streaming Lists](../streaming/lists.md) - Streaming list results
- [Lists and Arrays](../../concepts/lists.md) - Concepts related to list extraction
## Next Steps
- Learn about [Nested Structure](./nested_structure.md) for complex data
- Explore [Streaming Lists](../streaming/lists.md) for handling large lists
- Check out [Field Validation](./field_validation.md) for validation techniques
+357
View File
@@ -0,0 +1,357 @@
---
title: Nested Structure Extraction with Instructor
description: Learn how to extract complex nested data structures from LLMs using hierarchical Pydantic models.
---
# Simple Nested Structure
This guide explains how to extract nested structured data using Instructor. Nested structures allow you to represent complex, hierarchical data relationships.
## Understanding Nested Structures
Nested structures are objects that contain other objects as fields. They're useful for representing:
1. Parent-child relationships
2. Complex entities with sub-components
3. Hierarchical data
4. Related data that belongs together
## Basic Nested Structure Example
Here's a simple example of extracting a nested structure:
```python
from pydantic import BaseModel, Field
import instructor
from typing import List, Optional
# Initialize the client
client = instructor.from_provider("openai/gpt-5-nano")
# Define nested models
class Address(BaseModel):
street: str
city: str
state: str
zip_code: str
class Person(BaseModel):
name: str
age: int
address: Address # Nested structure
# Extract the nested data
response = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": """
John Smith is 35 years old.
He lives at 123 Main Street, Boston, MA 02108.
"""}
],
response_model=Person
)
# Access the nested data
print(f"Name: {response.name}")
print(f"Age: {response.age}")
print(f"Address: {response.address.street}, {response.address.city}, "
f"{response.address.state} {response.address.zip_code}")
```
## Multiple Levels of Nesting
You can use multiple levels of nesting for more complex structures:
```python
from pydantic import BaseModel, Field
import instructor
from typing import List, Optional
client = instructor.from_provider("openai/gpt-5-nano")
class EmployeeDetails(BaseModel):
department: str
position: str
start_date: str
class ContactInfo(BaseModel):
phone: str
email: str
class Address(BaseModel):
street: str
city: str
state: str
zip_code: str
class Person(BaseModel):
name: str
age: int
contact: ContactInfo # First level nesting
address: Address # First level nesting
employment: Optional[EmployeeDetails] = None # Optional nested structure
# Extract deeply nested data
response = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": """
Employee Profile:
Name: Jane Doe
Age: 32
Phone: (555) 123-4567
Email: jane.doe@example.com
Address: 456 Oak Avenue, Chicago, IL 60601
Department: Engineering
Position: Senior Developer
Start Date: 2021-03-15
"""}
],
response_model=Person
)
```
## Nested Lists
You can combine nesting with lists to represent complex collections:
```python
from pydantic import BaseModel, Field
import instructor
from typing import List
client = instructor.from_provider("openai/gpt-5-nano")
class Ingredient(BaseModel):
name: str
amount: str
unit: str
class Recipe(BaseModel):
title: str
description: str
ingredients: List[Ingredient] # Nested list of ingredients
steps: List[str] # List of strings
# Extract nested list data
response = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": """
Recipe: Chocolate Chip Cookies
Description: Classic homemade chocolate chip cookies that are soft in the middle and crispy on the edges.
Ingredients:
- 2 1/4 cups all-purpose flour
- 1 teaspoon baking soda
- 1 teaspoon salt
- 1 cup butter
- 3/4 cup white sugar
- 3/4 cup brown sugar
- 2 eggs
- 2 teaspoons vanilla extract
- 2 cups chocolate chips
Instructions:
1. Preheat oven to 375°F (190°C)
2. Mix flour, baking soda, and salt
3. Cream butter and sugars, then add eggs and vanilla
4. Gradually add dry ingredients
5. Stir in chocolate chips
6. Drop by rounded tablespoons onto ungreased baking sheets
7. Bake for 9 to 11 minutes or until golden brown
8. Cool on wire racks
"""}
],
response_model=Recipe
)
```
For more information on working with lists, see the [List Extraction](list_extraction.md) guide.
## Handling Optional Nested Fields
Sometimes parts of a nested structure might be missing. Use Optional to handle this:
```python
from pydantic import BaseModel, Field
import instructor
from typing import Optional
client = instructor.from_provider("openai/gpt-5-nano")
class SocialMedia(BaseModel):
twitter: Optional[str] = None
linkedin: Optional[str] = None
instagram: Optional[str] = None
class ContactInfo(BaseModel):
email: str
phone: Optional[str] = None
social: Optional[SocialMedia] = None # Optional nested structure
class Person(BaseModel):
name: str
contact: ContactInfo
```
For more information on optional fields, see the [Optional Fields](optional_fields.md) guide.
## Nested Structure Validation
You can add validation to nested structures at any level:
```python
from pydantic import BaseModel, Field, field_validator, model_validator
import instructor
import re
client = instructor.from_provider("openai/gpt-5-nano")
class EmailContact(BaseModel):
email: str
@field_validator('email')
@classmethod
def validate_email(cls, v):
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
if not re.match(pattern, v):
raise ValueError("Invalid email format")
return v
class Customer(BaseModel):
name: str
contact: EmailContact # Nested structure with its own validation
@model_validator(mode='after')
def validate_name_email_match(self):
name_part = self.name.lower().split()[0]
if name_part not in self.contact.email.lower():
print(f"Warning: Email {self.contact.email} may not match name {self.name}")
return self
```
For more on validation, see [Field Validation](field_validation.md) and [Validation Basics](../validation/basics.md).
## Working with Recursive Structures
For more complex hierarchical data, you can use recursive structures:
```python
from typing import List, Optional
from pydantic import BaseModel, Field
import instructor
client = instructor.from_provider("openai/gpt-5-nano")
class Comment(BaseModel):
text: str
author: str
replies: List["Comment"] = [] # Recursive structure
# Update the Comment class reference for Pydantic
Comment.model_rebuild()
class Post(BaseModel):
title: str
content: str
author: str
comments: List[Comment] = []
# Extract recursive nested data
response = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": """
Blog Post: "Python Tips and Tricks"
Author: John Smith
Content: Here are some helpful Python tips for beginners...
Comments:
1. Alice: "Great post! Very helpful."
- Bob: "I agree, I learned a lot."
- Alice: "Bob, did you try the last example?"
- Charlie: "Thanks for sharing this."
2. David: "Could you explain the second tip more?"
- John: "Sure, I'll add more details."
"""}
],
response_model=Post
)
```
For more advanced recursive structures, see the [Recursive Structures](../../examples/recursive.md) guide.
## Real-world Example: Organization Structure
Here's a more complete example extracting an organization structure:
```python
from typing import List, Optional
from pydantic import BaseModel, Field
import instructor
client = instructor.from_provider("openai/gpt-5-nano")
class Employee(BaseModel):
name: str
title: str
class Department(BaseModel):
name: str
head: Employee
employees: List[Employee]
sub_departments: List["Department"] = []
# Update for Pydantic's recursive model support
Department.model_rebuild()
class Organization(BaseModel):
name: str
ceo: Employee
departments: List[Department]
# Extract organization structure
response = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": """
Acme Corporation
CEO: Jane Smith, Chief Executive Officer
Departments:
1. Engineering
Head: Bob Johnson, CTO
Employees:
- Sarah Lee, Senior Engineer
- Tom Brown, Software Developer
Sub-departments:
- Frontend Team
Head: Lisa Wang, Frontend Lead
Employees:
- Mike Chen, UI Developer
- Ana Garcia, UX Designer
- Backend Team
Head: David Kim, Backend Lead
Employees:
- James Wright, Database Engineer
- Rachel Patel, API Developer
2. Marketing
Head: Michael Davis, CMO
Employees:
- Jennifer Miller, Marketing Specialist
- Robert Chen, Content Creator
"""}
],
response_model=Organization
)
```
## Related Resources
- [Simple Object Extraction](./simple_object.md) - Extracting basic objects
- [List Extraction](./list_extraction.md) - Working with lists of objects
- [Optional Fields](./optional_fields.md) - Handling optional data
- [Recursive Structures](../../examples/recursive.md) - Building more complex hierarchies
- [Field Validation](./field_validation.md) - Adding validation to your fields
+191
View File
@@ -0,0 +1,191 @@
---
title: Working with Optional Fields in Instructor
description: Learn how to use optional fields in Pydantic models to handle missing or uncertain information from LLM outputs.
---
# Optional Fields
This guide explains how to work with optional fields in your data models. Optional fields allow the model to skip fields when information is unavailable or uncertain.
## Why Use Optional Fields?
Optional fields are useful when:
1. Some information is missing from the input text
2. Certain fields are only relevant in specific contexts
3. The LLM can't confidently extract all fields
4. You want to allow partial success instead of complete failure
## Basic Optional Fields
To make a field optional, use Python's `Optional` type and provide a default value:
```python
from typing import Optional
from pydantic import BaseModel
import instructor
client = instructor.from_provider("openai/gpt-5-nano")
class Person(BaseModel):
name: str # Required field
age: Optional[int] = None # Optional field with None default
occupation: Optional[str] = None # Optional field with None default
```
Here, `name` is required, while `age` and `occupation` are optional and will default to `None` if not found.
## Using Default Values
You can provide meaningful default values for optional fields:
```python
from typing import List
from pydantic import BaseModel
import instructor
client = instructor.from_provider("openai/gpt-5-nano")
class Product(BaseModel):
name: str
price: float
currency: str = "USD" # Default value
in_stock: bool = True # Default value
tags: List[str] = [] # Default empty list
```
## Optional Fields with Validation
You can add the `Field` class for more control and validation:
```python
from typing import Optional
from pydantic import BaseModel, Field
import instructor
client = instructor.from_provider("openai/gpt-5-nano")
class UserProfile(BaseModel):
username: str
email: str
bio: Optional[str] = Field(
None, # Default value
max_length=200, # Validation applies if present
description="User's biography, limited to 200 characters"
)
```
## Optional Nested Structures
Entire nested structures can be optional:
```python
from typing import Optional
from pydantic import BaseModel
import instructor
client = instructor.from_provider("openai/gpt-5-nano")
class Address(BaseModel):
street: str
city: str
state: str
zip_code: str
class Contact(BaseModel):
email: str
phone: Optional[str] = None
address: Optional[Address] = None # Optional nested structure
class Person(BaseModel):
name: str
contact: Contact
```
When using nested optional structures, check if they exist before accessing:
```python
# Access nested data safely
if person.contact.address:
print(f"Address: {person.contact.address.city}")
else:
print("No address information available")
```
## Using `Maybe` for Uncertain Fields
Instructor provides a `Maybe` type for uncertain or ambiguous fields:
```python
from pydantic import BaseModel
import instructor
from instructor.types import Maybe
client = instructor.from_provider("openai/gpt-5-nano")
class PersonInfo(BaseModel):
name: str
age: Maybe[int] = None # Maybe type for uncertain fields
```
Check if a `Maybe` field contains uncertain information:
```python
if person.age and person.age.is_uncertain:
print(f"Uncertain age: approximately {person.age.value}")
elif person.age:
print(f"Age: {person.age.value}")
else:
print("Age: Unknown")
```
For more about the `Maybe` type, see the [Missing Concepts](../../concepts/maybe.md) page.
## Handling Optional Values
Always handle the possibility of `None` values in your code:
```python
# Check for None before using
if person.age is not None:
drinking_age = "Legal" if person.age >= 21 else "Underage"
else:
drinking_age = "Unknown"
# Use conditional expressions
price_display = f"${product.price}" if product.price is not None else "Price unavailable"
# Provide defaults with 'or'
display_name = user.nickname or user.username
```
## Validation with Optional Fields
Optional fields can still have validation when they're present:
```python
from typing import Optional
from pydantic import BaseModel, field_validator
import instructor
import re
client = instructor.from_provider("openai/gpt-5-nano")
class ContactInfo(BaseModel):
email: str
phone: Optional[str] = None
@field_validator('phone')
@classmethod
def validate_phone(cls, v):
if v is not None and not re.match(r'^\+?[1-9]\d{1,14}$', v):
raise ValueError("Invalid phone format")
return v
```
## Related Resources
- [Simple Object Extraction](./simple_object.md) - Extracting basic objects
- [Field Validation](./field_validation.md) - Adding validation to fields
- [Nested Structure](./nested_structure.md) - Working with complex data
- [Missing Concepts](../../concepts/maybe.md) - Using the Maybe type for uncertain fields
## Next Steps
- Learn about [Field Validation](./field_validation.md)
- Explore [Nested Structure](./nested_structure.md) for complex data
- Check out [Prompt Templates](./prompt_templates.md) for crafting prompts
+174
View File
@@ -0,0 +1,174 @@
---
title: Using Prompt Templates with Instructor
description: Learn how to create reusable prompt templates for consistent structured output extraction across different use cases.
---
# Prompt Templates
This guide covers how to use prompt templates with Instructor to create reusable, parameterized prompts for structured data extraction.
## Why Prompt Templates Matter
Good prompts are essential for effective structured data extraction. Prompt templates help you:
1. Create consistent and reusable prompts
2. Parameterize prompts with dynamic values
3. Separate prompt engineering from application logic
4. Standardize prompt patterns for different use cases
## Basic Prompt Templates
The simplest form of a prompt template is a string with placeholders for variables:
```python
from pydantic import BaseModel, Field
import instructor
client = instructor.from_provider("openai/gpt-5-nano")
class Person(BaseModel):
name: str
age: int
occupation: str
# Define a template with parameters
prompt_template = """
Extract information about the person mentioned in the following {document_type}:
{content}
Please provide their name, age, and occupation.
"""
# Use the template with specific values
document_type = "email"
content = "Hi team, I'm introducing our new project manager, Sarah Johnson. She's 34 and has been in project management for 8 years."
prompt = prompt_template.format(
document_type=document_type,
content=content
)
# Extract structured data using the formatted prompt
response = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": prompt}
],
response_model=Person
)
```
## Using f-strings for Simple Templates
For simple cases, you can use f-strings to create prompt templates:
```python
def extract_person(content, document_type="text"):
prompt = f"""
Extract information about the person mentioned in the following {document_type}:
{content}
Please provide their name, age, and occupation.
"""
return client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": prompt}
],
response_model=Person
)
# Use the function
person = extract_person(
"According to his resume, John Smith (42) works as a software developer.",
document_type="resume"
)
```
## Template Functions
For more complex templates, create dedicated template functions:
```python
from typing import List, Optional
from pydantic import BaseModel
import instructor
client = instructor.from_provider("openai/gpt-5-nano")
class ProductReview(BaseModel):
product_name: str
rating: int
pros: List[str]
cons: List[str]
summary: str
def create_review_extraction_prompt(
review_text: str,
product_category: str,
include_sentiment: bool = False
) -> str:
sentiment_instruction = """
Also include a brief sentiment analysis of the review.
""" if include_sentiment else ""
return f"""
Extract product review information from the following {product_category} review:
{review_text}
Please identify:
- The name of the product being reviewed
- The numerical rating (1-5)
- A list of pros/positive points
- A list of cons/negative points
- A brief summary of the review
{sentiment_instruction}
"""
# Use the template function
review_text = """
I recently purchased the UltraSound X300 headphones, and I'm mostly satisfied.
The sound quality is amazing and the battery lasts for days. They're also very
comfortable to wear for long periods. However, they're a bit pricey at $299, and
the Bluetooth occasionally disconnects. Overall, I'd give them 4 out of 5 stars.
"""
prompt = create_review_extraction_prompt(
review_text=review_text,
product_category="headphone",
include_sentiment=True
)
review = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": prompt}
],
response_model=ProductReview
)
```
## Best Practices for Prompt Templates
1. **Be explicit about the output format**: Clearly specify what fields you need and in what format
2. **Use consistent language**: Maintain consistent terminology throughout the template
3. **Keep it concise**: Avoid unnecessary verbosity that could confuse the model
4. **Parameterize only what varies**: Only make template parameters for parts that need to change
5. **Include examples for complex tasks**: Provide few-shot examples for more complex extractions
6. **Test with different inputs**: Ensure your template works well with a variety of inputs
## Related Resources
- [Simple Object Extraction](./simple_object.md) - Extracting basic objects
- [List Extraction](./list_extraction.md) - Working with lists of objects
- [Optional Fields](./optional_fields.md) - Handling optional data
- [Prompting](../../concepts/prompting.md) - General prompting concepts
- [Templating](../../concepts/templating.md) - Advanced template techniques
## Next Steps
- Explore [Field Validation](./field_validation.md) for ensuring data quality
- Try [List Extraction](./list_extraction.md) for extracting multiple items
- Learn about [Nested Structure](./nested_structure.md) for complex data
+145
View File
@@ -0,0 +1,145 @@
---
title: Simple Object Extraction Pattern
description: Learn the fundamental pattern of extracting simple objects from text using Instructor with type-safe validation.
---
# Simple Object Extraction: LLM Tutorial for Structured Data
Learn how to extract structured objects from text using LLMs in this comprehensive tutorial. We'll cover the fundamental pattern of transforming unstructured text into validated Python objects using Instructor with GPT-4, Claude, and other language models.
## Basic LLM Object Extraction Tutorial
```python
from pydantic import BaseModel
import instructor
# Define your LLM extraction schema
class Person(BaseModel):
name: str
age: int
occupation: str
# Extract structured data from LLM
client = instructor.from_provider("openai/gpt-5-nano")
person = client.create(
model="gpt-5.4-mini", # Works with GPT-4, Claude, Gemini
messages=[
{"role": "user", "content": "John Smith is a 35-year-old software engineer."}
],
response_model=Person # Type-safe LLM extraction
)
print(f"Name: {person.name}")
print(f"Age: {person.age}")
print(f"Occupation: {person.occupation}")
```
```
┌───────────────┐ ┌───────────────┐
│ Define Model │ │ Extracted │
│ name: str │ Extract │ name: "John" │
│ age: int │ ─────────> │ age: 35 │
│ occupation: str│ │ occupation: │
└───────────────┘ │ "software..." │
└───────────────┘
```
## Enhance LLM Extraction with Field Descriptions
Guide your LLM with clear field descriptions for more accurate extraction:
```python
from pydantic import BaseModel, Field
class Book(BaseModel):
title: str = Field(description="The full title of the book")
author: str = Field(description="The author's full name")
publication_year: int = Field(description="The year the book was published")
```
Field descriptions serve as prompts for the LLM, improving extraction accuracy and reducing errors in your structured outputs.
## Handle Missing Data in LLM Responses
Real-world LLM extractions often encounter missing information. Here's how to handle it gracefully:
```python
from typing import Optional
from pydantic import BaseModel
class MovieReview(BaseModel):
title: str
director: Optional[str] = None # Optional field
rating: float
```
Using `Optional` fields ensures your LLM extraction remains robust when dealing with incomplete or partial information.
## Validate LLM Outputs with Pydantic
Ensure LLM outputs meet your requirements with built-in validation:
```python
from pydantic import BaseModel, Field
class Product(BaseModel):
name: str
price: float = Field(gt=0, description="The product price in USD")
in_stock: bool
```
Pydantic validation ensures your LLM outputs are not just structured, but also correct and business-rule compliant.
## Production-Ready LLM Extraction Example
Here's a complete example showing nested object extraction from LLMs:
```python
from pydantic import BaseModel
from typing import Optional
class Address(BaseModel):
street: str
city: str
state: str
zip_code: str
class ContactInfo(BaseModel):
name: str
email: str
phone: Optional[str] = None
address: Optional[Address] = None
# Extract structured data
client = instructor.from_provider("openai/gpt-5-nano")
contact = client.create(
model="gpt-5.4-mini",
messages=[
{"role": "user", "content": """
Contact information:
Name: Sarah Johnson
Email: sarah.j@example.com
Phone: (555) 123-4567
Address: 123 Main St, Boston, MA 02108
"""}
],
response_model=ContactInfo
)
print(f"Name: {contact.name}")
print(f"Email: {contact.email}")
```
## Common LLM Object Extraction Use Cases
- **Contact Information**: Extract names, emails, phones from unstructured text
- **Product Details**: Parse product descriptions into structured catalogs
- **Event Information**: Extract dates, locations, attendees from event descriptions
- **Entity Recognition**: Identify and structure people, places, organizations
## Continue Your LLM Tutorial Journey
- **[List Extraction Tutorial](list_extraction.md)** - Extract multiple objects from LLM responses
- **[Nested Structures](nested_structure.md)** - Handle complex hierarchical data from LLMs
- **[Advanced Validation](field_validation.md)** - Implement business rules for LLM outputs
Master these patterns to build production-ready LLM applications with reliable structured outputs!