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,45 @@
1
+ """Agent endpoint adapter tree (plan §4.1 + §13).
2
+
3
+ Each adapter conforms to ``AgentEndpoint``. Existing runtime paths keep
4
+ running through ``LiveKitEngine`` — the adapters here give hosted-runner
5
+ and matrix-runner callers a stable spec-level surface without waiting
6
+ for a full engine rewrite.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from .base import (
12
+ AgentEndpoint,
13
+ AgentEndpointManifest,
14
+ DiscoveryRequest,
15
+ DiscoverySnapshot,
16
+ EndpointHandle,
17
+ ReadinessResult,
18
+ ReconciliationResult,
19
+ )
20
+ from .callable import CallableAgentEndpoint
21
+ from .http import HttpAgentEndpoint
22
+ from .livekit import LiveKitAgentEndpoint
23
+ from .retell import RetellAgentEndpoint, RetellCall, RetellCallOriginator
24
+ from .vapi import VapiAgentEndpoint, VapiCall, VapiCallOriginator
25
+ from .websocket import WebSocketAgentEndpoint
26
+
27
+ __all__ = [
28
+ "AgentEndpoint",
29
+ "AgentEndpointManifest",
30
+ "CallableAgentEndpoint",
31
+ "DiscoveryRequest",
32
+ "DiscoverySnapshot",
33
+ "EndpointHandle",
34
+ "HttpAgentEndpoint",
35
+ "LiveKitAgentEndpoint",
36
+ "ReadinessResult",
37
+ "ReconciliationResult",
38
+ "RetellAgentEndpoint",
39
+ "RetellCall",
40
+ "RetellCallOriginator",
41
+ "VapiAgentEndpoint",
42
+ "VapiCall",
43
+ "VapiCallOriginator",
44
+ "WebSocketAgentEndpoint",
45
+ ]
@@ -0,0 +1,73 @@
1
+ """Turn-based HTTP target agent (used by the ``http`` actor source).
2
+
3
+ Lifted out of ``hosted/targets.py`` so ``endpoints`` can own it without a cycle
4
+ (``targets`` now dispatches through the endpoint registry).
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ from typing import Any, Optional
11
+
12
+ import httpx
13
+
14
+ from fi.simulate.agent.wrapper import AgentInput, AgentWrapper
15
+
16
+ _HTTP_TIMEOUT_SECONDS = 60.0
17
+
18
+
19
+ class HttpChatAgent(AgentWrapper):
20
+ """Turn-based target that relays each turn to an HTTP chat endpoint."""
21
+
22
+ def __init__(
23
+ self,
24
+ *,
25
+ url: str,
26
+ auth_header: str = "Authorization",
27
+ auth_env: Optional[str] = None,
28
+ extra_headers: Optional[dict[str, str]] = None,
29
+ ) -> None:
30
+ self._url = url
31
+ self._auth_header = auth_header
32
+ self._auth_env = auth_env
33
+ self._extra_headers = extra_headers or {}
34
+
35
+ def _headers(self) -> dict[str, str]:
36
+ headers = {"Content-Type": "application/json", **self._extra_headers}
37
+ if self._auth_env:
38
+ token = os.environ.get(self._auth_env)
39
+ if token:
40
+ headers[self._auth_header] = token
41
+ return headers
42
+
43
+ async def call(self, input: AgentInput) -> str:
44
+ payload = {
45
+ "thread_id": input.thread_id,
46
+ "messages": input.messages,
47
+ "new_message": input.new_message,
48
+ }
49
+ async with httpx.AsyncClient(timeout=_HTTP_TIMEOUT_SECONDS) as client:
50
+ response = await client.post(
51
+ self._url, json=payload, headers=self._headers()
52
+ )
53
+ response.raise_for_status()
54
+ return _extract_reply(response.json())
55
+
56
+
57
+ def _extract_reply(body: Any) -> str:
58
+ if isinstance(body, str):
59
+ return body
60
+ if isinstance(body, dict):
61
+ for key in ("content", "reply", "message", "response", "output", "text"):
62
+ value = body.get(key)
63
+ if isinstance(value, str) and value:
64
+ return value
65
+ choices = body.get("choices")
66
+ if isinstance(choices, list) and choices:
67
+ message = choices[0].get("message") if isinstance(choices[0], dict) else None
68
+ if isinstance(message, dict) and isinstance(message.get("content"), str):
69
+ return message["content"]
70
+ return ""
71
+
72
+
73
+ __all__ = ["HttpChatAgent"]
@@ -0,0 +1,243 @@
1
+ """Actor-source resolvers (canonical plan §4.1) — the "drop in any agent" surface.
2
+
3
+ A turn-based target agent can be declared as any of several *kinds*, resolved to
4
+ a runnable ``AgentWrapper`` / callable the environment drives. The kinds and their
5
+ config keys are the pydantic-schema twin of the manifest ``agent:`` block
6
+ (``target`` / ``factory`` / ``args`` / ``kwargs`` / ``method`` / ``input_mode`` /
7
+ ``system_prompt`` / …), so a manifest agent and a spec ActorSource are the same
8
+ declaration in two encodings — no third vocabulary.
9
+
10
+ Each resolver is attached to an ``EndpointProfile`` in ``profiles.py`` and reached
11
+ through the one ``endpoint_registry``. Heavy imports (``wrap_agent``, LLM clients,
12
+ ``httpx``) are deferred into the resolver bodies so the planner/registry stay light.
13
+
14
+ Security (hosted runs execute *customer-supplied* config on our infra):
15
+
16
+ * Kinds that import + call caller-named Python (``python_callable`` /
17
+ ``import_object`` / ``factory`` / ``framework``) are **rejected in hosted runs**
18
+ — the gate lives in ``resolve_chat_target`` and reads ``EndpointProfile.
19
+ runs_caller_code`` (deny-by-default), not a denylist. In-process execution of
20
+ customer code belongs in the sandboxed runtime (``RuntimeIsolation`` above
21
+ ``shared_runner_process``), not the runner process. A single explicit,
22
+ scarily-named escape (``ALK_UNSAFE_INPROCESS_CODE_ACTORS``) exists only for a
23
+ trusted operator-configured default target and tests — never set it in prod.
24
+ * Env reads by ``system_prompt`` / ``http`` are restricted in hosted runs to the
25
+ keys the job itself provisioned (``secret_refs``), so a job cannot name an
26
+ arbitrary env var (e.g. another tenant's secret) to exfiltrate.
27
+ * Local (developer) runs resolve with ``hosted=False`` and stay permissive — the
28
+ developer is running their own code on their own machine.
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ import importlib
34
+ import os
35
+ from typing import Any, Callable, Mapping, Optional
36
+
37
+ _UNSAFE_INPROCESS_ENV = "ALK_UNSAFE_INPROCESS_CODE_ACTORS"
38
+
39
+ _WRAP_KEYS = (
40
+ "method",
41
+ "input_mode",
42
+ "input_key",
43
+ "input_kwargs",
44
+ "output_key",
45
+ "system_prompt",
46
+ "metadata",
47
+ )
48
+
49
+
50
+ class ActorSourceError(ValueError):
51
+ """Raised when an actor-source config cannot be resolved to a runnable agent."""
52
+
53
+
54
+ def inprocess_code_allowed() -> bool:
55
+ """Whether in-process execution of caller code is explicitly permitted (the
56
+ trusted operator-default target / tests). Deny by default."""
57
+ return os.environ.get(_UNSAFE_INPROCESS_ENV, "").strip().lower() in (
58
+ "1",
59
+ "true",
60
+ "yes",
61
+ )
62
+
63
+
64
+ def _allowed_env_keys(secret_refs: Optional[Mapping[str, Any]]) -> set[str]:
65
+ keys: set[str] = set()
66
+ for name, ref in (secret_refs or {}).items():
67
+ ref_key = getattr(ref, "key", None)
68
+ keys.add(str(ref_key if ref_key is not None else name))
69
+ return keys
70
+
71
+
72
+ def _require_env_allowed(
73
+ env_key: str, secret_refs: Optional[Mapping[str, Any]], *, hosted: bool
74
+ ) -> None:
75
+ if not hosted:
76
+ return
77
+ if env_key not in _allowed_env_keys(secret_refs):
78
+ raise ActorSourceError(
79
+ f"env_not_provisioned: hosted actor may only read job-provisioned "
80
+ f"secrets; {env_key!r} is not in the job's secret_env"
81
+ )
82
+
83
+
84
+ def _load_attr(ref: Any) -> Any:
85
+ if not isinstance(ref, str) or ":" not in ref:
86
+ raise ActorSourceError("actor target requires 'module:attribute'")
87
+ module_name, _, attr = ref.partition(":")
88
+ if not module_name or not attr:
89
+ raise ActorSourceError(f"actor target malformed: {ref!r}")
90
+ module = importlib.import_module(module_name)
91
+ obj = getattr(module, attr, None)
92
+ if obj is None:
93
+ raise ActorSourceError(f"actor target not found: {ref!r}")
94
+ return obj
95
+
96
+
97
+ def _wrap_opts(config: Mapping[str, Any]) -> dict[str, Any]:
98
+ return {key: config[key] for key in _WRAP_KEYS if config.get(key) is not None}
99
+
100
+
101
+ def _instantiate_if_factory(loaded: Any, config: Mapping[str, Any]) -> Any:
102
+ if not (config.get("factory") or config.get("instantiate")):
103
+ return loaded
104
+ args = config.get("args") or config.get("factory_args") or []
105
+ kwargs = config.get("kwargs") or config.get("factory_kwargs") or {}
106
+ return loaded(*list(args), **dict(kwargs))
107
+
108
+
109
+ # --------------------------------------------------------------------------- #
110
+ # resolvers — (config, secret_refs, *, hosted) -> AgentWrapper | Callable
111
+ # The code-loading kinds are rejected in hosted runs by resolve_chat_target
112
+ # (profile.runs_caller_code) before they are ever called; ``hosted`` is accepted
113
+ # here for a uniform signature and defensive checks.
114
+ # --------------------------------------------------------------------------- #
115
+ def resolve_python_callable(
116
+ config: Mapping[str, Any],
117
+ secret_refs: Optional[Mapping[str, Any]] = None,
118
+ *,
119
+ hosted: bool = False,
120
+ ) -> Callable[..., Any]:
121
+ ref = config.get("target") or config.get("callable")
122
+ target = _load_attr(ref)
123
+ if not callable(target):
124
+ raise ActorSourceError(f"actor target is not callable: {ref!r}")
125
+ return target
126
+
127
+
128
+ def resolve_import_object(
129
+ config: Mapping[str, Any],
130
+ secret_refs: Optional[Mapping[str, Any]] = None,
131
+ *,
132
+ hosted: bool = False,
133
+ ) -> Any:
134
+ from fi.simulate.agent.generic import wrap_agent
135
+
136
+ obj = _load_attr(config.get("target"))
137
+ return wrap_agent(obj, **_wrap_opts(config))
138
+
139
+
140
+ def resolve_factory(
141
+ config: Mapping[str, Any],
142
+ secret_refs: Optional[Mapping[str, Any]] = None,
143
+ *,
144
+ hosted: bool = False,
145
+ ) -> Any:
146
+ from fi.simulate.agent.generic import wrap_agent
147
+
148
+ cls = _load_attr(config.get("target"))
149
+ instance = _instantiate_if_factory(cls, {**config, "factory": True})
150
+ return wrap_agent(instance, **_wrap_opts(config))
151
+
152
+
153
+ def resolve_framework(
154
+ config: Mapping[str, Any],
155
+ secret_refs: Optional[Mapping[str, Any]] = None,
156
+ *,
157
+ hosted: bool = False,
158
+ ) -> Any:
159
+ from fi.simulate.agent.generic import wrap_agent
160
+
161
+ loaded = _instantiate_if_factory(_load_attr(config.get("target")), config)
162
+ return wrap_agent(loaded, **_wrap_opts(config))
163
+
164
+
165
+ def resolve_system_prompt(
166
+ config: Mapping[str, Any],
167
+ secret_refs: Optional[Mapping[str, Any]] = None,
168
+ *,
169
+ hosted: bool = False,
170
+ ) -> Any:
171
+ from fi.simulate.agent.wrappers import OpenAIAgentWrapper
172
+
173
+ prompt = config.get("system_prompt") or config.get("prompt")
174
+ if not prompt:
175
+ raise ActorSourceError("system_prompt actor requires 'system_prompt'")
176
+ api_key_env = str(config.get("api_key_env", "OPENAI_API_KEY"))
177
+ _require_env_allowed(api_key_env, secret_refs, hosted=hosted)
178
+ api_key = os.environ.get(api_key_env)
179
+ if not api_key:
180
+ raise ActorSourceError(f"system_prompt actor needs {api_key_env} in the env")
181
+ try:
182
+ import openai
183
+ except ImportError as exc: # pragma: no cover - optional dep
184
+ raise ActorSourceError("system_prompt actor requires the openai package") from exc
185
+ client_kwargs: dict[str, Any] = {"api_key": api_key}
186
+ base_url = config.get("base_url")
187
+ if base_url:
188
+ client_kwargs["base_url"] = str(base_url)
189
+ client = openai.AsyncOpenAI(**client_kwargs)
190
+ return OpenAIAgentWrapper(
191
+ client, model=str(config.get("model", "gpt-4-turbo")), system_prompt=str(prompt)
192
+ )
193
+
194
+
195
+ def resolve_http(
196
+ config: Mapping[str, Any],
197
+ secret_refs: Optional[Mapping[str, Any]] = None,
198
+ *,
199
+ hosted: bool = False,
200
+ ) -> Any:
201
+ from fi.simulate.endpoints._http_actor import HttpChatAgent
202
+
203
+ url = config.get("url")
204
+ if not isinstance(url, str) or not url:
205
+ raise ActorSourceError("http actor requires config.url")
206
+ # auth_env is derived from the job's own secret_refs, so it is provisioned by
207
+ # construction — no arbitrary env read.
208
+ return HttpChatAgent(
209
+ url=url,
210
+ auth_header=str(config.get("auth_header") or "Authorization"),
211
+ auth_env=_auth_env_from_refs(secret_refs),
212
+ extra_headers=_string_map(config.get("headers")),
213
+ )
214
+
215
+
216
+ def _auth_env_from_refs(secret_refs: Optional[Mapping[str, Any]]) -> Optional[str]:
217
+ if not secret_refs:
218
+ return None
219
+ for purpose in ("api_key", "authorization", "token"):
220
+ for name, ref in secret_refs.items():
221
+ ref_purpose = getattr(ref, "purpose", None)
222
+ ref_key = getattr(ref, "key", None)
223
+ if ref_purpose == purpose or name == purpose:
224
+ return ref_key
225
+ return None
226
+
227
+
228
+ def _string_map(value: Any) -> dict[str, str]:
229
+ if not isinstance(value, dict):
230
+ return {}
231
+ return {str(k): str(v) for k, v in value.items()}
232
+
233
+
234
+ __all__ = [
235
+ "ActorSourceError",
236
+ "inprocess_code_allowed",
237
+ "resolve_factory",
238
+ "resolve_framework",
239
+ "resolve_http",
240
+ "resolve_import_object",
241
+ "resolve_python_callable",
242
+ "resolve_system_prompt",
243
+ ]
@@ -0,0 +1,107 @@
1
+ """AgentEndpoint Protocol + shared data types (plan §4.1)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import AsyncIterator
6
+ from datetime import datetime
7
+ from typing import Protocol
8
+
9
+ from pydantic import BaseModel, Field, JsonValue
10
+
11
+ from fi.simulate.realtime.events import RealtimeEvent
12
+ from fi.simulate.realtime.media import AudioFrame
13
+ from fi.simulate.runtime.capabilities import EndpointCapabilities
14
+
15
+
16
+ class AgentEndpointManifest(BaseModel):
17
+ """Static declaration of an endpoint adapter's identity + shape.
18
+
19
+ The planner records the manifest on the plan so hosted runs can
20
+ reconstruct which adapter (name + version) executed a case.
21
+ """
22
+
23
+ name: str
24
+ version: str = "1"
25
+ provider: str
26
+ world_kinds: list[str] = Field(default_factory=lambda: ["voice"])
27
+ capabilities: EndpointCapabilities = Field(default_factory=EndpointCapabilities)
28
+ metadata: dict[str, JsonValue] = Field(default_factory=dict)
29
+
30
+
31
+ class DiscoveryRequest(BaseModel):
32
+ run_id: str
33
+ test_case_id: str
34
+ required_capabilities: list[str] = Field(default_factory=list)
35
+ metadata: dict[str, JsonValue] = Field(default_factory=dict)
36
+
37
+
38
+ class DiscoverySnapshot(BaseModel):
39
+ capabilities: EndpointCapabilities
40
+ supported: bool = True
41
+ reasons: list[str] = Field(default_factory=list)
42
+ metadata: dict[str, JsonValue] = Field(default_factory=dict)
43
+
44
+
45
+ class EndpointHandle(BaseModel):
46
+ """Opaque adapter handle returned by ``prepare`` and reused for the case."""
47
+
48
+ handle_id: str
49
+ endpoint_name: str
50
+ created_at: datetime
51
+ metadata: dict[str, JsonValue] = Field(default_factory=dict)
52
+
53
+
54
+ class ReadinessResult(BaseModel):
55
+ ready: bool
56
+ latency_ms: float | None = None
57
+ reason: str | None = None
58
+ metadata: dict[str, JsonValue] = Field(default_factory=dict)
59
+
60
+
61
+ class ReconciliationResult(BaseModel):
62
+ reconciled: bool
63
+ orphan_ids: list[str] = Field(default_factory=list)
64
+ metadata: dict[str, JsonValue] = Field(default_factory=dict)
65
+
66
+
67
+ class AgentEndpoint(Protocol):
68
+ """Session-oriented target-agent adapter contract (plan §4.1).
69
+
70
+ Turn-based agents can still use the legacy ``AgentWrapper.call``
71
+ surface; this Protocol is the target for realtime/voice/session
72
+ agents where the engine needs prepare/wait_ready/stop lifecycle.
73
+ """
74
+
75
+ manifest: AgentEndpointManifest
76
+ capabilities: EndpointCapabilities
77
+
78
+ async def discover(self, request: DiscoveryRequest) -> DiscoverySnapshot: ...
79
+
80
+ async def prepare(self, plan) -> EndpointHandle: ... # SimulationPlan
81
+
82
+ async def wait_ready(self, handle: EndpointHandle) -> ReadinessResult: ...
83
+
84
+ async def send(
85
+ self, handle: EndpointHandle, event: RealtimeEvent | AudioFrame
86
+ ) -> None: ...
87
+
88
+ async def receive(
89
+ self, handle: EndpointHandle
90
+ ) -> AsyncIterator[RealtimeEvent | AudioFrame]: ...
91
+
92
+ async def stop(self, handle: EndpointHandle) -> None: ...
93
+
94
+ async def cleanup(self, handle: EndpointHandle) -> None: ...
95
+
96
+ async def reconcile(self, handle: EndpointHandle) -> ReconciliationResult: ...
97
+
98
+
99
+ __all__ = [
100
+ "AgentEndpoint",
101
+ "AgentEndpointManifest",
102
+ "DiscoveryRequest",
103
+ "DiscoverySnapshot",
104
+ "EndpointHandle",
105
+ "ReadinessResult",
106
+ "ReconciliationResult",
107
+ ]
@@ -0,0 +1,10 @@
1
+ """Back-compat shim. Builtin endpoint registrations moved to
2
+ ``fi.simulate.endpoints.profiles`` (slice 3) — importing this module still
3
+ triggers registration via that module's import side effect.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from fi.simulate.endpoints import profiles as _profiles # noqa: F401
9
+
10
+ __all__: list[str] = []
@@ -0,0 +1,95 @@
1
+ """Callable target-agent adapter for turn-based chat runs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import uuid
6
+ from collections.abc import AsyncIterator, Callable
7
+ from datetime import datetime, timezone
8
+ from typing import Any
9
+
10
+ from fi.simulate.realtime.events import RealtimeEvent
11
+ from fi.simulate.realtime.media import AudioFrame
12
+ from fi.simulate.runtime.capabilities import EndpointCapabilities
13
+
14
+ from .base import (
15
+ AgentEndpointManifest,
16
+ DiscoveryRequest,
17
+ DiscoverySnapshot,
18
+ EndpointHandle,
19
+ ReadinessResult,
20
+ ReconciliationResult,
21
+ )
22
+
23
+
24
+ class CallableAgentEndpoint:
25
+ """Wraps a plain callable/coroutine target agent."""
26
+
27
+ def __init__(
28
+ self,
29
+ agent_callable: Callable[..., Any],
30
+ *,
31
+ name: str = "callable-agent",
32
+ ) -> None:
33
+ self._callable = agent_callable
34
+ self.manifest = AgentEndpointManifest(
35
+ name=name,
36
+ provider="callable",
37
+ world_kinds=["chat"],
38
+ capabilities=EndpointCapabilities(text=True, streaming=False),
39
+ )
40
+ self.capabilities = self.manifest.capabilities
41
+
42
+ async def discover(self, request: DiscoveryRequest) -> DiscoverySnapshot:
43
+ missing = [
44
+ cap
45
+ for cap in request.required_capabilities
46
+ if cap not in self.capabilities.supported()
47
+ ]
48
+ return DiscoverySnapshot(
49
+ capabilities=self.capabilities,
50
+ supported=not missing,
51
+ reasons=[f"unsupported:{cap}" for cap in missing],
52
+ )
53
+
54
+ async def prepare(self, plan) -> EndpointHandle: # noqa: ANN001
55
+ return EndpointHandle(
56
+ handle_id=f"cb-{uuid.uuid4().hex[:12]}",
57
+ endpoint_name=self.manifest.name,
58
+ created_at=datetime.now(timezone.utc),
59
+ metadata={"plan_id": getattr(plan, "plan_id", None)},
60
+ )
61
+
62
+ async def wait_ready(self, handle: EndpointHandle) -> ReadinessResult:
63
+ del handle
64
+ return ReadinessResult(ready=True)
65
+
66
+ async def send(
67
+ self, handle: EndpointHandle, event: RealtimeEvent | AudioFrame
68
+ ) -> None:
69
+ raise NotImplementedError("CallableAgentEndpoint is turn-based; use invoke()")
70
+
71
+ async def receive(
72
+ self, handle: EndpointHandle
73
+ ) -> AsyncIterator[RealtimeEvent | AudioFrame]:
74
+ raise NotImplementedError("CallableAgentEndpoint is turn-based; use invoke()")
75
+ # unreachable, kept for Protocol conformance
76
+ yield # type: ignore[unreachable]
77
+
78
+ async def stop(self, handle: EndpointHandle) -> None:
79
+ del handle
80
+
81
+ async def cleanup(self, handle: EndpointHandle) -> None:
82
+ del handle
83
+
84
+ async def reconcile(self, handle: EndpointHandle) -> ReconciliationResult:
85
+ del handle
86
+ return ReconciliationResult(reconciled=True)
87
+
88
+ async def invoke(self, *args: Any, **kwargs: Any) -> Any:
89
+ result = self._callable(*args, **kwargs)
90
+ if hasattr(result, "__await__"):
91
+ return await result
92
+ return result
93
+
94
+
95
+ __all__ = ["CallableAgentEndpoint"]
@@ -0,0 +1,76 @@
1
+ """HTTP target-agent adapter — capability declaration + Stage-6 seam."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import uuid
6
+ from collections.abc import AsyncIterator
7
+ from datetime import datetime, timezone
8
+
9
+ from pydantic import AnyHttpUrl
10
+
11
+ from fi.simulate.realtime.events import RealtimeEvent
12
+ from fi.simulate.realtime.media import AudioFrame
13
+ from fi.simulate.runtime.capabilities import EndpointCapabilities
14
+
15
+ from .base import (
16
+ AgentEndpointManifest,
17
+ DiscoveryRequest,
18
+ DiscoverySnapshot,
19
+ EndpointHandle,
20
+ ReadinessResult,
21
+ ReconciliationResult,
22
+ )
23
+
24
+
25
+ class HttpAgentEndpoint:
26
+ """Points at an HTTP agent surface. Wire-up lands in Stage 6."""
27
+
28
+ def __init__(self, *, name: str, url: AnyHttpUrl | str) -> None:
29
+ self.manifest = AgentEndpointManifest(
30
+ name=name,
31
+ provider="http",
32
+ world_kinds=["chat"],
33
+ capabilities=EndpointCapabilities(text=True, tool_events=True),
34
+ metadata={"url": str(url)},
35
+ )
36
+ self.capabilities = self.manifest.capabilities
37
+
38
+ async def discover(self, request: DiscoveryRequest) -> DiscoverySnapshot:
39
+ del request
40
+ return DiscoverySnapshot(capabilities=self.capabilities)
41
+
42
+ async def prepare(self, plan) -> EndpointHandle: # noqa: ANN001
43
+ return EndpointHandle(
44
+ handle_id=f"http-{uuid.uuid4().hex[:12]}",
45
+ endpoint_name=self.manifest.name,
46
+ created_at=datetime.now(timezone.utc),
47
+ metadata={"plan_id": getattr(plan, "plan_id", None)},
48
+ )
49
+
50
+ async def wait_ready(self, handle: EndpointHandle) -> ReadinessResult:
51
+ del handle
52
+ raise NotImplementedError("HttpAgentEndpoint readiness lands in Stage 6")
53
+
54
+ async def send(
55
+ self, handle: EndpointHandle, event: RealtimeEvent | AudioFrame
56
+ ) -> None:
57
+ raise NotImplementedError("HttpAgentEndpoint send lands in Stage 6")
58
+
59
+ async def receive(
60
+ self, handle: EndpointHandle
61
+ ) -> AsyncIterator[RealtimeEvent | AudioFrame]:
62
+ raise NotImplementedError("HttpAgentEndpoint receive lands in Stage 6")
63
+ yield # type: ignore[unreachable]
64
+
65
+ async def stop(self, handle: EndpointHandle) -> None:
66
+ del handle
67
+
68
+ async def cleanup(self, handle: EndpointHandle) -> None:
69
+ del handle
70
+
71
+ async def reconcile(self, handle: EndpointHandle) -> ReconciliationResult:
72
+ del handle
73
+ return ReconciliationResult(reconciled=True)
74
+
75
+
76
+ __all__ = ["HttpAgentEndpoint"]