Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

soup_ai-0.2.1.tar.gz (46.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

soup_ai-0.2.1-py3-none-any.whl (31.3 kB view details)

Uploaded Python 3

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

Hashes for soup_ai-0.2.1.tar.gz
Algorithm Hash digest
SHA256 9edb592749664d007db3aa6293401080d53d25035445ad4e9ec5e208fbd12216
MD5 2a7a7b06c9fae3ff0cf5965cf6da23b9
BLAKE2b-256 e3848d7fe493629164317517acede2714864ab71c40f05e27353516897d565d3

See more details on using hashes here.

Provenance

The following attestation bundles were made for soup_ai-0.2.1.tar.gz:

Publisher: publish.yml on southwind-ai/soup

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for soup_ai-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d368669e583729ee7533feac04516fe13c6d9ba78c1123b6ecfb7e5580beeec8
MD5 3e46a1ba5e7e14abb1247258c7f373ac
BLAKE2b-256 de3fab677f4bcab0f2b863428b4c712c8cf38e2250ddda1651623e607f372df7

See more details on using hashes here.

Provenance

The following attestation bundles were made for soup_ai-0.2.1-py3-none-any.whl:

Publisher: publish.yml on southwind-ai/soup

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page