agent-learning-kit 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (642) hide show
  1. agent_learning_kit-0.1.0.dist-info/METADATA +381 -0
  2. agent_learning_kit-0.1.0.dist-info/RECORD +642 -0
  3. agent_learning_kit-0.1.0.dist-info/WHEEL +4 -0
  4. agent_learning_kit-0.1.0.dist-info/entry_points.txt +5 -0
  5. agent_learning_kit-0.1.0.dist-info/licenses/LICENSE +173 -0
  6. agent_learning_kit-0.1.0.dist-info/licenses/NOTICE +7 -0
  7. fi/__init__.py +5 -0
  8. fi/alk/__init__.py +57 -0
  9. fi/alk/_facade.py +31 -0
  10. fi/alk/_module_alias.py +68 -0
  11. fi/alk/_paths.py +14 -0
  12. fi/alk/_schema.py +522 -0
  13. fi/alk/actions.py +727 -0
  14. fi/alk/bench/__init__.py +517 -0
  15. fi/alk/bench/_codeexec.py +213 -0
  16. fi/alk/bench/_coding.py +215 -0
  17. fi/alk/bench/_docker.py +237 -0
  18. fi/alk/bench/_grader.py +286 -0
  19. fi/alk/bench/_pull.py +212 -0
  20. fi/alk/bench/_voice.py +147 -0
  21. fi/alk/capabilities.py +627 -0
  22. fi/alk/cli.py +6396 -0
  23. fi/alk/config.py +130 -0
  24. fi/alk/cua_loop.py +562 -0
  25. fi/alk/evals.py +2351 -0
  26. fi/alk/extensions.py +163 -0
  27. fi/alk/harness/ARCHITECTURE.md +231 -0
  28. fi/alk/harness/DESIGN.md +246 -0
  29. fi/alk/harness/ENVIRONMENT_CONFORMANCE.md +127 -0
  30. fi/alk/harness/HOW-IT-WORKS.md +297 -0
  31. fi/alk/harness/IMPLEMENTATION_AND_VALIDATION_STATUS.md +229 -0
  32. fi/alk/harness/README.md +417 -0
  33. fi/alk/harness/__init__.py +77 -0
  34. fi/alk/harness/__main__.py +3 -0
  35. fi/alk/harness/amend.py +312 -0
  36. fi/alk/harness/artifacts.py +319 -0
  37. fi/alk/harness/authoring_entrypoint.py +189 -0
  38. fi/alk/harness/authoring_runtime_validation.py +267 -0
  39. fi/alk/harness/backends/README.md +43 -0
  40. fi/alk/harness/backends/__init__.py +122 -0
  41. fi/alk/harness/backends/base.py +241 -0
  42. fi/alk/harness/backends/claude.py +211 -0
  43. fi/alk/harness/backends/files.py +182 -0
  44. fi/alk/harness/backends/vertex_gemini.py +457 -0
  45. fi/alk/harness/background_noise.py +95 -0
  46. fi/alk/harness/build.py +385 -0
  47. fi/alk/harness/bundle.py +593 -0
  48. fi/alk/harness/bundle_author_v2.py +1831 -0
  49. fi/alk/harness/bundle_v2.py +719 -0
  50. fi/alk/harness/call_runner.py +1440 -0
  51. fi/alk/harness/callback_http_adapter.py +111 -0
  52. fi/alk/harness/catalogue.py +287 -0
  53. fi/alk/harness/chat.py +428 -0
  54. fi/alk/harness/chat_call_runner.py +506 -0
  55. fi/alk/harness/checks.py +136 -0
  56. fi/alk/harness/cli.py +1354 -0
  57. fi/alk/harness/config.py +338 -0
  58. fi/alk/harness/contract.py +718 -0
  59. fi/alk/harness/credentials.py +674 -0
  60. fi/alk/harness/data/persona_vocabulary.json +111 -0
  61. fi/alk/harness/environment.py +99 -0
  62. fi/alk/harness/environment_plan.py +168 -0
  63. fi/alk/harness/events.py +125 -0
  64. fi/alk/harness/executor.py +304 -0
  65. fi/alk/harness/folder.py +234 -0
  66. fi/alk/harness/generated_runtime.py +815 -0
  67. fi/alk/harness/github.py +72 -0
  68. fi/alk/harness/hosted_authoring_entrypoint.py +183 -0
  69. fi/alk/harness/hosted_entrypoint.py +2402 -0
  70. fi/alk/harness/hosted_scheduler.py +2218 -0
  71. fi/alk/harness/job.py +426 -0
  72. fi/alk/harness/judge.py +184 -0
  73. fi/alk/harness/livekit_source.py +50 -0
  74. fi/alk/harness/livekit_tool_trace_bootstrap.py +71 -0
  75. fi/alk/harness/observability.py +208 -0
  76. fi/alk/harness/outbound.py +3252 -0
  77. fi/alk/harness/packaging.py +515 -0
  78. fi/alk/harness/persona_guides.py +157 -0
  79. fi/alk/harness/platform.py +692 -0
  80. fi/alk/harness/process_preflight.py +764 -0
  81. fi/alk/harness/process_runtime.py +5670 -0
  82. fi/alk/harness/prove.py +425 -0
  83. fi/alk/harness/provider_import.py +703 -0
  84. fi/alk/harness/provider_lifecycle.py +392 -0
  85. fi/alk/harness/provision.py +2896 -0
  86. fi/alk/harness/reception.py +147 -0
  87. fi/alk/harness/retell_chat_call_runner.py +373 -0
  88. fi/alk/harness/run/__init__.py +296 -0
  89. fi/alk/harness/run/alk.py +184 -0
  90. fi/alk/harness/run/call.py +162 -0
  91. fi/alk/harness/run/conversation.py +264 -0
  92. fi/alk/harness/run/data/voices_by_language_and_gender.json +693 -0
  93. fi/alk/harness/run/evidence.py +195 -0
  94. fi/alk/harness/run/grade.py +598 -0
  95. fi/alk/harness/run/live.py +297 -0
  96. fi/alk/harness/run/models.py +56 -0
  97. fi/alk/harness/run/platform_evals.py +227 -0
  98. fi/alk/harness/run/sdk_voice.py +130 -0
  99. fi/alk/harness/run/simulation.py +1209 -0
  100. fi/alk/harness/run/stage.py +91 -0
  101. fi/alk/harness/run/targets.py +508 -0
  102. fi/alk/harness/run/tools.py +601 -0
  103. fi/alk/harness/run/voice.py +340 -0
  104. fi/alk/harness/runtime.py +172 -0
  105. fi/alk/harness/sandbox_server.py +2011 -0
  106. fi/alk/harness/sandbox_worker.py +44 -0
  107. fi/alk/harness/scenario.py +1048 -0
  108. fi/alk/harness/scenario_source.py +879 -0
  109. fi/alk/harness/scenario_tools.py +1143 -0
  110. fi/alk/harness/scenarios.py +915 -0
  111. fi/alk/harness/secrets.py +168 -0
  112. fi/alk/harness/service_catalog.py +97 -0
  113. fi/alk/harness/session.py +391 -0
  114. fi/alk/harness/sessions.py +372 -0
  115. fi/alk/harness/simulator.py +76 -0
  116. fi/alk/harness/simulator_voice.py +928 -0
  117. fi/alk/harness/skills/build-environment/SKILL.md +538 -0
  118. fi/alk/harness/skills/harness.md +131 -0
  119. fi/alk/harness/skills/kinds/chat.md +48 -0
  120. fi/alk/harness/skills/kinds/voice-voicemail.md +63 -0
  121. fi/alk/harness/skills/kinds/voice.md +59 -0
  122. fi/alk/harness/skills/plan-suite/SKILL.md +103 -0
  123. fi/alk/harness/skills/provision-environment/SKILL.md +136 -0
  124. fi/alk/harness/skills/run-scenarios/SKILL.md +112 -0
  125. fi/alk/harness/skills/understand-agent/SKILL.md +251 -0
  126. fi/alk/harness/skills/write-scenarios/SKILL.md +606 -0
  127. fi/alk/harness/skills/write-scenarios/references/refusals.md +28 -0
  128. fi/alk/harness/skills/write-scenarios/references/world-api.md +92 -0
  129. fi/alk/harness/source_data_invariants.py +444 -0
  130. fi/alk/harness/source_tool_evidence.py +79 -0
  131. fi/alk/harness/sources.py +253 -0
  132. fi/alk/harness/spend.py +140 -0
  133. fi/alk/harness/tool_trace_proxy.py +104 -0
  134. fi/alk/harness/tools.py +1018 -0
  135. fi/alk/harness/understand.py +169 -0
  136. fi/alk/harness/voicemail_audio.py +74 -0
  137. fi/alk/harness/world/__init__.py +33 -0
  138. fi/alk/harness/world/errors.py +68 -0
  139. fi/alk/harness/world/expectations.py +91 -0
  140. fi/alk/harness/world/handle.py +538 -0
  141. fi/alk/harness/world/kinds.py +196 -0
  142. fi/alk/harness/world/mutate.py +186 -0
  143. fi/alk/harness/world/probe.py +413 -0
  144. fi/alk/harness/world/provision.py +511 -0
  145. fi/alk/harness/world/provisioned.py +191 -0
  146. fi/alk/harness/world/runtime.py +616 -0
  147. fi/alk/harness/world/snapshot.py +288 -0
  148. fi/alk/harness/world/stores/__init__.py +305 -0
  149. fi/alk/harness/world/stores/container.py +215 -0
  150. fi/alk/harness/world/stores/inprocess.py +346 -0
  151. fi/alk/harness/world/stores/postgres.py +481 -0
  152. fi/alk/harness/world/stores/prove.py +202 -0
  153. fi/alk/harness/world/stores/sqlite.py +245 -0
  154. fi/alk/harness/world/stores/written.py +182 -0
  155. fi/alk/harness/world/tools.py +1516 -0
  156. fi/alk/harness/world/workspace.py +144 -0
  157. fi/alk/image_loop.py +453 -0
  158. fi/alk/image_perturb.py +241 -0
  159. fi/alk/improve.py +274 -0
  160. fi/alk/live/__init__.py +154 -0
  161. fi/alk/live/_attribution.py +184 -0
  162. fi/alk/live/_capture.py +264 -0
  163. fi/alk/live/_codec.py +391 -0
  164. fi/alk/live/_contract.py +134 -0
  165. fi/alk/live/_loopback.py +316 -0
  166. fi/alk/live/_perturb.py +449 -0
  167. fi/alk/live/_runner.py +386 -0
  168. fi/alk/live/_stats.py +561 -0
  169. fi/alk/live/_transcript.py +240 -0
  170. fi/alk/live/_workers/__init__.py +9 -0
  171. fi/alk/live/_workers/a2a_worker.py +316 -0
  172. fi/alk/live/_workers/langgraph_worker.py +217 -0
  173. fi/alk/live/_workers/livekit_worker.py +207 -0
  174. fi/alk/live/_workers/mcp_loopback_server.py +46 -0
  175. fi/alk/live/_workers/mcp_worker.py +158 -0
  176. fi/alk/live/_workers/pipecat_worker.py +189 -0
  177. fi/alk/live/a2a_lane.py +138 -0
  178. fi/alk/live/langgraph_lane.py +339 -0
  179. fi/alk/live/livekit_lane.py +376 -0
  180. fi/alk/live/mcp_lane.py +172 -0
  181. fi/alk/live/pipecat_lane.py +341 -0
  182. fi/alk/live/voice_redteam.py +494 -0
  183. fi/alk/loss.py +306 -0
  184. fi/alk/optimize.py +36260 -0
  185. fi/alk/practice/__init__.py +51 -0
  186. fi/alk/practice/_assess.py +103 -0
  187. fi/alk/practice/_budget.py +81 -0
  188. fi/alk/practice/_calibrate.py +69 -0
  189. fi/alk/practice/_capstone.py +86 -0
  190. fi/alk/practice/_contract.py +91 -0
  191. fi/alk/practice/_diagnose.py +79 -0
  192. fi/alk/practice/_drill.py +196 -0
  193. fi/alk/practice/_experiment.py +720 -0
  194. fi/alk/practice/_schedule.py +102 -0
  195. fi/alk/practice/_store.py +194 -0
  196. fi/alk/practice/_trainer.py +245 -0
  197. fi/alk/practice/_update.py +125 -0
  198. fi/alk/redteam.py +2621 -0
  199. fi/alk/rewardhack.py +237 -0
  200. fi/alk/simulate.py +10351 -0
  201. fi/alk/studio/__init__.py +82 -0
  202. fi/alk/studio/_bias.py +314 -0
  203. fi/alk/studio/_calibration.py +522 -0
  204. fi/alk/studio/_coverage.py +262 -0
  205. fi/alk/studio/_download.py +665 -0
  206. fi/alk/studio/_fidelity_attack.py +114 -0
  207. fi/alk/studio/_generate.py +652 -0
  208. fi/alk/studio/_library.py +370 -0
  209. fi/alk/studio/_scan.py +134 -0
  210. fi/alk/studio/_upgrade.py +42 -0
  211. fi/alk/studio/_vendor.py +172 -0
  212. fi/alk/suite.py +4200 -0
  213. fi/alk/tasks.py +828 -0
  214. fi/alk/telemetry/__init__.py +149 -0
  215. fi/alk/telemetry/_contract.py +141 -0
  216. fi/alk/telemetry/_emit.py +182 -0
  217. fi/alk/telemetry/_ledger.py +296 -0
  218. fi/alk/telemetry/_queue.py +127 -0
  219. fi/alk/telemetry/_row.py +294 -0
  220. fi/alk/telemetry/_run.py +233 -0
  221. fi/alk/telemetry/_sync.py +193 -0
  222. fi/alk/telemetry/_url.py +119 -0
  223. fi/alk/trinity.py +49397 -0
  224. fi/alk/voice_loop.py +174 -0
  225. fi/api/__init__.py +1 -0
  226. fi/api/auth.py +137 -0
  227. fi/api/types.py +29 -0
  228. fi/cli/__init__.py +9 -0
  229. fi/cli/assertions/__init__.py +25 -0
  230. fi/cli/assertions/conditions.py +76 -0
  231. fi/cli/assertions/evaluator.py +286 -0
  232. fi/cli/assertions/exit_codes.py +20 -0
  233. fi/cli/assertions/parser.py +131 -0
  234. fi/cli/assertions/reporter.py +194 -0
  235. fi/cli/commands/__init__.py +9 -0
  236. fi/cli/commands/config.py +165 -0
  237. fi/cli/commands/export.py +208 -0
  238. fi/cli/commands/init.py +112 -0
  239. fi/cli/commands/list_cmd.py +213 -0
  240. fi/cli/commands/run.py +486 -0
  241. fi/cli/commands/validate.py +173 -0
  242. fi/cli/commands/view.py +424 -0
  243. fi/cli/config/__init__.py +6 -0
  244. fi/cli/config/defaults.py +206 -0
  245. fi/cli/config/loader.py +155 -0
  246. fi/cli/config/schema.py +174 -0
  247. fi/cli/main.py +78 -0
  248. fi/cli/output/__init__.py +6 -0
  249. fi/cli/output/formatters.py +106 -0
  250. fi/cli/output/reporters.py +46 -0
  251. fi/cli/storage/__init__.py +5 -0
  252. fi/cli/storage/run_history.py +249 -0
  253. fi/cli/utils/__init__.py +5 -0
  254. fi/cli/utils/console.py +44 -0
  255. fi/evals/__init__.py +131 -0
  256. fi/evals/autoeval/__init__.py +137 -0
  257. fi/evals/autoeval/analyzer.py +211 -0
  258. fi/evals/autoeval/config.py +244 -0
  259. fi/evals/autoeval/export.py +213 -0
  260. fi/evals/autoeval/interactive.py +283 -0
  261. fi/evals/autoeval/pipeline.py +625 -0
  262. fi/evals/autoeval/prompts.py +139 -0
  263. fi/evals/autoeval/recommender.py +242 -0
  264. fi/evals/autoeval/rules.py +589 -0
  265. fi/evals/autoeval/templates.py +299 -0
  266. fi/evals/autoeval/types.py +232 -0
  267. fi/evals/core/__init__.py +16 -0
  268. fi/evals/core/cloud_registry.py +184 -0
  269. fi/evals/core/engines.py +368 -0
  270. fi/evals/core/evaluate.py +319 -0
  271. fi/evals/core/judge_prompt.py +90 -0
  272. fi/evals/core/prompt_generator.py +83 -0
  273. fi/evals/core/registry.py +57 -0
  274. fi/evals/core/result.py +55 -0
  275. fi/evals/evaluator.py +721 -0
  276. fi/evals/execution.py +168 -0
  277. fi/evals/feedback/__init__.py +32 -0
  278. fi/evals/feedback/calibrator.py +160 -0
  279. fi/evals/feedback/collector.py +214 -0
  280. fi/evals/feedback/hooks.py +81 -0
  281. fi/evals/feedback/retriever.py +128 -0
  282. fi/evals/feedback/store.py +272 -0
  283. fi/evals/feedback/types.py +99 -0
  284. fi/evals/framework/README.md +79 -0
  285. fi/evals/framework/__init__.py +267 -0
  286. fi/evals/framework/backends/Dockerfile.eval-runner +33 -0
  287. fi/evals/framework/backends/__init__.py +99 -0
  288. fi/evals/framework/backends/_container.py +141 -0
  289. fi/evals/framework/backends/_utils.py +145 -0
  290. fi/evals/framework/backends/base.py +223 -0
  291. fi/evals/framework/backends/celery_backend.py +417 -0
  292. fi/evals/framework/backends/celery_worker.py +78 -0
  293. fi/evals/framework/backends/kubernetes_backend.py +665 -0
  294. fi/evals/framework/backends/ray_backend.py +521 -0
  295. fi/evals/framework/backends/temporal.py +350 -0
  296. fi/evals/framework/backends/temporal_worker.py +126 -0
  297. fi/evals/framework/backends/thread_pool.py +286 -0
  298. fi/evals/framework/context.py +258 -0
  299. fi/evals/framework/enrichment.py +306 -0
  300. fi/evals/framework/evals/__init__.py +68 -0
  301. fi/evals/framework/evals/agentic.py +399 -0
  302. fi/evals/framework/evals/builder.py +609 -0
  303. fi/evals/framework/evals/semantic.py +142 -0
  304. fi/evals/framework/evaluator.py +647 -0
  305. fi/evals/framework/evaluators/__init__.py +22 -0
  306. fi/evals/framework/evaluators/blocking.py +347 -0
  307. fi/evals/framework/evaluators/non_blocking.py +577 -0
  308. fi/evals/framework/propagation.py +421 -0
  309. fi/evals/framework/protocols.py +385 -0
  310. fi/evals/framework/registry.py +370 -0
  311. fi/evals/framework/resilience/__init__.py +150 -0
  312. fi/evals/framework/resilience/circuit_breaker.py +309 -0
  313. fi/evals/framework/resilience/degradation.py +355 -0
  314. fi/evals/framework/resilience/health.py +505 -0
  315. fi/evals/framework/resilience/rate_limiter.py +228 -0
  316. fi/evals/framework/resilience/retry.py +274 -0
  317. fi/evals/framework/resilience/types.py +288 -0
  318. fi/evals/framework/resilience/wrapper.py +433 -0
  319. fi/evals/framework/types.py +218 -0
  320. fi/evals/guardrails/README.md +915 -0
  321. fi/evals/guardrails/__init__.py +96 -0
  322. fi/evals/guardrails/backends/__init__.py +43 -0
  323. fi/evals/guardrails/backends/azure.py +361 -0
  324. fi/evals/guardrails/backends/base.py +88 -0
  325. fi/evals/guardrails/backends/generic_llm.py +163 -0
  326. fi/evals/guardrails/backends/granite.py +216 -0
  327. fi/evals/guardrails/backends/llamaguard.py +221 -0
  328. fi/evals/guardrails/backends/local_base.py +479 -0
  329. fi/evals/guardrails/backends/openai.py +365 -0
  330. fi/evals/guardrails/backends/qwen.py +170 -0
  331. fi/evals/guardrails/backends/shieldgemma.py +154 -0
  332. fi/evals/guardrails/backends/turing.py +235 -0
  333. fi/evals/guardrails/backends/vllm_client.py +321 -0
  334. fi/evals/guardrails/backends/wildguard.py +188 -0
  335. fi/evals/guardrails/base.py +888 -0
  336. fi/evals/guardrails/config.py +221 -0
  337. fi/evals/guardrails/discovery.py +243 -0
  338. fi/evals/guardrails/gateway.py +437 -0
  339. fi/evals/guardrails/registry.py +231 -0
  340. fi/evals/guardrails/scanners/__init__.py +127 -0
  341. fi/evals/guardrails/scanners/base.py +191 -0
  342. fi/evals/guardrails/scanners/code_injection.py +243 -0
  343. fi/evals/guardrails/scanners/eval_delegate.py +574 -0
  344. fi/evals/guardrails/scanners/invisible_chars.py +351 -0
  345. fi/evals/guardrails/scanners/jailbreak.py +412 -0
  346. fi/evals/guardrails/scanners/language.py +288 -0
  347. fi/evals/guardrails/scanners/pipeline.py +260 -0
  348. fi/evals/guardrails/scanners/regex.py +311 -0
  349. fi/evals/guardrails/scanners/secrets.py +274 -0
  350. fi/evals/guardrails/scanners/topics.py +649 -0
  351. fi/evals/guardrails/scanners/urls.py +341 -0
  352. fi/evals/guardrails/types.py +96 -0
  353. fi/evals/llm/__init__.py +3 -0
  354. fi/evals/llm/base_llm_provider.py +35 -0
  355. fi/evals/llm/providers/litellm.py +70 -0
  356. fi/evals/local/__init__.py +90 -0
  357. fi/evals/local/evaluator.py +690 -0
  358. fi/evals/local/execution_mode.py +121 -0
  359. fi/evals/local/llm.py +489 -0
  360. fi/evals/local/metrics/__init__.py +19 -0
  361. fi/evals/local/registry.py +360 -0
  362. fi/evals/manager.py +1018 -0
  363. fi/evals/manager_types.py +362 -0
  364. fi/evals/metrics/__init__.py +185 -0
  365. fi/evals/metrics/agents/__init__.py +74 -0
  366. fi/evals/metrics/agents/metrics.py +693 -0
  367. fi/evals/metrics/agents/report.py +36463 -0
  368. fi/evals/metrics/agents/types.py +160 -0
  369. fi/evals/metrics/base_llm_metric.py +111 -0
  370. fi/evals/metrics/base_metric.py +138 -0
  371. fi/evals/metrics/code_security/__init__.py +305 -0
  372. fi/evals/metrics/code_security/analyzer.py +985 -0
  373. fi/evals/metrics/code_security/benchmarks/__init__.py +73 -0
  374. fi/evals/metrics/code_security/benchmarks/builtin.py +750 -0
  375. fi/evals/metrics/code_security/benchmarks/loader.py +580 -0
  376. fi/evals/metrics/code_security/benchmarks/types.py +308 -0
  377. fi/evals/metrics/code_security/detectors/__init__.py +186 -0
  378. fi/evals/metrics/code_security/detectors/base.py +394 -0
  379. fi/evals/metrics/code_security/detectors/cryptography.py +345 -0
  380. fi/evals/metrics/code_security/detectors/injection.py +744 -0
  381. fi/evals/metrics/code_security/detectors/secrets.py +287 -0
  382. fi/evals/metrics/code_security/detectors/serialization.py +192 -0
  383. fi/evals/metrics/code_security/joint_metrics.py +588 -0
  384. fi/evals/metrics/code_security/judges/__init__.py +83 -0
  385. fi/evals/metrics/code_security/judges/base.py +238 -0
  386. fi/evals/metrics/code_security/judges/dual_judge.py +534 -0
  387. fi/evals/metrics/code_security/judges/llm_judge.py +301 -0
  388. fi/evals/metrics/code_security/judges/pattern_judge.py +515 -0
  389. fi/evals/metrics/code_security/metrics.py +388 -0
  390. fi/evals/metrics/code_security/modes/__init__.py +63 -0
  391. fi/evals/metrics/code_security/modes/adversarial.py +284 -0
  392. fi/evals/metrics/code_security/modes/autocomplete.py +198 -0
  393. fi/evals/metrics/code_security/modes/base.py +283 -0
  394. fi/evals/metrics/code_security/modes/instruct.py +253 -0
  395. fi/evals/metrics/code_security/modes/repair.py +230 -0
  396. fi/evals/metrics/code_security/reports/__init__.py +57 -0
  397. fi/evals/metrics/code_security/reports/generator.py +404 -0
  398. fi/evals/metrics/code_security/reports/leaderboard.py +509 -0
  399. fi/evals/metrics/code_security/types.py +534 -0
  400. fi/evals/metrics/function_calling/__init__.py +34 -0
  401. fi/evals/metrics/function_calling/metrics.py +573 -0
  402. fi/evals/metrics/function_calling/types.py +87 -0
  403. fi/evals/metrics/hallucination/__init__.py +54 -0
  404. fi/evals/metrics/hallucination/detector.py +149 -0
  405. fi/evals/metrics/hallucination/metrics.py +390 -0
  406. fi/evals/metrics/hallucination/nli.py +253 -0
  407. fi/evals/metrics/hallucination/sentinel.py +106 -0
  408. fi/evals/metrics/hallucination/types.py +132 -0
  409. fi/evals/metrics/heuristics/aggregation_metrics.py +85 -0
  410. fi/evals/metrics/heuristics/json_metrics.py +87 -0
  411. fi/evals/metrics/heuristics/similarity_metrics.py +375 -0
  412. fi/evals/metrics/heuristics/string_metrics.py +391 -0
  413. fi/evals/metrics/llm_as_judges/__init__.py +17 -0
  414. fi/evals/metrics/llm_as_judges/custom_judge/metric.py +112 -0
  415. fi/evals/metrics/llm_as_judges/custom_judge/prompts.py +26 -0
  416. fi/evals/metrics/llm_as_judges/types.py +48 -0
  417. fi/evals/metrics/rag/__init__.py +111 -0
  418. fi/evals/metrics/rag/advanced/__init__.py +14 -0
  419. fi/evals/metrics/rag/advanced/multi_hop.py +283 -0
  420. fi/evals/metrics/rag/advanced/source_attribution.py +344 -0
  421. fi/evals/metrics/rag/generation/__init__.py +17 -0
  422. fi/evals/metrics/rag/generation/answer_relevancy.py +176 -0
  423. fi/evals/metrics/rag/generation/context_utilization.py +245 -0
  424. fi/evals/metrics/rag/generation/faithfulness.py +241 -0
  425. fi/evals/metrics/rag/generation/groundedness.py +131 -0
  426. fi/evals/metrics/rag/rag_score.py +277 -0
  427. fi/evals/metrics/rag/retrieval/__init__.py +20 -0
  428. fi/evals/metrics/rag/retrieval/context_entity_recall.py +124 -0
  429. fi/evals/metrics/rag/retrieval/context_precision.py +158 -0
  430. fi/evals/metrics/rag/retrieval/context_recall.py +106 -0
  431. fi/evals/metrics/rag/retrieval/noise_sensitivity.py +163 -0
  432. fi/evals/metrics/rag/retrieval/ranking.py +261 -0
  433. fi/evals/metrics/rag/types.py +100 -0
  434. fi/evals/metrics/rag/utils/__init__.py +62 -0
  435. fi/evals/metrics/rag/utils/claims.py +189 -0
  436. fi/evals/metrics/rag/utils/entities.py +244 -0
  437. fi/evals/metrics/rag/utils/nli.py +92 -0
  438. fi/evals/metrics/rag/utils/similarity.py +345 -0
  439. fi/evals/metrics/structured/__init__.py +114 -0
  440. fi/evals/metrics/structured/field_completeness.py +313 -0
  441. fi/evals/metrics/structured/hierarchy_score.py +366 -0
  442. fi/evals/metrics/structured/json_validation.py +190 -0
  443. fi/evals/metrics/structured/schema_compliance.py +280 -0
  444. fi/evals/metrics/structured/structured_output_score.py +298 -0
  445. fi/evals/metrics/structured/types.py +108 -0
  446. fi/evals/metrics/structured/validators/__init__.py +30 -0
  447. fi/evals/metrics/structured/validators/base.py +189 -0
  448. fi/evals/metrics/structured/validators/json_validator.py +196 -0
  449. fi/evals/metrics/structured/validators/pydantic_validator.py +178 -0
  450. fi/evals/metrics/structured/validators/yaml_validator.py +248 -0
  451. fi/evals/otel/__init__.py +266 -0
  452. fi/evals/otel/config.py +400 -0
  453. fi/evals/otel/conventions.py +463 -0
  454. fi/evals/otel/enrichment.py +371 -0
  455. fi/evals/otel/instrumentors/__init__.py +140 -0
  456. fi/evals/otel/instrumentors/anthropic.py +517 -0
  457. fi/evals/otel/instrumentors/base.py +382 -0
  458. fi/evals/otel/instrumentors/openai.py +673 -0
  459. fi/evals/otel/processors/__init__.py +36 -0
  460. fi/evals/otel/processors/base.py +473 -0
  461. fi/evals/otel/processors/cost.py +445 -0
  462. fi/evals/otel/processors/evaluation.py +559 -0
  463. fi/evals/otel/processors/llm.py +462 -0
  464. fi/evals/otel/tracer.py +506 -0
  465. fi/evals/otel/types.py +232 -0
  466. fi/evals/otel_utils.py +23 -0
  467. fi/evals/protect.py +671 -0
  468. fi/evals/protect_input_adapter.py +154 -0
  469. fi/evals/streaming/__init__.py +88 -0
  470. fi/evals/streaming/buffer.py +213 -0
  471. fi/evals/streaming/evaluator.py +551 -0
  472. fi/evals/streaming/policy.py +307 -0
  473. fi/evals/streaming/scorers.py +368 -0
  474. fi/evals/streaming/types.py +238 -0
  475. fi/evals/templates.py +472 -0
  476. fi/evals/types.py +156 -0
  477. fi/opt/__init__.py +221 -0
  478. fi/opt/_objective_scoring.py +85 -0
  479. fi/opt/base/__init__.py +11 -0
  480. fi/opt/base/base_generator.py +33 -0
  481. fi/opt/base/base_mapper.py +26 -0
  482. fi/opt/base/base_optimizer.py +45 -0
  483. fi/opt/base/evaluator.py +211 -0
  484. fi/opt/components.py +3095 -0
  485. fi/opt/datamappers/__init__.py +3 -0
  486. fi/opt/datamappers/basic_mapper.py +40 -0
  487. fi/opt/deployment.py +1021 -0
  488. fi/opt/evidence.py +4332 -0
  489. fi/opt/generators/__init__.py +3 -0
  490. fi/opt/generators/litellm.py +66 -0
  491. fi/opt/integrations/__init__.py +23 -0
  492. fi/opt/integrations/generative_suite.py +410 -0
  493. fi/opt/integrations/simulate.py +1313 -0
  494. fi/opt/mutations.py +771 -0
  495. fi/opt/observability.py +4639 -0
  496. fi/opt/optimizer_trace.py +889 -0
  497. fi/opt/optimizers/__init__.py +80 -0
  498. fi/opt/optimizers/agent.py +331 -0
  499. fi/opt/optimizers/agent_bandit.py +392 -0
  500. fi/opt/optimizers/agent_curriculum.py +635 -0
  501. fi/opt/optimizers/agent_evolution.py +894 -0
  502. fi/opt/optimizers/agent_feedback.py +1863 -0
  503. fi/opt/optimizers/agent_pareto.py +547 -0
  504. fi/opt/optimizers/agent_social_memory.py +1113 -0
  505. fi/opt/optimizers/agent_tpe.py +321 -0
  506. fi/opt/optimizers/bayesian_search.py +449 -0
  507. fi/opt/optimizers/council.py +2075 -0
  508. fi/opt/optimizers/futureagi_replay.py +799 -0
  509. fi/opt/optimizers/gepa.py +322 -0
  510. fi/opt/optimizers/metaprompt.py +243 -0
  511. fi/opt/optimizers/promptwizard.py +417 -0
  512. fi/opt/optimizers/protegi.py +329 -0
  513. fi/opt/optimizers/random_search.py +224 -0
  514. fi/opt/research.py +518 -0
  515. fi/opt/simulation.py +260 -0
  516. fi/opt/targets.py +232 -0
  517. fi/opt/types.py +66 -0
  518. fi/opt/utils/__init__.py +4 -0
  519. fi/opt/utils/early_stopping.py +266 -0
  520. fi/opt/utils/setup_logging.py +82 -0
  521. fi/simulate/__init__.py +540 -0
  522. fi/simulate/_hashing.py +35 -0
  523. fi/simulate/_logging.py +10 -0
  524. fi/simulate/adapters.py +87 -0
  525. fi/simulate/agent/__init__.py +120 -0
  526. fi/simulate/agent/browser.py +658 -0
  527. fi/simulate/agent/definition.py +587 -0
  528. fi/simulate/agent/frameworks.py +3528 -0
  529. fi/simulate/agent/generic.py +8286 -0
  530. fi/simulate/agent/import_probe.py +227 -0
  531. fi/simulate/agent/memory.py +905 -0
  532. fi/simulate/agent/mocks.py +101 -0
  533. fi/simulate/agent/multi_agent.py +361 -0
  534. fi/simulate/agent/orchestration.py +903 -0
  535. fi/simulate/agent/realtime.py +665 -0
  536. fi/simulate/agent/wrapper.py +99 -0
  537. fi/simulate/agent/wrappers/__init__.py +18 -0
  538. fi/simulate/agent/wrappers/anthropic.py +62 -0
  539. fi/simulate/agent/wrappers/gemini.py +65 -0
  540. fi/simulate/agent/wrappers/http.py +404 -0
  541. fi/simulate/agent/wrappers/langchain.py +80 -0
  542. fi/simulate/agent/wrappers/openai.py +75 -0
  543. fi/simulate/agent/wrappers/websocket.py +326 -0
  544. fi/simulate/artifacts/__init__.py +11 -0
  545. fi/simulate/artifacts/manifest.py +62 -0
  546. fi/simulate/cli.py +20560 -0
  547. fi/simulate/endpoints/__init__.py +45 -0
  548. fi/simulate/endpoints/_http_actor.py +73 -0
  549. fi/simulate/endpoints/actor_sources.py +243 -0
  550. fi/simulate/endpoints/base.py +107 -0
  551. fi/simulate/endpoints/builtins.py +10 -0
  552. fi/simulate/endpoints/callable.py +95 -0
  553. fi/simulate/endpoints/http.py +76 -0
  554. fi/simulate/endpoints/livekit.py +138 -0
  555. fi/simulate/endpoints/originators.py +132 -0
  556. fi/simulate/endpoints/profiles.py +348 -0
  557. fi/simulate/endpoints/retell.py +633 -0
  558. fi/simulate/endpoints/vapi.py +205 -0
  559. fi/simulate/endpoints/websocket.py +76 -0
  560. fi/simulate/environment.py +33026 -0
  561. fi/simulate/environments/__init__.py +11 -0
  562. fi/simulate/environments/base.py +73 -0
  563. fi/simulate/environments/chat.py +697 -0
  564. fi/simulate/environments/voice.py +212 -0
  565. fi/simulate/evaluation/__init__.py +4 -0
  566. fi/simulate/evaluation/ai_eval.py +227 -0
  567. fi/simulate/evidence/__init__.py +35 -0
  568. fi/simulate/evidence/base.py +59 -0
  569. fi/simulate/evidence/caller_observed.py +50 -0
  570. fi/simulate/evidence/livekit_instrumentation.py +51 -0
  571. fi/simulate/evidence/livekit_room.py +50 -0
  572. fi/simulate/evidence/otel.py +49 -0
  573. fi/simulate/evidence/providers/__init__.py +24 -0
  574. fi/simulate/evidence/providers/base.py +61 -0
  575. fi/simulate/evidence/providers/retell.py +376 -0
  576. fi/simulate/evidence/providers/vapi.py +426 -0
  577. fi/simulate/hosted/__init__.py +32 -0
  578. fi/simulate/hosted/child_entrypoint.py +306 -0
  579. fi/simulate/hosted/job.py +150 -0
  580. fi/simulate/hosted/targets.py +53 -0
  581. fi/simulate/instrumentation/__init__.py +5 -0
  582. fi/simulate/instrumentation/livekit/__init__.py +122 -0
  583. fi/simulate/manifest.py +1033 -0
  584. fi/simulate/matrix_cli.py +165 -0
  585. fi/simulate/realtime/__init__.py +40 -0
  586. fi/simulate/realtime/events.py +107 -0
  587. fi/simulate/realtime/media.py +61 -0
  588. fi/simulate/realtime/session.py +91 -0
  589. fi/simulate/recording/__init__.py +5 -0
  590. fi/simulate/recording/room_recorder.py +326 -0
  591. fi/simulate/registry.py +185 -0
  592. fi/simulate/results/__init__.py +9 -0
  593. fi/simulate/results/base.py +18 -0
  594. fi/simulate/results/filesystem.py +71 -0
  595. fi/simulate/results/futureagi.py +1340 -0
  596. fi/simulate/runtime/__init__.py +85 -0
  597. fi/simulate/runtime/capabilities.py +40 -0
  598. fi/simulate/runtime/events.py +63 -0
  599. fi/simulate/runtime/failures.py +25 -0
  600. fi/simulate/runtime/ids.py +34 -0
  601. fi/simulate/runtime/plan.py +70 -0
  602. fi/simulate/runtime/planner.py +102 -0
  603. fi/simulate/runtime/report.py +174 -0
  604. fi/simulate/runtime/run.py +75 -0
  605. fi/simulate/runtime/runner.py +333 -0
  606. fi/simulate/runtime/spec.py +186 -0
  607. fi/simulate/simulation/__init__.py +30 -0
  608. fi/simulate/simulation/behavior_policy.py +425 -0
  609. fi/simulate/simulation/bridge/__init__.py +9 -0
  610. fi/simulate/simulation/bridge/audio.py +29 -0
  611. fi/simulate/simulation/bridge/connector.py +46 -0
  612. fi/simulate/simulation/bridge/livekit.py +252 -0
  613. fi/simulate/simulation/bridge/retell.py +188 -0
  614. fi/simulate/simulation/bridge/vapi.py +177 -0
  615. fi/simulate/simulation/contract.py +419 -0
  616. fi/simulate/simulation/engines/__init__.py +12 -0
  617. fi/simulate/simulation/engines/base.py +21 -0
  618. fi/simulate/simulation/engines/cloud.py +517 -0
  619. fi/simulate/simulation/engines/livekit.py +4167 -0
  620. fi/simulate/simulation/engines/local_text.py +89 -0
  621. fi/simulate/simulation/fidelity.py +374 -0
  622. fi/simulate/simulation/gemini_tts_stream.py +110 -0
  623. fi/simulate/simulation/generator.py +91 -0
  624. fi/simulate/simulation/goal_machine.py +185 -0
  625. fi/simulate/simulation/livekit_models.py +467 -0
  626. fi/simulate/simulation/matrix.py +170 -0
  627. fi/simulate/simulation/models.py +279 -0
  628. fi/simulate/simulation/runner.py +153 -0
  629. fi/simulate/simulation/synthetic.py +880 -0
  630. fi/simulate/simulation/voice_prompt.py +502 -0
  631. fi/simulate/simulator/__init__.py +55 -0
  632. fi/simulate/simulator/builtins.py +53 -0
  633. fi/simulate/suite.py +1288 -0
  634. fi/simulate/utils/routes.py +164 -0
  635. fi/simulate/voice.py +225 -0
  636. fi/simulate/voice_cli.py +182 -0
  637. fi/utils/__init__.py +1 -0
  638. fi/utils/constants.py +14 -0
  639. fi/utils/errors.py +200 -0
  640. fi/utils/executor.py +26 -0
  641. fi/utils/routes.py +119 -0
  642. fi/utils/utils.py +17 -0
