empathy-framework 5.2.1__py3-none-any.whl → 5.4.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 (480) hide show
  1. empathy_framework-5.4.0.dist-info/METADATA +47 -0
  2. empathy_framework-5.4.0.dist-info/RECORD +8 -0
  3. {empathy_framework-5.2.1.dist-info → empathy_framework-5.4.0.dist-info}/top_level.txt +0 -1
  4. empathy_healthcare_plugin/__init__.py +12 -11
  5. empathy_llm_toolkit/__init__.py +12 -26
  6. empathy_os/__init__.py +12 -356
  7. empathy_software_plugin/__init__.py +12 -11
  8. empathy_framework-5.2.1.dist-info/METADATA +0 -1002
  9. empathy_framework-5.2.1.dist-info/RECORD +0 -478
  10. empathy_framework-5.2.1.dist-info/entry_points.txt +0 -26
  11. empathy_framework-5.2.1.dist-info/licenses/LICENSE +0 -201
  12. empathy_framework-5.2.1.dist-info/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +0 -101
  13. empathy_healthcare_plugin/monitors/__init__.py +0 -9
  14. empathy_healthcare_plugin/monitors/clinical_protocol_monitor.py +0 -315
  15. empathy_healthcare_plugin/monitors/monitoring/__init__.py +0 -44
  16. empathy_healthcare_plugin/monitors/monitoring/protocol_checker.py +0 -300
  17. empathy_healthcare_plugin/monitors/monitoring/protocol_loader.py +0 -214
  18. empathy_healthcare_plugin/monitors/monitoring/sensor_parsers.py +0 -306
  19. empathy_healthcare_plugin/monitors/monitoring/trajectory_analyzer.py +0 -389
  20. empathy_healthcare_plugin/protocols/cardiac.json +0 -93
  21. empathy_healthcare_plugin/protocols/post_operative.json +0 -92
  22. empathy_healthcare_plugin/protocols/respiratory.json +0 -92
  23. empathy_healthcare_plugin/protocols/sepsis.json +0 -141
  24. empathy_llm_toolkit/README.md +0 -553
  25. empathy_llm_toolkit/agent_factory/__init__.py +0 -53
  26. empathy_llm_toolkit/agent_factory/adapters/__init__.py +0 -85
  27. empathy_llm_toolkit/agent_factory/adapters/autogen_adapter.py +0 -312
  28. empathy_llm_toolkit/agent_factory/adapters/crewai_adapter.py +0 -483
  29. empathy_llm_toolkit/agent_factory/adapters/haystack_adapter.py +0 -298
  30. empathy_llm_toolkit/agent_factory/adapters/langchain_adapter.py +0 -362
  31. empathy_llm_toolkit/agent_factory/adapters/langgraph_adapter.py +0 -333
  32. empathy_llm_toolkit/agent_factory/adapters/native.py +0 -228
  33. empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +0 -423
  34. empathy_llm_toolkit/agent_factory/base.py +0 -305
  35. empathy_llm_toolkit/agent_factory/crews/__init__.py +0 -67
  36. empathy_llm_toolkit/agent_factory/crews/code_review.py +0 -1113
  37. empathy_llm_toolkit/agent_factory/crews/health_check.py +0 -1262
  38. empathy_llm_toolkit/agent_factory/crews/refactoring.py +0 -1128
  39. empathy_llm_toolkit/agent_factory/crews/security_audit.py +0 -1018
  40. empathy_llm_toolkit/agent_factory/decorators.py +0 -287
  41. empathy_llm_toolkit/agent_factory/factory.py +0 -558
  42. empathy_llm_toolkit/agent_factory/framework.py +0 -193
  43. empathy_llm_toolkit/agent_factory/memory_integration.py +0 -328
  44. empathy_llm_toolkit/agent_factory/resilient.py +0 -320
  45. empathy_llm_toolkit/agents_md/__init__.py +0 -22
  46. empathy_llm_toolkit/agents_md/loader.py +0 -218
  47. empathy_llm_toolkit/agents_md/parser.py +0 -271
  48. empathy_llm_toolkit/agents_md/registry.py +0 -307
  49. empathy_llm_toolkit/claude_memory.py +0 -466
  50. empathy_llm_toolkit/cli/__init__.py +0 -8
  51. empathy_llm_toolkit/cli/sync_claude.py +0 -487
  52. empathy_llm_toolkit/code_health.py +0 -1313
  53. empathy_llm_toolkit/commands/__init__.py +0 -51
  54. empathy_llm_toolkit/commands/context.py +0 -375
  55. empathy_llm_toolkit/commands/loader.py +0 -301
  56. empathy_llm_toolkit/commands/models.py +0 -231
  57. empathy_llm_toolkit/commands/parser.py +0 -371
  58. empathy_llm_toolkit/commands/registry.py +0 -429
  59. empathy_llm_toolkit/config/__init__.py +0 -29
  60. empathy_llm_toolkit/config/unified.py +0 -291
  61. empathy_llm_toolkit/context/__init__.py +0 -22
  62. empathy_llm_toolkit/context/compaction.py +0 -455
  63. empathy_llm_toolkit/context/manager.py +0 -434
  64. empathy_llm_toolkit/contextual_patterns.py +0 -361
  65. empathy_llm_toolkit/core.py +0 -907
  66. empathy_llm_toolkit/git_pattern_extractor.py +0 -435
  67. empathy_llm_toolkit/hooks/__init__.py +0 -24
  68. empathy_llm_toolkit/hooks/config.py +0 -306
  69. empathy_llm_toolkit/hooks/executor.py +0 -289
  70. empathy_llm_toolkit/hooks/registry.py +0 -302
  71. empathy_llm_toolkit/hooks/scripts/__init__.py +0 -39
  72. empathy_llm_toolkit/hooks/scripts/evaluate_session.py +0 -201
  73. empathy_llm_toolkit/hooks/scripts/first_time_init.py +0 -285
  74. empathy_llm_toolkit/hooks/scripts/pre_compact.py +0 -207
  75. empathy_llm_toolkit/hooks/scripts/session_end.py +0 -183
  76. empathy_llm_toolkit/hooks/scripts/session_start.py +0 -163
  77. empathy_llm_toolkit/hooks/scripts/suggest_compact.py +0 -225
  78. empathy_llm_toolkit/learning/__init__.py +0 -30
  79. empathy_llm_toolkit/learning/evaluator.py +0 -438
  80. empathy_llm_toolkit/learning/extractor.py +0 -514
  81. empathy_llm_toolkit/learning/storage.py +0 -560
  82. empathy_llm_toolkit/levels.py +0 -227
  83. empathy_llm_toolkit/pattern_confidence.py +0 -414
  84. empathy_llm_toolkit/pattern_resolver.py +0 -272
  85. empathy_llm_toolkit/pattern_summary.py +0 -350
  86. empathy_llm_toolkit/providers.py +0 -967
  87. empathy_llm_toolkit/routing/__init__.py +0 -32
  88. empathy_llm_toolkit/routing/model_router.py +0 -362
  89. empathy_llm_toolkit/security/IMPLEMENTATION_SUMMARY.md +0 -413
  90. empathy_llm_toolkit/security/PHASE2_COMPLETE.md +0 -384
  91. empathy_llm_toolkit/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +0 -271
  92. empathy_llm_toolkit/security/QUICK_REFERENCE.md +0 -316
  93. empathy_llm_toolkit/security/README.md +0 -262
  94. empathy_llm_toolkit/security/__init__.py +0 -62
  95. empathy_llm_toolkit/security/audit_logger.py +0 -929
  96. empathy_llm_toolkit/security/audit_logger_example.py +0 -152
  97. empathy_llm_toolkit/security/pii_scrubber.py +0 -640
  98. empathy_llm_toolkit/security/secrets_detector.py +0 -678
  99. empathy_llm_toolkit/security/secrets_detector_example.py +0 -304
  100. empathy_llm_toolkit/security/secure_memdocs.py +0 -1192
  101. empathy_llm_toolkit/security/secure_memdocs_example.py +0 -278
  102. empathy_llm_toolkit/session_status.py +0 -745
  103. empathy_llm_toolkit/state.py +0 -246
  104. empathy_llm_toolkit/utils/__init__.py +0 -5
  105. empathy_llm_toolkit/utils/tokens.py +0 -349
  106. empathy_os/adaptive/__init__.py +0 -13
  107. empathy_os/adaptive/task_complexity.py +0 -127
  108. empathy_os/agent_monitoring.py +0 -414
  109. empathy_os/cache/__init__.py +0 -117
  110. empathy_os/cache/base.py +0 -166
  111. empathy_os/cache/dependency_manager.py +0 -256
  112. empathy_os/cache/hash_only.py +0 -251
  113. empathy_os/cache/hybrid.py +0 -453
  114. empathy_os/cache/storage.py +0 -285
  115. empathy_os/cache_monitor.py +0 -356
  116. empathy_os/cache_stats.py +0 -298
  117. empathy_os/cli/__init__.py +0 -152
  118. empathy_os/cli/__main__.py +0 -12
  119. empathy_os/cli/commands/__init__.py +0 -1
  120. empathy_os/cli/commands/batch.py +0 -256
  121. empathy_os/cli/commands/cache.py +0 -248
  122. empathy_os/cli/commands/help.py +0 -331
  123. empathy_os/cli/commands/info.py +0 -140
  124. empathy_os/cli/commands/inspect.py +0 -436
  125. empathy_os/cli/commands/inspection.py +0 -57
  126. empathy_os/cli/commands/memory.py +0 -48
  127. empathy_os/cli/commands/metrics.py +0 -92
  128. empathy_os/cli/commands/orchestrate.py +0 -184
  129. empathy_os/cli/commands/patterns.py +0 -207
  130. empathy_os/cli/commands/profiling.py +0 -198
  131. empathy_os/cli/commands/provider.py +0 -98
  132. empathy_os/cli/commands/routing.py +0 -285
  133. empathy_os/cli/commands/setup.py +0 -96
  134. empathy_os/cli/commands/status.py +0 -235
  135. empathy_os/cli/commands/sync.py +0 -166
  136. empathy_os/cli/commands/tier.py +0 -121
  137. empathy_os/cli/commands/utilities.py +0 -114
  138. empathy_os/cli/commands/workflow.py +0 -575
  139. empathy_os/cli/core.py +0 -32
  140. empathy_os/cli/parsers/__init__.py +0 -68
  141. empathy_os/cli/parsers/batch.py +0 -118
  142. empathy_os/cli/parsers/cache 2.py +0 -65
  143. empathy_os/cli/parsers/cache.py +0 -65
  144. empathy_os/cli/parsers/help.py +0 -41
  145. empathy_os/cli/parsers/info.py +0 -26
  146. empathy_os/cli/parsers/inspect.py +0 -66
  147. empathy_os/cli/parsers/metrics.py +0 -42
  148. empathy_os/cli/parsers/orchestrate.py +0 -61
  149. empathy_os/cli/parsers/patterns.py +0 -54
  150. empathy_os/cli/parsers/provider.py +0 -40
  151. empathy_os/cli/parsers/routing.py +0 -110
  152. empathy_os/cli/parsers/setup.py +0 -42
  153. empathy_os/cli/parsers/status.py +0 -47
  154. empathy_os/cli/parsers/sync.py +0 -31
  155. empathy_os/cli/parsers/tier.py +0 -33
  156. empathy_os/cli/parsers/workflow.py +0 -77
  157. empathy_os/cli/utils/__init__.py +0 -1
  158. empathy_os/cli/utils/data.py +0 -242
  159. empathy_os/cli/utils/helpers.py +0 -68
  160. empathy_os/cli_legacy.py +0 -3957
  161. empathy_os/cli_minimal.py +0 -1159
  162. empathy_os/cli_router 2.py +0 -416
  163. empathy_os/cli_router.py +0 -437
  164. empathy_os/cli_unified.py +0 -814
  165. empathy_os/config/__init__.py +0 -66
  166. empathy_os/config/xml_config.py +0 -286
  167. empathy_os/config.py +0 -532
  168. empathy_os/coordination.py +0 -870
  169. empathy_os/core.py +0 -1511
  170. empathy_os/core_modules/__init__.py +0 -15
  171. empathy_os/cost_tracker.py +0 -626
  172. empathy_os/dashboard/__init__.py +0 -41
  173. empathy_os/dashboard/app 2.py +0 -512
  174. empathy_os/dashboard/app.py +0 -512
  175. empathy_os/dashboard/simple_server 2.py +0 -403
  176. empathy_os/dashboard/simple_server.py +0 -403
  177. empathy_os/dashboard/standalone_server 2.py +0 -536
  178. empathy_os/dashboard/standalone_server.py +0 -547
  179. empathy_os/discovery.py +0 -306
  180. empathy_os/emergence.py +0 -306
  181. empathy_os/exceptions.py +0 -123
  182. empathy_os/feedback_loops.py +0 -373
  183. empathy_os/hot_reload/README.md +0 -473
  184. empathy_os/hot_reload/__init__.py +0 -62
  185. empathy_os/hot_reload/config.py +0 -83
  186. empathy_os/hot_reload/integration.py +0 -229
  187. empathy_os/hot_reload/reloader.py +0 -298
  188. empathy_os/hot_reload/watcher.py +0 -183
  189. empathy_os/hot_reload/websocket.py +0 -177
  190. empathy_os/levels.py +0 -577
  191. empathy_os/leverage_points.py +0 -441
  192. empathy_os/logging_config.py +0 -261
  193. empathy_os/mcp/__init__.py +0 -10
  194. empathy_os/mcp/server.py +0 -506
  195. empathy_os/memory/__init__.py +0 -237
  196. empathy_os/memory/claude_memory.py +0 -469
  197. empathy_os/memory/config.py +0 -224
  198. empathy_os/memory/control_panel.py +0 -1290
  199. empathy_os/memory/control_panel_support.py +0 -145
  200. empathy_os/memory/cross_session.py +0 -845
  201. empathy_os/memory/edges.py +0 -179
  202. empathy_os/memory/encryption.py +0 -159
  203. empathy_os/memory/file_session.py +0 -770
  204. empathy_os/memory/graph.py +0 -570
  205. empathy_os/memory/long_term.py +0 -913
  206. empathy_os/memory/long_term_types.py +0 -99
  207. empathy_os/memory/mixins/__init__.py +0 -25
  208. empathy_os/memory/mixins/backend_init_mixin.py +0 -244
  209. empathy_os/memory/mixins/capabilities_mixin.py +0 -199
  210. empathy_os/memory/mixins/handoff_mixin.py +0 -208
  211. empathy_os/memory/mixins/lifecycle_mixin.py +0 -49
  212. empathy_os/memory/mixins/long_term_mixin.py +0 -352
  213. empathy_os/memory/mixins/promotion_mixin.py +0 -109
  214. empathy_os/memory/mixins/short_term_mixin.py +0 -182
  215. empathy_os/memory/nodes.py +0 -179
  216. empathy_os/memory/redis_bootstrap.py +0 -540
  217. empathy_os/memory/security/__init__.py +0 -31
  218. empathy_os/memory/security/audit_logger.py +0 -932
  219. empathy_os/memory/security/pii_scrubber.py +0 -640
  220. empathy_os/memory/security/secrets_detector.py +0 -678
  221. empathy_os/memory/short_term.py +0 -2150
  222. empathy_os/memory/simple_storage.py +0 -302
  223. empathy_os/memory/storage/__init__.py +0 -15
  224. empathy_os/memory/storage_backend.py +0 -167
  225. empathy_os/memory/summary_index.py +0 -583
  226. empathy_os/memory/types.py +0 -441
  227. empathy_os/memory/unified.py +0 -182
  228. empathy_os/meta_workflows/__init__.py +0 -74
  229. empathy_os/meta_workflows/agent_creator.py +0 -248
  230. empathy_os/meta_workflows/builtin_templates.py +0 -567
  231. empathy_os/meta_workflows/cli_commands/__init__.py +0 -56
  232. empathy_os/meta_workflows/cli_commands/agent_commands.py +0 -321
  233. empathy_os/meta_workflows/cli_commands/analytics_commands.py +0 -442
  234. empathy_os/meta_workflows/cli_commands/config_commands.py +0 -232
  235. empathy_os/meta_workflows/cli_commands/memory_commands.py +0 -182
  236. empathy_os/meta_workflows/cli_commands/template_commands.py +0 -354
  237. empathy_os/meta_workflows/cli_commands/workflow_commands.py +0 -382
  238. empathy_os/meta_workflows/cli_meta_workflows.py +0 -59
  239. empathy_os/meta_workflows/form_engine.py +0 -292
  240. empathy_os/meta_workflows/intent_detector.py +0 -409
  241. empathy_os/meta_workflows/models.py +0 -569
  242. empathy_os/meta_workflows/pattern_learner.py +0 -738
  243. empathy_os/meta_workflows/plan_generator.py +0 -384
  244. empathy_os/meta_workflows/session_context.py +0 -397
  245. empathy_os/meta_workflows/template_registry.py +0 -229
  246. empathy_os/meta_workflows/workflow.py +0 -984
  247. empathy_os/metrics/__init__.py +0 -12
  248. empathy_os/metrics/collector.py +0 -31
  249. empathy_os/metrics/prompt_metrics.py +0 -194
  250. empathy_os/models/__init__.py +0 -172
  251. empathy_os/models/__main__.py +0 -13
  252. empathy_os/models/adaptive_routing 2.py +0 -437
  253. empathy_os/models/adaptive_routing.py +0 -437
  254. empathy_os/models/auth_cli.py +0 -444
  255. empathy_os/models/auth_strategy.py +0 -450
  256. empathy_os/models/cli.py +0 -655
  257. empathy_os/models/empathy_executor.py +0 -354
  258. empathy_os/models/executor.py +0 -257
  259. empathy_os/models/fallback.py +0 -762
  260. empathy_os/models/provider_config.py +0 -282
  261. empathy_os/models/registry.py +0 -472
  262. empathy_os/models/tasks.py +0 -359
  263. empathy_os/models/telemetry/__init__.py +0 -71
  264. empathy_os/models/telemetry/analytics.py +0 -594
  265. empathy_os/models/telemetry/backend.py +0 -196
  266. empathy_os/models/telemetry/data_models.py +0 -431
  267. empathy_os/models/telemetry/storage.py +0 -489
  268. empathy_os/models/token_estimator.py +0 -420
  269. empathy_os/models/validation.py +0 -280
  270. empathy_os/monitoring/__init__.py +0 -52
  271. empathy_os/monitoring/alerts.py +0 -946
  272. empathy_os/monitoring/alerts_cli.py +0 -448
  273. empathy_os/monitoring/multi_backend.py +0 -271
  274. empathy_os/monitoring/otel_backend.py +0 -362
  275. empathy_os/optimization/__init__.py +0 -19
  276. empathy_os/optimization/context_optimizer.py +0 -272
  277. empathy_os/orchestration/__init__.py +0 -67
  278. empathy_os/orchestration/agent_templates.py +0 -707
  279. empathy_os/orchestration/config_store.py +0 -499
  280. empathy_os/orchestration/execution_strategies.py +0 -2111
  281. empathy_os/orchestration/meta_orchestrator.py +0 -1168
  282. empathy_os/orchestration/pattern_learner.py +0 -696
  283. empathy_os/orchestration/real_tools.py +0 -931
  284. empathy_os/pattern_cache.py +0 -187
  285. empathy_os/pattern_library.py +0 -542
  286. empathy_os/patterns/debugging/all_patterns.json +0 -81
  287. empathy_os/patterns/debugging/workflow_20260107_1770825e.json +0 -77
  288. empathy_os/patterns/refactoring_memory.json +0 -89
  289. empathy_os/persistence.py +0 -564
  290. empathy_os/platform_utils.py +0 -265
  291. empathy_os/plugins/__init__.py +0 -28
  292. empathy_os/plugins/base.py +0 -361
  293. empathy_os/plugins/registry.py +0 -268
  294. empathy_os/project_index/__init__.py +0 -32
  295. empathy_os/project_index/cli.py +0 -335
  296. empathy_os/project_index/index.py +0 -667
  297. empathy_os/project_index/models.py +0 -504
  298. empathy_os/project_index/reports.py +0 -474
  299. empathy_os/project_index/scanner.py +0 -777
  300. empathy_os/project_index/scanner_parallel 2.py +0 -291
  301. empathy_os/project_index/scanner_parallel.py +0 -291
  302. empathy_os/prompts/__init__.py +0 -61
  303. empathy_os/prompts/config.py +0 -77
  304. empathy_os/prompts/context.py +0 -177
  305. empathy_os/prompts/parser.py +0 -285
  306. empathy_os/prompts/registry.py +0 -313
  307. empathy_os/prompts/templates.py +0 -208
  308. empathy_os/redis_config.py +0 -302
  309. empathy_os/redis_memory.py +0 -799
  310. empathy_os/resilience/__init__.py +0 -56
  311. empathy_os/resilience/circuit_breaker.py +0 -256
  312. empathy_os/resilience/fallback.py +0 -179
  313. empathy_os/resilience/health.py +0 -300
  314. empathy_os/resilience/retry.py +0 -209
  315. empathy_os/resilience/timeout.py +0 -135
  316. empathy_os/routing/__init__.py +0 -43
  317. empathy_os/routing/chain_executor.py +0 -433
  318. empathy_os/routing/classifier.py +0 -217
  319. empathy_os/routing/smart_router.py +0 -234
  320. empathy_os/routing/workflow_registry.py +0 -343
  321. empathy_os/scaffolding/README.md +0 -589
  322. empathy_os/scaffolding/__init__.py +0 -35
  323. empathy_os/scaffolding/__main__.py +0 -14
  324. empathy_os/scaffolding/cli.py +0 -240
  325. empathy_os/socratic/__init__.py +0 -256
  326. empathy_os/socratic/ab_testing.py +0 -958
  327. empathy_os/socratic/blueprint.py +0 -533
  328. empathy_os/socratic/cli.py +0 -703
  329. empathy_os/socratic/collaboration.py +0 -1114
  330. empathy_os/socratic/domain_templates.py +0 -924
  331. empathy_os/socratic/embeddings.py +0 -738
  332. empathy_os/socratic/engine.py +0 -794
  333. empathy_os/socratic/explainer.py +0 -682
  334. empathy_os/socratic/feedback.py +0 -772
  335. empathy_os/socratic/forms.py +0 -629
  336. empathy_os/socratic/generator.py +0 -732
  337. empathy_os/socratic/llm_analyzer.py +0 -637
  338. empathy_os/socratic/mcp_server.py +0 -702
  339. empathy_os/socratic/session.py +0 -312
  340. empathy_os/socratic/storage.py +0 -667
  341. empathy_os/socratic/success.py +0 -730
  342. empathy_os/socratic/visual_editor.py +0 -860
  343. empathy_os/socratic/web_ui.py +0 -958
  344. empathy_os/telemetry/__init__.py +0 -39
  345. empathy_os/telemetry/agent_coordination 2.py +0 -478
  346. empathy_os/telemetry/agent_coordination.py +0 -476
  347. empathy_os/telemetry/agent_tracking 2.py +0 -350
  348. empathy_os/telemetry/agent_tracking.py +0 -348
  349. empathy_os/telemetry/approval_gates 2.py +0 -563
  350. empathy_os/telemetry/approval_gates.py +0 -551
  351. empathy_os/telemetry/cli.py +0 -1231
  352. empathy_os/telemetry/commands/__init__.py +0 -14
  353. empathy_os/telemetry/commands/dashboard_commands.py +0 -696
  354. empathy_os/telemetry/event_streaming 2.py +0 -405
  355. empathy_os/telemetry/event_streaming.py +0 -405
  356. empathy_os/telemetry/feedback_loop 2.py +0 -557
  357. empathy_os/telemetry/feedback_loop.py +0 -554
  358. empathy_os/telemetry/usage_tracker.py +0 -591
  359. empathy_os/templates.py +0 -754
  360. empathy_os/test_generator/__init__.py +0 -38
  361. empathy_os/test_generator/__main__.py +0 -14
  362. empathy_os/test_generator/cli.py +0 -234
  363. empathy_os/test_generator/generator.py +0 -355
  364. empathy_os/test_generator/risk_analyzer.py +0 -216
  365. empathy_os/tier_recommender.py +0 -384
  366. empathy_os/tools.py +0 -183
  367. empathy_os/trust/__init__.py +0 -28
  368. empathy_os/trust/circuit_breaker.py +0 -579
  369. empathy_os/trust_building.py +0 -527
  370. empathy_os/validation/__init__.py +0 -19
  371. empathy_os/validation/xml_validator.py +0 -281
  372. empathy_os/vscode_bridge 2.py +0 -173
  373. empathy_os/vscode_bridge.py +0 -173
  374. empathy_os/workflow_commands.py +0 -780
  375. empathy_os/workflow_patterns/__init__.py +0 -33
  376. empathy_os/workflow_patterns/behavior.py +0 -249
  377. empathy_os/workflow_patterns/core.py +0 -76
  378. empathy_os/workflow_patterns/output.py +0 -99
  379. empathy_os/workflow_patterns/registry.py +0 -255
  380. empathy_os/workflow_patterns/structural.py +0 -288
  381. empathy_os/workflows/__init__.py +0 -539
  382. empathy_os/workflows/autonomous_test_gen.py +0 -1268
  383. empathy_os/workflows/base.py +0 -2667
  384. empathy_os/workflows/batch_processing.py +0 -342
  385. empathy_os/workflows/bug_predict.py +0 -1084
  386. empathy_os/workflows/builder.py +0 -273
  387. empathy_os/workflows/caching.py +0 -253
  388. empathy_os/workflows/code_review.py +0 -1048
  389. empathy_os/workflows/code_review_adapters.py +0 -312
  390. empathy_os/workflows/code_review_pipeline.py +0 -722
  391. empathy_os/workflows/config.py +0 -645
  392. empathy_os/workflows/dependency_check.py +0 -644
  393. empathy_os/workflows/document_gen/__init__.py +0 -25
  394. empathy_os/workflows/document_gen/config.py +0 -30
  395. empathy_os/workflows/document_gen/report_formatter.py +0 -162
  396. empathy_os/workflows/document_gen/workflow.py +0 -1426
  397. empathy_os/workflows/document_gen.py +0 -29
  398. empathy_os/workflows/document_manager.py +0 -216
  399. empathy_os/workflows/document_manager_README.md +0 -134
  400. empathy_os/workflows/documentation_orchestrator.py +0 -1205
  401. empathy_os/workflows/history.py +0 -510
  402. empathy_os/workflows/keyboard_shortcuts/__init__.py +0 -39
  403. empathy_os/workflows/keyboard_shortcuts/generators.py +0 -391
  404. empathy_os/workflows/keyboard_shortcuts/parsers.py +0 -416
  405. empathy_os/workflows/keyboard_shortcuts/prompts.py +0 -295
  406. empathy_os/workflows/keyboard_shortcuts/schema.py +0 -193
  407. empathy_os/workflows/keyboard_shortcuts/workflow.py +0 -509
  408. empathy_os/workflows/llm_base.py +0 -363
  409. empathy_os/workflows/manage_docs.py +0 -87
  410. empathy_os/workflows/manage_docs_README.md +0 -134
  411. empathy_os/workflows/manage_documentation.py +0 -821
  412. empathy_os/workflows/new_sample_workflow1.py +0 -149
  413. empathy_os/workflows/new_sample_workflow1_README.md +0 -150
  414. empathy_os/workflows/orchestrated_health_check.py +0 -849
  415. empathy_os/workflows/orchestrated_release_prep.py +0 -600
  416. empathy_os/workflows/output.py +0 -410
  417. empathy_os/workflows/perf_audit.py +0 -863
  418. empathy_os/workflows/pr_review.py +0 -762
  419. empathy_os/workflows/progress.py +0 -779
  420. empathy_os/workflows/progress_server.py +0 -322
  421. empathy_os/workflows/progressive/README 2.md +0 -454
  422. empathy_os/workflows/progressive/README.md +0 -454
  423. empathy_os/workflows/progressive/__init__ 2.py +0 -92
  424. empathy_os/workflows/progressive/__init__.py +0 -82
  425. empathy_os/workflows/progressive/cli 2.py +0 -242
  426. empathy_os/workflows/progressive/cli.py +0 -219
  427. empathy_os/workflows/progressive/core 2.py +0 -488
  428. empathy_os/workflows/progressive/core.py +0 -488
  429. empathy_os/workflows/progressive/orchestrator 2.py +0 -701
  430. empathy_os/workflows/progressive/orchestrator.py +0 -723
  431. empathy_os/workflows/progressive/reports 2.py +0 -528
  432. empathy_os/workflows/progressive/reports.py +0 -520
  433. empathy_os/workflows/progressive/telemetry 2.py +0 -280
  434. empathy_os/workflows/progressive/telemetry.py +0 -274
  435. empathy_os/workflows/progressive/test_gen 2.py +0 -514
  436. empathy_os/workflows/progressive/test_gen.py +0 -495
  437. empathy_os/workflows/progressive/workflow 2.py +0 -628
  438. empathy_os/workflows/progressive/workflow.py +0 -589
  439. empathy_os/workflows/refactor_plan.py +0 -694
  440. empathy_os/workflows/release_prep.py +0 -895
  441. empathy_os/workflows/release_prep_crew.py +0 -969
  442. empathy_os/workflows/research_synthesis.py +0 -404
  443. empathy_os/workflows/routing.py +0 -168
  444. empathy_os/workflows/secure_release.py +0 -593
  445. empathy_os/workflows/security_adapters.py +0 -297
  446. empathy_os/workflows/security_audit.py +0 -1329
  447. empathy_os/workflows/security_audit_phase3.py +0 -355
  448. empathy_os/workflows/seo_optimization.py +0 -633
  449. empathy_os/workflows/step_config.py +0 -234
  450. empathy_os/workflows/telemetry_mixin.py +0 -269
  451. empathy_os/workflows/test5.py +0 -125
  452. empathy_os/workflows/test5_README.md +0 -158
  453. empathy_os/workflows/test_coverage_boost_crew.py +0 -849
  454. empathy_os/workflows/test_gen/__init__.py +0 -52
  455. empathy_os/workflows/test_gen/ast_analyzer.py +0 -249
  456. empathy_os/workflows/test_gen/config.py +0 -88
  457. empathy_os/workflows/test_gen/data_models.py +0 -38
  458. empathy_os/workflows/test_gen/report_formatter.py +0 -289
  459. empathy_os/workflows/test_gen/test_templates.py +0 -381
  460. empathy_os/workflows/test_gen/workflow.py +0 -655
  461. empathy_os/workflows/test_gen.py +0 -54
  462. empathy_os/workflows/test_gen_behavioral.py +0 -477
  463. empathy_os/workflows/test_gen_parallel.py +0 -341
  464. empathy_os/workflows/test_lifecycle.py +0 -526
  465. empathy_os/workflows/test_maintenance.py +0 -627
  466. empathy_os/workflows/test_maintenance_cli.py +0 -590
  467. empathy_os/workflows/test_maintenance_crew.py +0 -840
  468. empathy_os/workflows/test_runner.py +0 -622
  469. empathy_os/workflows/tier_tracking.py +0 -531
  470. empathy_os/workflows/xml_enhanced_crew.py +0 -285
  471. empathy_software_plugin/SOFTWARE_PLUGIN_README.md +0 -57
  472. empathy_software_plugin/cli/__init__.py +0 -120
  473. empathy_software_plugin/cli/inspect.py +0 -362
  474. empathy_software_plugin/cli.py +0 -574
  475. empathy_software_plugin/plugin.py +0 -188
  476. workflow_scaffolding/__init__.py +0 -11
  477. workflow_scaffolding/__main__.py +0 -12
  478. workflow_scaffolding/cli.py +0 -206
  479. workflow_scaffolding/generator.py +0 -265
  480. {empathy_framework-5.2.1.dist-info → empathy_framework-5.4.0.dist-info}/WHEEL +0 -0
