oc_chat
Loading...
Searching...
No Matches
scenarios.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# oc_chat/benchmarks/scenarios.py
4
5"""
6Fixed, version-stamped benchmark scenarios.
7
8Purpose
9-------
10A scenario is a declarative bundle of an ordered list of user prompts
11plus the mode it must run in. Scenarios are **fixed and version-
12stamped** so before/after runs are comparable; changing a scenario's
13prompts means a new id (or a bumped ``version``), never a silent
14in-place edit (keep receipts). All v1 prompts are **read-only** (no
15``write_file`` / no destructive ``run_command``) and reference stable
16worktree files, so runs are comparable and safe.
17
18All three v1 scenarios declare ``mode = "option-c"`` — the mode whose
19tool-dump prompt Plan 135 slims; ``standard`` mode exposes no tools or
20project tree and would not exercise the surface under test.
21
22Responsibilities
23----------------
24- :class:`Scenario` — name / version / mode / turns / description.
25- :data:`SCENARIOS` — the registry; :func:`get` / :func:`names`.
26
27Diagnostics
28-----------
29Domain: OC_CHAT.MAIN
30Levels:
31 L2 — lifecycle events
32 L3 — semantic details
33 L4 — deep tracing
34
35Contracts
36---------
37- :func:`get` raises ``KeyError`` with the valid names on an unknown id
38 (the CLI turns that into a clean exit-1, never a traceback).
39- Prompts are read-only and reference stable repository paths.
40
41Dependencies
42------------
43- oc_diagnostics (structured debug logging via _dbg)
44"""
45from __future__ import annotations
46
47from dataclasses import dataclass, field
48
49from oc_diagnostics import _dbg
50
51
52@dataclass(frozen=True)
54 """A fixed, version-stamped scripted scenario."""
55
56 name: str
57 version: str
58 mode: str
59 turns: tuple[str, ...]
60 description: str = ""
61
62
63SCENARIOS: dict[str, Scenario] = {
64 "qa_tool_light": Scenario(
65 name="qa_tool_light",
66 version="1",
67 mode="option-c",
68 description="Plain Q&A, little/no tool use — isolates prompt-size / cache / TTFT.",
69 turns=(
70 "In two sentences, what is the ReAct loop in an agentic chat assistant?",
71 "What is the difference between a model's context window and its prefix cache?",
72 "Summarise your two previous answers as three bullet points.",
73 ),
74 ),
75 "tool_heavy": Scenario(
76 name="tool_heavy",
77 version="1",
78 mode="option-c",
79 description="Read/list/search tools + a likely sub-loop offload.",
80 turns=(
81 "List the Python files in oc_chat/core/ and tell me how many there are.",
82 # OCT-LINT: disable=no_hardcoded_secrets reason="benchmark scenario prompt text naming a repository file path; no credential material (operator-ruled 2026-09-28)"
83 "Read oc_chat/core/sub_loop.py and explain in two sentences what "
84 "_DEFAULT_SUB_SYSTEM is for.",
85 "Search the codebase for where stream_chat is defined and list the files "
86 "that define it.",
87 ),
88 ),
89 "reasoning_heavy": Scenario(
90 name="reasoning_heavy",
91 version="1",
92 mode="option-c",
93 description="Multi-step reasoning to surface reasoning_tokens on a thinking model.",
94 turns=(
95 "Walk through, step by step, how a single user turn flows from run_turn to "
96 "the provider and back, including when and why a sub-loop is used.",
97 "Now identify the two places in that flow where token cost is recorded but "
98 "context-window saturation is not, and explain why they are kept separate.",
99 ),
100 ),
101}
102
103
104def names() -> list[str]:
105 """Sorted list of scenario ids."""
106 return sorted(SCENARIOS)
107
108
109def get(name: str) -> Scenario:
110 """Return the scenario for *name*, or raise ``KeyError`` listing valid ids."""
111 func = "get"
112 _dbg("OC_CHAT.MAIN", func, f"scenario lookup name={name!r}", 3)
113 try:
114 return SCENARIOS[name]
115 except KeyError:
116 valid = ", ".join(names())
117 raise KeyError(f"unknown scenario {name!r}; valid: {valid}") from None
118
119
120def all_scenarios() -> list[Scenario]:
121 """Every scenario, in sorted-name order."""
122 return [SCENARIOS[n] for n in names()]
123
124
125__all__ = ["Scenario", "SCENARIOS", "names", "get", "all_scenarios"]
Scenario get(str name)
Definition scenarios.py:109
list[Scenario] all_scenarios()
Definition scenarios.py:120