A super easy AI LLM library.
Project description
supereasyai
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
)
Getting Available Models
You can get a list of available models:
# Prints a string list of all available models
print(ai.get_models())
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 thesystemrole
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
querywith theformatparameter - 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 theget_passwordtoolToolMessage- A response for the AI that would say"spaghetti"AssistantMessage- A single tool call for theget_secrettool 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file supereasyai-1.0.8.tar.gz.
File metadata
- Download URL: supereasyai-1.0.8.tar.gz
- Upload date:
- Size: 20.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
53119da10e20074e88fa6293d598d8f58c9c69c05cb0bf24ddc6c99376fecf8c
|
|
| MD5 |
341ee8802ec29ba1f2b17abc18b0d4af
|
|
| BLAKE2b-256 |
ad762ac6b017cbbece2a2a72e89a1abd98a5ee3ce9b3508d2d462b4a3f8512f8
|
Provenance
The following attestation bundles were made for supereasyai-1.0.8.tar.gz:
Publisher:
publish.yml on DominicDJC/supereasyai
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
supereasyai-1.0.8.tar.gz -
Subject digest:
53119da10e20074e88fa6293d598d8f58c9c69c05cb0bf24ddc6c99376fecf8c - Sigstore transparency entry: 976084582
- Sigstore integration time:
-
Permalink:
DominicDJC/supereasyai@3733eece9bf2a291d7bf515fa9a3b4a7b0b52296 -
Branch / Tag:
refs/tags/v1.0.8 - Owner: https://github.com/DominicDJC
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3733eece9bf2a291d7bf515fa9a3b4a7b0b52296 -
Trigger Event:
release
-
Statement type:
File details
Details for the file supereasyai-1.0.8-py3-none-any.whl.
File metadata
- Download URL: supereasyai-1.0.8-py3-none-any.whl
- Upload date:
- Size: 16.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cfb6fdc47d0726c383a58027ae446ba18a76d61ac2cb0bcf96c1748d1e4a9dd6
|
|
| MD5 |
b16663c7988001eda0b01a43f1c5fb9c
|
|
| BLAKE2b-256 |
1d04c54bd40392d4eb3802ea0f019ca73f2cb4e4da53008c95aac1abc8eb2a9b
|
Provenance
The following attestation bundles were made for supereasyai-1.0.8-py3-none-any.whl:
Publisher:
publish.yml on DominicDJC/supereasyai
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
supereasyai-1.0.8-py3-none-any.whl -
Subject digest:
cfb6fdc47d0726c383a58027ae446ba18a76d61ac2cb0bcf96c1748d1e4a9dd6 - Sigstore transparency entry: 976084584
- Sigstore integration time:
-
Permalink:
DominicDJC/supereasyai@3733eece9bf2a291d7bf515fa9a3b4a7b0b52296 -
Branch / Tag:
refs/tags/v1.0.8 - Owner: https://github.com/DominicDJC
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3733eece9bf2a291d7bf515fa9a3b4a7b0b52296 -
Trigger Event:
release
-
Statement type: