One interface. 8 providers. 63 models. Text, images, vision, RAG, agents, parallel pipelines.
Project description
aichain
The simplest way to build AI pipelines. 8 providers. 1 interface. Zero lock-in.
from yait_aichain import Model, Skill
skill = Skill(
model = Model("claude-sonnet-4-6"), # change this one word to switch providers
input = {"messages": [{"role": "user", "parts": ["Summarise: {text}"]}]},
)
result = skill.run(variables={"text": "..."})
Change "claude-sonnet-4-6" to "gpt-4o", "gemini-2.5-pro", or "grok-3" — nothing else changes.
Why aichain?
Every major AI library makes you choose: LangChain is too complex, LlamaIndex is RAG-only, CrewAI is agents-only, AutoGen requires a PhD to configure.
aichain covers the full stack with the simplest interface:
| Need | aichain primitive |
|---|---|
| Call any LLM | Skill |
| Chain steps together | Chain |
| Run tasks in parallel | Pool |
| Autonomous reasoning | Agent |
| Vector search + RAG | VectorDB + vectorQuery |
| Rerank results | Reranker |
| Call any tool or MCP server | Tool / MCPTools |
All of these work identically across 63 models from 8 providers — with one line to swap any of them.
Install
pip install yait-aichain
Optional extras:
pip install markitdown # file/URL → Markdown
pip install pyyaml # save & load Skills and Chains
pip install fastmcp # MCP server integration
API keys — only for the providers you use:
export ANTHROPIC_API_KEY="sk-ant-…"
export OPENAI_API_KEY="sk-…"
export GOOGLE_AI_API_KEY="AIza…"
export XAI_API_KEY="xai-…"
export PERPLEXITY_API_KEY="pplx-…"
export MOONSHOT_API_KEY="sk-…" # Kimi
export DEEPSEEK_API_KEY="sk-…"
export DASHSCOPE_API_KEY="sk-…" # Qwen
export COHERE_API_KEY="…" # embeddings + reranking
export VOYAGE_API_KEY="…" # embeddings + reranking
8 providers, one syntax
from yait_aichain.models import Model
Model("claude-sonnet-4-6") # Anthropic
Model("gpt-4o") # OpenAI
Model("gemini-2.5-flash") # Google
Model("grok-3") # xAI
Model("sonar-pro") # Perplexity
Model("kimi-k2.5") # Kimi
Model("deepseek-chat") # DeepSeek
Model("qwen-max") # Qwen
63 models total. Full list: model registry →
Core concepts
Skill — one prompt, any model
skill = Skill(model=Model("gpt-4o-mini"), input={...})
result = skill.run(variables={"topic": "neural networks"})
Chain — sequential steps, automatic variable flow
chain = Chain(steps=[
(fetch_tool, "page"),
(summarise, "summary"),
(translate, "result"),
])
result = chain.run(variables={"url": "https://…", "language": "French"})
print(chain.history) # full audit trail
Pool — parallel execution
pool = Pool(summarise_skill, items=[{"text": t} for t in documents], max_flows=10)
results = pool.run() # all documents processed simultaneously
print(pool.status) # {PENDING: 0, RUNNING: 0, DONE: 50, FAILED: 0}
print(pool.history) # per-item: status, output, error, duration
Agent — autonomous reasoning
agent = Agent(
orchestrator = Model("claude-opus-4-8"),
tools = [searchPerplexity(), convertToMD()],
mode = "agile",
max_steps = 10,
)
result = agent.run("Compare the top 3 vector databases.")
print(result.output)
print(f"steps={result.steps_taken} tokens={result.tokens_used:,}")
Full RAG pipeline
from yait_aichain.tools.embedding import Embedding
from yait_aichain.tools.vectordb import VectorDB, vectorChunk, vectorUpsert, vectorQuery
from yait_aichain.tools.reranking import Reranker
store = VectorDB("chroma", "docs", embedder=Embedding("cohere/embed-v4.0"))
reranker = Reranker("cohere/rerank-v3.5")
# Ingest
chunks = vectorChunk(max_chars=800).run(my_document)
vectorUpsert(store).run([{"id": f"c{i}", **c} for i, c in enumerate(chunks)])
# Query → rerank → answer
pipeline = Chain(steps=[
(vectorQuery(store), "candidates", {"input": "{question}", "options": {"n": 20}}),
(reranker, "context", {"input": "{candidates}",
"options": {"query": "{question}", "top_n": 5}}),
answer_skill,
])
answer = pipeline.run(variables={"question": "How does KV caching work?"})
Vector DB providers: Chroma · Qdrant · Pinecone Reranking providers: Cohere · Voyage · Qwen
Cost, routing & resilience
These are all opt-in — the minimal program above is unchanged.
Token usage & cost — every result carries normalised usage:
skill.run()
skill.last_usage.input_tokens # 1240
skill.last_usage.total_tokens # 1310
skill.last_usage.cost # 0.0032 (USD, None if the model has no price)
chain.run()
chain.last_usage.cost # summed across all Skill steps
Explicit provider routing — pick the provider with a provider/model
prefix; it also unlocks custom / fine-tuned names the auto-detector can't
recognise:
Model("openai/gpt-4o") # same as Model("gpt-4o")
Model("openai/ft:gpt-4o:acme:42") # custom name, explicit provider
Fallback chain — pass a list; a transient failure (rate limit / server / network) advances to the next model. A real error (bad key, bad request) still raises immediately:
skill = Skill(
model = [Model("claude-sonnet-4-6"), Model("gpt-4o")], # primary, then backup
input = {"messages": [{"role": "user", "parts": ["..."]}]},
)
Typed errors — catch a specific failure mode, or APIError for all:
from yait_aichain import RateLimitError, AuthenticationError, APIError
try:
skill.run()
except RateLimitError as e:
wait(e.retry_after) # honours the Retry-After header
except AuthenticationError:
...
Keep the model list fresh — diff the registry against a provider's live roster:
from yait_aichain.models import registry
registry.refresh("openai") # → {"new": [...], "removed": [...], ...}
Built-in tools
Search
searchPerplexity · searchBrave · searchSerp · searchOpenAI
Convert
convertToMD · convertToHTML · convertToPDF · TTS(provider) · STT(provider)
Embeddings
Embedding("openai/text-embedding-3-small")
Embedding("cohere/embed-v4.0")
Embedding("voyage/voyage-3-large")
Examples
→ examples/ — 18 focused examples, one concept each
| # | File | What it shows |
|---|---|---|
| 01 | 01_skill.py |
The minimum viable aichain program |
| 02 | 02_skill_models.py |
Same prompt, Claude + GPT + Gemini |
| 03 | 03_skill_multimodal.py |
Text → image → vision, three providers |
| 04 | 04_skill_save_load.py |
Save to YAML, reload anywhere |
| 05 | 05_tool_convert.py |
URL → Markdown |
| 06 | 06_tool_mcp.py |
Connect an MCP server, discover + call tools |
| 07 | 07_tool_custom.py |
Build your own Tool, plug into a Chain |
| 08 | 08_chain.py |
GPT writes → Claude reviews |
| 09 | 09_chain_tool_skill.py |
Fetch page → summarise |
| 10 | 10_chain_save_load.py |
Save/reload a full pipeline |
| 11 | 11_pool.py |
5 topics, all in parallel |
| 12 | 12_pool_chain.py |
Chain-per-item, all in parallel |
| 13 | 13_agent.py |
Autonomous agent, one tool |
| 14 | 14_agent_tools.py |
Agent picks its own tools |
| 15 | 15_agent_orchestrator.py |
Orchestrator spawns sub-agents |
| 16 | 16_debug.py |
Inspect Chain history, Pool status, Agent steps |
| 17 | 17_chain_human_input.py |
Pause a chain for human approval, then resume |
| 18 | 18_agent_external_trigger.py |
Suspend an agent; a webhook resumes it (cross-process) |
Persist and reload
skill.save("skills/translator.yaml")
skill = Skill.load("skills/translator.yaml") # API key from env, not file
chain.save("chains/research.yaml")
chain = Chain.load("chains/research.yaml")
Supported providers
| Provider | Text | Vision | Image gen | Env var |
|---|---|---|---|---|
| Anthropic | Claude Fable 5, Opus / Sonnet / Haiku 4 | ✓ | — | ANTHROPIC_API_KEY |
| OpenAI | GPT-5.5, GPT-5.4, GPT-4o | ✓ | ChatGPT-Image, GPT-Image-2 | OPENAI_API_KEY |
| Gemini 2.5 Pro / Flash, 3.x | ✓ | Gemini image models | GOOGLE_AI_API_KEY |
|
| xAI | Grok 4, Grok 3 | ✓ | Grok-Imagine | XAI_API_KEY |
| Perplexity | Sonar Pro, Sonar, Deep Research | — | — | PERPLEXITY_API_KEY |
| Kimi | K2.5, K2, K2 Turbo, K2 Thinking | ✓ | — | MOONSHOT_API_KEY |
| DeepSeek | DeepSeek-V3, DeepSeek-R1 | — | — | DEEPSEEK_API_KEY |
| Qwen | Qwen-Max, Qwen3, QwQ | ✓ | Wan 2.2 image | DASHSCOPE_API_KEY |
Embedding: OpenAI · Cohere · Voyage · Google · Qwen
Reranking: Cohere · Voyage · Qwen
Vector DB: Chroma · Qdrant · Pinecone
License
MIT
Changelog
1.3.5
Model registry refresh — Google image models.
- Gemini image generation is now GA:
gemini-3.1-flash-imageandgemini-3-pro-image(the-previewsuffixes are gone), plus the newgemini-2.5-flash-image. These are the migration target for Google's discontinued Imagen 4 endpoints (imagen-4.0-*), which this library never referenced.
1.3.4
Agent engine, token accounting, and transport fixes from the audit.
- Token budget is enforced within a step (after the action and execution
calls, not only between steps), so one step can no longer overshoot
max_tokens; agile replans are capped; tokens from an unparseable orchestrator reply are still counted. - Honest success: a run fails if any executed step ended in an execution
error (not only the last), and the final output is the last executed step's
output (even
None) rather than a stale earlier value. - Agent LLM calls go through the
send()seam (consistent with Skill; async providers work). - Usage:
Skill.last_usageresets each run (no stale value after a failure);chain.last_usageincludes Agent-step tokens;NetworkErroris retried within a model whenmax_retries > 0. - Robustness: token extraction tolerates
usage: null/ non-numeric; the DeepSeek-reasoner gate matches the name exactly; Google embeddings acceptGOOGLE_API_KEYorGOOGLE_AI_API_KEY. - Unified HTTP transport: one
make_http()factory builds the urllib3 manager for both model and tool clients and honoursHTTPS_PROXY/HTTP_PROXY— a proxy now applies to tool traffic (search, fetch, REST, …), not just LLM calls.
1.3.3
Durable-run (suspend/resume) correctness fixes from the audit.
- Resume no longer re-runs already-attempted steps: skipped/failed steps get a terminal status and the resume cursor skips past them; a terminal error during a resumed run clears the parked document, so a duplicate trigger is a no-op (no double side effects).
- A nested
Agentthat pauses (Wait/Gate) inside aChainnow propagates the suspension up —chain.run()returns aSuspendedResultinstead of reporting a failed step, andchain.resume()continues the child agent run. RunContextis now real: exposed aschain.context/agent.contextduring a run, persisted in the run document, and restored onresume(Agent.run/Agent.resumeacceptcontext=too).FileStore: a non-JSON-serialisable variable/output at a suspend point raises a clear error instead of a rawTypeError.
1.3.2
Correctness & safety fixes from a code audit.
- Qwen image: a terminal task failure (FAILED / timeout / no result) now raises
a non-retryable
TaskFailedError, so a retry or model fallback can't silently submit a second billable generation. - SSRF hardening:
RestApiToolnow blocks private / loopback / metadata targets (opt out withAICHAIN_ALLOW_PRIVATE_URLS); the Qwen TTS audio download is guarded and no longer follows redirects. - VectorDB:
ChromaBackend.deleterefuses an emptyids+filter(which would wipe the whole collection), matching Qdrant. FileStore:fsyncbefore the atomic rename (durable parked runs); a corrupt run file raises a clear error instead of a rawJSONDecodeError.convertToHTMLwrites through the confined output path, consistent with the other convert tools.
1.3.1
Image-generation fixes and additions.
- Qwen text-to-image now works. DashScope serves the
wanmodels only through its native asynchronous task API, so a new request lifecycle (submit → poll → download) runs behind asend()seam on the client and returns the image in the same shape the synchronous path does. Registry updated to the availablewan2.2-t2i-flash/wan2.2-t2i-plus(the oldwanx2.1-*ids 404'd). - New OpenAI image models:
chatgpt-image-latest(the always-current model used by ChatGPT — fast, recommended default) andgpt-image-2. - Transparent backgrounds for
gpt-image-*/chatgpt-image-*:backgroundandoutput_formatare forwarded from the output format (background: "transparent",output_format: "png"), so these models can emit real PNG alpha. Theresponse_formatparam is correctly omitted for thechatgpt-image-*family (it returns base64 natively and rejects the param).
1.3.0
Durable, resumable runs — the serverless core. A Chain or Agent can pause until an external signal arrives (a human, a webhook, a cron tick) and resume later, even in a different process. All additive; existing programs are unchanged.
Wait/Gatesuspend tools — pause a run until a signal arrives; on resume the signal drives the step.Waitis a leaf (its output is the signal);Gatewraps any tool behind an approval decision.Chain.resume(run_id, signal)/Agent.resume(run_id, signal)— continue a suspended run from where it paused; completed steps are not re-run.- Self-contained run documents in a pluggable
StateStore:InMemoryStore(default, process-local) andFileStore(survives restart); subclass for S3/DynamoDB/any KV. The store holds only suspended runs and a resume is idempotent — a duplicate trigger is a no-op. run()returns a falsySuspendedResult(carryingrun_idandawaiting) when paused instead of a final result.RunContext(tenant, metadata) can be passed torun()for per-request context.
1.2.6
- Security hardening: SSRF guard on outbound URL tools (private/loopback ranges
blocked; opt out with
AICHAIN_ALLOW_PRIVATE_URLS=1), URL-scheme allow-list, and output-path confinement (AICHAIN_OUTPUT_ROOT). Safe-class checks on tool instantiation during chain load.
1.2.5
- Correctness fixes across the model and tooling layer: response parsing, search/MCP timeouts, table chunking when a header exceeds the chunk size, and assorted edge cases surfaced by analysis.
- Refreshed the model registry to current provider catalogues and June 2026 pricing.
1.2.3
Two-tier model layer: format is code (by API family), provider is data.
Changing provider — one word in Model("…") — changes only data, never the
request format or any model code. Behaviour is byte-for-byte unchanged
(guarded by characterisation tests).
Modelis a single, thin, data-driven class: it resolves the provider from the name and delegates the wire format to the matching family client. The provider is exposed asmodel._provider.- Provider settings, model capabilities and prices live in data — one file per
provider under
models/providers/*.toml. - The protocol layer (
clients/) owns the wire format. Five family clients cover all eight providers:OpenAIClient(openai, xai, kimi, deepseek),PerplexityClient,QwenClient,AnthropicClient,GoogleClient. - Removed the per-provider
Modelsubclasses and client classes; useModel("name")(or an explicitprovider/prefix for custom names). - Fixed Qwen region endpoints (
us,hk) and base URL.
1.2.1
- Removed leaked application code from the library (
skills/summarise.py,tools/section_context.py) — an app-specific pipeline that did not belong in the general-purpose library.
1.2.0
Mechanism 1 — "LLM layer as data". All additive; the minimal program is unchanged.
Usageon every result — normalisedinput_tokens/output_tokens/total_tokensacross providers; additive;skill.last_usageandchain.last_usagesum across steps.- Cost estimation from a per-model price table (
usage.cost;Nonewhen a model is unpriced). - Exception hierarchy under
APIError(RateLimitError,AuthenticationError,InvalidRequestError,NotFoundError,ServerError,NetworkError). provider/modelrouting —Model("openai/gpt-4o"); unlocks custom / fine-tuned names.- Model fallback chain —
Skill(model=[primary, backup, …])advances on a transient failure; a non-transient failure propagates immediately. registry.refresh(provider)— diffs the registry against the provider's livelist_models().
1.1.0
Foundation repaired: five features that never worked in the installed package are revived, plus fragility closed across the library. 69 regression tests.
- Fixed
Chain.load(), Agent insideChain, the Qwen embedder/reranker, and Agent persistent memory — each previously crashed or was dead code. - Honest
success=Falseon token-budget exhaustion and exhausted step retries. - Hardened LLM-response and agent-JSON parsing; timeouts on all search tools
and the MCP bridge;
delete(ids=[])no longer wipes a collection. - Chunker contract fixes; thread-safe
Chain.run(); fixedfrom yait_aichain.tools import *. - POST retry policy (429/503); Anthropic auto-raises
max_tokensabove the thinking budget; gpt-5 / o-series parameter correctness; Chroma v2; Qdrant string IDs; batched VectorDBupsert; safe prompt templating. - Security: the Google API key moved from the query string to the
x-goog-api-keyheader.
1.0.0
Initial release — Skill, Chain, Pool, Agent, Tool / MCPTools,
VectorDB, Reranker, and 8 providers (Anthropic, OpenAI, Google, xAI,
Perplexity, Kimi, DeepSeek, Qwen).
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 yait_aichain-1.3.5.tar.gz.
File metadata
- Download URL: yait_aichain-1.3.5.tar.gz
- Upload date:
- Size: 206.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2368d72366615ff90c9df950d8b2a210a1a9e25ff3d15dcbc735cc508e515070
|
|
| MD5 |
a6fff6f7730dddfbc8285c21c3a60779
|
|
| BLAKE2b-256 |
56516bd99fcf96c10b024b06fcbde3f7bc5788096ad2cdac420d28e81bd8e1e5
|
Provenance
The following attestation bundles were made for yait_aichain-1.3.5.tar.gz:
Publisher:
publish.yml on yaitio/aichain
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
yait_aichain-1.3.5.tar.gz -
Subject digest:
2368d72366615ff90c9df950d8b2a210a1a9e25ff3d15dcbc735cc508e515070 - Sigstore transparency entry: 1826767939
- Sigstore integration time:
-
Permalink:
yaitio/aichain@c02eb6ee9cf7a62c0698b96755ab84fecf152efd -
Branch / Tag:
refs/tags/v1.3.5 - Owner: https://github.com/yaitio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c02eb6ee9cf7a62c0698b96755ab84fecf152efd -
Trigger Event:
push
-
Statement type:
File details
Details for the file yait_aichain-1.3.5-py3-none-any.whl.
File metadata
- Download URL: yait_aichain-1.3.5-py3-none-any.whl
- Upload date:
- Size: 240.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ad41a6cda27b045eeef3c2417418be4a5dee6804fc0c22a49e29feee971db08
|
|
| MD5 |
6daf428b336f98a4cd31ce71fc8bb515
|
|
| BLAKE2b-256 |
6d03e516a407b9a4d547cbd8fcdd8f9bb018c495fb83b1912121a8e70a770bf2
|
Provenance
The following attestation bundles were made for yait_aichain-1.3.5-py3-none-any.whl:
Publisher:
publish.yml on yaitio/aichain
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
yait_aichain-1.3.5-py3-none-any.whl -
Subject digest:
9ad41a6cda27b045eeef3c2417418be4a5dee6804fc0c22a49e29feee971db08 - Sigstore transparency entry: 1826768403
- Sigstore integration time:
-
Permalink:
yaitio/aichain@c02eb6ee9cf7a62c0698b96755ab84fecf152efd -
Branch / Tag:
refs/tags/v1.3.5 - Owner: https://github.com/yaitio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c02eb6ee9cf7a62c0698b96755ab84fecf152efd -
Trigger Event:
push
-
Statement type: