Metadata-Version: 2.4
Name: flowforge-sdk
Version: 0.3.3
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.

## Streaming Run Events (SSE)

Stream real-time events from a running workflow via Server-Sent Events:

```python
from flowforge import FlowForge, RunEvent

flowforge = FlowForge(app_id="my-app", api_key="ff_live_...")

# Async iterator
async for event in flowforge.stream_run("run-uuid"):
    print(f"[{event.event_type.value}] {event.data}")

# With callback
async for event in flowforge.stream_run("run-uuid", on_event=lambda e: print(e)):
    pass
```

The stream automatically closes when the run completes or fails. Available event types:

- `step_started`, `step_completed`, `step_failed`
- `thinking`, `thinking_chunk`
- `tool_call_started`, `tool_call_completed`
- `approval_required`, `approval_resolved`
- `run_started`, `run_paused`, `run_resumed`, `run_completed`, `run_failed`

Options:

```python
async for event in flowforge.stream_run(
    "run-uuid",
    include_history=True,   # Include past events on connect (default: True)
    timeout=300.0,          # Server-side stream timeout in seconds (default: 300)
    on_event=my_callback,   # Optional callback for each event
):
    if event.is_terminal:
        print("Run finished:", event.data)
```
