Skip to content

Lilypad

Project is in alpha phase

This means that things like the user interface, database schemas, etc. are still subject to change. We do not yet recommend fully relying on this project in production, but we've found it works quite well for local development in its current stage.

An open-source prompt engineering framework built on these principles:

  • Prompt engineering is an optimization process, which requires...
  • Automatic versioning and tracing
  • Developer-centric prompt template editor
  • Proper syncing between prompts and code

We're looking for early design partners!

We are also working on tooling for improved collaboration between technical and non-technical team members. This is particularly important for involving domain experts who may not have the technical chops to contribute to a code base.

If you're interested, join our community and DM William Bakst :)

There are limited spots.

30 Second Quickstart

Install Lilypad, specifying the provider(s) you intend to use, and set your API key:

pip install "python-lilypad[openai]"
export OPENAI_API_KEY=XXXXX
pip install "python-lilypad[anthropic]"
export ANTHROPIC_API_KEY=XXXXX
pip install "python-lilypad[openai]"
set OPENAI_API_KEY=XXXXX
pip install "python-lilypad[anthropic]"
set ANTHROPIC_API_KEY=XXXXX

Create your first synced prompt to recommend a book.

For example, you could use the prompt Recommend a fantasy book:

lilypad start                  # initialize local project
lilypad create recommend_book  # creates a synced LLM function
lilypad run recommend_book     # runs the function (and opens editor)

Once you hit "Submit" you'll see the function run in your shell. Follow the link to see the version and trace in an interactive UI.

Next, try editing the function signature to take a genre: str argument. When you run the function again it will open the editor and give you access to the {genre} template variable (with autocomplete).

Features

We are working on updating this section, which covers the basics for now

Official SDK Usage

You can use the lilypad.llm_fn decorator on any function that uses an LLM API to generate it's response. This function will then be versioned and traced automatically for you, all the way through the entire lexical closure:

import lilypad
from openai import OpenAI

client = OpenAI()


@lilypad.llm_fn()
def recommend_book(genre: str) -> str:
    completion = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": f"Recommend a {genre} book"}],
    )
    return str(completion.choices[0].message.content)


output = recommend_book("fantasy")
print(output)
import lilypad
from anthropic import Anthropic

client = Anthropic()


@lilypad.llm_fn()
def recommend_book(genre: str) -> str:
    message = client.messages.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": f"Recommend a {genre} book"}],
        max_tokens=1024,
    )
    block = message.content[0]
    return block.text if block.type == "text" else ""


output = recommend_book("fantasy")
print(output)

Synced LLM Function Usage

Setting synced=True in the lilypad.llm_fn decorator indicates that the decorated function should be synced. The result is that only the function definition / signature will be used and everything else that goes into calling the LLM API (i.e. the prompt) lives in the database.

import lilypad


@lilypad.llm_fn(synced=True)
def recommend_book(genre: str) -> str: ...

Note that this is truly just a shim where everything lives externally -- down to the provider and model to be used.

You can also change the return type of the shim and it will be automatically handled:

import lilypad
from pydantic import BaseModel


class Book(BaseModel):
    title: str
    author: str


@lilypad.llm_fn(synced=True)
def recommend_book(genre: str) -> Book: ...