Debug the below code for me as an expert - 
import openai
from typing_extensions import TypedDict  # Use this for Python < 3.12from pydantic import BaseModel, Field
from io import BytesIO
from PIL import Image
from IPython.display import display

# Set your OpenAI API key
openai.api_key = "sk-"

# Define the structure for BP fields
class OutputField(TypedDict):
    name: str
    description: str
    type: str
    source: str
    required: bool

class BPState(TypedDict):
    goal: str
    output_fields: List[OutputField]
    examples: List[str]
    improved_goal: str
    grokked_output_fields: List[OutputField]
    generated_examples: List[str]
    chosen_prompt: str

# Define structured models for OpenAI's LLM function calls

class ImprovedGoal(BaseModel):
    """Structured output for improved goal."""
    improved_goal: str = Field(description="The improved or rephrased version of the user's goal.")

class GrokkedOutputFields(BaseModel):
    """Structured output for grokked output fields."""
    grokked_output_fields: List[OutputField] = Field(description="The enhanced output fields list.")

class GeneratedExamples(BaseModel):
    """Structured output for generated examples."""
    examples: List[str] = Field(description="Generated examples based on the goal and output fields.")

class ChosenPrompt(BaseModel):
    """Structured output for chosen prompt."""
    chosen_prompt: str = Field(description="The most complete prompt chosen for inference.")

# Define the OpenAI LLM interactions

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

# 1. Improve Goal
def improve_goal(state: BPState) -> BPState:
    """Improve or rephrase the goal using OpenAI API."""
    goal_system = """You are a goal improvement agent. Improve the following goal."""
    goal_prompt = ChatPromptTemplate.from_messages(
        [
            ("system", goal_system),
            ("human", "Goal: {goal}")
        ]
    )
    structured_llm_improver = llm.with_structured_output(ImprovedGoal)
    goal_improvement_chain = goal_prompt | structured_llm_improver
    result = goal_improvement_chain.invoke({"goal": state["goal"]})
    state["goal"] = result.improved_goal  # Replace original with improved goal
    return state

# 2. Auto Grok Output Fields
def auto_grok_output_fields(state: BPState) -> BPState:
    """Auto grok output fields using OpenAI API."""
    grok_system = """You are a field augmentation agent. Suggest output fields based on the goal."""
    grok_prompt = ChatPromptTemplate.from_messages(
        [
            ("system", grok_system),
            ("human", "Goal: {goal}")
        ]
    )
    structured_llm_grok = llm.with_structured_output(GrokkedOutputFields)
    grok_chain = grok_prompt | structured_llm_grok
    result = grok_chain.invoke({"goal": state["goal"]})
    state["output_fields"] = result.grokked_output_fields  # Replace or enhance original fields
    return state

# 3. Generate Examples
def generate_examples(state: BPState) -> BPState:
    """Generate examples using OpenAI API."""
    examples_system = """You are an example generation agent. Create example inputs and outputs for the following goal and fields."""
    examples_prompt = ChatPromptTemplate.from_messages(
        [
            ("system", examples_system),
            ("human", "Goal: {goal}, Output Fields: {output_fields}")
        ]
    )
    structured_llm_examples = llm.with_structured_output(GeneratedExamples)
    examples_chain = examples_prompt | structured_llm_examples
    result = examples_chain.invoke({
        "goal": state["goal"],
        "output_fields": state["output_fields"]
    })
    state["examples"] = result.examples  # Replace original examples
    return state

# 4. Generate Candidate Prompts (with structured outputs)
def generate_candidate_prompts(state: BPState) -> List[str]:
    """Generate three candidate prompts using structured output."""
    prompt_system = """You are a prompt generator. Create a complete prompt based on the following goal, output fields, and examples."""
    candidates = []
    structured_llm_prompt_gen = llm.with_structured_output(ChosenPrompt)
    
    for i in range(3):  # Generate 3 candidate prompts
        prompt_template = ChatPromptTemplate.from_messages(
            [
                ("system", prompt_system),
                ("human", f"Goal: {state['goal']}, Output Fields: {state['output_fields']}, Examples: {state['examples']}")
            ]
        )
        prompt_chain = prompt_template | structured_llm_prompt_gen
        result = prompt_chain.invoke({
            "goal": state["goal"],
            "output_fields": state["output_fields"],
            "examples": state["examples"]
        })
        candidates.append(result.chosen_prompt)
    
    return candidates

# 5. Evaluate Prompts
def evaluate_prompts(candidates: List[str]) -> str:
    """Select the best prompt using a heuristic."""
    # For now, we're using length as a heuristic; replace with G-Eval later
    best_prompt = max(candidates, key=len)
    return best_prompt

# Overall Workflow
def run_blueprint_workflow(goal: str, output_fields: List[OutputField], examples: List[str]) -> str:
    """Run the BP workflow and return the chosen prompt."""
    # Initial state with user inputs
    state: BPState = {
        "goal": goal,
        "output_fields": output_fields,
        "examples": examples,
        "improved_goal": "",
        "grokked_output_fields": [],
        "generated_examples": [],
        "chosen_prompt": ""
    }
    
    # Step 1: Improve the Goal
    state = improve_goal(state)
    
    # Step 2: Auto Grok Output Fields
    state = auto_grok_output_fields(state)
    
    # Step 3: Generate Examples
    state = generate_examples(state)
    
    # Step 4: Generate Candidate Prompts
    candidates = generate_candidate_prompts(state)
    
    # Step 5: Evaluate and choose the best prompt
    state["chosen_prompt"] = evaluate_prompts(candidates)
    
    return state["chosen_prompt"]

# Example Input Data
goal_input = "Create an AI agent that helps with task scheduling based on user availability."
output_fields_input = [
    {
        "name": "task_name",
        "description": "The name of the task to schedule.",
        "type": "string",
        "source": "user input",
        "required": True
    },
    {
        "name": "scheduled_time",
        "description": "The time at which the task is scheduled.",
        "type": "datetime",
        "source": "calendar",
        "required": True
    }
]
examples_input = [
    "Input: Schedule a meeting for tomorrow at 10 AM. Output: 'task_name': 'meeting', 'scheduled_time': '2023-10-23T10:00:00'.",
    "Input: Set a reminder for next Monday. Output: 'task_name': 'reminder', 'scheduled_time': '2023-10-30T09:00:00'."
]

# Running the workflow with the input data
chosen_prompt = run_blueprint_workflow(goal_input, output_fields_input, examples_input)

# Output the chosen prompt
print("Chosen Prompt for Inference:")
print(chosen_prompt)