structllm


Namestructllm JSON
Version 0.1.0 PyPI version JSON
download
home_pageNone
SummaryUniversal Python library for Structured Outputs with any LLM provider
upload_time2025-07-24 23:32:32
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT
keywords ai anthropic json-schema litellm llm openai pydantic structured-outputs
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # structllm

<div style="text-align: center;">
  <img width="100%" src="structllm.svg" alt="Logo">
</div>

[![PyPI version](https://badge.fury.io/py/structllm.svg)](https://badge.fury.io/py/structllm)
[![Python Support](https://img.shields.io/pypi/pyversions/structllm.svg)](https://pypi.org/project/structllm/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**structllm** is a universal and lightweight Python library that provides [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses) functionality for any LLM provider (OpenAI, Anthropic, Mistral, local models, etc.), not just OpenAI. It guarantees that LLM responses conform to your provided JSON schema using Pydantic models.

If your LLM model has 7B parameters or more, it can be used with structllm.

## Installation

```bash
pip install structllm
```

Or using uv (recommended):

```bash
uv add structllm
```

## Quick Start

```python
from pydantic import BaseModel
from structllm import StructLLM
from typing import List

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: List[str]

client = StructLLM(
    api_base="https://openrouter.ai/api/v1",
    api_key="sk-or-v1-...",
)

messages = [
    {"role": "system", "content": "Extract the event information."},
    {"role": "user", "content": "Alice and Bob are going to a science fair on Friday."},
]

response = client.parse(
    model="openrouter/moonshotai/kimi-k2",
    messages=messages,
    response_format=CalendarEvent,
)

if response.output_parsed:
    print(response.output_parsed)
    # {"name": "science fair", "date": "Friday", "participants": ["Alice", "Bob"]}
else:
    print("Failed to parse structured output")
```

## Provider Support

StructLLM works with **100+ LLM providers** through LiteLLM. Check the [LiteLLM documentation](https://docs.litellm.ai/docs/providers) for the full list of supported providers.

## Advanced Usage

### Complex Data Structures

```python
from pydantic import BaseModel, Field
from typing import List, Optional
from enum import Enum

class Priority(str, Enum):
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"

class Task(BaseModel):
    title: str = Field(description="The task title")
    description: Optional[str] = Field(default=None, description="Task description")
    priority: Priority = Field(description="Task priority level")
    assignees: List[str] = Field(description="List of assigned people")
    due_date: Optional[str] = Field(default=None, description="Due date in YYYY-MM-DD format")

client = StructLLM(
    api_base="https://openrouter.ai/api/v1",
    api_key="sk-or-v1-...",
)

response = client.parse(
    model="gpt-4o-2024-08-06",
    messages=[
        {
            "role": "user",
            "content": "Create a high-priority task for John and Sarah to review the quarterly report by next Friday."
        }
    ],
    response_format=Task,
)

task = response.output_parsed
print(f"Task: {task.title}")
print(f"Priority: {task.priority}")
print(f"Assignees: {task.assignees}")
```

### Error Handling

```python
response = client.parse(
    model="gpt-4o-2024-08-06",
    messages=messages,
    response_format=CalendarEvent,
)

if response.output_parsed:
    # Successfully parsed
    event = response.output_parsed
    print(f"Parsed event: {event}")
else:
    # Parsing failed, but raw response is available
    print("Failed to parse structured output")
    print(f"Raw response: {response.raw_response.choices[0].message.content}")
```

### Custom Configuration

```python
client = StructLLM(
    api_base="https://api.custom-provider.com/v1",
    api_key="your-api-key"
)

response = client.parse(
    model="custom/model-name",
    messages=messages,
    response_format=YourModel,
    temperature=0.1,
    top_p=0.1,
    max_tokens=1000,
    # Any additional parameters supported by the LiteLLM interface
    custom_parameter="value"
)
```

## How It Works

StructLLM uses prompt engineering to ensure structured outputs:

1. **Schema Injection**: Automatically injects your Pydantic model's JSON schema into the system prompt
2. **Format Instructions**: Adds specific instructions for JSON-only responses
3. **Intelligent Parsing**: Extracts JSON from responses even when wrapped in additional text
4. **Validation**: Uses Pydantic for robust type checking and validation
5. **Fallback Handling**: Gracefully handles parsing failures while preserving raw responses

By default it uses low `temperature` and `top_p` settings to ensure consistent outputs, but you can customize these parameters as needed.

## Testing

Run the test suite:

```bash
# Install dependencies
uv sync

# Run tests
uv run pytest
uv run pytest -m "not integration"

# Run integration tests (requires external services)
uv run pytest -m "integration"

# Run linting
uv run ruff check .
```

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

1. Fork the repository
2. Create a feature branch: `git checkout -b feature/amazing-feature`
3. Make your changes with tests
4. Run the test suite: `uv run pytest`
5. Run linting: `uv run ruff check .`
6. Submit a pull request

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Acknowledgments

- [LiteLLM](https://github.com/BerriAI/litellm) for providing the universal LLM interface
- [Pydantic](https://github.com/pydantic/pydantic) for structured data validation
            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "structllm",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "ai, anthropic, json-schema, litellm, llm, openai, pydantic, structured-outputs",
    "author": null,
    "author_email": "Piotr Bednarski <piotr.maciej.bednarski@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/16/ed/722355ea5cb6406e54d41cf2148dad6db8e7702b255464a94e433c7fe8f4/structllm-0.1.0.tar.gz",
    "platform": null,
    "description": "# structllm\n\n<div style=\"text-align: center;\">\n  <img width=\"100%\" src=\"structllm.svg\" alt=\"Logo\">\n</div>\n\n[![PyPI version](https://badge.fury.io/py/structllm.svg)](https://badge.fury.io/py/structllm)\n[![Python Support](https://img.shields.io/pypi/pyversions/structllm.svg)](https://pypi.org/project/structllm/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n**structllm** is a universal and lightweight Python library that provides [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses) functionality for any LLM provider (OpenAI, Anthropic, Mistral, local models, etc.), not just OpenAI. It guarantees that LLM responses conform to your provided JSON schema using Pydantic models.\n\nIf your LLM model has 7B parameters or more, it can be used with structllm.\n\n## Installation\n\n```bash\npip install structllm\n```\n\nOr using uv (recommended):\n\n```bash\nuv add structllm\n```\n\n## Quick Start\n\n```python\nfrom pydantic import BaseModel\nfrom structllm import StructLLM\nfrom typing import List\n\nclass CalendarEvent(BaseModel):\n    name: str\n    date: str\n    participants: List[str]\n\nclient = StructLLM(\n    api_base=\"https://openrouter.ai/api/v1\",\n    api_key=\"sk-or-v1-...\",\n)\n\nmessages = [\n    {\"role\": \"system\", \"content\": \"Extract the event information.\"},\n    {\"role\": \"user\", \"content\": \"Alice and Bob are going to a science fair on Friday.\"},\n]\n\nresponse = client.parse(\n    model=\"openrouter/moonshotai/kimi-k2\",\n    messages=messages,\n    response_format=CalendarEvent,\n)\n\nif response.output_parsed:\n    print(response.output_parsed)\n    # {\"name\": \"science fair\", \"date\": \"Friday\", \"participants\": [\"Alice\", \"Bob\"]}\nelse:\n    print(\"Failed to parse structured output\")\n```\n\n## Provider Support\n\nStructLLM works with **100+ LLM providers** through LiteLLM. Check the [LiteLLM documentation](https://docs.litellm.ai/docs/providers) for the full list of supported providers.\n\n## Advanced Usage\n\n### Complex Data Structures\n\n```python\nfrom pydantic import BaseModel, Field\nfrom typing import List, Optional\nfrom enum import Enum\n\nclass Priority(str, Enum):\n    LOW = \"low\"\n    MEDIUM = \"medium\"\n    HIGH = \"high\"\n\nclass Task(BaseModel):\n    title: str = Field(description=\"The task title\")\n    description: Optional[str] = Field(default=None, description=\"Task description\")\n    priority: Priority = Field(description=\"Task priority level\")\n    assignees: List[str] = Field(description=\"List of assigned people\")\n    due_date: Optional[str] = Field(default=None, description=\"Due date in YYYY-MM-DD format\")\n\nclient = StructLLM(\n    api_base=\"https://openrouter.ai/api/v1\",\n    api_key=\"sk-or-v1-...\",\n)\n\nresponse = client.parse(\n    model=\"gpt-4o-2024-08-06\",\n    messages=[\n        {\n            \"role\": \"user\",\n            \"content\": \"Create a high-priority task for John and Sarah to review the quarterly report by next Friday.\"\n        }\n    ],\n    response_format=Task,\n)\n\ntask = response.output_parsed\nprint(f\"Task: {task.title}\")\nprint(f\"Priority: {task.priority}\")\nprint(f\"Assignees: {task.assignees}\")\n```\n\n### Error Handling\n\n```python\nresponse = client.parse(\n    model=\"gpt-4o-2024-08-06\",\n    messages=messages,\n    response_format=CalendarEvent,\n)\n\nif response.output_parsed:\n    # Successfully parsed\n    event = response.output_parsed\n    print(f\"Parsed event: {event}\")\nelse:\n    # Parsing failed, but raw response is available\n    print(\"Failed to parse structured output\")\n    print(f\"Raw response: {response.raw_response.choices[0].message.content}\")\n```\n\n### Custom Configuration\n\n```python\nclient = StructLLM(\n    api_base=\"https://api.custom-provider.com/v1\",\n    api_key=\"your-api-key\"\n)\n\nresponse = client.parse(\n    model=\"custom/model-name\",\n    messages=messages,\n    response_format=YourModel,\n    temperature=0.1,\n    top_p=0.1,\n    max_tokens=1000,\n    # Any additional parameters supported by the LiteLLM interface\n    custom_parameter=\"value\"\n)\n```\n\n## How It Works\n\nStructLLM uses prompt engineering to ensure structured outputs:\n\n1. **Schema Injection**: Automatically injects your Pydantic model's JSON schema into the system prompt\n2. **Format Instructions**: Adds specific instructions for JSON-only responses\n3. **Intelligent Parsing**: Extracts JSON from responses even when wrapped in additional text\n4. **Validation**: Uses Pydantic for robust type checking and validation\n5. **Fallback Handling**: Gracefully handles parsing failures while preserving raw responses\n\nBy default it uses low `temperature` and `top_p` settings to ensure consistent outputs, but you can customize these parameters as needed.\n\n## Testing\n\nRun the test suite:\n\n```bash\n# Install dependencies\nuv sync\n\n# Run tests\nuv run pytest\nuv run pytest -m \"not integration\"\n\n# Run integration tests (requires external services)\nuv run pytest -m \"integration\"\n\n# Run linting\nuv run ruff check .\n```\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n1. Fork the repository\n2. Create a feature branch: `git checkout -b feature/amazing-feature`\n3. Make your changes with tests\n4. Run the test suite: `uv run pytest`\n5. Run linting: `uv run ruff check .`\n6. Submit a pull request\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Acknowledgments\n\n- [LiteLLM](https://github.com/BerriAI/litellm) for providing the universal LLM interface\n- [Pydantic](https://github.com/pydantic/pydantic) for structured data validation",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Universal Python library for Structured Outputs with any LLM provider",
    "version": "0.1.0",
    "project_urls": {
        "Documentation": "https://github.com/piotrmaciejbednarski/structllm#readme",
        "Homepage": "https://github.com/piotrmaciejbednarski/structllm",
        "Issues": "https://github.com/piotrmaciejbednarski/structllm/issues",
        "Repository": "https://github.com/piotrmaciejbednarski/structllm.git"
    },
    "split_keywords": [
        "ai",
        " anthropic",
        " json-schema",
        " litellm",
        " llm",
        " openai",
        " pydantic",
        " structured-outputs"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "3bf3120e580ca90092b4c75ad9e458fa986001fce430bd3d3f882cc85142ce11",
                "md5": "558960119aae9032640f92bcc7ff51ec",
                "sha256": "0c6cf3e2d9589eb03101095c798d94f8e508ed72fef4e288c1c8605cc6833eb1"
            },
            "downloads": -1,
            "filename": "structllm-0.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "558960119aae9032640f92bcc7ff51ec",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 6647,
            "upload_time": "2025-07-24T23:32:30",
            "upload_time_iso_8601": "2025-07-24T23:32:30.681539Z",
            "url": "https://files.pythonhosted.org/packages/3b/f3/120e580ca90092b4c75ad9e458fa986001fce430bd3d3f882cc85142ce11/structllm-0.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "16ed722355ea5cb6406e54d41cf2148dad6db8e7702b255464a94e433c7fe8f4",
                "md5": "6ae983ac74b382da3d14a38e95da1e4b",
                "sha256": "1c91af6bf2745f709e0bb1cce52da5bf3e27ff9882d54381cbbcaf8c4fdaeffd"
            },
            "downloads": -1,
            "filename": "structllm-0.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "6ae983ac74b382da3d14a38e95da1e4b",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 233636,
            "upload_time": "2025-07-24T23:32:32",
            "upload_time_iso_8601": "2025-07-24T23:32:32.579727Z",
            "url": "https://files.pythonhosted.org/packages/16/ed/722355ea5cb6406e54d41cf2148dad6db8e7702b255464a94e433c7fe8f4/structllm-0.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-24 23:32:32",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "piotrmaciejbednarski",
    "github_project": "structllm#readme",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "structllm"
}
        
Elapsed time: 0.66335s