Skip to main content
The Session API provides persistent, database-backed sessions with natural language HITL (Human-in-the-Loop) methods. Ideal for agent-driven workflows where state needs to persist across interactions.

Creating Sessions

from synkro import Session
from synkro.models import Google

# Create a persistent session (SQLite by default)
session = await Session.create(
    policy="Expenses over $50 need manager approval...",
    session_id="exp001"  # Optional - auto-generated if not provided
)

# Configure models
session.model = Google.GEMINI_25_FLASH
session.grading_model = Google.GEMINI_25_PRO

Parameters

ParameterTypeDescription
policystrPolicy text to extract rules from
session_idstrOptional session identifier (auto-generated if not provided)
db_urlstrOptional database URL (defaults to SQLite at ~/.synkro/sessions.db)

Loading Sessions

Resume a previously saved session:
session = await Session.load_from_db("exp001")

# With custom database
session = await Session.load_from_db("exp001", db_url="postgresql://...")

Pipeline Methods

Run pipeline steps with automatic persistence:
# Extract rules from policy
await session.extract_rules(session.policy)

# Generate taxonomy (optional)
await session.generate_taxonomy()

# Generate scenarios
await session.generate_scenarios(count=30)

# Synthesize traces
await session.synthesize_traces()

# Verify traces
await session.verify_traces()

HITL Refinement Methods

Refine rules, scenarios, and taxonomy with natural language commands:

refine_rules()

# Add rules
await session.refine_rules("add rule: meals capped at $75/day")

# Remove rules
await session.refine_rules("remove R005")

# Merge rules
await session.refine_rules("merge R002 and R003")

# Modify rules
await session.refine_rules("change R001 threshold from $50 to $100")

refine_scenarios()

# Add scenarios
await session.refine_scenarios("add 5 edge cases for meal limits")

# Remove scenarios
await session.refine_scenarios("delete S3")

# Target specific rules
await session.refine_scenarios("add more negative cases for R002")

refine_taxonomy()

# Add categories
await session.refine_taxonomy("add category for travel expenses")

# Rename categories
await session.refine_taxonomy("rename SC001 to Approval Thresholds")

refine_trace()

# Refine individual trace
result = await session.refine_trace(0, "make the response more concise")
print(result)  # "Trace #0 refined - now passes"

Show Commands

Inspect session state at any time:
# Show extracted rules
print(session.show_rules(limit=5))

# Show generated scenarios
print(session.show_scenarios(limit=10))

# Filter scenarios by type
print(session.show_scenarios(filter="edge_case"))

# Show scenario distribution
print(session.show_distribution())

# Show taxonomy categories
print(session.show_taxonomy())

# Show passing traces
print(session.show_passed())

# Show failed traces
print(session.show_failed())

# Show individual trace
print(session.show_trace(0))

# One-liner status summary (includes cost)
print(session.status())
# Output: Rules: ✓ (18) | Taxonomy: ✓ (12) | Scenarios: ✓ (30) | Traces: ✓ (30) | Verified: ✓ (28/30) | Cost: $0.42

# Cost summary
print(session.show_cost_summary())
# Output: Cost: $0.4265 | Calls: 75 | Time: 1m 34s

# Detailed cost breakdown by phase
print(session.show_cost())
# Output:
# Phase Breakdown:
# --------------------------------------------------
# Phase             Cost      Calls       Time
# --------------------------------------------------
# extraction       $0.0012        3       2.1s
# scenarios        $0.0089       12       8.4s
# traces           $0.2341       30      45.2s
# verification     $0.1823       30      38.1s
# --------------------------------------------------
# Total            $0.4265       75

done()

Complete the pipeline in one call - synthesizes traces, verifies them, and optionally exports:
dataset = await session.done(output="training.jsonl")

Parameters

ParameterTypeDescription
outputstrOptional output file path
countintOptional number of traces to generate

Session Management

list_sessions()

sessions = await Session.list_sessions()
for s in sessions:
    print(f"{s['session_id']} - updated: {s['updated_at']}")

delete()

deleted = await session.delete()
print(deleted)  # True

undo()

result = await session.undo()
print(result)  # "Restored: Before edit: add rule..."

status()

print(session.status())
# Rules: ✓ (18) | Taxonomy: ✓ (12) | Scenarios: ✓ (30) | Traces: ✓ (30) | Verified: ✓ (28/30)

Cost Tracking

Every session tracks LLM calls, costs, and timing - including all HITL refinement calls:
# Quick summary
print(session.show_cost_summary())
# Cost: $0.1795 | Calls: 21 | Time: 1m 59s

# Detailed breakdown (includes HITL)
print(session.show_cost())
# Phase Breakdown:
# --------------------------------------------------
# Phase             Cost      Calls       Time
# --------------------------------------------------
# extraction       $0.0173        1      24.3s
# hitl             $0.0190        3       0.0s  <-- refine_* calls
# scenarios        $0.0083        3       6.4s
# traces           $0.0861        7      28.1s
# verification     $0.0488        7      1m 0s
# --------------------------------------------------
# Total            $0.1795       21

# Programmatic access
print(session.metrics.total_cost)      # 0.1795
print(session.metrics.total_calls)     # 21
print(session.metrics.breakdown)       # {'extraction': 0.017, 'hitl': 0.019, ...}
All operations are tracked:
  • extract_rules() → extraction phase
  • generate_scenarios() → scenarios phase
  • refine_rules(), refine_scenarios(), refine_taxonomy(), refine_trace() → hitl phase
  • synthesize_traces() → traces phase
  • verify_traces() → verification phase
Cost data persists with the session and is restored on reload.

Remote Database (Postgres)

Use Postgres instead of SQLite for multi-user or production deployments:
import os

# Create with Postgres
session = await Session.create(
    policy="...",
    db_url=os.environ["DATABASE_URL"]  # postgresql://user:pass@host/db
)

# Load from Postgres
session = await Session.load_from_db("exp001", db_url=os.environ["DATABASE_URL"])

# List sessions from Postgres
sessions = await Session.list_sessions(db_url=os.environ["DATABASE_URL"])
Supports:
  • Neon
  • Supabase
  • AWS RDS
  • Self-hosted Postgres

Complete Example

from synkro import Session
from synkro.models import Google

async def main():
    # Create session
    session = await Session.create(
        policy="Expenses over $50 need manager approval...",
        session_id="demo"
    )
    session.model = Google.GEMINI_25_FLASH

    # Extract and refine rules
    await session.extract_rules(session.policy)
    await session.refine_rules("add rule: receipts required over $25")
    print(session.show_rules())

    # Generate and refine scenarios
    await session.generate_scenarios(count=20)
    await session.refine_scenarios("add 3 edge cases")
    print(session.show_distribution())

    # Complete pipeline
    dataset = await session.done(output="training.jsonl")
    print(session.show_passed())

    # Later: resume session
    session = await Session.load_from_db("demo")
    print(session.status())

Properties

PropertyTypeDescription
session_idstrSession identifier
policyPolicyPolicy document
logic_mapLogicMapExtracted rules
scenarioslist[GoldenScenario]Generated scenarios
traceslist[Trace]Generated traces
verified_traceslist[Trace]Verified traces
modelModelGeneration model
grading_modelModelGrading model
dataset_typeDatasetTypeOutput dataset type