Skip to content

Latest commit

 

History

37 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Enzyme SDK

With the Enzyme SDK, you can automatically manage your app's user conversations, collections, and agent traces, enabling you to quickly experiment with context-efficient and responsive agent-collaborative UX and MCP integrations.

Enzyme is the most performant context graph builder by a wide margin. It indexes and retrieves relevant context at near-constant time complexity as your collection of agent traces and accumulated user data grows, whether it's 50 records per user or 20,000.

This SDK is in beta and is subject to change. Please raise issues or reach out to support@enzyme.garden to discuss your use case.

Example

A cooking app user has 407 saved recipes with annotations spanning 6 years. The agent gets asked: "I'm hosting a dinner party, a couple friends are vegetarian. What should I make?"

You've noted your Mushroom Hash with Black Rice is "now in my repertoire" and that it scales well. For a side, your Orecchiette with Swiss Chard and Feta was "very pretty, easy, and delicious."

A warning from your own notes: your Red Cabbage and Black Rice lacked flavor and the cabbage got "lost." Season the hash aggressively.

The user never asked for "black rice." Enzyme surfaced it because the index had already noticed that pattern across the user's recipe comments.

Install

pip install -e .

For local development with the examples:

pip install -e ".[dev,cluster,examples]"

Example: Build an MCP Connector

This example builds from a sample dataset of NYT Cooking comments. Rows have user_key, user_id, recipe_name, comment, and date.

from dataclasses import dataclass
from typing import Iterable

from enzyme_sdk import Activity, CatalystProfile, EnzymeConnector, enzyme


@dataclass
class UserRecipeComment:
    id: str
    user_id: str
    recipe_name: str
    comment: str
    created_at: str
    tags: list[str]

@dataclass
class AgentObservedPreference:
    id: str
    user_id: str
    topic: str
    summary: str
    source_activity_id: str
    created_at: str

@dataclass
class PreferenceSubstitution:
    pass

@dataclass
class PreferenceRepeatWorthyRecipe:
    pass

@dataclass
class PreferenceSweetnessAdjustment:
    pass

PREFERENCE_COLLECTIONS = {
    "substitutions": PreferenceSubstitution,
    "repeat-worthy recipes": PreferenceRepeatWorthyRecipe,
    "sweetness adjustments": PreferenceSweetnessAdjustment,
}

connector = EnzymeConnector(
    app_id="nyt-cooking",
    display_name="NYT Cooking",
    content_label="cooking notes",
    catalyze_tool="catalyze_cooking_notes",
    catalyze_description=(
        "Search this user's cooking history by concept: saved recipes, "
        "annotations, substitutions, outcomes, and personal notes. Results "
        "include the source notes plus the catalysts that explain why they matched."
    ),
    profile_tool="get_cooking_profile",
    profile_description=(
        "Inspect this user's cooking profile: recurring ingredients, techniques, "
        "cuisines, constraints, and the catalysts that characterize each area."
    ),
    collections=[
        UserRecipeComment,
        AgentObservedPreference,
        PreferenceSubstitution,
        PreferenceRepeatWorthyRecipe,
        PreferenceSweetnessAdjustment,
    ],
    catalyst_profiles={
        UserRecipeComment: CatalystProfile.PREFERENCE_EVIDENCE,
        AgentObservedPreference: CatalystProfile.PREFERENCE_EVIDENCE,
        PreferenceSubstitution: CatalystProfile.PREFERENCE_EVIDENCE,
        PreferenceRepeatWorthyRecipe: CatalystProfile.PREFERENCE_EVIDENCE,
        PreferenceSweetnessAdjustment: CatalystProfile.PREFERENCE_EVIDENCE,
    },
)

@enzyme.hydrate(connector)
def hydrate_recipes(user_id: str) -> Iterable[UserRecipeComment | AgentObservedPreference]:
    return db.load_recipe_activity_and_observations(user_id)

