Skip to main content

A library for building reliable, state-driven LLM workflows using the State+CRUD pattern

Project description

stateagent

A lightweight Python framework to structure LLM conversations using a validated state model and a tool-based interaction design.

Table of Contents

Installation

pip install stateagent

Quick Start

Here's a simple example to get you up and running:

Example Conversation

🤖 Hi! I need to collect some contact information from you.

👤 My name is John Smith

🤖 Great! I've recorded your name as John Smith.
    Now, could you please provide your email address?

👤 john@company.com

🤖 Perfect! I've saved your email.
    Finally, what message would you like to send?

👤 I'm interested in learning more about your services.

🤖 Excellent! I have all the information I need:
    ✓ Name: John Smith
    ✓ Email: john@company.com
    ✓ Message: I'm interested in learning more about your services.

    Thank you for your submission!

🏗️ Core Concepts

StateModel

Define your data structure with validation:

from dataclasses import dataclass, field
from llm_structured_agent import StateModel, Field, create_range_validator

@dataclass
class UserProfile(StateModel):
    name: str = field(default=None)
    age: int = field(default=None)
    email: str = field(default=None)

    _field_info = {
        'name': Field(
            required=True,
            description="User's full name",
            validator=create_length_validator(min_length=2)
        ),
        'age': Field(
            required=True,
            description="User's age",
            validator=create_range_validator(min_val=13, max_val=120)
        ),
        'email': Field(
            required=True,
            description="Email address",
            validator=create_email_validator()
        )
    }

CRUD Tools

The LLM automatically gets these tools to interact with state:

  • set_field(field_name, value) - Update a field with validation
  • validate_state() - Check if all required fields are complete
  • get_state() - View current state snapshot
  • clear_state() - Reset all fields to defaults

Structured Agent

The main orchestrator that manages the conversation:

agent = StructuredAgent(
    state_cls=UserProfile,
    llm=OpenAIAdapter(model="gpt-4o-mini"),
    hooks={
        "on_field_set": lambda state, field: print(f"Updated {field}"),
        "on_submit": lambda state: save_to_database(state.snapshot())
    },
    max_turns=20
)

🎨 Advanced Features

Custom Validation

import re
from llm_structured_agent import ValidationError

def validate_phone_number(value: str):
    if not re.match(r'^\+?[\d\s\-]+$', value):
        raise ValidationError("Invalid phone format")
    return value.strip()

@dataclass
class ContactInfo(StateModel):
    phone: str = field(default=None)

    _field_info = {
        'phone': Field(
            required=True,
            description="Phone number with country code",
            validator=validate_phone_number
        )
    }

Conditional Logic

@dataclass
class OrderForm(StateModel):
    item_type: str = field(default=None)
    shipping_address: str = field(default=None)
    gift_message: str = field(default=None)

    def validate(self) -> List[str]:
        missing = super().validate()

        # Gift message required for gift items
        if (self.item_type == "gift" and
            not self.gift_message):
            missing.append("gift_message")

        return missing

Lifecycle Hooks

def on_field_updated(state, field_name):
    print(f"📝 {field_name} updated to: {getattr(state, field_name)}")

def on_form_complete(state):
    # Save to database
    save_to_database(state.snapshot())

    # Send notification
    send_notification(state.email, "Form completed!")

    # Log completion
    logger.info(f"Form completed for {state.name}")

agent = StructuredAgent(
    state_cls=MyForm,
    llm=OpenAIAdapter(),
    hooks={
        "on_field_set": on_field_updated,
        "on_submit": on_form_complete
    }
)

Multiple LLM Providers

# OpenAI (default)
agent = StructuredAgent(
    state_cls=MyState,
    llm=OpenAIAdapter(model="gpt-4o-mini")
)

# Custom LLM provider
class CustomLLMAdapter(LLMAdapter):
    def chat(self, messages, tools=None):
        # Your implementation
        pass

    def extract_function_calls(self, response):
        # Your implementation
        pass

agent = StructuredAgent(
    state_cls=MyState,
    llm=CustomLLMAdapter()
)

Non-Interactive Usage

# For API endpoints or batch processing
agent = StructuredAgent(state_cls=ContactForm, llm=OpenAIAdapter())

# Process single message
result = agent.process_single_turn("My name is John and email is john@example.com")

