Soup is a spec-compliant Agent Skills router for LLMs with deterministic BM25 routing: define many small skills and inject only the relevant ones into each call.
Project description
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.
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 soup_ai-0.2.1.tar.gz.
File metadata
- Download URL: soup_ai-0.2.1.tar.gz
- Upload date:
- Size: 46.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9edb592749664d007db3aa6293401080d53d25035445ad4e9ec5e208fbd12216
|
|
| MD5 |
2a7a7b06c9fae3ff0cf5965cf6da23b9
|
|
| BLAKE2b-256 |
e3848d7fe493629164317517acede2714864ab71c40f05e27353516897d565d3
|
Provenance
The following attestation bundles were made for soup_ai-0.2.1.tar.gz:
Publisher:
publish.yml on southwind-ai/soup
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
soup_ai-0.2.1.tar.gz -
Subject digest:
9edb592749664d007db3aa6293401080d53d25035445ad4e9ec5e208fbd12216 - Sigstore transparency entry: 2102931316
- Sigstore integration time:
-
Permalink:
southwind-ai/soup@ba8d72e3a4f3283d0c06a9df6154e6b3af7892e6 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/southwind-ai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ba8d72e3a4f3283d0c06a9df6154e6b3af7892e6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file soup_ai-0.2.1-py3-none-any.whl.
File metadata
- Download URL: soup_ai-0.2.1-py3-none-any.whl
- Upload date:
- Size: 31.3 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 |
d368669e583729ee7533feac04516fe13c6d9ba78c1123b6ecfb7e5580beeec8
|
|
| MD5 |
3e46a1ba5e7e14abb1247258c7f373ac
|
|
| BLAKE2b-256 |
de3fab677f4bcab0f2b863428b4c712c8cf38e2250ddda1651623e607f372df7
|
Provenance
The following attestation bundles were made for soup_ai-0.2.1-py3-none-any.whl:
Publisher:
publish.yml on southwind-ai/soup
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
soup_ai-0.2.1-py3-none-any.whl -
Subject digest:
d368669e583729ee7533feac04516fe13c6d9ba78c1123b6ecfb7e5580beeec8 - Sigstore transparency entry: 2102931971
- Sigstore integration time:
-
Permalink:
southwind-ai/soup@ba8d72e3a4f3283d0c06a9df6154e6b3af7892e6 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/southwind-ai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ba8d72e3a4f3283d0c06a9df6154e6b3af7892e6 -
Trigger Event:
push
-
Statement type: