← All code samples

Ask → export report (complete)

When the workflow does not always write session-report.md, export the session write-up with export_session. This page embeds the full CLI from examples/ask/ask-export-report.py.

How to run · Complete script

How to run

pip install stackgen-sdk
# or editable: pip install -e ./clients/python

export STACKGEN_URL=https://app.stackgen.com
export STACKGEN_TOKEN=stackgen_…
export STACKGEN_PROJECT=<project-uuid>

python examples/ask/ask-export-report.py \
  --workflow incident-triage \
  --message 'Investigate the checkout CPU alert in prod' \
  --start-only

python examples/ask/ask-export-report.py \
  --workflow incident-triage \
  --message 'Investigate the checkout CPU alert in prod'

Complete script

#!/usr/bin/env python3
"""Ask Guild → poll execution → fetch write-up (export or artifact).

Proves the free-text automation path: start a pinned workflow, wait until
settled, then pull the session report via export_session (fallback when
session-report.md is missing).

Documented at:
  https://appcd-dev.github.io/stackgen-sdk/code-samples/automation-surface/

Credentials (flags override env):
  STACKGEN_URL, STACKGEN_TOKEN, STACKGEN_PROJECT
  (STACKGEN_PROJECT_ID is accepted as an alias for STACKGEN_PROJECT)

Usage:

  pip install -e ./clients/python

  python examples/ask/ask-export-report.py \\
    --workflow incident-triage \\
    --message 'Investigate the checkout CPU alert in prod' \\
    --start-only

  python examples/ask/ask-export-report.py \\
    --workflow incident-triage \\
    --message 'Investigate the checkout CPU alert in prod'
"""

from __future__ import annotations

import argparse
import json
import os
import sys
from pathlib import Path

from stackgen import StackgenClient, StackgenConfig
from stackgen.errors import StackgenError


def _org_id(cli_value: str | None) -> str:
    return (
        (cli_value or "").strip()
        or os.environ.get("STACKGEN_PROJECT", "").strip()
        or os.environ.get("STACKGEN_PROJECT_ID", "").strip()
    )


def _config(args: argparse.Namespace) -> StackgenConfig:
    base = (args.base_url or os.environ.get("STACKGEN_URL", "")).strip()
    token = (args.api_token or os.environ.get("STACKGEN_TOKEN", "")).strip()
    org = _org_id(args.org_id)
    if not base or not token or not org:
        raise SystemExit(
            "set --base-url/--api-token/--org-id or "
            "STACKGEN_URL / STACKGEN_TOKEN / STACKGEN_PROJECT"
        )
    return StackgenConfig(
        base_url=base,
        api_token=token,
        org_id=org,
        timeout_seconds=args.timeout_seconds,
        poll_interval_seconds=args.poll_interval_seconds,
        artifact_name=args.artifact_name,
        output_path=args.output_path,
    )


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--base-url", default="")
    parser.add_argument("--api-token", default="")
    parser.add_argument("--org-id", default="")
    parser.add_argument(
        "--workflow",
        default=os.environ.get("STACKGEN_WORKFLOW", "incident-triage"),
        help="Workflow name for entity_refs pin (env STACKGEN_WORKFLOW)",
    )
    parser.add_argument(
        "--message",
        default="Investigate the checkout CPU alert in prod",
    )
    parser.add_argument("--correlation-id", default="corr-123")
    parser.add_argument("--incident-id", default="INC-1001")
    parser.add_argument("--timeout-seconds", type=int, default=1800)
    parser.add_argument("--poll-interval-seconds", type=int, default=5)
    parser.add_argument(
        "--start-only",
        action="store_true",
        help="Print start payload and exit (smoke test)",
    )
    parser.add_argument(
        "--try-artifact",
        action="store_true",
        help="Try downloading session-report.md before export_session",
    )
    parser.add_argument("--artifact-name", default="session-report.md")
    parser.add_argument("--output-path", default="session-report.md")
    args = parser.parse_args()

    client = StackgenClient(_config(args))
    metadata = {
        "correlation_id": args.correlation_id,
        "incident_id": args.incident_id,
    }

    start = client.aiden.start_ask(
        args.message,
        entity_refs=[{"name": args.workflow, "type": "workflow"}],
        source_metadata=metadata,
    )
    print("start:", json.dumps(start, indent=2, default=str))
    if args.start_only:
        return 0

    trace_id = start.get("trace_id")
    if not trace_id:
        print("error: start response missing trace_id", file=sys.stderr)
        return 1

    trace = client.aiden.wait_for_execution(str(trace_id))
    execution = trace.get("execution") or {}
    session_id = start.get("session_id") or trace.get("session_id")
    print(
        f"settled status={execution.get('status')} "
        f"trace_settled={execution.get('trace_settled')} "
        f"session_id={session_id}",
        file=sys.stderr,
    )
    if not session_id:
        print("error: no session_id after wait", file=sys.stderr)
        return 1

    detail = client.aiden.get_session(str(session_id))
    print("get_session:", json.dumps(detail, indent=2, default=str))

    sessions = client.aiden.list_sessions(q=args.correlation_id, limit=5)
    print("list_sessions:", json.dumps(sessions, indent=2, default=str))

    if args.try_artifact:
        dest = Path(args.output_path)
        try:
            path = client.aiden.download_session_artifact(
                str(session_id),
                args.artifact_name,
                str(dest),
            )
            print(f"artifact_saved={path}", file=sys.stderr)
            return 0
        except StackgenError as exc:
            print(f"artifact miss ({exc}); falling back to export_session", file=sys.stderr)

    export = client.aiden.export_session(str(session_id), parts="report")
    print("export:", json.dumps(export, indent=2, default=str))
    parts = export.get("parts") or []
    if not any(isinstance(p, dict) and p.get("kind") == "report" for p in parts):
        print("error: export missing report part", file=sys.stderr)
        return 1
    return 0


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except StackgenError as exc:
        print(f"error: {exc}", file=sys.stderr)
        raise SystemExit(1)

Related: Async Ask worker, Ask → artifact, Automation surface.