Makes working with LLMs like OpenAI GPT, Anthropic Claude, Google Gemini and Open source models super easy
Project description
JustAI
Package to make working with Large Language Models in Python super easy. Supports OpenAI, Anthropic Claude, Google Gemini, X Grok, DeepSeek, Perplexity, Reve, OpenRouter, Kimi (Moonshot) and local GGUF models.
Author: Hans-Peter Harmsen (hp@harmsen.nl)
Current version: 5.6.5
Installation
- Install the package:
pip install justai
-
Create an API key for the provider(s) you intend to use:
- OpenAI: platform.openai.com
- Anthropic: console.anthropic.com
- Google: aistudio.google.com
- X AI: console.x.ai
- DeepSeek: platform.deepseek.com
- Moonshot: platform.moonshot.ai
-
Create a
.envfile with the relevant keys:
OPENAI_API_KEY=your-openai-api-key
ANTHROPIC_API_KEY=your-anthropic-api-key
GOOGLE_API_KEY=your-google-api-key
X_API_KEY=your-x-ai-api-key
DEEPSEEK_API_KEY=your-deepseek-api-key
PERPLEXITY_API_KEY=your-perplexity-api-key
MOONSHOT_API_KEY=your-moonshot-api-key
Basic usage
from justai import Model
model = Model('gpt-5-mini')
model.system = """You are a movie critic. I feed you with movie
titles and you give me a review in 50 words."""
response = model.chat("Forrest Gump", cached=True)
print(response)
The cached=True parameter tells justai to cache the prompt and response locally.
Models
The provider is chosen automatically based on the model name prefix:
| Prefix | Provider |
|---|---|
gpt*, o1*, o3* |
OpenAI |
claude* |
Anthropic |
gemini* |
|
grok* |
X AI |
deepseek* |
DeepSeek |
sonar* |
Perplexity |
reve* |
Reve |
openrouter/* |
OpenRouter |
kimi*, moonshot* |
Moonshot |
*.gguf |
Local GGUF |
Features
JSON and structured output
model = Model('gemini-2.5-flash')
prompt = 'Give me the main characters from Seinfeld. Return json with keys name, profession and weirdness'
data = model.chat(prompt, return_json=True)
For typed structured output, pass a Pydantic model or Python type as response_format:
from pydantic import BaseModel as PydanticModel
class Character(PydanticModel):
name: str
profession: str
weirdness: str
result = model.chat(prompt, response_format=list[Character])
Images
Pass images as URLs, raw bytes or PIL images:
model = Model('gpt-5-nano')
url = 'https://upload.wikimedia.org/wikipedia/commons/9/94/Common_dolphin.jpg'
message = model.chat("What is in this image", images=url)
Image generation
model = Model('gpt-5')
pil_image = model.generate_image("A dolphin reading a book")
Input images can be passed for editing or style transfer:
model = Model('gemini-2.5-flash-image-preview')
pil_image = model.generate_image("Convert to Van Gogh style", images=source_image)
Async streaming
import asyncio
async def stream(model_name, prompt):
model = Model(model_name)
async for word in model.chat_async(prompt):
print(word, end='')
asyncio.run(stream('sonar-pro', 'Give me 5 names for a juice bar'))
Prompt caching (Anthropic)
model = Model('claude-sonnet-4-6')
model.system_message = 'You are an experienced book analyzer'
model.cached_prompt = SOME_LONG_TEXT
response = model.chat('Who is the main character?', cached=False)
Effort
Control reasoning depth with a single portable setting. The library translates it to each provider's native parameter.
model = Model('claude-fable-5', effort='low')
# or
model = Model('gpt-5.6-terra')
model.effort = 'xhigh'
Valid values: 'low', 'medium', 'high', 'xhigh', 'max', or None (default — send nothing, provider default applies).
None vs 'none' — two different things:
Model('gpt-5.6-sol', effort=None) # default, no reasoning field sent
Model('gpt-5.6-sol', effort='none') # explicitly turn reasoning off (GPT-5.6 only)
'none' as a string is a pass-through only accepted by GPT-5.6 models. On every other provider it raises ValueError.
| Provider | Native support |
|---|---|
| Anthropic (Fable 5, Mythos 5, Opus 4.7/4.8, Sonnet 5) | full set (low/medium/high/xhigh/max) |
| Anthropic (Opus 4.6, Sonnet 4.6) | xhigh maps up to max with warning |
| Anthropic (Opus 4.5) | xhigh and max map down to high with warning |
OpenAI (gpt-5.6-*) |
full set; max maps to xhigh (SDK cap) with warning; also accepts 'none' |
| Google (Gemini 3.x) | low/medium/high; xhigh/max map to HIGH with warning |
xAI (grok-4.5, grok-4.3, grok-4.20-multi-agent) |
low/medium/high; xhigh/max map to high with warning |
| OpenRouter | passed through raw; OpenRouter maps server-side |
| Older Anthropic (Sonnet 4.5, Haiku 4.5), older OpenAI (o1, o3), Gemini 2.x, DeepSeek, Perplexity, Reve, GGUF | ignored with a warning (dedup'd per Model instance) |
Downmap warnings use a dedicated EffortDownmapWarning category so you can filter them:
import warnings
from justai import EffortDownmapWarning
warnings.filterwarnings('ignore', category=EffortDownmapWarning)
# or promote to an error for strict pipelines:
warnings.filterwarnings('error', category=EffortDownmapWarning)
Effort is captured at request initiation. Mutating model.effort during an in-flight chat_async does not affect that request.
Related knobs to consider when raising effort:
- Anthropic reasoning tokens count against
max_tokens. Oneffort >= 'high'justai auto-raisesmax_tokensto4096if you did not set it explicitly (emits aUserWarning). Setmax_tokensyourself to disable. - For
effort='xhigh'or'max'on any provider, the defaulttimeout=120seconds is often insufficient. Settimeout=300(or higher) explicitly.
Level names are not calibrated across providers — 'high' on Anthropic burns different tokens than 'high' on OpenAI. Re-test cost/latency when switching models.
Agent
JustAI includes an Agent class for autonomous, tool-using agent execution. The agent runs in a loop: it reads a task file, calls tools as needed, and returns a final answer.
Basic agent usage
import asyncio
from justai import Agent, FileSystemTool
agent = Agent(
model='claude-sonnet-4-6',
role='Code reviewer',
goal='Review Python files and report issues',
tools=[FileSystemTool(read=['/path/to/src'])],
max_iterations=10,
)
async def main():
async for event in agent.run('tasks.md'):
if event.type == 'response':
print(event.content, end='')
elif event.type == 'done':
print(f'\nAnswer: {event.result.answer}')
asyncio.run(main())
Built-in tools
FileSystemTool — read/write files with path traversal protection:
FileSystemTool(read=['/allowed/read/dir'], write=['/allowed/write/dir'])
ShellTool — run shell commands with allowlist-based security:
ShellTool(allowlist=['echo', 'ls', 'python'])
WebFetchTool — fetch URLs with SSRF protection:
WebFetchTool()
Custom tools
@agent.tool
def search_database(ctx, query: str) -> str:
"""Search the database for matching records."""
return db.search(query)
Dynamic instructions
@agent.instructions
def inject_context(ctx) -> str:
return f'Current user: {ctx.deps["username"]}'
Skills
Load .md skill files to extend the agent's system prompt:
agent = Agent(
model='claude-sonnet-4-6',
role='Assistant',
goal='Help with tasks',
skills_dir='./skills',
)
Agent events
The agent.run() async generator yields AgentEvent objects with these types:
status— status messagesresponse— streamed text from the modeltool_call— tool invocation (withname,arguments,tool_result)error— error messagesdone— final result withAgentResult(answer, audit trail, token usage, iterations)
License
MIT
Project details
Release history Release notifications | RSS feed
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 justai-5.6.5.tar.gz.
File metadata
- Download URL: justai-5.6.5.tar.gz
- Upload date:
- Size: 279.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a46781362cd2ec4e3ba898af4225cc76c56b1b26ff98e465e8e01e43cdf93ba0
|
|
| MD5 |
a9692f8d6477dd42e42b1798aaf855c6
|
|
| BLAKE2b-256 |
ac4fb750226d8063ad4a9f6fd6001cdc35453ee9e287202a11bff59fcb16eb1e
|
File details
Details for the file justai-5.6.5-py3-none-any.whl.
File metadata
- Download URL: justai-5.6.5-py3-none-any.whl
- Upload date:
- Size: 60.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1be6a234ffc4f368fffa10cfb1a3cae245a8ae3c8f0f85f4e4ffc4621756fa9f
|
|
| MD5 |
e1aed7d9f21207a912f78c6e96e2337d
|
|
| BLAKE2b-256 |
a0e112ee3fb3d970b93757d50a02d61f81af043a088d9e5b1038053f035ab50c
|