Skip to main content

A super easy AI LLM library.

Project description

supereasyai

PyPI Tests Changelog License Ask DeepWiki

A super easy AI LLM library.

Installation

Install this library using pip:

pip install supereasyai

Usage

Get an AI up and running fast with super easy tool support via Python functions.

Initializing an AI

Supported Providers (So far)

  • OpenAI
  • Groq
  • Ollama

OpenAI

ai = create_openai(
    api_key=OPENAI_API_KEY,
    model="gpt-4.1-mini"
)

Groq

ai = create_groq(
    api_key=GROQ_API_KEY,
    model="llama-3.3-70b-versatile"
)

Ollama

ai = create_ollama(
    model="llama-3.1"
)

Using Environment Variables

You can store your API keys in an environment if prefered. The default key is AI_API_KEY. This can be changed by setting the api_environment_key variable when initializing an AI.

ai = create_openai(
    model="gpt-4.1-mini",
    api_environment_key="OPENAI_API_KEY"
)

Setting Models

You do not have to have a preset model. The model used can be changed on the AI object or overridden when calling the AI at any time.

# Creates a GROQ AI with no model set
ai = create_groq(
    api_key=GROQ_API_KEY
)

ai.model = "llama-3.3-70b-versatile" # Changes the set model

ai.query(
    model="llama-3.3-70b-versatile" # Ignores whatever set model is, if any, and uses the input one instead
)

Messages

When querying an AI, you need to pass in an array of messages. There are multiple types of messages, but they are all built upon the base Message class.

A message can have one of five roles. Each one has its own unique class to make creating them easier.

  • system - used to define the main prompt for the AI
SystemMessage("You are an AI chatbot. Assist the user to the best of your ability.")
  • developer - essentially the same as the system role
DeveloperMessage("You are an AI chatbot. Assist the user to the best of your ability.")
  • assistant - a message that the AI has sent
AssistantMessage("Hi there, how can I help you!")
  • tool - a response from a tool
# Tool messages are a little more complex as they are used in response to tool calls.
# So they require the tool_call_id and tool name
ToolMessage("abc123", "get_weather", "It's a nice day outside")
  • user - a message that the user has sent
UserMessage("How long is a humpback whale?")

If you prefer to use the Message class, you can manual set the role:

Message("system", "You are an AI chatbot. Assist the user to the best of your ability.")

Advanced Messages

Some types of messages have support for more than just text.

Assistant messages can have tool calls. They are used by the AI to declare that it's calling a function. I contains a unique id, the tool's name, and the arguments to pass to the tool. Typically, you wouldn't create tool calls or assistant messages, as they are generated by the AI.

In response to tool calls, there are tool messages. A ToolMessage is just like any other message, but it also contains a tool_call_id to link it back to the ToolCall that called it.

Converting to and from JSON

In some cases, you may need to convert messages to JSON or parse message from JSON, such as storing and retreiving chat history in a file. This can be done via the pack_messages and unpack_messages functions.

pack_messages will pack a list of messages into a list of dicts.

messages: list[Message] = [AssistantMessage("Hello user!"), UserMessage("Hello AI!")]
print(pack_messages(messages))

# Would convert to:
# [{"role": "assistant", "content": "Hello user!"}, {"role": "user", "content": "Hello AI!"}]

unpack_messages will unpack a list of dicts into a list of messages.

messages: list[dict] = [{"role": "assistant", "content": "Hello user!"}, {"role": "user", "content": "Hello AI!"}]
print(unpack_messages(messages))

# Would convert to:
# [AssistantMessage("Hello user!"), UserMessage("Hello AI!")]

Querying an AI

There are three different ways to query an AI with supereasyai:

  • A standard query via query
  • A format query that converts the response to a python object via query with the format parameter
  • An automated query that automatically runs any called tools with variable autonomy via query_and_run_tools

Query

The query method returns either a AssistantMessage or an AssistantMessageStream depending on whether or not you want to stream the response.

response: AssistantMessage = ai.query(
    messages=[UserMessage("What is 2 + 2?")]
)
# Access the message's actual content
print(response.content)

# The printed output would likely be something along the lines of:
# "2 + 2 = 4."

If you are streaming the response, it needs to be iterated:

response: AssistantMessageStream = ai.query(
    messages=[UserMessage("What is 4 + 4?")],
    stream=True
)
# Iterates through the response as it's being generated
for chunk in response:
    # Print each together
    print(chunk, end="", flush=True)

# The printed output would likely be something along the lines of:
# "4 + 4 = 8."

Once an AssistantMessageStream has been fully iterated, you can access the full completed result and properties just like a regular AssistantMessage:

print(response.content)
# 4 + 4 = 8.

Query Format

In some scenarios, you may want an AI's response to be restricted to a specific format. This can be easily achieved via the format parameter.

The format parameter should be an object type, which will be automatically converted into a JSON schema for the AI to interpret.

Responses when using format will come back as a FormattedAssistantMessage. It extends AssistantMessage but also holds the formatted response in the object type provided as the format

class MathReasoning:
    def __init__(self, steps: list[str], answer: str) -> None:
        self.steps: list[str] = steps
        self.answer: str = answer

response: FormattedAssistantMessage = ai.query(
    messages=[UserMessage("What is 2 + 2?")],
    format=MathReasoning
)
formatted: MathReasoning = response.formatted

print(formatted.steps)
print(formatted.answer)

# The printed output would probably look something like this:
# ["Start with the number 2.", "Add another 2 to it.", "2 + 2 equals 4."]
# 4

You can provide descriptions for the AI via docstrings in the reStructured (reST) format. This can help the AI understand what to put where if the format is more complex.

class MathReasoning:
    def __init__(self, steps: list[str], answer: str) -> None:
        """
        :param steps: The steps to solve the problem
        :param answer: The answer to the problem
        """
        self.steps: list[str] = steps
        self.answer: str = answer

Using Tools

With supereasyai, regular Python functions can be used as tools.

def add(a: int, b: int) -> str:
    return f"{a} + {b} = {a + b}"

response: AssistantMessage = ai.query(
    messages=[UserMessage("What is 2 + 2?")],
    tools=[add]
)

In this scenario, the AI will likely create a tool call to run the add tool with the arguments: {"a": 2, "b": 2}.

The query function does not automatically run its tool calls. You can either handle it manually, running the functions and creating the ToolMessage responses:

# Maybe something like this
tool_messages: list[ToolMessage] = []
for tool_call in response.tool_calls:
    # Check which tool was called
    if tool_call.name == "add":
        tool_messages.append(ToolMessage(
            tool_call_id=tool_call.id,
            name=tool_call.name,
            content=add(tool_call.arguments["a"], tool_call.arguments["b"])
            ))
    # This would easily pile up with more and more functions

Or you can use the run_tool_calls method to automatically run the functions and get a ToolMessage responses for each one.

# Pass in any of the tools that the AI could have called
tool_messages: list[ToolMessage] = response.run_tool_calls([add])

Query and Run Tools

The query_and_run_tools method makes it even easier. It will query the AI and run the tools for you automatically, returning both the AssistantMessage(s) and ToolMessage(s).

response: list[Message] = ai.query_and_run_tools(
    messages=[UserMessage("What is 2 + 2?")],
    tools=[add]
)

# This would automatically call and run the "add" function with a=2 and b=2

You can set the level of autonomy that the function runs at:

  • "none" - The default value. Only queries the AI once. The AI will either provide a direct response or call tools that will be automatically run.
  • "follow_up" - Runs any tools and ensures the AI provides a worded response afterwords. This queries the AI a maximum of 2 times.
  • "full" - Queries the AI until it decides to provide a worded response. Useful when the AI needs to complete multiple steps.

With the previous example above, if you were to use follow_up, the response would end with an AssistantMessage probably saying something along the lines of "The answer to 2 + 2 is 4." instead of just running the function.

Now consider this example:

def get_password() -> str:
    return "spaghetti"

def get_secret(password: str) -> str:
    if password != "spaghetti":
        return "The password is incorrect"
    return "The secret is a good sauce"

response: list[Message] = ai.query_and_run_tools(
    messages=[UserMessage("What's the secret?")],
    tools=[get_password, get_secret],
    autonomy="full"
)

The AI can't get the secret until it knows the password, so it must first call the get_password tool. Afterwards, it can use the password it just learned to get the secret, then it will finally respond to the user with what the secret is. The response list would probably be something like this:

  • AssistantMessage - A single tool call for the get_password tool
  • ToolMessage - A response for the AI that would say "spaghetti"
  • AssistantMessage - A single tool call for the get_secret tool with the arguments: {"password": "spaghetti"}
  • ToolMessage - A response for the AI that would say "The secret is a good sauce"
  • AssistantMessage - The AI would finally tell the user what the secret is.

