Skip to content

ja-818/ombra

Repository files navigation

Ombra

Minimal code, maximum automation for AI workflows.

Write regular Python functions. Add two decorators. Get FastAPI endpoints, execution history, and time travel debugging automatically.

Ombra solves the biggest pain point in AI engineering: debugging complex chains. Instead of re-running expensive/slow LLM calls from scratch every time you tweak a prompt, Ombra lets you "time travel" back to any step, edit its inputs, and replay from there using cached results for everything upstream.

Features

  • Instant API: Your Python functions become production-ready FastAPI endpoints.
  • 🔄 Time Travel: Resume execution from any step. Tweaked a prompt in step 3? Replay from step 3, reusing results from steps 1 & 2.
  • 📊 Visual Dashboard: See exactly what happened, when, and why. Inspect inputs/outputs for every step.
  • 💾 Automatic Persistence: All execution history, inputs, and outputs are saved to SQLite.
  • 🤖 LLM Integration: Built-in call_llm helper that handles multimodal inputs (like PDFs) seamlessly.

Quick Start

Installation

pip install ombra
# or
uv add ombra

Requires Python ≥3.12.

Your First Workflow: PDF Analysis & Email Drafting

Let's build a real-world example: A workflow that takes a PDF, analyzes it using Gemini, and drafts a response email.

1. Define Data Models (models.py)

Define structured inputs/outputs using Pydantic.

from pydantic import BaseModel

class DocumentAnalysis(BaseModel):
    """Structured analysis of a document."""
    summary: str
    key_entities: list[str]
    sentiment: str
    requires_action: bool

class EmailDraft(BaseModel):
    """Proposed email draft based on analysis."""
    subject: str
    body: str
    recipient_role: str

2. Create Steps (steps.py)

Write async functions decorated with @step(). Use call_llm for AI tasks.

from ombra import step, File, call_llm, Model, Message, Role
from models import DocumentAnalysis, EmailDraft

@step()
async def analyze_document(file: File) -> DocumentAnalysis:
    """Sends PDF to Gemini for analysis."""
    prompt = """
    Analyze the attached PDF. Provide a summary, key entities, sentiment, 
    and if action is required.
    """

    response = await call_llm(
        model=Model.GEMINI_2_5_FLASH_LITE,
        messages=[
            Message(role=Role.USER, content=[prompt, file]) # Native file support!
        ],
        response_format=DocumentAnalysis # Structured Output!
    )
    
    # Ombra + LiteLLM guarantees the response matches your Pydantic model
    return DocumentAnalysis.model_validate_json(response.content)

@step()
async def draft_response_email(analysis: DocumentAnalysis) -> EmailDraft:
    """Drafts an email based on the analysis."""
    prompt = f"""
    Draft a response based on this analysis:
    Summary: {analysis.summary}
    Sentiment: {analysis.sentiment}
    """
    
    response = await call_llm(
        model=Model.GEMINI_2_5_FLASH_LITE,
        messages=[Message(role=Role.USER, content=prompt)],
        response_format=EmailDraft # Structured Output!
    )
    
    return EmailDraft.model_validate_json(response.content)

3. Connect the Workflow (workflow.py)

Decorate your main function with @workflow.

from ombra import workflow, File
from models import EmailDraft
from steps import analyze_document, draft_response_email

@workflow(name="process_document", description="Analyze PDF and draft email")
async def process_document_workflow(file: File) -> EmailDraft:
    # Step 1: Analyze the PDF
    analysis = await analyze_document(file)
    
    # Step 2: Draft an email based on the analysis
    email_draft = await draft_response_email(analysis)
    
    return email_draft

Run It

Start the development server pointing to your code:

uv run ombra dev --workflows-dir .

You'll see:

INFO:     🚀 Starting Ombra Dev Server at http://127.0.0.1:8000
...
INFO:     ✨ Registered workflow: process_document
INFO:     
INFO:     📖 Run Workflows:    http://localhost:8000/docs
INFO:     👀 View Executions:  http://localhost:8000/workflows
INFO:     

Use It

  1. Execute: Go to http://localhost:8000/docs, find POST /workflows/process_document/execute, upload a PDF, and run it.
  2. Visualize: Go to http://localhost:8000/workflows to see the execution live. You'll see the analyze_document step completing, followed by draft_response_email.
  3. Time Travel:
    • Let's say you don't like the email tone in Step 2.
    • Instead of re-uploading the PDF and re-running the expensive Step 1, just click "Resume" on the draft_response_email step in the UI.
    • You can even edit the inputs (e.g., change the prompt logic in your code) and replay just that step!

Core Concepts

@step()

  • Wraps any async function.
  • Automatically serializes and persists inputs/outputs to SQLite.
  • Handles nesting (steps calling steps).
  • Supports file handling natively.

@workflow()

  • Wraps the entry point function.
  • Automatically generates a FastAPI route based on type hints.
  • Manages the execution context.

call_llm

  • Built-in wrapper around standard LLM APIs.
  • Multimodal: Pass text and File objects directly in the content list. Ombra handles the base64 conversion and formatting for you.
  • Structured Outputs: Pass a Pydantic model to response_format to guarantee valid JSON outputs matching your schema.
  • Visualized: Shows token usage and model details directly in the execution dashboard.

CLI Commands

  • ombra dev: Starts the dev server with hot reloading.
    • --workflows-dir: Specify where your workflow files are (defaults to current dir).
  • ombra serve: Starts a production-ready server.

License

MIT

About

AI Stack to build apps in python

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors