Metadata-Version: 2.1
Name: struct-gpt
Version: 3.0.0
Summary: structured llm outputs
Author-Email: Stephan Fitzpatrick <knowsuchagency@gmail.com>
License: MIT License
         
         Copyright (c) 2023 Stephan Fitzpatrick
         
         Permission is hereby granted, free of charge, to any person obtaining a copy
         of this software and associated documentation files (the "Software"), to deal
         in the Software without restriction, including without limitation the rights
         to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         copies of the Software, and to permit persons to whom the Software is
         furnished to do so, subject to the following conditions:
         
         The above copyright notice and this permission notice shall be included in all
         copies or substantial portions of the Software.
         
         THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         SOFTWARE.
         
Project-URL: Source, https://github.com/knowsuchagency/struct-gpt
Requires-Python: >=3.10
Requires-Dist: openai>=1.0.0
Requires-Dist: pydantic>=1.10.7
Requires-Dist: ruamel-yaml>=0.17.24
Description-Content-Type: text/markdown

# struct-gpt

[![codecov](https://codecov.io/gh/knowsuchagency/struct-gpt/branch/main/graph/badge.svg?token=TMUQNTCTDI)](https://codecov.io/gh/knowsuchagency/struct-gpt)
![PyPI](https://img.shields.io/pypi/v/struct-gpt)


Get structured output from LLM's.

```python
from struct_gpt import OpenAiBase
from pydantic import Field


class SentimentSchema(OpenAiBase):
    """
    Determine the sentiment of the given text:

    {content}
    """
    # 👆this becomes the prompt

    sentiment: str = Field(description="Either -1, 0, or 1.")


result = SentimentSchema.from_openai(content="I love pizza!").json()
                                     # 👆this ends up in the prompt
print(result)
```
outputs:
```json
{
  "sentiment": "1"
}
```

## Features

* Easy creation of custom models using the OpenAI API
* Integration with Pydantic for model validation and serialization
* Flexible configuration with retries and temperature settings

## Installation

`pip install struct-gpt`


## Usage

* Template variables in the class' docstring are replaced with the keyword arguments passed to `from_openai`
* Classes can reference one another
* You can use the `OpenAiMixin` to add functionality to existing Pydantic classes
* Requires `openai>=1.0`. By default `from_openai` builds an `openai.OpenAI()` client, which reads `OPENAI_API_KEY` from the environment. Pass `client=` to use your own (e.g. `AzureOpenAI` or a custom `base_url`)
* JSON mode (`response_format={"type": "json_object"}`) is on by default. Pass `json_mode=False` for models that don't support it
* The default model is `gpt-4o-mini`; pass `model=` to change it

```python
from struct_gpt import OpenAiBase, OpenAiMixin
from pydantic import Field, BaseModel
from typing import Mapping


class SentimentSchema(OpenAiBase):
    sentiment: str = Field(description="Either -1, 0, or 1.")


class SentimentAnalysis(BaseModel, OpenAiMixin):
    """
    Determine the sentiment of each word in the following: {text}

    Also determine the overall sentiment of the text and who the narrator is.
    """

    words_to_sentiment: Mapping[str, SentimentSchema]
    overall_sentiment: SentimentSchema
    narrator: str


print(
    SentimentAnalysis.from_openai(
        text="As president, I loved the beautiful scenery, but the long hike was exhausting."
    ).json(indent=2)
)
```
<details>
<summary><b>See Output</b></summary>

```json
{
  "words_to_sentiment": {
    "As": {
      "sentiment": "0"
    },
    "president,": {
      "sentiment": "1"
    },
    "I": {
      "sentiment": "0"
    },
    "loved": {
      "sentiment": "1"
    },
    "the": {
      "sentiment": "0"
    },
    "beautiful": {
      "sentiment": "1"
    },
    "scenery,": {
      "sentiment": "1"
    },
    "but": {
      "sentiment": "-1"
    },
    "long": {
      "sentiment": "-1"
    },
    "hike": {
      "sentiment": "-1"
    },
    "was": {
      "sentiment": "0"
    },
    "exhausting.": {
      "sentiment": "-1"
    }
  },
  "overall_sentiment": {
    "sentiment": "0"
  },
  "narrator": "president"
}
```

</details>

### Improving reliability with examples

`from_openai` can accept a list of examples to guide the model and improve its accuracy. 

Each example is a dictionary containing an `input` and `output` key. 

`output` should be an instance of the model, a dictionary, or its json string representation,.

```python
from struct_gpt import OpenAiBase
from pydantic import Field


class SentimentSchema(OpenAiBase):
    """
    Determine the sentiment of the given text:

    {content}
    """

    sentiment: str = Field(description="Either -1, 0, or 1.")


examples = [
    {
        "input": "I love the beach!",
        "output": {"sentiment": "1"},
    },
    {
        "input": "don't touch that",
        "output": '{"sentiment": "-1"}',
    },
    {
        "input": "this library is neat!",
        "output": SentimentSchema(sentiment="1"),
    },
]

print(SentimentSchema.from_openai(content="I love pizza!", examples=examples).json())
```
outputs:
```json
{
  "sentiment": "1"
}
```