@@ -1,967 +0,0 @@
1
- """LLM Provider Adapters
2
-
3
- Unified interface for different LLM providers (OpenAI, Anthropic, local models).
4
-
5
- Copyright 2025 Smart AI Memory, LLC
6
- Licensed under Fair Source 0.9
7
- """
8
-
9
- import asyncio
10
- import logging
11
- from abc import ABC, abstractmethod
12
- from dataclasses import dataclass
13
- from datetime import datetime
14
- from typing import Any
15
-
16
- logger = logging.getLogger(__name__)
17
-
18
-
19
- @dataclass
20
- class LLMResponse:
21
- """Standardized response from any LLM provider"""
22
-
23
- content: str
24
- model: str
25
- tokens_used: int
26
- finish_reason: str
27
- metadata: dict[str, Any]
28
-
29
-
30
- class BaseLLMProvider(ABC):
31
- """Base class for all LLM providers.
32
-
33
- Provides unified interface regardless of backend.
34
- """
35
-
36
- def __init__(self, api_key: str | None = None, **kwargs):
37
- self.api_key = api_key
38
- self.config = kwargs
39
-
40
- @abstractmethod
41
- async def generate(
42
- self,
43
- messages: list[dict[str, str]],
44
- system_prompt: str | None = None,
45
- temperature: float = 0.7,
46
- max_tokens: int = 1024,
47
- **kwargs,
48
- ) -> LLMResponse:
49
- """Generate response from LLM.
50
-
51
- Args:
52
- messages: List of {"role": "user/assistant", "content": "..."}
53
- system_prompt: Optional system prompt
54
- temperature: Sampling temperature
55
- max_tokens: Maximum tokens in response
56
- **kwargs: Provider-specific options
57
-
58
- Returns:
59
- LLMResponse with standardized format
60
-
61
- """
62
-
63
- @abstractmethod
64
- def get_model_info(self) -> dict[str, Any]:
65
- """Get information about the model being used"""
66
-
67
- def estimate_tokens(self, text: str) -> int:
68
- """Estimate token count for text.
69
-
70
- Rough approximation: ~4 chars per token
71
- """
72
- return len(text) // 4
73
-
74
-
75
- class AnthropicProvider(BaseLLMProvider):
76
- """Anthropic (Claude) provider with enhanced features.
77
-
78
- Supports Claude 3 family models with advanced capabilities:
79
- - Extended context windows (200K tokens)
80
- - Prompt caching for faster repeated queries
81
- - Thinking mode for complex reasoning
82
- - Batch processing for cost optimization
83
- """
84
-
85
- def __init__(
86
- self,
87
- api_key: str | None = None,
88
- model: str = "claude-sonnet-4-5-20250929",
89
- use_prompt_caching: bool = True, # CHANGED: Default to True for 20-30% cost savings
90
- use_thinking: bool = False,
91
- use_batch: bool = False,
92
- **kwargs,
93
- ):
94
- super().__init__(api_key, **kwargs)
95
- self.model = model
96
- self.use_prompt_caching = use_prompt_caching
97
- self.use_thinking = use_thinking
98
- self.use_batch = use_batch
99
-
100
- # Validate API key is provided
101
- if not api_key or not api_key.strip():
102
- raise ValueError(
103
- "API key is required for Anthropic provider. "
104
- "Provide via api_key parameter or ANTHROPIC_API_KEY environment variable",
105
- )
106
-
107
- # Lazy import to avoid requiring anthropic if not used
108
- # v4.6.3: Use AsyncAnthropic for true async I/O (prevents event loop blocking)
109
- try:
110
- import anthropic
111
-
112
- self.client = anthropic.AsyncAnthropic(api_key=api_key)
113
- except ImportError as e:
114
- raise ImportError(
115
- "anthropic package required. Install with: pip install anthropic",
116
- ) from e
117
-
118
- # Initialize batch provider if needed
119
- if use_batch:
120
- self.batch_provider = AnthropicBatchProvider(api_key=api_key)
121
- else:
122
- self.batch_provider = None
123
-
124
- async def generate(
125
- self,
126
- messages: list[dict[str, str]],
127
- system_prompt: str | None = None,
128
- temperature: float = 0.7,
129
- max_tokens: int = 1024,
130
- **kwargs,
131
- ) -> LLMResponse:
132
- """Generate response using Anthropic API with enhanced features.
133
-
134
- Claude-specific enhancements:
135
- - Prompt caching for repeated system prompts (90% cost reduction)
136
- - Extended context (200K tokens) for large codebase analysis
137
- - Thinking mode for complex reasoning tasks
138
-
139
- Prompt caching is enabled by default (use_prompt_caching=True).
140
- This marks system prompts with cache_control for Anthropic's cache.
141
- Break-even: ~3 requests with same context, 5-minute TTL.
142
- """
143
- # Build kwargs for Anthropic
144
- api_kwargs = {
145
- "model": self.model,
146
- "max_tokens": max_tokens,
147
- "temperature": temperature,
148
- "messages": messages,
149
- }
150
-
151
- # Enable prompt caching for system prompts (Claude-specific)
152
- if system_prompt and self.use_prompt_caching:
153
- api_kwargs["system"] = [
154
- {
155
- "type": "text",
156
- "text": system_prompt,
157
- "cache_control": {"type": "ephemeral"}, # Cache for 5 minutes
158
- },
159
- ]
160
- elif system_prompt:
161
- api_kwargs["system"] = system_prompt
162
-
163
- # Enable extended thinking for complex tasks (Claude-specific)
164
- if self.use_thinking:
165
- api_kwargs["thinking"] = {
166
- "type": "enabled",
167
- "budget_tokens": 2000, # Allow 2K tokens for reasoning
168
- }
169
-
170
- # Add any additional kwargs
171
- api_kwargs.update(kwargs)
172
-
173
- # Call Anthropic API (async with AsyncAnthropic)
174
- response = await self.client.messages.create(**api_kwargs) # type: ignore[call-overload]
175
-
176
- # Extract thinking content if present
177
- thinking_content = None
178
- response_content = ""
179
-
180
- for block in response.content:
181
- if hasattr(block, "type"):
182
- if block.type == "thinking":
183
- thinking_content = block.thinking
184
- elif block.type == "text":
185
- response_content = block.text
186
- else:
187
- response_content = block.text
188
-
189
- # Convert to standardized format
190
- metadata = {
191
- "input_tokens": response.usage.input_tokens,
192
- "output_tokens": response.usage.output_tokens,
193
- "provider": "anthropic",
194
- "model_family": "claude-3",
195
- }
196
-
197
- # Add cache performance metrics if available
198
- if hasattr(response.usage, "cache_creation_input_tokens"):
199
- cache_creation = getattr(response.usage, "cache_creation_input_tokens", 0)
200
- cache_read = getattr(response.usage, "cache_read_input_tokens", 0)
201
-
202
- # Ensure values are numeric (handle mock objects in tests)
203
- if isinstance(cache_creation, int) and isinstance(cache_read, int):
204
- metadata["cache_creation_tokens"] = cache_creation
205
- metadata["cache_read_tokens"] = cache_read
206
-
207
- # Log cache performance for monitoring with detailed cost savings
208
- # Cache reads cost 90% less than regular input tokens
209
- # Cache writes cost 25% more than regular input tokens
210
- if cache_read > 0:
211
- # Sonnet 4.5 input: $3/M tokens, cache read: $0.30/M tokens (90% discount)
212
- savings_per_token = 0.003 / 1000 * 0.9 # 90% of regular cost
213
- total_savings = cache_read * savings_per_token
214
- logger.info(
215
- f"Cache HIT: {cache_read:,} tokens read from cache "
216
- f"(saved ${total_savings:.4f} vs full price)"
217
- )
218
- if cache_creation > 0:
219
- # Cache write cost: $3.75/M tokens (25% markup)
220
- write_cost = cache_creation * 0.00375 / 1000
221
- logger.debug(
222
- f"Cache WRITE: {cache_creation:,} tokens written to cache "
223
- f"(cost ${write_cost:.4f})"
224
- )
225
-
226
- # Add thinking content if present
227
- if thinking_content:
228
- metadata["thinking"] = thinking_content
229
-
230
- return LLMResponse(
231
- content=response_content,
232
- model=response.model,
233
- tokens_used=response.usage.input_tokens + response.usage.output_tokens,
234
- finish_reason=response.stop_reason,
235
- metadata=metadata,
236
- )
237
-
238
- async def analyze_large_codebase(
239
- self,
240
- codebase_files: list[dict[str, str]],
241
- analysis_prompt: str,
242
- **kwargs,
243
- ) -> LLMResponse:
244
- """Analyze large codebases using Claude's 200K context window.
245
-
246
- Claude-specific feature: Can process entire repositories in one call.
247
-
248
- Args:
249
- codebase_files: List of {"path": "...", "content": "..."} dicts
250
- analysis_prompt: What to analyze for
251
- **kwargs: Additional generation parameters
252
-
253
- Returns:
254
- LLMResponse with analysis results
255
-
256
- """
257
- # Build context from all files
258
- file_context = "\n\n".join(
259
- [f"# File: {file['path']}\n{file['content']}" for file in codebase_files],
260
- )
261
-
262
- # Create system prompt with caching for file context
263
- system_parts = [
264
- {
265
- "type": "text",
266
- "text": "You are a code analysis expert using the Empathy Framework.",
267
- },
268
- {
269
- "type": "text",
270
- "text": f"Codebase files:\n\n{file_context}",
271
- "cache_control": {"type": "ephemeral"}, # Cache the codebase
272
- },
273
- ]
274
-
275
- messages = [{"role": "user", "content": analysis_prompt}]
276
-
277
- # Use extended max_tokens for comprehensive analysis
278
- return await self.generate(
279
- messages=messages,
280
- system_prompt=None, # We'll pass it directly in api_kwargs
281
- max_tokens=kwargs.pop("max_tokens", 4096),
282
- **{**kwargs, "system": system_parts},
283
- )
284
-
285
- def get_model_info(self) -> dict[str, Any]:
286
- """Get Claude model information with extended context capabilities"""
287
- model_info = {
288
- "claude-3-opus-20240229": {
289
- "max_tokens": 200000,
290
- "cost_per_1m_input": 15.00,
291
- "cost_per_1m_output": 75.00,
292
- "supports_prompt_caching": True,
293
- "supports_thinking": True,
294
- "ideal_for": "Complex reasoning, large codebases",
295
- },
296
- "claude-3-5-sonnet-20241022": {
297
- "max_tokens": 200000,
298
- "cost_per_1m_input": 3.00,
299
- "cost_per_1m_output": 15.00,
300
- "supports_prompt_caching": True,
301
- "supports_thinking": True,
302
- "ideal_for": "General development, balanced cost/performance",
303
- },
304
- "claude-3-haiku-20240307": {
305
- "max_tokens": 200000,
306
- "cost_per_1m_input": 0.25,
307
- "cost_per_1m_output": 1.25,
308
- "supports_prompt_caching": True,
309
- "supports_thinking": False,
310
- "ideal_for": "Fast responses, simple tasks",
311
- },
312
- }
313
-
314
- return model_info.get(
315
- self.model,
316
- {
317
- "max_tokens": 200000,
318
- "cost_per_1m_input": 3.00,
319
- "cost_per_1m_output": 15.00,
320
- "supports_prompt_caching": True,
321
- "supports_thinking": True,
322
- },
323
- )
324
-
325
- def estimate_tokens(self, text: str) -> int:
326
- """Estimate token count using accurate token counter (overrides base class).
327
-
328
- Uses tiktoken for fast local estimation (~98% accurate).
329
- Falls back to heuristic if tiktoken unavailable.
330
-
331
- Args:
332
- text: Text to count tokens for
333
-
334
- Returns:
335
- Estimated token count
336
- """
337
- try:
338
- from .utils.tokens import count_tokens
339
-
340
- return count_tokens(text, model=self.model, use_api=False)
341
- except ImportError:
342
- # Fallback to base class heuristic if utils not available
343
- return super().estimate_tokens(text)
344
-
345
- def calculate_actual_cost(
346
- self,
347
- input_tokens: int,
348
- output_tokens: int,
349
- cache_creation_tokens: int = 0,
350
- cache_read_tokens: int = 0,
351
- ) -> dict[str, Any]:
352
- """Calculate actual cost based on precise token counts.
353
-
354
- Includes Anthropic prompt caching cost adjustments:
355
- - Cache writes: 25% markup over standard input pricing
356
- - Cache reads: 90% discount from standard input pricing
357
-
358
- Args:
359
- input_tokens: Regular input tokens (not cached)
360
- output_tokens: Output tokens
361
- cache_creation_tokens: Tokens written to cache
362
- cache_read_tokens: Tokens read from cache
363
-
364
- Returns:
365
- Dictionary with cost breakdown:
366
- - base_cost: Cost for regular input/output tokens
367
- - cache_write_cost: Cost for cache creation (if any)
368
- - cache_read_cost: Cost for cache reads (if any)
369
- - total_cost: Total cost including all components
370
- - savings: Amount saved by cache reads vs. full price
371
-
372
- Example:
373
- >>> provider = AnthropicProvider(api_key="...")
374
- >>> cost = provider.calculate_actual_cost(
375
- ... input_tokens=1000,
376
- ... output_tokens=500,
377
- ... cache_read_tokens=10000
378
- ... )
379
- >>> cost["total_cost"]
380
- 0.0105 # Significantly less than without cache
381
- """
382
- # Get pricing for this model
383
- model_info = self.get_model_info()
384
- input_price_per_million = model_info["cost_per_1m_input"]
385
- output_price_per_million = model_info["cost_per_1m_output"]
386
-
387
- # Base cost (non-cached tokens)
388
- base_cost = (input_tokens / 1_000_000) * input_price_per_million
389
- base_cost += (output_tokens / 1_000_000) * output_price_per_million
390
-
391
- # Cache write cost (25% markup)
392
- cache_write_price = input_price_per_million * 1.25
393
- cache_write_cost = (cache_creation_tokens / 1_000_000) * cache_write_price
394
-
395
- # Cache read cost (90% discount = 10% of input price)
396
- cache_read_price = input_price_per_million * 0.1
397
- cache_read_cost = (cache_read_tokens / 1_000_000) * cache_read_price
398
-
399
- # Calculate savings from cache reads
400
- full_price_for_cached = (cache_read_tokens / 1_000_000) * input_price_per_million
401
- savings = full_price_for_cached - cache_read_cost
402
-
403
- return {
404
- "base_cost": round(base_cost, 6),
405
- "cache_write_cost": round(cache_write_cost, 6),
406
- "cache_read_cost": round(cache_read_cost, 6),
407
- "total_cost": round(base_cost + cache_write_cost + cache_read_cost, 6),
408
- "savings": round(savings, 6),
409
- "currency": "USD",
410
- }
411
-
412
-
413
- class AnthropicBatchProvider:
414
- """Provider for Anthropic Batch API (50% cost reduction).
415
-
416
- The Batch API processes requests asynchronously within 24 hours
417
- at 50% of the standard API cost. Ideal for non-urgent, bulk tasks.
418
-
419
- Example:
420
- >>> provider = AnthropicBatchProvider(api_key="sk-ant-...")
421
- >>> requests = [
422
- ... {
423
- ... "custom_id": "task_1",
424
- ... "model": "claude-sonnet-4-5",
425
- ... "messages": [{"role": "user", "content": "Analyze X"}],
426
- ... "max_tokens": 1024
427
- ... }
428
- ... ]
429
- >>> batch_id = provider.create_batch(requests)
430
- >>> # Wait for processing (up to 24 hours)
431
- >>> results = await provider.wait_for_batch(batch_id)
432
- """
433
-
434
- def __init__(self, api_key: str | None = None):
435
- """Initialize batch provider.
436
-
437
- Args:
438
- api_key: Anthropic API key (defaults to ANTHROPIC_API_KEY env var)
439
- """
440
- if not api_key or not api_key.strip():
441
- raise ValueError(
442
- "API key is required for Anthropic Batch API. "
443
- "Provide via api_key parameter or ANTHROPIC_API_KEY environment variable"
444
- )
445
-
446
- try:
447
- import anthropic
448
-
449
- self.client = anthropic.Anthropic(api_key=api_key)
450
- self._batch_jobs: dict[str, Any] = {}
451
- except ImportError as e:
452
- raise ImportError(
453
- "anthropic package required for Batch API. Install with: pip install anthropic"
454
- ) from e
455
-
456
- def create_batch(self, requests: list[dict[str, Any]], job_id: str | None = None) -> str:
457
- """Create a batch job.
458
-
459
- Args:
460
- requests: List of request dicts with 'custom_id' and 'params' containing message creation parameters.
461
- Format: [{"custom_id": "id1", "params": {"model": "...", "messages": [...], "max_tokens": 1024}}]
462
- job_id: Optional job identifier for tracking (unused, for API compatibility)
463
-
464
- Returns:
465
- Batch job ID for polling status
466
-
467
- Raises:
468
- ValueError: If requests is empty or invalid
469
- RuntimeError: If API call fails
470
-
471
- Example:
472
- >>> requests = [
473
- ... {
474
- ... "custom_id": "task_1",
475
- ... "params": {
476
- ... "model": "claude-sonnet-4-5-20250929",
477
- ... "messages": [{"role": "user", "content": "Test"}],
478
- ... "max_tokens": 1024
479
- ... }
480
- ... }
481
- ... ]
482
- >>> batch_id = provider.create_batch(requests)
483
- >>> print(f"Batch created: {batch_id}")
484
- Batch created: msgbatch_abc123
485
- """
486
- if not requests:
487
- raise ValueError("requests cannot be empty")
488
-
489
- # Validate and convert old format to new format if needed
490
- formatted_requests = []
491
- for req in requests:
492
- if "params" not in req:
493
- # Old format: convert to new format with params wrapper
494
- formatted_req = {
495
- "custom_id": req.get("custom_id", f"req_{id(req)}"),
496
- "params": {
497
- "model": req.get("model", "claude-sonnet-4-5-20250929"),
498
- "messages": req.get("messages", []),
499
- "max_tokens": req.get("max_tokens", 4096),
500
- },
501
- }
502
- # Copy other optional params
503
- for key in ["temperature", "system", "stop_sequences"]:
504
- if key in req:
505
- formatted_req["params"][key] = req[key]
506
- formatted_requests.append(formatted_req)
507
- else:
508
- formatted_requests.append(req)
509
-
510
- try:
511
- # Use correct Message Batches API endpoint
512
- batch = self.client.messages.batches.create(requests=formatted_requests)
513
- self._batch_jobs[batch.id] = batch
514
- logger.info(f"Created batch {batch.id} with {len(formatted_requests)} requests")
515
- return batch.id
516
- except Exception as e:
517
- logger.error(f"Failed to create batch: {e}")
518
- raise RuntimeError(f"Batch creation failed: {e}") from e
519
-
520
- def get_batch_status(self, batch_id: str) -> Any:
521
- """Get status of batch job.
522
-
523
- Args:
524
- batch_id: Batch job ID
525
-
526
- Returns:
527
- MessageBatch object with processing_status field:
528
- - "in_progress": Batch is being processed
529
- - "canceling": Cancellation initiated
530
- - "ended": Batch processing ended (check request_counts for success/errors)
531
-
532
- Example:
533
- >>> status = provider.get_batch_status("msgbatch_abc123")
534
- >>> print(status.processing_status)
535
- in_progress
536
- >>> print(f"Succeeded: {status.request_counts.succeeded}")
537
- """
538
- try:
539
- # Use correct Message Batches API endpoint
540
- batch = self.client.messages.batches.retrieve(batch_id)
541
- self._batch_jobs[batch_id] = batch
542
- return batch
543
- except Exception as e:
544
- logger.error(f"Failed to get batch status for {batch_id}: {e}")
545
- raise RuntimeError(f"Failed to get batch status: {e}") from e
546
-
547
- def get_batch_results(self, batch_id: str) -> list[dict[str, Any]]:
548
- """Get results from completed batch.
549
-
550
- Args:
551
- batch_id: Batch job ID
552
-
553
- Returns:
554
- List of result dicts. Each dict contains:
555
- - custom_id: Request identifier
556
- - result: Either {"type": "succeeded", "message": {...}} or {"type": "errored", "error": {...}}
557
-
558
- Raises:
559
- ValueError: If batch has not ended processing
560
- RuntimeError: If API call fails
561
-
562
- Example:
563
- >>> results = provider.get_batch_results("msgbatch_abc123")
564
- >>> for result in results:
565
- ... if result['result']['type'] == 'succeeded':
566
- ... message = result['result']['message']
567
- ... print(f"{result['custom_id']}: {message.content[0].text}")
568
- ... else:
569
- ... error = result['result']['error']
570
- ... print(f"{result['custom_id']}: Error {error['type']}")
571
- """
572
- status = self.get_batch_status(batch_id)
573
-
574
- # Check processing_status instead of status
575
- if status.processing_status != "ended":
576
- raise ValueError(
577
- f"Batch {batch_id} has not ended processing (status: {status.processing_status})"
578
- )
579
-
580
- try:
581
- # Use correct Message Batches API endpoint
582
- # results() returns an iterator, convert to list
583
- results_iterator = self.client.messages.batches.results(batch_id)
584
- return list(results_iterator)
585
- except Exception as e:
586
- logger.error(f"Failed to get batch results for {batch_id}: {e}")
587
- raise RuntimeError(f"Failed to get batch results: {e}") from e
588
-
589
- async def wait_for_batch(
590
- self,
591
- batch_id: str,
592
- poll_interval: int = 60,
593
- timeout: int = 86400, # 24 hours
594
- ) -> list[dict[str, Any]]:
595
- """Wait for batch to complete with polling.
596
-
597
- Args:
598
- batch_id: Batch job ID
599
- poll_interval: Seconds between status checks (default: 60)
600
- timeout: Maximum wait time in seconds (default: 86400 = 24 hours)
601
-
602
- Returns:
603
- Batch results when processing ends
604
-
605
- Raises:
606
- TimeoutError: If batch doesn't complete within timeout
607
- RuntimeError: If batch had errors during processing
608
-
609
- Example:
610
- >>> results = await provider.wait_for_batch(
611
- ... "msgbatch_abc123",
612
- ... poll_interval=300, # Check every 5 minutes
613
- ... )
614
- >>> print(f"Batch completed: {len(results)} results")
615
- """
616
-
617
- start_time = datetime.now()
618
-
619
- while True:
620
- status = self.get_batch_status(batch_id)
621
-
622
- # Check if batch processing has ended
623
- if status.processing_status == "ended":
624
- # Check request counts to see if there were errors
625
- counts = status.request_counts
626
- logger.info(
627
- f"Batch {batch_id} ended: "
628
- f"{counts.succeeded} succeeded, {counts.errored} errored, "
629
- f"{counts.canceled} canceled, {counts.expired} expired"
630
- )
631
-
632
- # Return results even if some requests failed
633
- # The caller can inspect individual results for errors
634
- return self.get_batch_results(batch_id)
635
-
636
- # Check timeout
637
- elapsed = (datetime.now() - start_time).total_seconds()
638
- if elapsed > timeout:
639
- raise TimeoutError(f"Batch {batch_id} did not complete within {timeout}s")
640
-
641
- # Log progress with request counts
642
- try:
643
- counts = status.request_counts
644
- logger.debug(
645
- f"Batch {batch_id} status: {status.processing_status} "
646
- f"(processing: {counts.processing}, elapsed: {elapsed:.0f}s)"
647
- )
648
- except AttributeError:
649
- logger.debug(
650
- f"Batch {batch_id} status: {status.processing_status} (elapsed: {elapsed:.0f}s)"
651
- )
652
-
653
- # Wait before next poll
654
- await asyncio.sleep(poll_interval)
655
-
656
-
657
- class OpenAIProvider(BaseLLMProvider):
658
- """OpenAI provider.
659
-
660
- Supports GPT-4, GPT-3.5, and other OpenAI models.
661
- """
662
-
663
- def __init__(self, api_key: str | None = None, model: str = "gpt-4-turbo-preview", **kwargs):
664
- super().__init__(api_key, **kwargs)
665
- self.model = model
666
-
667
- # Validate API key is provided
668
- if not api_key or not api_key.strip():
669
- raise ValueError(
670
- "API key is required for OpenAI provider. "
671
- "Provide via api_key parameter or OPENAI_API_KEY environment variable",
672
- )
673
-
674
- # Lazy import
675
- try:
676
- import openai
677
-
678
- self.client = openai.AsyncOpenAI(api_key=api_key)
679
- except ImportError as e:
680
- raise ImportError("openai package required. Install with: pip install openai") from e
681
-
682
- async def generate(
683
- self,
684
- messages: list[dict[str, str]],
685
- system_prompt: str | None = None,
686
- temperature: float = 0.7,
687
- max_tokens: int = 1024,
688
- **kwargs,
689
- ) -> LLMResponse:
690
- """Generate response using OpenAI API"""
691
- # Add system prompt if provided
692
- if system_prompt:
693
- messages = [{"role": "system", "content": system_prompt}] + messages
694
-
695
- # Call OpenAI API
696
- response = await self.client.chat.completions.create(
697
- model=self.model,
698
- messages=messages, # type: ignore[arg-type]
699
- temperature=temperature,
700
- max_tokens=max_tokens,
701
- **kwargs,
702
- )
703
-
704
- # Convert to standardized format
705
- content = response.choices[0].message.content or ""
706
- usage = response.usage
707
- return LLMResponse(
708
- content=content,
709
- model=response.model,
710
- tokens_used=usage.total_tokens if usage else 0,
711
- finish_reason=response.choices[0].finish_reason,
712
- metadata={
713
- "input_tokens": usage.prompt_tokens if usage else 0,
714
- "output_tokens": usage.completion_tokens if usage else 0,
715
- "provider": "openai",
716
- },
717
- )
718
-
719
- def get_model_info(self) -> dict[str, Any]:
720
- """Get OpenAI model information"""
721
- model_info = {
722
- "gpt-4-turbo-preview": {
723
- "max_tokens": 128000,
724
- "cost_per_1m_input": 10.00,
725
- "cost_per_1m_output": 30.00,
726
- },
727
- "gpt-4": {"max_tokens": 8192, "cost_per_1m_input": 30.00, "cost_per_1m_output": 60.00},
728
- "gpt-3.5-turbo": {
729
- "max_tokens": 16385,
730
- "cost_per_1m_input": 0.50,
731
- "cost_per_1m_output": 1.50,
732
- },
733
- }
734
-
735
- return model_info.get(
736
- self.model,
737
- {"max_tokens": 128000, "cost_per_1m_input": 10.00, "cost_per_1m_output": 30.00},
738
- )
739
-
740
-
741
- class GeminiProvider(BaseLLMProvider):
742
- """Google Gemini provider with cost tracking integration.
743
-
744
- Supports Gemini models:
745
- - gemini-2.0-flash-exp: Fast, cheap tier (1M context)
746
- - gemini-1.5-pro: Balanced, capable tier (2M context)
747
- - gemini-2.5-pro: Premium reasoning tier
748
- """
749
-
750
- def __init__(
751
- self,
752
- api_key: str | None = None,
753
- model: str = "gemini-1.5-pro",
754
- **kwargs,
755
- ):
756
- super().__init__(api_key, **kwargs)
757
- self.model = model
758
-
759
- # Validate API key is provided
760
- if not api_key or not api_key.strip():
761
- raise ValueError(
762
- "API key is required for Gemini provider. "
763
- "Provide via api_key parameter or GOOGLE_API_KEY environment variable",
764
- )
765
-
766
- # Lazy import to avoid requiring google-generativeai if not used
767
- try:
768
- import google.generativeai as genai
769
-
770
- genai.configure(api_key=api_key)
771
- self.genai = genai
772
- self.client = genai.GenerativeModel(model)
773
- except ImportError as e:
774
- raise ImportError(
775
- "google-generativeai package required. Install with: pip install google-generativeai",
776
- ) from e
777
-
778
- async def generate(
779
- self,
780
- messages: list[dict[str, str]],
781
- system_prompt: str | None = None,
782
- temperature: float = 0.7,
783
- max_tokens: int = 1024,
784
- **kwargs,
785
- ) -> LLMResponse:
786
- """Generate response using Google Gemini API.
787
-
788
- Gemini-specific features:
789
- - Large context windows (1M-2M tokens)
790
- - Multimodal support
791
- - Grounding with Google Search
792
- """
793
- import asyncio
794
-
795
- # Convert messages to Gemini format
796
- gemini_messages = []
797
- for msg in messages:
798
- role = "user" if msg["role"] == "user" else "model"
799
- gemini_messages.append({"role": role, "parts": [msg["content"]]})
800
-
801
- # Build generation config
802
- generation_config = self.genai.GenerationConfig(
803
- temperature=temperature,
804
- max_output_tokens=max_tokens,
805
- )
806
-
807
- # Create model with system instruction if provided
808
- if system_prompt:
809
- model = self.genai.GenerativeModel(
810
- self.model,
811
- system_instruction=system_prompt,
812
- )
813
- else:
814
- model = self.client
815
-
816
- # Call Gemini API (run sync in thread pool for async compatibility)
817
- loop = asyncio.get_event_loop()
818
- response = await loop.run_in_executor(
819
- None,
820
- lambda: model.generate_content(
821
- gemini_messages, # type: ignore[arg-type]
822
- generation_config=generation_config,
823
- ),
824
- )
825
-
826
- # Extract token counts from usage metadata
827
- input_tokens = 0
828
- output_tokens = 0
829
- if hasattr(response, "usage_metadata"):
830
- input_tokens = getattr(response.usage_metadata, "prompt_token_count", 0)
831
- output_tokens = getattr(response.usage_metadata, "candidates_token_count", 0)
832
-
833
- # Log to cost tracker
834
- try:
835
- from empathy_os.cost_tracker import log_request
836
-
837
- tier = self._get_tier()
838
- log_request(
839
- model=self.model,
840
- input_tokens=input_tokens,
841
- output_tokens=output_tokens,
842
- task_type=kwargs.get("task_type", "gemini_generate"),
843
- tier=tier,
844
- )
845
- except ImportError:
846
- pass # Cost tracking not available
847
-
848
- # Convert to standardized format
849
- content = ""
850
- if response.candidates:
851
- content = response.candidates[0].content.parts[0].text
852
-
853
- finish_reason = "stop"
854
- if response.candidates and hasattr(response.candidates[0], "finish_reason"):
855
- finish_reason = str(response.candidates[0].finish_reason.name).lower()
856
-
857
- return LLMResponse(
858
- content=content,
859
- model=self.model,
860
- tokens_used=input_tokens + output_tokens,
861
- finish_reason=finish_reason,
862
- metadata={
863
- "input_tokens": input_tokens,
864
- "output_tokens": output_tokens,
865
- "provider": "google",
866
- "model_family": "gemini",
867
- },
868
- )
869
-
870
- def _get_tier(self) -> str:
871
- """Determine tier from model name."""
872
- if "flash" in self.model.lower():
873
- return "cheap"
874
- if "2.5" in self.model or "ultra" in self.model.lower():
875
- return "premium"
876
- return "capable"
877
-
878
- def get_model_info(self) -> dict[str, Any]:
879
- """Get Gemini model information"""
880
- model_info = {
881
- "gemini-2.0-flash-exp": {
882
- "max_tokens": 1000000,
883
- "cost_per_1m_input": 0.075,
884
- "cost_per_1m_output": 0.30,
885
- "supports_vision": True,
886
- "ideal_for": "Fast responses, simple tasks, large context",
887
- },
888
- "gemini-1.5-pro": {
889
- "max_tokens": 2000000,
890
- "cost_per_1m_input": 1.25,
891
- "cost_per_1m_output": 5.00,
892
- "supports_vision": True,
893
- "ideal_for": "Complex reasoning, large codebases",
894
- },
895
- "gemini-2.5-pro": {
896
- "max_tokens": 1000000,
897
- "cost_per_1m_input": 2.50,
898
- "cost_per_1m_output": 10.00,
899
- "supports_vision": True,
900
- "ideal_for": "Advanced reasoning, complex tasks",
901
- },
902
- }
903
-
904
- return model_info.get(
905
- self.model,
906
- {
907
- "max_tokens": 1000000,
908
- "cost_per_1m_input": 1.25,
909
- "cost_per_1m_output": 5.00,
910
- "supports_vision": True,
911
- },
912
- )
913
-
914
-
915
- class LocalProvider(BaseLLMProvider):
916
- """Local model provider (Ollama, LM Studio, etc.).
917
-
918
- For running models locally.
919
- """
920
-
921
- def __init__(self, endpoint: str = "http://localhost:11434", model: str = "llama2", **kwargs):
922
- super().__init__(api_key=None, **kwargs)
923
- self.endpoint = endpoint
924
- self.model = model
925
-
926
- async def generate(
927
- self,
928
- messages: list[dict[str, str]],
929
- system_prompt: str | None = None,
930
- temperature: float = 0.7,
931
- max_tokens: int = 1024,
932
- **kwargs,
933
- ) -> LLMResponse:
934
- """Generate response using local model"""
935
- import aiohttp
936
-
937
- # Format for Ollama-style API
938
- payload = {
939
- "model": self.model,
940
- "messages": messages,
941
- "stream": False,
942
- "options": {"temperature": temperature, "num_predict": max_tokens},
943
- }
944
-
945
- if system_prompt:
946
- payload["system"] = system_prompt
947
-
948
- async with aiohttp.ClientSession() as session:
949
- async with session.post(f"{self.endpoint}/api/chat", json=payload) as response:
950
- result = await response.json()
951
-
952
- return LLMResponse(
953
- content=result.get("message", {}).get("content", ""),
954
- model=self.model,
955
- tokens_used=result.get("eval_count", 0) + result.get("prompt_eval_count", 0),
956
- finish_reason="stop",
957
- metadata={"provider": "local", "endpoint": self.endpoint},
958
- )
959
-
960
- def get_model_info(self) -> dict[str, Any]:
961
- """Get local model information"""
962
- return {
963
- "max_tokens": 4096, # Depends on model
964
- "cost_per_1m_input": 0.0, # Free (local)
965
- "cost_per_1m_output": 0.0,
966
- "endpoint": self.endpoint,
967
- }