Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.hellofriday.ai/llms.txt

Use this file to discover all available pages before exploring further.

Class: StreamEmitter

class StreamEmitter:
    def emit(self, event_type: str, data: dict | str) -> None: ...
    def progress(self, content: str, *, tool_name: str | None = None) -> None: ...
    def intent(self, content: str) -> None: ...

Methods

emit()

Emit a raw stream event to the host. Parameters:
ParameterTypeRequiredDescription
event_typestrYesEvent type identifier
datadict | strYesEvent payload (dict serialized to JSON, or string)
Example:
ctx.stream.emit("custom-phase", {"step": 3, "total": 10})
ctx.stream.emit("debug", "Processing complete")

progress()

Emit a data-tool-progress event for UI progress display. Parameters:
ParameterTypeRequiredDefaultDescription
contentstrYesProgress message
tool_namestr | NoneNoNoneTool identifier for grouping
Example:
ctx.stream.progress("Starting analysis...")
ctx.stream.progress("Fetching repository data", tool_name="GitHub")
ctx.stream.progress("Analysing code patterns", tool_name="Analyser")

intent()

Emit a data-intent event for high-level state changes. Parameters:
ParameterTypeRequiredDescription
contentstrYesIntent description
Example:
ctx.stream.intent("Discovering repository structure")
ctx.stream.intent("Identifying security issues")
ctx.stream.intent("Generating recommendations")

Common Usage Patterns

Phase-Based Progress

def execute(prompt, ctx):
    ctx.stream.progress("Phase 1: Parsing input")
    config = parse_input(prompt)

    ctx.stream.progress("Phase 2: Fetching data", tool_name="GitHub")
    data = ctx.tools.call("fetch_repo", config)

    ctx.stream.progress("Phase 3: Analysing", tool_name="LLM")
    analysis = ctx.llm.generate(...)

    ctx.stream.progress("Phase 4: Finalising")
    return ok({"result": analysis.text})

Intent for State Changes

def execute(prompt, ctx):
    ctx.stream.intent("Understanding task requirements")
    requirements = extract_requirements(prompt)

    ctx.stream.intent("Planning approach")
    plan = create_plan(requirements)

    ctx.stream.intent("Executing plan")
    for step in plan.steps:
        ctx.stream.progress(f"Step {step.number}: {step.description}")
        execute_step(step)

    ctx.stream.intent("Finalising results")
    return ok({"completed": True})

Tool-Associated Progress

def execute(prompt, ctx):
    ctx.stream.progress("Initialising", tool_name="Setup")

    ctx.stream.progress("Querying database", tool_name="PostgreSQL")
    rows = ctx.tools.call("query", {"sql": "SELECT ..."})

    ctx.stream.progress("Processing results", tool_name="Processor")
    processed = [transform(r) for r in rows]

    ctx.stream.progress("Storing analysis", tool_name="Storage")
    ctx.http.fetch(..., method="POST", body=json.dumps(processed))

    ctx.stream.progress("Complete", tool_name="Setup")
    return ok({"count": len(processed)})

Fallback When Unavailable

ctx.stream may be None in test contexts:
def execute(prompt, ctx):
    # Safe wrapper
    def progress(msg, tool=None):
        if ctx.stream:
            ctx.stream.progress(msg, tool_name=tool)

    progress("Starting...")

    # Work...

    progress("Complete")
    return ok({"done": True})

Emission During LLM Calls

Progress emits are fire-and-forget over NATS — they do not block:
ctx.stream.progress("Starting LLM call...")  # Sent immediately

# LLM call blocks until response; progress already sent
result = ctx.llm.generate(messages, model="claude-sonnet-4-6")

# Back in Python
ctx.stream.progress("LLM complete")  # Sent now
The host may emit its own progress events during the suspension.

Event Types

Standard types used by Friday:
TypeUsage
data-tool-progressAgent progress updates (use progress())
data-intentHigh-level state changes (use intent())
data-errorError events (usually emitted by host)
Custom types can be emitted via emit() but may not have UI handlers.

Best Practices

  • Emit before expensive operations — Warn users before long LLM calls
  • Use tool_name for grouping — Helps UI organise progress by component
  • Keep messages concise — 50-100 characters ideal for UI display
  • Avoid tight loop emission — Batch or debounce high-frequency updates
  • Prefer intent for phases, progress for detail — Two-level hierarchy
  • Always safe to callctx.stream never None, but may no-op in tests

When to Emit

ScenarioMethodExample
Starting a phaseintent()”Analysing repository”
Detailed progressprogress()”Fetching 50 files…”
Tool-specific workprogress(tool_name=...)tool_name=“GitHub”
Fallback scenariosprogress()”Retrying with alternate model…”
Completionintent()”Analysis complete”

See Also

How to Stream Progress

Task-oriented guide

How Agents Work

The subprocess model and host capabilities