@enzyme.transform(connector)
def recipe_collection(recipe: UserRecipeComment | AgentObservedPreference) -> Activity:
    if isinstance(recipe, UserRecipeComment):
        return Activity(
            title=recipe.recipe_name,
            content=recipe.comment,
            created_at=recipe.created_at,
            source_id=recipe.id,
            collections=[UserRecipeComment],
            metadata={
                "activity_type": "recipe_comment",
                "recipe_name": recipe.recipe_name,
                "labels": recipe.tags,
            },
        )

    return Activity(
        title=f"Observed preference: {recipe.topic}",
        content=recipe.summary,
        created_at=recipe.created_at,
        source_id=recipe.id,
        collections=[AgentObservedPreference, PREFERENCE_COLLECTIONS[recipe.topic]],
        metadata={
            "activity_type": "observed_preference",
            "topic": recipe.topic,
            "derived_from": recipe.source_activity_id,
        },
    )

@enzyme.on_save(connector)
def save_activity(
    user_id: str,
    recipe: UserRecipeComment | AgentObservedPreference,
) -> UserRecipeComment | AgentObservedPreference:
    return db.save(recipe)

Field Mapping

Field Purpose
@enzyme.transform Converts your app-native object into an Activity ingest payload. Hydrate and save hooks both use it.
collections Maps one item to one or more per-user activity classes. Enzyme stores them as stable collection ids, such as recipe-comment, observed-preference, message, or folder-inbox. CLI-backed ingest also associates these ids with the document as folder-style catalyst entities.
catalyst_profiles Optionally tells catalyst generation how to treat a collection, for example preference evidence, operational traces, or decision traces.
source_id Tells your app how to hydrate an activity back into its own UX.
content + metadata Body text plus small structured context; the SDK folds both into the string Enzyme ingests.
created_at Enables recency-aware ranking and catalyst context.

If your app has distinct activity types, such as recipe comments, saved recipes, agent-observed preferences, messages, projects, or artifacts, model them as small typed collection classes and return those classes from Activity.collections. If an item belongs to multiple activity collections, return multiple classes; Enzyme can route through several catalysts and converge on the same chunk or document.

Serve MCP

connector.serve(port=9460, init_users=["user-1", "user-2"])

serve() hydrates each user, builds the catalyst index, and starts a JSON-RPC 2.0 MCP server.

python examples/run_mcp_server.py --ngrok

examples/dishgen_app.py shows mounting MCP alongside a CRUD API on the same FastAPI server.

API keys for the connector path: sign in at enzyme.garden, create a key at /settings, and set ENZYME_API_KEY. Catalyst generation uses Enzyme's hosted generation path, so no OpenAI key is needed for this connector flow.

Query Hosted Search

Hosted search uses the same connector semantics. The service composes the user's collections into one app/user scope.

scope = connector.hosted("user-123")
response = scope.catalyze("quick weeknight dinners with ginger", limit=8)

for result in response.results:
    print(result.source_id, result.title)

overview = scope.petri(top=12)
status = scope.status()

catalyze() searches the full app/user scope. It does not take a public collection selector, and normal results do not expose storage collection ids. Use status() for internal health, collection counts, and cache epochs.

Self-Hosting

If you have access to the source-available enzyme-rust workspace, run the search service and point the SDK at it:

cd ../enzyme-rust
cargo build --release -p enzyme-search

PORT=8766 \
./target/release/enzyme-search
scope = connector.self_hosted(
    "user-123",
    base_url="http://localhost:8766",
)

response = scope.catalyze("what has this user been returning to?")

For storage setup, app/user scope manifests, and optional ingest endpoints, see docs/self-hosting.md.

What Comes Back

catalyze() returns the matched catalysts and the entries they routed:

Query: "vegetarian dinner ideas"

Routing signals:
- auto-cluster-black-rice: What makes black rice the user's reliable base
  when cooking for groups? (routed 2 results)

Matched documents:
1. Mushroom Hash with Black Rice
   "now in my repertoire"
2. Red Cabbage and Black Rice
   "the cabbage got lost"

Agents should use catalysts as evidence for why something surfaced, then quote the user's own source text.

Run The Example

python examples/agent_test.py

examples/nyt_sample_comments.json includes comments for three sample users. agent_test.py hydrates the decorated connector from examples/run_mcp_server.py, prints the typed activity collection counts, builds a temporary local index, and then runs an agent over that user's indexed recipe notes.

Use OPENAI_MODEL to change models. Set OPENAI_BASE_URL for a compatible provider.

ENZYME_TEST_USER=dimmerswitch python examples/agent_test.py

About

Python SDK for Enzyme — manage user conversations, collections, and agent traces with near-constant-time context retrieval. Beta.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages