Skip to main content

Rhylthyme Importers

Import plugins for converting external data sources into Rhylthyme programs.

Installation

pip install rhylthyme-importers              # TheMealDB, Spoonacular, protocols.io, Opentrons, Benchling, recipe sites
pip install "rhylthyme-importers[cooklang]"   # + CookLang .cook files

Python 3.12 or newer. From a checkout: pip install -e ./rhylthyme-importers.

Importers read a URL, an id, or text you pass them. import_from_url() refuses a local path unless the importer was built with allow_local_files=True; the command-line tools do that, a server should not.

What is here

Each importer turns one kind of source into a Rhylthyme program: parallel tracks of timed steps with shared-equipment limits, ready for rhylthyme validate, rhylthyme publish or the web app. An importer reads structure, not meaning, so what it produces is faithful to the source and mostly sequential; see Enrichment for how tracks and cross-track triggers are added afterwards.

Importer Source Input Needs
themealdb TheMealDB, a free recipe API meal id or URL nothing
spoonacular Spoonacular recipe API recipe id or URL SPOONACULAR_API_KEY
recipe-scrapers several hundred recipe websites recipe page URL nothing
cooklang CookLang .cook files raw or GitHub URL, or a file [cooklang] extra
protocolsio protocols.io protocol URL or id PROTOCOLS_IO_TOKEN
opentrons Opentrons Protocol API v2 .py file or pasted source nothing
benchling Benchling protocols, workflow tasks, notebook entries protocol URL or id a Benchling API token
slidedeck PowerPoint .pptx file nothing

rhylthyme-import list prints the registered importers; rhylthyme-import import <url> picks one by URL.

TheMealDB

Recipes from TheMealDB. No key needed.

rhylthyme-import search "chicken curry" -i themealdb
rhylthyme-import import "https://www.themealdb.com/meal/52772" -o curry.json --pretty
rhylthyme-import mealdb random -o random_meal.json --pretty
rhylthyme-import mealdb categories
from rhylthyme_importers import TheMealDBImporter
result = TheMealDBImporter().import_from_url("52772")

Spoonacular

Recipes from Spoonacular, with structured ingredients, equipment and nutrition. Set SPOONACULAR_API_KEY (free tier available); without it, search returns nothing and imports fail with a message saying so.

export SPOONACULAR_API_KEY=...
rhylthyme-import search "pad thai" -i spoonacular
rhylthyme-import import "https://spoonacular.com/recipes/pad-thai-716429" -o pad_thai.json --pretty
from rhylthyme_importers import SpoonacularImporter
SpoonacularImporter(api_key="...").import_from_url("716429")

Recipe sites (recipe-scrapers)

Any of the several hundred recipe websites that recipe-scrapers understands (Serious Eats, BBC Good Food, AllRecipes, NYT Cooking, ...). Pass the page URL; the importer is chosen automatically when the host is supported.

rhylthyme-import import "https://www.seriouseats.com/the-best-chili-recipe" -o chili.json --pretty

rhylthyme-sweep-supported-sites checks which of those sites still parse (for catalogue maintenance).

CookLang

Individual CookLang .cook files, from a raw URL, a GitHub blob URL (converted to raw automatically) or a local file. Needs the cooklang extra: pip install "rhylthyme-importers[cooklang]".

rhylthyme-import import "https://github.com/cooklang/cookcli/blob/main/seed/Neapolitan%20Pizza.cook" \
    -o pizza.json --pretty
from rhylthyme_importers import CooklangImporter
CooklangImporter().import_from_url("https://raw.githubusercontent.com/cooklang/cookcli/main/seed/Neapolitan%20Pizza.cook")
CooklangImporter(allow_local_files=True).import_from_url("Pizza.cook")
CooklangImporter().import_from_content(open("Pizza.cook").read(), source_name="Pizza")

Timers (~{10%minutes}) become step durations; ingredients and cookware land in metadata.

CookLang Federation (bulk)

Scrape every recipe in the Cooklang Federation, about 76 GitHub repositories, and filter out placeholders. Reproducible and idempotent on re-run.

rhylthyme-import-cooklang-federation --discover                       # 1. list source repos
rhylthyme-import-cooklang-federation --import-all --staged-out /tmp/staged.json   # 2. scrape + filter
rhylthyme-import-cooklang-federation --review /tmp/staged.json        # 3. what passed, what was dropped
rhylthyme-import-cooklang-federation --refilter /tmp/staged.json      # 4. re-apply edited rules, no re-download

The staged file is a list of programs; --upload sends it to a Rhylthyme catalogue.

The filter rejects placeholder names (recipe, untitled, template, test, Copy of ...), hash-like or all-digit names, and programs with no ingredients or no steps. Short real names (Roux, Filo, Dahl, Toum) are kept.

protocols.io

Laboratory protocols from protocols.io. Needs PROTOCOLS_IO_TOKEN (from https://www.protocols.io/developers). Each protocol step keeps its source sentence on the step, so enrichment can say which words a step came from.

export PROTOCOLS_IO_TOKEN=...
rhylthyme-import search "western blot" -i protocolsio
rhylthyme-import import "https://www.protocols.io/view/western-blot-..." -o blot.json --pretty
from rhylthyme_importers import ProtocolsIOImporter
ProtocolsIOImporter(access_token="...").import_from_url("https://www.protocols.io/view/...")

Opentrons

An Opentrons Protocol API v2 .py file (OT-2 or Flex) becomes a program with one track per pipette mount or module; durations come from a static lookup table. Nothing to configure. Full feature matrix in docs/opentrons.md.

rhylthyme-import-opentrons path/to/protocol.py                 # program JSON on stdout
rhylthyme-import-opentrons path/to/protocol.py -o program.json
cat protocol.py | rhylthyme-import-opentrons -                 # from stdin
from rhylthyme_importers import OpentronsImporter
OpentronsImporter().import_from_source(open("protocol.py").read())

Covers OT-2 and Flex pipettes (1-, 8- and 96-channel), the Flex gripper, and the heater-shaker, magnetic, temperature, thermocycler and absorbance modules. transfer, distribute and consolidate expand into the same low-level events the Opentrons run log emits. A protocol the stubbed simulator cannot execute falls back to a static AST parse and carries a WARNING event, which the web app shows as a banner.

Benchling

A Benchling Protocol, Workflow Task or Notebook Entry becomes a program with an instrument-aware track split. Live import needs a Benchling API token for the tenant; the web app stores one per user (encrypted) after a one-time connect, and the MCP server's import_from_source with source: "benchling" searches your library and imports by id or URL. Details, field mapping and the roadmap: docs/benchling.md, docs/benchling-field-mapping.md.

from rhylthyme_importers.benchling import BenchlingImporter
importer = BenchlingImporter()
importer.search_live(tenant="acme", token="...", query="PCR")
importer.import_from_url("https://acme.benchling.com/acme/protocols/prt_abc123", token="...")

The command line works offline on a saved API response, which is how the importer is tested:

rhylthyme-import-benchling --from-file protocol_response.json --tenant acme -o program.json

Slide decks

A PowerPoint .pptx becomes a presentation timeline: one step per slide, timed from speaker-notes cues where present.

from rhylthyme_importers import SlideDeckImporter
SlideDeckImporter().import_from_url("talk.pptx")

Other commands

The remaining rhylthyme-* commands (rhylthyme-backfill-taxonomy, rhylthyme-clean-titles, rhylthyme-backfill-language, rhylthyme-embed-recipes, rhylthyme-detect-text-thumbs, rhylthyme-replace-broken-thumbs, rhylthyme-import-recipe-scrapers, rhylthyme-sweep-supported-sites) maintain the public catalogue on rhylthyme.com and are not needed to import anything. Their extra dependencies are the catalog extra.

Enrichment is not part of BaseImporter

An importer reads structure, not meaning, so it produces one track of steps chained head-to-tail. Splitting that into parallel tracks with cross-track triggers takes a language model, and that lives in rhylthyme-server, not here: rhylthyme_server._prompting.enrich_program runs turn 4 of the plan_schedule prompt over the program an importer has already returned, exposed as enrich: true on POST /api/import and on the MCP import_from_source tool.

Nothing in this package changes for it. BaseImporter has no enrich hook, no Anthropic dependency and no network requirement beyond the source API it already talks to; importers stay deterministic, offline- testable and free to run without an ANTHROPIC_API_KEY. Enrichment is a post-processor over ImportResult.program, opt-in per request and rate-limited, and a failed enrichment returns the importer's program unchanged.

Two conventions make an importer enrich well:

  • Keep the source sentence on the step. enrich reads step["description"] (falling back to step["notes"]) as the step's metadata.sourceSpan.quote, so the enriched program can say which words each step came from. protocols.io's importer already does this.
  • Keep stepIds stable. Enrichment preserves every imported stepId, and marks any step it adds with metadata.inferred = true; the diff between the two is how the UI tells read-from-source apart from inferred.

Python API

from rhylthyme_importers import TheMealDBImporter, ProtocolsIOImporter, ImporterRegistry

# TheMealDB
mealdb = TheMealDBImporter()
results = mealdb.search("pasta")
result = mealdb.import_from_url("52771")

if result.success:
    program = result.program
    print(f"Imported: {program['name']}")

# Protocols.io
protocolsio = ProtocolsIOImporter(access_token="your_token")
result = protocolsio.import_from_url("https://www.protocols.io/view/...")

# Auto-detect importer from URL
importer = ImporterRegistry.find_for_url("https://www.themealdb.com/meal/52771")
if importer:
    result = importer.import_from_url("https://www.themealdb.com/meal/52771")

Creating Custom Importers

Extend the BaseImporter class to create new importers:

from rhylthyme_importers import BaseImporter, ImportResult, ImporterRegistry

class MyCustomImporter(BaseImporter):
    name = "myimporter"
    description = "Import from my custom source"
    supported_domains = ["example.com"]

    def can_import(self, url_or_query: str) -> bool:
        return "example.com" in url_or_query

    def search(self, query: str) -> list:
        # Return list of {name, url, description}
        return []

    def import_from_url(self, url: str) -> ImportResult:
        # Fetch data and convert to Rhylthyme program
        program = self.create_base_program(
            name="My Program",
            description="Description",
            environment_type="kitchen",
            source_url=url,
            source_type="myimporter"
        )
        # Add tracks, steps, etc.
        return ImportResult(success=True, program=program)

# Register the importer
ImporterRegistry.register(MyCustomImporter())

License

Apache License 2.0

Metadata

Release files for rhylthyme-importers 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rhylthyme-importers 0.4.0
File Size Uploaded
rhylthyme_importers-0.4.0.tar.gz 172.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rhylthyme-importers 0.4.0
File Interpreter ABI Platform
rhylthyme_importers-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 336.1 kB

Release files / rhylthyme_importers-0.4.0.tar.gz

Download URL rhylthyme_importers-0.4.0.tar.gz
Size 172.8 kB
Tags Source
SHA-256 checksum
How to use checksums
24fe10e55769c1c3dae08fdf90027c843ec83ecd85574ce0fced0c4da93cd221
BLAKE2b-256 checksum
How to use checksums
3f010748639258180fc9034c51a153ccccd164bc00d8f314dd71b666056b039f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.9

Release files / rhylthyme_importers-0.4.0-py3-none-any.whl

Download URL rhylthyme_importers-0.4.0-py3-none-any.whl
Size 163.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f9b947443ae4bb6b188f0bfcab5a70e3d8f9e9bd4a6149cd928a6762fab0dcdd
BLAKE2b-256 checksum
How to use checksums
200cffcbe159ad44c47b9e05d3c2854f420198ead2eea559485abfa9f2c4f270
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.9

Release history Release notifications | RSS feed

0.4.7

2 release files

0.4.6

2 release files

0.4.5

2 release files

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

This release

0.4.0 This release

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page