#4657 Skill Markdown Files with Pydantic Class(es) Included

#4657 Skill Markdown Files with Pydantic Class(es) Included

#4657 Can a class be created dynamically from a text string in Python?

Yes, absolutely! You can create a class dynamically from a text string in Python. Depending on your safety requirements and use case, there are two primary ways to do this:

1. Using type() (The Native & Clean Approach)

If you already know the attributes and methods you want to attach, Python’s built-in type() function lets you construct classes on the fly without evaluating raw strings.

When called with three arguments—type(name, bases, dict)—it creates a brand new class:

# Define a method to attach to the new class
def speak(self):
    return f"Hello, I am {self.name}!"

# Dynamically create the class named 'Dog'
class_name = "Dog"
attributes = {
    "species": "Canine",
    "__init__": lambda self, name: setattr(self, "name", name),
    "bark": speak
}

# Syntax: type(className, superclasses, attributeDict)
DynamicDog = type(class_name, (object,), attributes)

# Usage
my_dog = DynamicDog("Buddy")
print(my_dog.bark())     # Output: Hello, I am Buddy!
print(my_dog.species)    # Output: Canine

2. Using exec() (Executing Code from a String)

If you have a complete class definition written out as a literal string of Python code, you can execute it using exec().

class_code = """
class DynamicCar:
    def __init__(self, make, model):
        self.make = make
        self.model = model

    def drive(self):
        return f"Vroom! Driving a {self.make} {self.model}."
"""

# Create a dictionary to act as the local scope
namespace = {}

# Execute the code string within that namespace
exec(class_code, namespace)

# Retrieve the class object from the namespace
DynamicCar = namespace["DynamicCar"]

# Usage
my_car = DynamicCar("Tesla", "Model 3")
print(my_car.drive())  # Output: Vroom! Driving a Tesla Model 3.

Comparison & Safety

MethodBest ForSecurity Risk
type()Constructing classes programmatically with known structuresLow
exec()Parsing raw Python source code stringsHigh (if handling untrusted input)

Warning: Be extremely cautious when using exec() with unvalidated input from users, API requests, or external files, as it allows arbitrary code execution.


Create a class for use by Pydantic

When working with Pydantic, the best way to construct a class dynamically depends on what you are starting with:

  1. pydantic.create_model: Use this if you are building fields programmatically (using Python types, defaults, or Field()).

  2. exec() with BaseModel: Use this if you have a literal Python code string containing a class definition.

Method 1: Using pydantic.create_model (Recommended)

Pydantic provides a built-in helper called create_model specifically designed for dynamic model creation.

Fields are passed as keyword arguments using tuples formatted as (type, default_value) or (type, Field(...)). Use ... (Ellipsis) or Field(..., ...) to denote a required field.

from pydantic import Field, create_model

# 1. Define fields programmatically
# Syntax: field_name=(type, default_or_Field)
fields = {
    "username": (str, ...),  # Required
    "age": (int, Field(default=18, ge=0)),  # Optional with default and validation
    "is_active": (bool, True),  # Optional with default
}

# 2. Dynamically create the Pydantic class
DynamicUser = create_model("DynamicUser", **fields)

# 3. Instantiate and validate
user = DynamicUser(username="alice", age=25)
print(user.model_dump())
# Output: {'username': 'alice', 'age': 25, 'is_active': True}

Method 2: From a String of Python Code (Using exec)

If your class definition is supplied as a raw code string, you can execute it within a local namespace where BaseModel is accessible.

from pydantic import BaseModel

class_code = """
class DynamicItem(BaseModel):
    name: str
    price: float
    in_stock: bool = True

    def summary(self) -> str:
        return f"{self.name} costs ${self.price:.2f}"
"""

# Provide Pydantic's BaseModel in the execution context
namespace = {"BaseModel": BaseModel}
exec(class_code, namespace)

# Fetch the generated class
DynamicItem = namespace["DynamicItem"]

# Instantiate and validate
item = DynamicItem(name="Coffee Mug", price=12.99)
print(item.summary())  # Output: Coffee Mug costs $12.99
print(item.model_dump_json())
# Output: {"name":"Coffee Mug","price":12.99,"in_stock":true}

Summary Tip

  • Use create_model() when building schemas dynamically from JSON input, databases, or runtime parameters.

  • Use exec() only if you are parsing literal Python code blocks from external configurations (and make sure to sanitize untrusted input!).


So if I create a skill markdown file, I could include a class definition which I could parse from Python and declare in-line

Yes, exactly! That pattern is super popular in agentic workflows, plugin systems, and LLM tool-calling engines (like Semantic Kernel or custom AI framework setups).

You can embed a block of Python code inside your Markdown "Skill" file, parse it out, and load it dynamically into your running Python session.

Here is a complete, practical pattern for doing this cleanly:

1. The Skill Markdown File (skills/calculator.md)

In your Markdown file, you can write the class definition inside a standard triple-backtick Python code block:

# Calculator Skill

This skill provides basic mathematical operations.

```python
from pydantic import BaseModel, Field

class CalculateSchema(BaseModel):
    operation: str = Field(description="The math operation to perform (+, -, *, /)")
    a: float = Field(description="First number")
    b: float = Field(description="Second number")

    def execute() -> float:
        if self.operation == "+": return self.a + self.b
        if self.operation == "-": return self.a - self.b
        if self.operation == "*": return self.a * self.b
        if self.operation == "/": return self.a / self.b
        raise ValueError("Invalid operation")
```

2. Parsing and In-Line Execution in Python

You can use regular expressions to extract the Python code block, execute it in an isolated namespace, and retrieve your dynamic Pydantic class:

import re
from pydantic import BaseModel, Field

def load_pydantic_class_from_markdown(md_filepath: str, class_name: str):
    with open(md_filepath, "r", encoding="utf-8") as f:
        content = f.read()

    # Regex to extract code from ```python ... ``` blocks
    code_match = re.search(r"```python\n(.*?\n)```", content, re.DOTALL)
    if not code_match:
        raise ValueError("No python code block found in markdown file.")

    code_string = code_match.group(1)

    # Provide necessary imports/globals into the execution namespace
    namespace = {
        "BaseModel": BaseModel,
        "Field": Field,
    }

    # Execute the code block in our controlled namespace
    exec(code_string, namespace)

    # Return the target class
    return namespace[class_name]

# Usage:
CalculateSchema = load_pydantic_class_from_markdown("skills/calculator.md", "CalculateSchema")

# Validate data directly into the dynamically parsed model
calc_input = CalculateSchema(operation="*", a=7.0, b=6.0)
print(calc_input.execute())  # Output: 42.0

Best Practices for this Pattern

  • Explicit Imports: Include needed imports (like from pydantic import BaseModel) inside the Markdown code block itself so the block is self-contained. Then pass globals() or standard modules as the base namespace in exec().

  • Schema Inspection: Because it's a real Pydantic model once loaded, you can call CalculateSchema.model_json_schema() to generate structured tool schemas for OpenAI, Anthropic, or local LLMs automatically.

  • Security Note: Make sure these .md files are coming from a trusted source (e.g., your local codebase or a secure repo), since exec() will run whatever Python code is inside those backticks!


From <https://gemini.google.com/app/1281184851a72d89>  Google Gemini (3.6 Flash)

Comments

Popular posts from this blog

#4297 ISS Astronaut Unable to Speak

#761 New Ways to Access Online Content, particularly Virtual Reality Content

#3679 Google Gemini 2.5 Pro: Create Python Application to View and Query Neo4j KG