Structured Outputs by Example Star on GitHub
Stay updated when new content is added and get tips from the Instructor team
Validation

Fallback Strategies

Edit

When working with LLMs, it's important to have fallback strategies for handling persistent failures or unexpected issues. Instructor provides several ways to implement robust fallback mechanisms.
import instructor
from openai import OpenAI
from pydantic import BaseModel, Field, ValidationError
from instructor.exceptions import InstructorRetryException

Initialize the client with instructor
client = instructor.from_openai(OpenAI())

Define primary model with strict validation
class DetailedUserProfile(BaseModel):
    name: str = Field(description="User's full name")
    age: int = Field(description="User's age in years", ge=18)
    occupation: str = Field(description="User's job or profession")
    income: int = Field(description="User's annual income in USD", ge=0)
    education: str = Field(description="User's highest education level")

Define simpler fallback model
class BasicUserProfile(BaseModel):
    name: str = Field(description="User's name")
    age: int = Field(description="User's age", ge=0)

Try extraction with fallback strategy
def extract_user_with_fallback(text: str):
    try:
        return client.chat.completions.create(  # First attempt with detailed model
            model="gpt-3.5-turbo",
            messages=[
                {
                    "role": "user",
                    "content": f"Extract user information: {text}"
                }
            ],
            response_model=DetailedUserProfile,
            max_retries=2
        )
    except InstructorRetryException:
        print("Detailed extraction failed, falling back to basic profile")
        return client.chat.completions.create(  # Fall back to simpler model
            model="gpt-3.5-turbo",
            messages=[
                {
                    "role": "user",
                    "content": f"Extract basic user information: {text}"
                }
            ],
            response_model=BasicUserProfile,
            max_retries=1
        )

Example usage
text = "John is 25 years old"
user = extract_user_with_fallback(text)
print(user.model_dump_json(indent=2))

Another approach is to use optional fields for less reliable information:
from typing import Optional
import instructor
from openai import OpenAI
from pydantic import BaseModel, Field

Initialize the client with instructor
client = instructor.from_openai(OpenAI())

Define model with optional fields
class FlexibleProfile(BaseModel):
    name: str = Field(description="Person's name")
    age: Optional[int] = Field(None, description="Person's age if mentioned")
    location: Optional[str] = Field(None, description="Person's location if mentioned")
    occupation: Optional[str] = Field(None, description="Person's job if mentioned")

Extract what's available without failing
profile = client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[
        {
            "role": "user",
            "content": "Sarah is a software engineer from Boston"
        }
    ],
    response_model=FlexibleProfile
)

print(profile.model_dump_json(indent=2))

For critical applications, you can implement a more comprehensive fallback strategy:
import instructor
from openai import OpenAI
from pydantic import BaseModel, Field, ValidationError
from enum import Enum

Initialize the client with instructor
client = instructor.from_openai(OpenAI())

Define extraction result status
class ExtractionStatus(str, Enum):
    SUCCESS = "success"
    PARTIAL = "partial"
    FAILED = "failed"

Define target model
class Contact(BaseModel):
    name: str
    email: str
    phone: str

Define wrapper for extraction result
class ExtractionResult(BaseModel):
    status: ExtractionStatus
    data: dict
    error_message: str = ""

Robust extraction function with fallbacks
def extract_with_robustness(text: str) -> ExtractionResult:
    try:
        result = client.chat.completions.create(  # Primary extraction attempt
            model="gpt-3.5-turbo",
            messages=[{"role": "user", "content": f"Extract contact info: {text}"}],
            response_model=Contact,
            max_retries=2
        )
        return ExtractionResult(
            status=ExtractionStatus.SUCCESS,
            data=result.model_dump()
        )

Attempt to salvage partial data when extraction fails
except InstructorRetryException as e:
        try:
            partial_data = {}
            error_msg = e.messages[-1]["content"]  # Parse the error message
            text_lines = text.split('\n')
            for line in text_lines:
                if "name:" in line.lower():
                    partial_data["name"] = line.split("name:")[1].strip()
                if "email:" in line.lower():
                    partial_data["email"] = line.split("email:")[1].strip()
                if "phone:" in line.lower():
                    partial_data["phone"] = line.split("phone:")[1].strip()

            if partial_data:
                return ExtractionResult(
                    status=ExtractionStatus.PARTIAL,
                    data=partial_data,
                    error_message=error_msg
                )
            else:
                return ExtractionResult(
                    status=ExtractionStatus.FAILED,
                    data={},
                    error_message=error_msg
                )
        except Exception as nested_error:
            return ExtractionResult(
                status=ExtractionStatus.FAILED,
                data={},
                error_message=f"Complete extraction failure: {str(nested_error)}"
            )

Running the Example

First, install Instructor and any dependencies
$ pip install instructor pydantic
Run the Python script
$ python fallback-strategies.py

Further Information