Soup
Soup is a provider-agnostic Agent Skills router for LLMs.
You define a large number of Agent Skills
(rules, best practices, instructions) — in code, as SKILL.md files or with remote sources. On every
LLM call, Soup injects only the skills that are actually relevant to the
request.
Why?
Monolithic prompts do not scale once your skill catalog spans multiple domains, tools, and workflows. Soup routes only the relevant Agent Skills for each request, so you do not ship unrelated instruction blocks on every call.
- Reducing tokens sent on each call.
- Improving context quality (only relevant instructions).
- Avoiding monolithic prompts.
- Making context modular, composable and reusable across projects, using the open Agent Skills format so your skills are portable.
Soup is completely independent from the LLM provider. It works with OpenAI, Anthropic, Gemini, Ollama, LiteLLM, LangChain, etc. — because it just transforms the prompt/messages you already have.
Install
pip install soup-ai # or: uv add soup-ai
Requires Python 3.12+. Runtime dependencies are pydantic and pyyaml.
Even faster: let your coding agent do it
This repo ships its own Agent Skill, skills/integrate-soup,
that automates the whole setup. Copy the skills/integrate-soup directory
into your project (e.g. Cursor users can drop it into .cursor/skills/), then
ask your agent something like "use the integrate-soup skill to add Soup to
this project." It scans your codebase for where LLM prompts/messages are
built, drafts a concrete integration plan, and — once you approve it — splits
your existing prompts into skills and wires soup.prepare() into your call
sites for you.
Quickstart
from soup import Soup
soup = Soup()
soup.register(
name="react-ui",
description="React UI implementation patterns. Use for React components, hooks, and layout tasks.",
instructions="""
Use React 19 function components.
Prefer composition and colocated state.
Keep accessibility semantics in markup.
""",
tags=["react", "ui", "components"],
)
soup.register(
name="sql-postgres",
description="Postgres SQL guidance. Use for query design and indexing tasks.",
instructions="""
Use parameterized queries.
Add indexes for frequent filters.
Review query plans before optimization.
""",
tags=["sql", "postgres", "database"],
)
# String prompt in, string prompt out:
prompt = soup.prepare("I need guidance on building a React dashboard card component with hooks.")
# ...or chat messages in, chat messages out:
messages = soup.prepare(
[{"role": "user", "content": "I need guidance on building a React dashboard card component with hooks."}]
)
The integration is meant to be invisible:
response = client.responses.create(
model="gpt-5",
input=soup.prepare(messages),
)
Registering skills
register() is the single entry point. It dispatches on what you give it.
# 1. Define a skill in code (name, description and instructions are required):
soup.register(
name="pdf-processing",
description="Extract PDF text, fill forms, and merge files. Use for PDF tasks.",
instructions="Use pypdf for extraction...",
)
# 2. Load a single local skill directory (a folder containing SKILL.md):
soup.register("./skills/pdf-processing")
# 3. Load a whole local collection (a folder of skill directories):
soup.register("./skills")
# 4. Load from a GitHub or GitLab repo over raw HTTP (no git required):
soup.register("https://github.com/vercel-labs/skills", ref="main")
soup.register("https://gitlab.com/group/project", ref="main")
Soup's optional routing behavior stays simple. For a single skill, pass overrides
as keyword arguments; for a collection or repo, pass per-skill options:
soup.register("./skills/pdf-processing", dependencies=["files"], version="1.0")
soup.register(
"./skills",
options={
"pdf-processing": {"dependencies": ["files"], "priority": 10},
"data-analysis": {"version": "2.1"},
},
)
Core concepts
Skill
A Skill is the atomic unit of context and follows the Agent Skills spec.
| Field | Group | Type | Purpose |
|---|---|---|---|
name |
required | str |
Unique identifier (spec naming rules; matches dir name). |
description |
required | str |
What it does and when to use it; primary routing signal. |
instructions |
required | str |
The context injected; the SKILL.md Markdown body. |
license |
spec | str | None |
License name or bundled file reference. |
compatibility |
spec | str | None |
Environment requirements (max 500 chars). |
allowed_tools |
spec | list[str] |
Pre-approved tools (allowed-tools, experimental). |
metadata |
spec | dict[str, str] |
Arbitrary key-value mapping; stores Soup extensions. |
tags |
soup | list[str] |
Keywords for tag-aware selection. |
examples |
soup | list[str] |
"Training phrases" also used for BM25 selection, rendered after the instructions. |
priority |
soup | int |
Tie-breaker; reserved for future compression. |
dependencies |
soup | list[str] |
Skills to always include alongside this one. |
extends |
soup | list[str] |
Parent skills this one specializes (composition). |
version |
soup | str | None |
Version string for sharing/reuse. |
SKILL.md files
A SKILL.md file is YAML frontmatter followed by a Markdown body. Soup's own
routing extensions live under spec-compliant metadata (string values are
canonical; comma-separated strings are parsed into lists):
---
name: pdf-processing
description: Extract PDF text, fill forms, and merge files. Use for PDF tasks.
license: MIT
metadata:
version: "1.0"
dependencies: "files"
tags: "pdf, forms, documents"
priority: "10"
---
Use pypdf for extraction. Validate inputs before merging...
The Markdown body becomes the skill's instructions. The name must follow the
spec rules (lowercase letters, numbers and hyphens, 1–64 chars) and match its
directory name.
Composition & versioning (extends)
Skills are composable and versionable, so you can build hierarchies and reuse rules across projects without duplication:
soup.register(name="frontend", description="Frontend basics. Use for UI work.",
tags=["ui"], instructions="Accessibility first.")
soup.register(name="react", description="React rules. Use for React apps.",
version="1.0", extends=["frontend"], tags=["react"], instructions="Use hooks.")
soup.register(name="nextjs", description="Next.js conventions. Use for Next.js.",
version="2.1", extends=["react"], tags=["nextjs"], instructions="Use the app router.")
Selecting nextjs automatically pulls in react and frontend, rendered
parent-first. dependencies work the same way (transitive inclusion) but express
a companion relationship rather than specialization. Cycles are detected and
broken, and missing references raise MissingDependencyError (configurable).
Selection strategies
By default, Soup uses a single built-in selector: BM25Strategy (no
embeddings, no vector DB, no RAG).
If you need custom behavior, you can still add your own strategy:
from soup import SelectionStrategy
class RegexStrategy(SelectionStrategy):
def select(self, query, skills):
...
soup.add_strategy(RegexStrategy())
For architecture and design details, see docs/architecture.md.
Examples
See examples/ for complete, runnable integrations with
OpenAI, OpenRouter, Anthropic, LiteLLM and Ollama.
Development
uv venv && uv pip install -e ".[dev]"
ruff check .
mypy
basedpyright
pytest --cov=soup --cov-report=term-missing
Roadmap
V1 loads SKILL.md instructions and metadata only. Full Agent Skills support —
exposing bundled scripts/, references/, and assets/ files to the
LLM/runtime — is tracked as a follow-up.
Changelog
See CHANGELOG.md for release notes.
License
MIT.
Metadata
Release files for soup-ai 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| soup_ai-0.2.1.tar.gz | 46.0 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| soup_ai-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 77.4 kB
Release files / soup_ai-0.2.1.tar.gz
| Download URL | soup_ai-0.2.1.tar.gz |
|---|---|
| Size | 46.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9edb592749664d007db3aa6293401080d53d25035445ad4e9ec5e208fbd12216
|
|
BLAKE2b-256 checksum How to use checksums |
e3848d7fe493629164317517acede2714864ab71c40f05e27353516897d565d3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 7, 2026.
Transparency logRelease files / soup_ai-0.2.1-py3-none-any.whl
| Download URL | soup_ai-0.2.1-py3-none-any.whl |
|---|---|
| Size | 31.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d368669e583729ee7533feac04516fe13c6d9ba78c1123b6ecfb7e5580beeec8
|
|
BLAKE2b-256 checksum How to use checksums |
de3fab677f4bcab0f2b863428b4c712c8cf38e2250ddda1651623e607f372df7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 7, 2026.
Transparency logSecurity for soup-ai 0.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for soup_ai-0.2.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 7, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.