Schemas

The AIs understand tools and formats via JSON Schemas.

supereasyai automatically creates these schemas with the doms-json Python package. To better understand the process, consider taking a look.

Examples

Simple Streamed Chatbot

from supereasyai import AI, create_ollama, Message, SystemMessage, UserMessage, AssistantMessageStream

PROMPT = """
You are a thoughtful and talkative AI chatbot.
"""

ai: AI = create_ollama("llama3.1")
history: list[Message] = [SystemMessage(PROMPT)]
while True:
    user_input: str = input("You: ")
    history.append(UserMessage(user_input))
    response: AssistantMessageStream = ai.query(
        messages=history,
        stream=True
    )
    history.append(response)
    print("AI: ", end="")
    for chunk in response:
        print(chunk, end="", flush=True)
    print("")

Simple Calculator AI

from supereasyai import AI, create_openai, Message, UserMessage

def add(a: float, b: float) -> float:
    return a + b

def subtract(a: float, b: float) -> float:
    return a - b

def multiply(a: float, b: float) -> float:
    return a * b

def divide(a: float, b: float) -> float:
    return a / b

ai: AI = create_openai(model="gpt-4.1-mini")

while True:
    user_input: str = input("Your simple math question: ")
    response: list[Message] = ai.query_and_run_tools(
        messages=[UserMessage(user_input)],
        tools=[add, subtract, multiply, divide],
        autonomy="full"
    )
    print("AI: " + response[-1].content)

Story Scene Generator

from supereasyai import AI, create_groq, SystemMessage, UserMessage

GROQ_API_KEY = "groq-api-key-here"

PROMPT = """
Your job is to create a detailed scene for a story based on the user's prompt.
"""

class Character:
    def __init__(self, name: str, description: str, traits: list[str]) -> None:
        self.name: str = name
        self.description: str = description
        self.traits: list[str] = traits

class Scene:
    def __init__(self, name: str, description: str, characters: list[Character]) -> None:
        self.name: str = name
        self.description: str = description
        self.characters: list[Character] = characters

ai: AI = create_groq(
    api_key=GROQ_API_KEY
)

user_input: str = input("Your scene prompt: ")
response: FormattedAssistantMessage = ai.query(
    messages=[
        SystemMessage(PROMPT),
        UserMessage(user_input)
    ],
    format=Scene,
    model="llama-3.3-70b-versatile"
)
scene: Scene = response.formatted

Development

To contribute to this library, first checkout the code. Then create a new virtual environment:

cd supereasyai
python -m venv venv
source venv/bin/activate

Now install the dependencies and test dependencies:

python -m pip install -e '.[test]'

To run the tests:

python -m pytest

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

supereasyai-1.0.6.tar.gz (20.2 kB view details)

Uploaded Source

Built Distribution

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

supereasyai-1.0.6-py3-none-any.whl (16.7 kB view details)

Uploaded Python 3

File details

Details for the file supereasyai-1.0.6.tar.gz.

File metadata

  • Download URL: supereasyai-1.0.6.tar.gz
  • Upload date:
  • Size: 20.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for supereasyai-1.0.6.tar.gz
Algorithm Hash digest
SHA256 73da161d34ca20aeb35df32658418d0ec5419531a1edaef8a19bd1f691001646
MD5 5e5f7872766c083c5393ee5c6392ac19
BLAKE2b-256 e1a984f9cc21035e025dcc20f796c0afc30e2f7413392b0725993114a7fb4946

See more details on using hashes here.

Provenance

The following attestation bundles were made for supereasyai-1.0.6.tar.gz:

Publisher: publish.yml on DominicDJC/supereasyai

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file supereasyai-1.0.6-py3-none-any.whl.

File metadata

  • Download URL: supereasyai-1.0.6-py3-none-any.whl
  • Upload date:
  • Size: 16.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for supereasyai-1.0.6-py3-none-any.whl
Algorithm Hash digest
SHA256 3c06f9885d80d60ef17b195447c7cdec518f8995dec85ba18962f76f5b1cf6d8
MD5 b097b2748853038c8819f64182539050
BLAKE2b-256 7a1d7dfa69a44f4a065f6d3c784d38892b2fd90c343a20d3a952410a261d06db

See more details on using hashes here.

Provenance

The following attestation bundles were made for supereasyai-1.0.6-py3-none-any.whl:

Publisher: publish.yml on DominicDJC/supereasyai

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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