if result["complete"]:
    print("Form completed:", result["state"])
else:
    print("Next response:", result["message"])
    print("Still missing:", result["missing_fields"])

🧪 Built-in Validators

The library includes common validators:

from llm_structured_agent import (
    create_email_validator,
    create_range_validator,
    create_length_validator,
    create_regex_validator,
    create_choice_validator
)

# Email validation
email_validator = create_email_validator()

# Numeric range
age_validator = create_range_validator(min_val=18, max_val=100)

# String length
name_validator = create_length_validator(min_length=2, max_length=50)

# Regex pattern
phone_validator = create_regex_validator(
    pattern=r'^\+?[\d\s\-]+$',
    error_message="Invalid phone number format"
)

# Choice validation
department_validator = create_choice_validator([
    "Engineering", "Sales", "Marketing", "HR"
])

📚 Examples

The library includes complete examples:

Customer Onboarding (KYC)

# Run the KYC example

python -m llm_structured_agent.examples.kyc_example

# Or if installed via pip

kyc-agent

Employee Onboarding

# Run the employee onboarding example
python -m llm_structured_agent.examples.onboarding_example

# Or if installed via pip
onboarding-agent

🔧 Configuration

Environment Variables

# Required for OpenAI

export OPENAI_API_KEY="your-openai-api-key"

# Optional: Custom model

export OPENAI_MODEL="gpt-4o-mini"

Custom System Prompts

custom_prompt = """You are a helpful assistant collecting user information.

Be conversational and friendly while systematically gathering:
- Name (required)
- Email (required)
- Phone (optional)

Use the provided tools to store information and check completeness."""

agent = StructuredAgent(
    state_cls=ContactForm,
    llm=OpenAIAdapter(),
    system_prompt=custom_prompt
)

🧪 Testing

# Install with dev dependencies

pip install stateagent[dev]

# Run tests

pytest

# Run with coverage

pytest --cov=llm_structured_agent

# Run specific test

pytest tests/test_state.py -v

📖 API Reference

StateModel

Base class for defining structured state:

class StateModel:
    def set_field(self, name: str, value: Any) -> None
    def validate(self) -> List[str]
    def snapshot(self) -> Dict[str, Any]
    def clear(self) -> None
    def get_field_info(self, name: str) -> Optional[Field]

Field

Field definition with validation:

class Field:
    def __init__(
        self,
        required: bool = False,
        description: str = "",
        validator: Optional[Callable] = None,
        default: Any = None
    )

StructuredAgent

Main orchestration class:

class StructuredAgent:
    def __init__(
        self,
        state_cls: type,
        llm: LLMAdapter,
        hooks: Optional[Dict[str, Callable]] = None,
        max_turns: int = 20,
        system_prompt: Optional[str] = None
    )

    def run_chat(self) -> None
    def process_single_turn(self, user_input: str) -> Dict[str, Any]
    def reset(self) -> None

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

📊 Roadmap

  • More LLM provider integrations
  • FastAPI integration
  • Streamlit demo interface
  • Database persistence layer
  • MCP Server

Built with ❤️ for the AI community

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stateagent-0.1.0.tar.gz (20.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stateagent-0.1.0-py3-none-any.whl (14.8 kB view details)

Uploaded Python 3

File details

Details for the file stateagent-0.1.0.tar.gz.

File metadata

  • Download URL: stateagent-0.1.0.tar.gz
  • Upload date:
  • Size: 20.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.18

File hashes

Hashes for stateagent-0.1.0.tar.gz
Algorithm Hash digest
SHA256 3f6a45b2cffacb4f1cca69a4a11f018e03974e78a3b8d21f729b9322e7085304
MD5 a36508b697a0bbd653926269d45c9758
BLAKE2b-256 0fc5fa7ddaca27983612aea29d1252e013390a707c194429903f5a4edc845c6d

See more details on using hashes here.

File details

Details for the file stateagent-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: stateagent-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 14.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.18

File hashes

Hashes for stateagent-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 aa19e829a747cb15ebb4edbb8099d339e37f7c944372b18beea6e0a7792a16b9
MD5 3af4a0956237b64ff345bb11048812eb
BLAKE2b-256 3e6ebc61a023941c07d32d961a67bce2d83cd6b2bc5f673926d3239eaa26ea51

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page