Files
praxis/tests/test_scenario_runtime.py
T
Praxis CI 30051fdfd6 feat(P01-03-03,P01-03-07): Pipecat Flows wiring + scenario-driven prompt
server/scenarios/runtime.py — ScenarioRuntime maps a Scenario to a Pipecat
Flows state-machine spec: initial 'conversation' state with the scenario's
system prompt + opening line, branch metadata carried in the spec (v0.1
has no in-flight transitions per G-002 — branching is post-hoc; Phase 2+
can fork without schema change). set_branch() resolves the branch outcome
from the classifier; debrief_focus() returns the per-branch focus.

TASK-03-07: server/pipeline.py build_pipeline() now accepts a scenario_id,
loads the runtime, and uses scenario.setup.system_prompt instead of the
SLICE-02 hardcoded walking-skeleton prompt. Falls back gracefully if the
scenario can't load. server/__main__.py passes
PRAXIS_SCENARIO=customer_service_refund_ca_v01 by default. 7 runtime tests
pass (system prompt, set_branch accept/escalate, unknown-branch error,
debrief_focus per branch, flows spec branches, debrief model config).

---ci---
phase: 1
milestone: v0.1
plan: 03
task: 03-03,03-07
status: execute
persona: backend-engineer
requirements:
  covered: [REQ-SCEN-01, REQ-SCEN-FMT-01, REQ-ORCH-02]
---/ci---
2026-08-01 13:11:30 +00:00

68 lines
2.3 KiB
Python

"""Unit tests for the scenario runtime + flows spec (TASK-03-03, TASK-03-07)."""
from __future__ import annotations
import pytest
from server.scenarios.runtime import (
ScenarioRuntime,
build_runtime,
build_runtime_from_id,
)
from server.scenarios.loader import load
def test_runtime_uses_scenario_system_prompt():
s = load("customer_service_refund_ca_v01")
rt = build_runtime(s)
assert "Jordan" in rt.system_prompt
assert "cracked" in rt.opening_line
def test_runtime_set_branch_escalate():
rt = build_runtime_from_id("customer_service_refund_ca_v01")
b = rt.set_branch("escalate")
assert b.outcome == "failure"
assert b.failure_mode == "escalates_unresolved"
assert rt.outcome == "failure"
assert rt.branch_id == "escalate"
def test_runtime_set_branch_accept():
rt = build_runtime_from_id("customer_service_refund_ca_v01")
b = rt.set_branch("accept_resolution")
assert b.outcome == "success"
assert rt.outcome == "success"
def test_runtime_set_branch_unknown_raises():
rt = build_runtime_from_id("customer_service_refund_ca_v01")
with pytest.raises(ValueError, match="Unknown branch id"):
rt.set_branch("nonexistent_branch")
def test_runtime_debrief_focus_per_branch():
rt = build_runtime_from_id("customer_service_refund_ca_v01")
# No branch set → default focus.
assert "General" in rt.debrief_focus()
rt.set_branch("escalate")
assert "escalated" in rt.debrief_focus().lower()
rt.set_branch("accept_resolution")
assert "did well" in rt.debrief_focus().lower()
def test_runtime_as_flow_spec_has_branches():
rt = build_runtime_from_id("customer_service_refund_ca_v01")
spec = rt.as_flow_spec()
assert spec["initial_state"] == "conversation"
assert "system_prompt" in spec["states"]["conversation"]
assert len(spec["states"]["conversation"]["branches"]) == 2
# v0.1: no in-flight transitions (G-002 — post-hoc classification).
assert spec["transitions"] == []
def test_runtime_default_debrief_model():
"""The scenario's debrief config uses deepseek-v4-flash:cloud no_think (D-020)."""
rt = build_runtime_from_id("customer_service_refund_ca_v01")
assert rt.scenario.debrief.model == "deepseek-v4-flash:cloud"
assert rt.scenario.debrief.mode == "no_think"