Launch quickstart
Run one OpenAI Chat Completions trace that matches the supported launch path: Python 3.11+, sync, non-streaming, explicit @st.agent and @st.tool, Observe, and configured Guard Audit/Enforce.
Anthropic Messages is supported with staso[anthropic]==0.5.15, anthropic==1.3.0, messages.create, and claude-haiku-4-5. Use the Anthropic quickstart if Anthropic is your default provider.
Create a requirements.txt
staso[openai]==0.5.15
openai==3.6.0Set the environment
Create a Staso API key in the dashboard for the workspace you selected. Use traces:write for Observe and add guard:evaluate when you test Guard Audit/Enforce.
export STASO_API_KEY=ak_...
export OPENAI_API_KEY=sk-...
export STASO_AGENT_NAME=launch-quickstart-agent
export STASO_ENVIRONMENT=production
export STASO_WORKSPACE_SLUG=default
export STASO_SESSION_ID=launch-quickstart-first-traceUse default only if that is the workspace selected in the dashboard.
Run the sample
from __future__ import annotations
import json
import os
from typing import Final
from openai import OpenAI
import staso as st
REQUIRED_ENV: Final = (
"STASO_API_KEY",
"OPENAI_API_KEY",
"STASO_AGENT_NAME",
"STASO_ENVIRONMENT",
"STASO_WORKSPACE_SLUG",
"STASO_SESSION_ID",
)
TOOLS: Final = [
{
"type": "function",
"function": {
"name": "lookup_order",
"description": "Look up the current status for a sample order.",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order ID to look up.",
}
},
"required": ["order_id"],
"additionalProperties": False,
},
},
}
]
def _load_settings() -> dict[str, str]:
missing = [name for name in REQUIRED_ENV if not os.environ.get(name)]
if missing:
raise RuntimeError("Missing required environment variables: " + ", ".join(sorted(missing)))
return {name: os.environ[name] for name in REQUIRED_ENV}
@st.tool(name="lookup_order")
def lookup_order(order_id: str) -> dict[str, str]:
return {
"order_id": order_id,
"status": "paid",
"shipping_state": "ready_to_ship",
}
@st.agent(name="launch-quickstart-agent")
def answer_order_question(order_id: str = "order_123") -> dict[str, object]:
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
try:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "user",
"content": f"Look up the shipping status for order {order_id}.",
}
],
tools=TOOLS,
tool_choice={"type": "function", "function": {"name": "lookup_order"}},
)
except st.GuardBlocked as blocked:
return {
"status": "blocked",
"tool": blocked.tool_name,
"reason": blocked.reason,
}
tool_calls = response.choices[0].message.tool_calls or []
if len(tool_calls) != 1:
raise RuntimeError("Expected one lookup_order tool proposal from OpenAI.")
tool_call = tool_calls[0]
arguments = json.loads(tool_call.function.arguments)
result = lookup_order(**arguments)
return {"status": "observed", "tool_result": result}
def main() -> None:
settings = _load_settings()
session_id = settings["STASO_SESSION_ID"]
st.init(
api_key=settings["STASO_API_KEY"],
agent_name=settings["STASO_AGENT_NAME"],
environment=settings["STASO_ENVIRONMENT"],
workspace_slug=settings["STASO_WORKSPACE_SLUG"],
)
try:
result = answer_order_question()
print(json.dumps(result, sort_keys=True))
finally:
delivery = st.shutdown(timeout=10)
if not delivery.ok:
raise RuntimeError(
"Trace delivery incomplete: "
f"last_failure={delivery.last_failure!r}, "
f"failed={delivery.failed}, "
f"pending={delivery.pending}, "
f"rejected={delivery.rejected}, "
f"last_rejection={delivery.last_rejection!r}"
)
print(
"Staso accepted trace delivery for "
f"session_id={session_id}. The dashboard/backend trace query may take a few "
"seconds to return the durable trace."
)
if __name__ == "__main__":
main()The same code lives in the repository at examples/launch_quickstart/openai_guard_quickstart.py.
The OpenAI client is constructed inside answer_order_question() after st.init() has run, so the traced agent input is only the safe sample order ID.
Configure a Guard audit
The sample guarantees one supported lookup_order proposal and its trace. It does not guarantee a rule match.
- Open Rules and choose a deterministic template shown for your organization.
- Open Policies and attach that rule to an audit policy scoped to the sample agent and environment.
- Run a proposal whose displayed fields intentionally meet that rule.
Built-in templates are inactive until attached to a policy. When an active audit policy matches, the trace records guard:would-block:lookup_order and lets the proposal continue. With no matching active rule, the trace has no would-block finding. When an active Enforce policy matches, the trace records guard:blocked:lookup_order and prevents the tool execution span.
Use the rules catalog and rules and policies pages to check the options currently shown for your organization.
What the dashboard should show
After the dashboard/backend trace query can see the trace, expect:
session_id = launch-quickstart-first-traceenvironment = productionworkspace_slugequalsSTASO_WORKSPACE_SLUG- root span
kind=agent,name=launch-quickstart-agent,status=ok - LLM child span
kind=llm,provider=openai,requested_model=gpt-4o-mini,name=chat:tool_calls(lookup_order) - tool child span
kind=tool,name=lookup_order,status=okwhen Guard allows or audits - a matching configured Guard audit adds
guard:would-block:lookup_order - a matching configured Guard Enforce policy adds
guard:blocked:lookup_orderand prevents thelookup_orderexecution span
DeliveryReport.ok means the SDK reached ingest acceptance. The onboarding milestone is satisfied only when the dashboard/backend returns the trace in the selected workspace.
How setup health is interpreted
connection_status=connectedmeans the selected workspace has a query-visible trace.last_successful_ingestion_atis the query-visible trace ingestion time, not client time.provider_patch_status=observedmeans a durably storedkind=llmspan hasprovider=openaiorprovider=anthropic.- Before that provider span appears, patch status is pending or unobserved. It is not guessed from install clicks.
- Setup failures use safe categories such as
invalid_key,forbidden,plan_expired,rate_limited,backend_unavailable,network_unavailable,timeout,malformed_response,queue_full,shutdown, anddisabled.
Guard boundary
Staso checks provider-proposed tool calls before your host code dispatches them.
- A matching configured Audit policy records
guard:would-block:<tool>and allows the proposal. - A matching configured Enforce policy records
guard:blocked:<tool>and raisesst.GuardBlocked. @st.toolobserves actual Python execution. It does not enforce Guard by itself.- Direct Python calls need
st.guard(...)when they require enforcement.
API key recovery
Key secrets are shown once. If you lose one, rotate the key or create a replacement in the dashboard, update STASO_API_KEY, restart long-lived processes, and revoke the old key. The SDK cannot show, rotate, revoke, or recover a raw secret.
If the trace does not appear
| Category | What to do |
|---|---|
invalid_key | Create a replacement key and update STASO_API_KEY. |
forbidden | Check workspace scope and traces:write. Add guard:evaluate for Guard. |
plan_expired | Fix the account plan or contact founders. |
rate_limited | Check current usage and rate limits in the dashboard. |
backend_unavailable, network_unavailable, timeout | Retry after the service or network recovers. |
malformed_response | Contact [email protected] with the safe failure category. |
queue_full | Flush more often, reduce burst size, or contact founders. |
shutdown | Re-run the process after initializing Staso. |
disabled | Check that tracing is enabled in local config. |
Do not send API keys, prompts, tool arguments, provider responses, or customer data when asking for help.