Skip to content

Commit db1db16

Browse files
committed
Finetuned canonical datamodel
1 parent 66bf203 commit db1db16

59 files changed

Lines changed: 3565 additions & 927 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/10-api-reference/database_schema.md

Lines changed: 253 additions & 58 deletions
Large diffs are not rendered by default.

samples/01_roa_agent/run.py

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,26 @@
1111
Requires PYTHONPATH including workspace src/ (see .vscode/settings.json).
1212
1313
ROA Manifesto alignment: §3-4, §6
14+
15+
Canonical telemetry: ``samples/01_roa_agent/data/01_roa_agent.db`` (optional).
1416
"""
1517
from __future__ import annotations
1618

1719
import json
1820
import logging
1921
import random
22+
import sys
2023
from abc import ABC, abstractmethod
2124
from datetime import datetime, timezone
2225
from pathlib import Path
2326
from typing import Any, Dict, List, Optional, Tuple, Union
2427

25-
from dir_core import (
28+
_REPO_ROOT = Path(__file__).resolve().parents[2]
29+
_SRC = _REPO_ROOT / "src"
30+
if str(_SRC) not in sys.path:
31+
sys.path.insert(0, str(_SRC))
32+
33+
from dir_core import ( # noqa: E402
2634
AgentState,
2735
DecisionRecord,
2836
EscalationRequest,
@@ -33,8 +41,16 @@
3341
SelfCheckResult,
3442
new_dfid,
3543
)
44+
from dir_core.storage import AuditStore, ensure_db, sqlite_storage # noqa: E402
3645
from dir_core.utils.logging_utils import log_with_dfid
3746

47+
from telemetry import ( # noqa: E402
48+
SIMULATION_ID,
49+
record_roa_cycle_result,
50+
record_simulation_end,
51+
record_simulation_start,
52+
)
53+
3854
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")
3955
logger = logging.getLogger(__name__)
4056

@@ -704,9 +720,9 @@ def formulate_policy(self, dfid: str, explain_result: ExplainResult) -> Policy:
704720
# =============================================================================
705721

706722

707-
def main() -> None:
708-
"""Demonstrate full ROA lifecycle with dynamic agents and escalation scenarios."""
709-
723+
def _run_roa_demo(audit: AuditStore) -> None:
724+
"""Scenarios A–G: ROA lifecycle (one ``decision_audit`` row per ``run_decision_cycle``)."""
725+
710726
print("=" * 70)
711727
print("ROA Agent Sample - Full Lifecycle Demonstration")
712728
print("=" * 70)
@@ -737,7 +753,8 @@ def main() -> None:
737753
}
738754

739755
result_a = btc_agent.run_decision_cycle(dfid_a, context_a)
740-
756+
record_roa_cycle_result(audit, dfid_a, SIMULATION_ID, result_a, scenario_label="A")
757+
741758
if isinstance(result_a, PolicyProposal):
742759
print(f"\n[RESULT A] PolicyProposal emitted:")
743760
print(f" DFID: {result_a.dfid}")
@@ -765,7 +782,8 @@ def main() -> None:
765782
}
766783

767784
result_b = btc_agent.run_decision_cycle(dfid_b, context_b)
768-
785+
record_roa_cycle_result(audit, dfid_b, SIMULATION_ID, result_b, scenario_label="B")
786+
769787
if isinstance(result_b, EscalationRequest):
770788
print(f"\n[RESULT B] EscalationRequest emitted:")
771789
print(f" DFID: {result_b.dfid}")
@@ -801,7 +819,8 @@ def main() -> None:
801819
context_c = {"price": 63500} # +5.8% profit
802820

803821
result_c = position_agent.run_decision_cycle(dfid_c, context_c)
804-
822+
record_roa_cycle_result(audit, dfid_c, SIMULATION_ID, result_c, scenario_label="C")
823+
805824
if isinstance(result_c, PolicyProposal):
806825
print(f"\n[RESULT C] PositionAgent PolicyProposal:")
807826
print(f" DFID: {result_c.dfid}")
@@ -824,7 +843,8 @@ def main() -> None:
824843
context_d = {"price": 57500} # -4.2% loss, exceeds 3% max_drawdown_limit
825844

826845
result_d = position_agent.run_decision_cycle(dfid_d, context_d)
827-
846+
record_roa_cycle_result(audit, dfid_d, SIMULATION_ID, result_d, scenario_label="D")
847+
828848
if isinstance(result_d, PolicyProposal):
829849
print(f"\n[RESULT D] Risk-triggered PolicyProposal:")
830850
print(f" Action: {result_d.policy_kind}")
@@ -895,7 +915,8 @@ def main() -> None:
895915
}
896916

897917
result_e = restored_agent.run_decision_cycle(dfid_e, context_e)
898-
918+
record_roa_cycle_result(audit, dfid_e, SIMULATION_ID, result_e, scenario_label="E")
919+
899920
if isinstance(result_e, PolicyProposal):
900921
print(f"\n[RESULT E] Restored agent decision:")
901922
print(f" Action: {result_e.policy_kind}")
@@ -964,11 +985,17 @@ def main() -> None:
964985
dfid_f1 = new_dfid()
965986
log_with_dfid(logger, dfid_f1, logging.INFO, "Growth agent analyzing...")
966987
result_f1 = growth_agent.run_decision_cycle(dfid_f1, same_context)
967-
988+
record_roa_cycle_result(
989+
audit, dfid_f1, SIMULATION_ID, result_f1, scenario_label="F_growth"
990+
)
991+
968992
dfid_f2 = new_dfid()
969993
log_with_dfid(logger, dfid_f2, logging.INFO, "Defensive agent analyzing...")
970994
result_f2 = defensive_agent.run_decision_cycle(dfid_f2, same_context)
971-
995+
record_roa_cycle_result(
996+
audit, dfid_f2, SIMULATION_ID, result_f2, scenario_label="F_defensive"
997+
)
998+
972999
print("\n [COMPARISON] Same data, different missions:")
9731000
print(f"\n Growth Agent (mission: 'Maximize alpha...'):")
9741001
print(f" Focus: opportunity_weight={growth_agent.parse_mission_focus()['opportunity_weight']:.1f}x")
@@ -1018,7 +1045,14 @@ def main() -> None:
10181045
dfid_g = new_dfid()
10191046
log_with_dfid(logger, dfid_g, logging.INFO, f"Challenge {i}: vol={ctx['volatility']:.1%}")
10201047
result_g = evolving_agent.run_decision_cycle(dfid_g, ctx)
1021-
1048+
record_roa_cycle_result(
1049+
audit,
1050+
dfid_g,
1051+
SIMULATION_ID,
1052+
result_g,
1053+
scenario_label=f"G_challenge_{i}",
1054+
)
1055+
10221056
outcome = "ESCALATED" if isinstance(result_g, EscalationRequest) else result_g.policy_kind
10231057
print(f" Challenge {i}: vol={ctx['volatility']:.1%} -> {outcome} (policy v{evolving_agent.state.policy_version})")
10241058

@@ -1032,6 +1066,31 @@ def main() -> None:
10321066
print(f" -> Agent {'shifted to more conservative strategy' if evolving_agent.state.policy_version > 1 else 'maintained original strategy'}")
10331067

10341068

1069+
def main() -> None:
1070+
"""Run ROA demo and append audit rows to canonical SQLite under ``data/``."""
1071+
data_dir = Path(__file__).resolve().parent / "data"
1072+
data_dir.mkdir(parents=True, exist_ok=True)
1073+
db_path = ensure_db(data_dir / "01_roa_agent.db")
1074+
bundle = sqlite_storage(str(db_path))
1075+
audit = AuditStore(bundle.decision_audit, bundle.idempotency)
1076+
run_status = "ok"
1077+
end_error: Optional[str] = None
1078+
try:
1079+
record_simulation_start(audit, SIMULATION_ID)
1080+
_run_roa_demo(audit)
1081+
except Exception as exc:
1082+
run_status = "error"
1083+
end_error = str(exc)
1084+
raise
1085+
finally:
1086+
record_simulation_end(
1087+
audit,
1088+
SIMULATION_ID,
1089+
status=run_status,
1090+
error_message=end_error,
1091+
)
1092+
1093+
10351094
if __name__ == "__main__":
10361095
main()
10371096

samples/01_roa_agent/telemetry.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
"""``AuditStore`` helpers — canonical decision audit rows for ROA demo runs.
2+
3+
Aligned with ``src/dir_core/storage/schema.sql`` and telemetry guidelines:
4+
5+
* ``root_dfid`` = ``simulation_id`` for the whole script run.
6+
* Per-cycle rows use the scenario ``dfid`` with the same ``root_dfid``.
7+
* ``detail_json`` includes ``correlation_id`` (= ``simulation_id``).
8+
"""
9+
10+
from __future__ import annotations
11+
12+
from typing import Any, Dict, Optional, Union
13+
14+
from dir_core import EscalationRequest, PolicyProposal
15+
from dir_core.storage.base import AuditStore
16+
17+
SIMULATION_ID = "sample_01_roa_agent"
18+
19+
20+
def _detail_base(
21+
simulation_id: str,
22+
extra: Optional[Dict[str, Any]] = None,
23+
*,
24+
causation_id: Optional[str] = None,
25+
) -> Dict[str, Any]:
26+
out: Dict[str, Any] = {
27+
"simulation_id": simulation_id,
28+
"correlation_id": simulation_id,
29+
}
30+
if causation_id:
31+
out["causation_id"] = causation_id
32+
if extra:
33+
out.update(extra)
34+
return out
35+
36+
37+
def record_simulation_start(
38+
audit: AuditStore,
39+
simulation_id: str = SIMULATION_ID,
40+
*,
41+
sample: str = "01_roa_agent",
42+
) -> None:
43+
audit.record(
44+
simulation_id,
45+
"SIMULATION_START",
46+
step_id="SIMULATION",
47+
state="RUNNING",
48+
details=_detail_base(
49+
simulation_id,
50+
{
51+
"sample": sample,
52+
"mode": "roa_educational_demo",
53+
},
54+
),
55+
root_dfid=simulation_id,
56+
severity="INFO",
57+
)
58+
59+
60+
def record_simulation_end(
61+
audit: AuditStore,
62+
simulation_id: str = SIMULATION_ID,
63+
*,
64+
status: str,
65+
error_message: Optional[str] = None,
66+
) -> None:
67+
extra: Dict[str, Any] = {"status": status}
68+
if error_message is not None:
69+
extra["error_message"] = error_message
70+
sev = "ERROR" if status.lower() == "error" else "INFO"
71+
audit.record(
72+
simulation_id,
73+
"SIMULATION_END",
74+
step_id="SIMULATION",
75+
state=status.upper(),
76+
details=_detail_base(simulation_id, extra),
77+
root_dfid=simulation_id,
78+
severity=sev,
79+
)
80+
81+
82+
def record_roa_cycle_result(
83+
audit: AuditStore,
84+
dfid: str,
85+
simulation_id: str,
86+
result: Union[PolicyProposal, EscalationRequest],
87+
*,
88+
scenario_label: str,
89+
) -> None:
90+
"""Emit one audit row per ``run_decision_cycle`` outcome."""
91+
if isinstance(result, PolicyProposal):
92+
pk = str(result.policy_kind)
93+
st = pk.upper().replace(" ", "_")[:120]
94+
audit.record(
95+
dfid,
96+
"AGENT_DECISION",
97+
step_id="ROA_CYCLE",
98+
state=st,
99+
details=_detail_base(
100+
simulation_id,
101+
{
102+
"scenario": scenario_label,
103+
"agent_id": result.agent_id,
104+
"policy_kind": result.policy_kind,
105+
"confidence": result.confidence,
106+
"justification": (result.justification or "")[:500],
107+
},
108+
),
109+
root_dfid=simulation_id,
110+
agent_id=result.agent_id,
111+
severity="INFO",
112+
)
113+
return
114+
115+
sev_esc = str(result.severity)
116+
audit.record(
117+
dfid,
118+
"ESCALATION_REQUESTED",
119+
step_id="ROA_ESCALATION",
120+
state="PENDING",
121+
details=_detail_base(
122+
simulation_id,
123+
{
124+
"scenario": scenario_label,
125+
"from_agent_id": result.from_agent_id,
126+
"to_agent_id": result.to_agent_id,
127+
"trigger": result.trigger,
128+
"severity": sev_esc,
129+
},
130+
),
131+
root_dfid=simulation_id,
132+
agent_id=result.from_agent_id,
133+
severity="WARNING",
134+
)

0 commit comments

Comments
 (0)