pysaif
A local-first library that handles AI provider logistics — API calls, config, memory, quota — so you don't have to think about them every time.
from pysaif import AI
ai = AI()
response = ai.send("Hello!")
print(response)
Install
pip install pysaif
Setup
On first run, pysaif creates a Config/ folder next to your project with
config.toml (API keys, model, personality — the stuff you'll actually
touch) and advanced_config.toml (timeouts, retries, per-model metadata —
the stuff most people never open). Drop your API key into config.toml and
you're done.
Each project gets its own config and its own key — pysaif never shares keys or settings across projects, and never reads them from your environment implicitly.
What's implemented right now
- Config: self-healing setup, two-file split, schema versioning and migration hook
- Providers: Google Gemini, OpenAI, Anthropic Claude, OpenRouter (sync and async)
- Provider selection:
manual,cheapest,fastest, andsmartstrategies - Quota-aware daily token limits and automatic provider failover with retries
- Local memory under
Memory/(rolling window, important, interests) with inject/store toggles - Usage tracking under
Usage/(state + optional JSONL logs; message content off by default) - In-memory response cache (opt-in via
cache.enabled) - Prompt assembly from optional context, background, personality, and instructions
Configuration
pysaif splits config into two files so you only have to look at one of them.
Config/config.toml — everything you're likely to actually change:
[provider]
active = "gemini" # used when strategy = "manual"
strategy = "manual" # manual | cheapest | fastest | smart
fallback_order = ["gemini", "openai", "claude", "openrouter"]
[providers.gemini]
api_key = ""
model = "gemini-2.0-flash"
[providers.openai]
api_key = ""
model = "gpt-4o-mini"
[personality]
traits = "" # see "Personality" below
[prompt]
include_context = false
include_background = false
include_personality = true
[memory]
store = true
inject = true
rolling_window_size = 40
context_window_size = 12
max_important_messages = 50
max_interest_entries = 50
[quota]
enabled = true
daily_token_limit = 0 # 0 = unlimited
[cache]
enabled = false
max_entries = 128
provider.strategy—manualusesprovider.activedirectly;cheapest/fastest/smartpick a provider automatically each call (seepysaif/strategy/)provider.fallback_order— if the selected provider errors out or is rate-limited, pysaif retries down this listmemory.store— persist each exchange to local memory;memory.inject— feed relevant memory back in as context on future calls (these are independent — you can store without injecting, or vice versa)quota.daily_token_limit—0means unlimited; sits on top of whatever the provider's own API limits are
Config/advanced_config.toml — timeouts, retry/backoff, per-model cost and capability metadata, and logging settings (whether usage logs include the actual message text). Most people never need to open this one.
Personality
personality.traits in config.toml is a single free-text field — not a set of named profiles. Whatever you put there gets passed straight into the prompt as-is:
[personality]
traits = "Blunt, dry humor, no filler, gets to the point."
If prompt.include_personality is true (the default) and traits isn't empty, every call sends the provider a line like:
Respond with this personality: Blunt, dry humor, no filler, gets to the point.
You can override this per call without touching the config file:
response = ai.send("Hello!", personality="Overly formal, addresses you as 'sir'")
A per-call personality argument always wins over config.toml's traits — it's a one-time substitution, not a merge. Pass nothing and it falls back to traits; if traits is also empty, no personality line gets added to the prompt at all, and include_personality has nothing to include.
There's no personality file, no "active profile" concept, and no preset library — it's intentionally just a string you write. If you want several distinct personas, keep the different strings somewhere in your own project and pass whichever one you want as personality= on each call.
What's designed but not built yet
Interactive setup wizard, richer capability tools (search/look/make), and
automated config migrations beyond schema backfill — see the module skeleton
under pysaif/capabilities/ for planned expansion points.
License
See LICENSE.
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 pysaif-0.1.1.tar.gz.
File metadata
- Download URL: pysaif-0.1.1.tar.gz
- Upload date:
- Size: 25.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
31bf0e26fcc4b9ae5df8191168bac8a15dd644cf1c6f5ecf788344c49a43838f
|
|
| MD5 |
59ade94013a4964908a814b241d5d40d
|
|
| BLAKE2b-256 |
8efe24b0cc64ffe9d3dd3c34b76e8a390906967641391b14b3c5cf778eabc344
|
File details
Details for the file pysaif-0.1.1-py3-none-any.whl.
File metadata
- Download URL: pysaif-0.1.1-py3-none-any.whl
- Upload date:
- Size: 35.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1f3dfdf7d6b7c1e9cdf0bd9ab21d36473de83c710337b22c0ecfde8ed772fd82
|
|
| MD5 |
8b22f2a995fc146ce9b567c52876356a
|
|
| BLAKE2b-256 |
f79e0c59c75369d1adbe87ff057a9e94e1a1955cf7c27ca2366462d1e71ab18b
|