@@ -0,0 +1,522 @@
1
+ """Persona authoring + calibration lifecycle (Phase 7, unit 4).
2
+
3
+ Lifecycle = ``sampled -> validated -> interrogated -> admitted`` (ARCH §4
4
+ canon). Interrogation is the PICon battery — probe legs ``internal`` /
5
+ ``external`` / ``retest`` — run against a SCRIPTED deterministic responder
6
+ (no LLM, no keys, no network). The retest leg is replay-based: the identical
7
+ battery re-runs ``repeats`` times under the same seed and the realization
8
+ vectors must agree fork-free (``_probe_divergence_step`` re-implements the
9
+ ``live/_stats.divergence_step`` SEMANTIC — the live package is never imported
10
+ here, per the live_lane_boundary rule).
11
+
12
+ Emits ``agent-learning.persona-calibration.v1`` artifacts; bias-lint results
13
+ ride INSIDE them as a ``bias_lint`` block (never a standalone kind).
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import hashlib
19
+ import json
20
+ import random
21
+ from typing import Any, Dict, List, Mapping, Optional, Sequence, Tuple, Union
22
+
23
+ from fi.simulate.simulation.behavior_policy import (
24
+ BEHAVIOR_POLICY_AXIS_FIELDS,
25
+ PERSONA_BEHAVIOR_AXES,
26
+ PERSONA_BEHAVIOR_REALIZATION_METRICS,
27
+ compile_behavior_policy,
28
+ )
29
+ from fi.simulate.simulation.models import (
30
+ AttackConditioning,
31
+ BehaviorPolicy,
32
+ Persona,
33
+ PersonaFact,
34
+ PersonaIdentity,
35
+ PersonaProvenance,
36
+ PersonaTemperament,
37
+ Scenario,
38
+ )
39
+
40
+ from ._library import (
41
+ PERSONA_CALIBRATION_KIND,
42
+ save_calibration,
43
+ )
44
+ from ._upgrade import upgrade_legacy_persona
45
+
46
+ PERSONA_CALIBRATION_STAGES = ("sampled", "validated", "interrogated", "admitted")
47
+ PERSONA_CALIBRATION_PROBES = ("internal", "external", "retest")
48
+
49
+ # Monotone upgrade lattice: calibration may only move a persona UP this rank.
50
+ # cloud_downloaded and trace_mined are provenance facts, never calibration
51
+ # outcomes — calibration stamps calibrated=True but leaves their class alone.
52
+ _CALIBRATABLE_RANK = {
53
+ "legacy": 0,
54
+ "hand_written": 1,
55
+ "schema_sampled": 2,
56
+ "policy_evolved": 3,
57
+ }
58
+ _CALIBRATION_TARGETS = ("hand_written", "schema_sampled", "policy_evolved")
59
+
60
+
61
+ def build_persona(
62
+ *,
63
+ name: str,
64
+ situation: str,
65
+ outcome: str,
66
+ role: Optional[str] = None,
67
+ summary: Optional[str] = None,
68
+ language: Optional[str] = None,
69
+ demographics: Optional[Mapping[str, Any]] = None,
70
+ style_notes: Sequence[str] = (),
71
+ temperament: Union[PersonaTemperament, Mapping[str, float], None] = None,
72
+ behavior_policy: Union[BehaviorPolicy, Mapping[str, Any], None] = None,
73
+ knowledge: Sequence[Union[PersonaFact, Mapping[str, Any]]] = (),
74
+ attack: Union[AttackConditioning, Mapping[str, Any], None] = None,
75
+ evidence_class: str = "hand_written",
76
+ ) -> Persona:
77
+ """Deterministic persona writer (hand_written / schema_sampled classes).
78
+
79
+ Same inputs -> byte-identical persona (content hash stable). When a
80
+ temperament is given without an explicit policy, the engine compiler
81
+ derives layer 3 (explicit beats derived, ARCH Decision 4)."""
82
+ if evidence_class not in ("hand_written", "schema_sampled"):
83
+ raise ValueError(
84
+ "build_persona writes hand_written or schema_sampled personas "
85
+ f"only (got {evidence_class!r}); other classes have their own "
86
+ "writers (calibration / pull / upgrade)"
87
+ )
88
+ temperament_model: Optional[PersonaTemperament] = None
89
+ if temperament is not None:
90
+ temperament_model = (
91
+ temperament if isinstance(temperament, PersonaTemperament)
92
+ else PersonaTemperament(**dict(temperament))
93
+ )
94
+ policy_model: Optional[BehaviorPolicy] = None
95
+ if behavior_policy is not None:
96
+ policy_model = (
97
+ behavior_policy if isinstance(behavior_policy, BehaviorPolicy)
98
+ else BehaviorPolicy(**dict(behavior_policy))
99
+ )
100
+ facts = [
101
+ fact if isinstance(fact, PersonaFact) else PersonaFact(**dict(fact))
102
+ for fact in knowledge
103
+ ]
104
+ attack_model: Optional[AttackConditioning] = None
105
+ if attack is not None:
106
+ attack_model = (
107
+ attack if isinstance(attack, AttackConditioning)
108
+ else AttackConditioning(**dict(attack))
109
+ )
110
+ embedded: Dict[str, Any] = {"name": name}
111
+ if role:
112
+ embedded["role"] = role
113
+ persona = Persona(
114
+ persona=embedded,
115
+ situation=situation,
116
+ outcome=outcome,
117
+ identity=PersonaIdentity(
118
+ name=name,
119
+ role=role,
120
+ summary=summary,
121
+ language=language,
122
+ demographics=dict(demographics or {}),
123
+ style_notes=list(style_notes),
124
+ ),
125
+ temperament=temperament_model,
126
+ behavior_policy=policy_model,
127
+ knowledge=facts,
128
+ attack=attack_model,
129
+ provenance=PersonaProvenance(evidence_class=evidence_class),
130
+ )
131
+ if persona.behavior_policy is None and temperament_model is not None:
132
+ compiled = compile_behavior_policy(persona)
133
+ persona = _rebuild(persona, behavior_policy=compiled)
134
+ return persona
135
+
136
+
137
+ def _rebuild(persona: Persona, **updates: Any) -> Persona:
138
+ """Re-validate after an update so the content-address version re-stamps."""
139
+ payload = persona.model_dump(exclude={"version"}, exclude_none=True)
140
+ for key, value in updates.items():
141
+ if value is None:
142
+ payload.pop(key, None)
143
+ else:
144
+ payload[key] = value
145
+ return Persona(**payload)
146
+
147
+
148
+ def _taxonomy() -> Tuple[List[str], List[str]]:
149
+ # Lazy read of the EXISTING gate-enforced 10x6 taxonomy constants —
150
+ # facade-side membership check (the engine never imports trinity).
151
+ from fi.alk import trinity
152
+
153
+ return (
154
+ list(trinity.V1_REDTEAM_RESEARCH_ATTACK_TYPES),
155
+ list(trinity.V1_REDTEAM_RESEARCH_SURFACES),
156
+ )
157
+
158
+
159
+ def validate_persona(persona_or_row: Union[Persona, Mapping[str, Any]]) -> Dict[str, Any]:
160
+ """SPASM-style schema + realization-metric validation (UI §2.1 shape)."""
161
+ findings: List[Dict[str, Any]] = []
162
+ checks: Dict[str, str] = {}
163
+ try:
164
+ persona = upgrade_legacy_persona(persona_or_row)
165
+ checks["schema"] = "pass"
166
+ except Exception as exc: # noqa: BLE001 — structured refusal, never a traceback
167
+ return {
168
+ "status": "invalid",
169
+ "exit_code": 1,
170
+ "checks": {"schema": "fail"},
171
+ "findings": [{
172
+ "type": "persona_schema_invalid",
173
+ "level": "error",
174
+ "reason": str(exc),
175
+ }],
176
+ "representativeness_claim": "none",
177
+ }
178
+
179
+ # every behavior axis carries its canon-paired metric (doctrine #7);
180
+ # the pairing is structural: 1:1, ordered, and policy fields exist.
181
+ pairing_ok = (
182
+ len(PERSONA_BEHAVIOR_AXES) == len(PERSONA_BEHAVIOR_REALIZATION_METRICS) == 6
183
+ and all(
184
+ axis == pair[0] and pair[1] in BehaviorPolicy.model_fields
185
+ for axis, pair in zip(PERSONA_BEHAVIOR_AXES, BEHAVIOR_POLICY_AXIS_FIELDS)
186
+ )
187
+ )
188
+ checks["realization_metrics_per_axis"] = "pass" if pairing_ok else "fail"
189
+ if not pairing_ok:
190
+ findings.append({
191
+ "type": "persona_axis_unobservable",
192
+ "level": "error",
193
+ "reason": "a behavior axis lost its canon realization-metric pairing",
194
+ })
195
+
196
+ try:
197
+ first = compile_behavior_policy(persona)
198
+ second = compile_behavior_policy(persona)
199
+ checks["policy_compiles"] = (
200
+ "pass" if first.model_dump() == second.model_dump() else "fail"
201
+ )
202
+ except Exception as exc: # noqa: BLE001
203
+ checks["policy_compiles"] = "fail"
204
+ findings.append({
205
+ "type": "persona_policy_compile_failed",
206
+ "level": "error",
207
+ "reason": str(exc),
208
+ })
209
+
210
+ # the persona never owns the task (2601.15290 separation)
211
+ owns_task = any(key in persona.persona for key in ("goal", "goals", "task"))
212
+ checks["goals_binding_is_scenario_scoped"] = "fail" if owns_task else "pass"
213
+ if owns_task:
214
+ findings.append({
215
+ "type": "persona_owns_task",
216
+ "level": "error",
217
+ "reason": "goals belong to the Scenario, never the Persona",
218
+ })
219
+
220
+ demographics_present = bool(
221
+ persona.identity is not None and persona.identity.demographics
222
+ )
223
+ checks["demographics"] = "flagged" if demographics_present else "absent"
224
+ if demographics_present:
225
+ findings.append({
226
+ "type": "persona_demographics_flagged",
227
+ "level": "info",
228
+ "reason": (
229
+ "demographic fields flag this persona for set-level bias "
230
+ "lint; admit is blocked until the lint passes (P7-D4)"
231
+ ),
232
+ })
233
+
234
+ if persona.attack is not None:
235
+ attack_types, surfaces = _taxonomy()
236
+ bad_strategies = sorted(set(persona.attack.strategies) - set(attack_types))
237
+ bad_surfaces = sorted(set(persona.attack.surfaces) - set(surfaces))
238
+ checks["attack_taxonomy"] = "fail" if (bad_strategies or bad_surfaces) else "pass"
239
+ if bad_strategies or bad_surfaces:
240
+ findings.append({
241
+ "type": "persona_attack_taxonomy_violation",
242
+ "level": "error",
243
+ "reason": (
244
+ f"strategies {bad_strategies} / surfaces {bad_surfaces} "
245
+ "are outside the gate-enforced 10x6 taxonomy"
246
+ ),
247
+ })
248
+
249
+ failed = any(value == "fail" for value in checks.values())
250
+ return {
251
+ "status": "invalid" if failed else "valid",
252
+ "exit_code": 1 if failed else 0,
253
+ "checks": checks,
254
+ "findings": findings,
255
+ "representativeness_claim": "none",
256
+ }
257
+
258
+
259
+ def validate_scenario(scenario: Scenario) -> Dict[str, Any]:
260
+ """Typed-scenario validation: kind contract + adversarial taxonomy."""
261
+ findings: List[Dict[str, Any]] = []
262
+ checks: Dict[str, str] = {"schema": "pass"}
263
+ if scenario.kind == "adversarial":
264
+ attack_types, surfaces = _taxonomy()
265
+ ok = scenario.attack_type in attack_types and scenario.attack_surface in surfaces
266
+ checks["attack_taxonomy"] = "pass" if ok else "fail"
267
+ if not ok:
268
+ findings.append({
269
+ "type": "scenario_attack_taxonomy_violation",
270
+ "level": "error",
271
+ "reason": (
272
+ f"attack_type={scenario.attack_type!r} / "
273
+ f"attack_surface={scenario.attack_surface!r} outside the "
274
+ "gate-enforced 10x6 taxonomy"
275
+ ),
276
+ })
277
+ failed = any(value == "fail" for value in checks.values())
278
+ return {
279
+ "status": "invalid" if failed else "valid",
280
+ "exit_code": 1 if failed else 0,
281
+ "checks": checks,
282
+ "findings": findings,
283
+ }
284
+
285
+
286
+ # ---------------------------------------------------------------------------
287
+ # PICon interrogation battery — deterministic scripted simulator
288
+ # ---------------------------------------------------------------------------
289
+
290
+ def _scripted_answer(
291
+ persona: Persona,
292
+ fact: PersonaFact,
293
+ *,
294
+ seed: int,
295
+ repeat: int,
296
+ ) -> str:
297
+ """The scripted responder: answers fact probes from the knowledge store.
298
+
299
+ ``retest_jitter`` in the embedded dict simulates a persona whose conduct
300
+ is NOT seed-stable (per-repeat token) — the designed-to-fail retest
301
+ fixture; within one battery run the answer is stable, across repeats it
302
+ forks."""
303
+ if fact.disclosure == "withhold":
304
+ return "(withheld)"
305
+ answer = fact.value
306
+ if persona.persona.get("retest_jitter"):
307
+ token = random.Random(f"{seed + repeat}:{fact.key}").random()
308
+ answer = f"{answer} #{token:.6f}"
309
+ return answer
310
+
311
+
312
+ def _battery_once(
313
+ persona: Persona,
314
+ scenario: Optional[Scenario],
315
+ *,
316
+ seed: int,
317
+ repeat: int = 0,
318
+ ) -> Dict[str, Any]:
319
+ internal_probes: List[Dict[str, Any]] = []
320
+ contradictions = 0
321
+ seen_by_key: Dict[str, str] = {}
322
+ for fact in persona.knowledge:
323
+ first = _scripted_answer(persona, fact, seed=seed, repeat=repeat)
324
+ paraphrase = _scripted_answer(persona, fact, seed=seed, repeat=repeat)
325
+ consistent = first == paraphrase
326
+ if fact.key in seen_by_key and seen_by_key[fact.key] != fact.value:
327
+ consistent = False
328
+ contradictions += 1
329
+ seen_by_key.setdefault(fact.key, fact.value)
330
+ internal_probes.append({
331
+ "key": fact.key,
332
+ "ask": first,
333
+ "paraphrase": paraphrase,
334
+ "consistent": consistent,
335
+ })
336
+
337
+ reality_breaks = 0
338
+ external_probes: List[Dict[str, Any]] = []
339
+ observable = (
340
+ dict(scenario.constraints.observable_state)
341
+ if scenario is not None and scenario.constraints is not None else {}
342
+ )
343
+ allowed_keys = (
344
+ set(scenario.constraints.max_user_knowledge)
345
+ if scenario is not None and scenario.constraints is not None
346
+ and scenario.constraints.max_user_knowledge else None
347
+ )
348
+ for fact in persona.knowledge:
349
+ breaks = False
350
+ if fact.key in observable and str(observable[fact.key]) != fact.value:
351
+ breaks = True # contradicts declared world facts
352
+ if allowed_keys is not None and fact.key not in allowed_keys:
353
+ breaks = True # knows state the scenario declares unobservable
354
+ if breaks:
355
+ reality_breaks += 1
356
+ external_probes.append({"key": fact.key, "reality_break": breaks})
357
+
358
+ probe_count = max(1, len(persona.knowledge))
359
+ return {
360
+ "internal": {
361
+ "score": round(1.0 - contradictions / probe_count, 6),
362
+ "probes": len(internal_probes),
363
+ "contradictions": contradictions,
364
+ },
365
+ "external": {
366
+ "score": round(1.0 - reality_breaks / probe_count, 6),
367
+ "probes": len(external_probes),
368
+ "reality_breaks": reality_breaks,
369
+ },
370
+ "trace": [probe["ask"] for probe in internal_probes]
371
+ + [probe["paraphrase"] for probe in internal_probes],
372
+ }
373
+
374
+
375
+ def _probe_divergence_step(
376
+ first: Sequence[Any],
377
+ second: Sequence[Any],
378
+ ) -> Optional[int]:
379
+ """First index where two probe trajectories fork (re-implementation of
380
+ the live/_stats.divergence_step SEMANTIC — never imported)."""
381
+ for index, (a, b) in enumerate(zip(first, second)):
382
+ if a != b:
383
+ return index
384
+ if len(first) != len(second):
385
+ return min(len(first), len(second))
386
+ return None
387
+
388
+
389
+ def calibrate_persona(
390
+ persona: Union[Persona, Mapping[str, Any]],
391
+ *,
392
+ library: Optional[Any] = None,
393
+ target_class: str = "schema_sampled",
394
+ repeats: int = 2,
395
+ seed: int = 7,
396
+ scenario: Optional[Scenario] = None,
397
+ bias_lint_result: Optional[Mapping[str, Any]] = None,
398
+ ) -> Dict[str, Any]:
399
+ """sampled -> validated -> interrogated -> admitted (R§3.1 lifecycle).
400
+
401
+ Deterministic, key-free, network-free. -> agent-learning.persona-calibration.v1.
402
+ Monotone upgrades only; cloud_downloaded/trace_mined are provenance facts,
403
+ never calibration outcomes. Any probe red -> the persona stays at its
404
+ current class; the artifact records which probe failed. Uncalibrated
405
+ personas RUN fine — they just carry the lowest class and cannot back
406
+ release claims (PRD §4.2)."""
407
+ if target_class not in _CALIBRATION_TARGETS:
408
+ raise ValueError(
409
+ f"target_class must be one of {list(_CALIBRATION_TARGETS)} "
410
+ "(cloud_downloaded and trace_mined are provenance facts, never "
411
+ "calibration outcomes)"
412
+ )
413
+ persona = upgrade_legacy_persona(persona)
414
+ stages: List[str] = ["sampled"]
415
+
416
+ validation = validate_persona(persona)
417
+ artifact: Dict[str, Any] = {
418
+ "kind": PERSONA_CALIBRATION_KIND,
419
+ "persona": {
420
+ "name": (persona.identity.name if persona.identity else None)
421
+ or persona.persona.get("name"),
422
+ "version_before": persona.version,
423
+ },
424
+ "validation": validation,
425
+ "representativeness_claim": "none",
426
+ }
427
+ if validation["status"] != "valid":
428
+ artifact.update({
429
+ "stages": stages,
430
+ "status": "failed",
431
+ "verdict": "not_admit_eligible",
432
+ "failed_probe": None,
433
+ "bias_lint": dict(bias_lint_result) if bias_lint_result else None,
434
+ })
435
+ return artifact
436
+ stages.append("validated")
437
+
438
+ runs = [
439
+ _battery_once(persona, scenario, seed=seed, repeat=index)
440
+ for index in range(max(1, int(repeats)))
441
+ ]
442
+ base = runs[0]
443
+ divergence: Optional[int] = None
444
+ for repeat in runs[1:]:
445
+ step = _probe_divergence_step(base["trace"], repeat["trace"])
446
+ if step is not None:
447
+ divergence = step if divergence is None else min(divergence, step)
448
+ retest = {
449
+ "score": 1.0 if divergence is None else 0.0,
450
+ "replays": len(runs),
451
+ "divergence_step": divergence,
452
+ "method": "deterministic_replay",
453
+ }
454
+ probes = {
455
+ "internal": base["internal"],
456
+ "external": base["external"],
457
+ "retest": retest,
458
+ }
459
+ stages.append("interrogated")
460
+
461
+ failed_probe: Optional[str] = None
462
+ for leg in PERSONA_CALIBRATION_PROBES:
463
+ if probes[leg]["score"] < 1.0:
464
+ failed_probe = leg
465
+ break
466
+
467
+ current_class = (
468
+ persona.provenance.evidence_class if persona.provenance is not None else "legacy"
469
+ )
470
+ evidence = {"before": current_class, "after": current_class}
471
+ if failed_probe is None:
472
+ stages.append("admitted")
473
+ if current_class in _CALIBRATABLE_RANK and (
474
+ _CALIBRATABLE_RANK[current_class] < _CALIBRATABLE_RANK[target_class]
475
+ ):
476
+ evidence["after"] = target_class
477
+ # else: monotone — keep the (equal-or-higher / provenance-fact) class
478
+
479
+ artifact.update({
480
+ "stages": stages,
481
+ "status": "passed" if failed_probe is None else "failed",
482
+ "verdict": "admit_eligible" if failed_probe is None else "not_admit_eligible",
483
+ "failed_probe": failed_probe,
484
+ "probes": probes,
485
+ "seed": seed,
486
+ "evidence_class": evidence,
487
+ "bias_lint": dict(bias_lint_result) if bias_lint_result else None,
488
+ })
489
+
490
+ if failed_probe is None:
491
+ ref_payload = json.dumps(
492
+ {k: v for k, v in artifact.items() if k != "persona_payload"},
493
+ sort_keys=True, separators=(",", ":"), default=str,
494
+ )
495
+ calibration_ref = "sha256:" + hashlib.sha256(
496
+ ref_payload.encode("utf-8")
497
+ ).hexdigest()
498
+ provenance = (persona.provenance or PersonaProvenance()).model_copy(update={
499
+ "calibrated": True,
500
+ "calibration_ref": calibration_ref,
501
+ "evidence_class": evidence["after"],
502
+ })
503
+ persona = _rebuild(persona, provenance=provenance.model_dump(exclude_none=True))
504
+ artifact["calibration_ref"] = calibration_ref
505
+ artifact["persona"]["version_after"] = persona.version
506
+ artifact["persona_payload"] = persona.model_dump(exclude_none=True)
507
+
508
+ if library is not None:
509
+ hex_digest = persona.content_hash().split(":", 1)[1]
510
+ path = save_calibration(artifact, hex_digest, library=library)
511
+ artifact["artifact_path"] = str(path)
512
+ return artifact
513
+
514
+
515
+ __all__ = [
516
+ "PERSONA_CALIBRATION_PROBES",
517
+ "PERSONA_CALIBRATION_STAGES",
518
+ "build_persona",
519
+ "calibrate_persona",
520
+ "validate_persona",
521
+ "validate_scenario",
522
+ ]