Metadata-Version: 2.4
Name: flowforge-sdk
Version: 0.3.0
Summary: Python SDK for FlowForge - AI workflow orchestration
Project-URL: Homepage, https://github.com/flowforge/flowforge
Project-URL: Documentation, https://flowforge.dev/docs
Project-URL: Repository, https://github.com/flowforge/flowforge
Author: FlowForge Team
License: MIT
Keywords: ai,async,durable,llm,orchestration,sdk,workflow
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: FastAPI
Classifier: Framework :: Flask
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: httpx>=0.25.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: typing-extensions>=4.8.0
Provides-Extra: ai
Requires-Dist: anthropic>=0.7.0; extra == 'ai'
Requires-Dist: litellm>=1.0.0; extra == 'ai'
Requires-Dist: openai>=1.0.0; extra == 'ai'
Provides-Extra: all
Requires-Dist: anthropic>=0.7.0; extra == 'all'
Requires-Dist: fastapi>=0.104.0; extra == 'all'
Requires-Dist: flask>=3.0.0; extra == 'all'
Requires-Dist: litellm>=1.0.0; extra == 'all'
Requires-Dist: mypy>=1.6.0; extra == 'all'
Requires-Dist: openai>=1.0.0; extra == 'all'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'all'
Requires-Dist: pytest-cov>=4.1.0; extra == 'all'
Requires-Dist: pytest>=7.4.0; extra == 'all'
Requires-Dist: ruff>=0.1.0; extra == 'all'
Requires-Dist: uvicorn>=0.24.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: mypy>=1.6.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
Requires-Dist: pytest>=7.4.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.104.0; extra == 'fastapi'
Requires-Dist: uvicorn>=0.24.0; extra == 'fastapi'
Provides-Extra: flask
Requires-Dist: flask>=3.0.0; extra == 'flask'
Description-Content-Type: text/markdown

# FlowForge SDK

Python SDK for FlowForge - AI workflow orchestration with durable execution.

## Installation

```bash
pip install flowforge-sdk
```

## Quick Start

```python
from flowforge import FlowForge, Context, step

flowforge = FlowForge(app_id="my-app")

@flowforge.function(
    id="my-workflow",
    trigger=flowforge.trigger.event("my/event"),
)
async def my_workflow(ctx: Context) -> dict:
    result = await step.run("process", lambda: "Hello, World!")
    return {"message": result}
```

## Features

- Durable execution with automatic checkpointing
- AI agent support with tool calling
- Multi-agent networks with routing
- Human-in-the-loop approvals

## Step Primitives

Inside a `@flowforge.function`, use the global `step` object:

```python
from flowforge import step

# Run any function with memoization (won't re-run on replay)
result = await step.run("validate", validate_order, order)

# Pause until an event arrives
event = await step.wait_for_event("wait-payment", event="payment/received", match="data.order_id == 'abc'")

# Sleep for a duration
await step.sleep("wait-1h", "1h")

# LLM call with automatic retry
reply = await step.ai("summarize", model="gpt-4o", prompt="Summarize: ...")

# AI agent loop with tool calling
result = await step.agent(
    "research-agent",
    model="claude-sonnet-4-6",
    system="You are a research assistant.",
    messages=[{"role": "user", "content": "Research this topic..."}],
    tools=[...],
    max_tokens=4096,
    max_tool_calls=20,
)
```

## Sending Events

```python
# Send a single event
event_id = await flowforge.send("order/created", data={"order_id": "123"})

# Send multiple events
event_ids = await flowforge.send_many([
    {"name": "user/signup", "data": {"user_id": "1"}},
    {"name": "user/signup", "data": {"user_id": "2"}},
])
```

## Run Management

```python
# Get run details (status, steps, output)
run = await flowforge.get_run("761c0321-...")

# Retry a failed run in-place — keeps all memoized (completed) steps,
# only re-executes from the point of failure
result = await flowforge.retry_run("761c0321-...")

# Cancel a running or pending run
result = await flowforge.cancel_run("761c0321-...")
```

`retry_run` is different from replaying: it preserves the memoized results of
all completed steps so execution resumes from where it failed rather than
starting over from scratch.
