heph 0.0.49__tar.gz

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 (353) hide show
  1. heph-0.0.49/LICENSE +21 -0
  2. heph-0.0.49/PKG-INFO +95 -0
  3. heph-0.0.49/README.md +61 -0
  4. heph-0.0.49/pyproject.toml +65 -0
  5. heph-0.0.49/setup.cfg +4 -0
  6. heph-0.0.49/src/ai/__init__.py +1 -0
  7. heph-0.0.49/src/ai/diagnostics/__init__.py +77 -0
  8. heph-0.0.49/src/ai/diagnostics/py.typed +1 -0
  9. heph-0.0.49/src/ai/logging/__init__.py +216 -0
  10. heph-0.0.49/src/ai/logging/py.typed +1 -0
  11. heph-0.0.49/src/ai/providers/__init__.py +24 -0
  12. heph-0.0.49/src/ai/providers/access.py +32 -0
  13. heph-0.0.49/src/ai/providers/api_profiles.py +127 -0
  14. heph-0.0.49/src/ai/providers/catalog.py +542 -0
  15. heph-0.0.49/src/ai/providers/config.py +358 -0
  16. heph-0.0.49/src/ai/providers/endpoints.py +21 -0
  17. heph-0.0.49/src/ai/providers/keyring_store.py +131 -0
  18. heph-0.0.49/src/ai/providers/llama_cpp.py +1464 -0
  19. heph-0.0.49/src/ai/providers/model_choices.py +175 -0
  20. heph-0.0.49/src/ai/providers/model_support.py +76 -0
  21. heph-0.0.49/src/ai/providers/oauth.py +554 -0
  22. heph-0.0.49/src/ai/providers/py.typed +1 -0
  23. heph-0.0.49/src/ai/providers/reasoning.py +33 -0
  24. heph-0.0.49/src/ai/providers/registry.py +441 -0
  25. heph-0.0.49/src/ai/providers/volatile_keys.py +17 -0
  26. heph-0.0.49/src/ai/py.typed +1 -0
  27. heph-0.0.49/src/ai/runtime/__init__.py +73 -0
  28. heph-0.0.49/src/ai/runtime/_api_types.py +40 -0
  29. heph-0.0.49/src/ai/runtime/codex_backend.py +475 -0
  30. heph-0.0.49/src/ai/runtime/config.py +71 -0
  31. heph-0.0.49/src/ai/runtime/conversation.py +37 -0
  32. heph-0.0.49/src/ai/runtime/delta.py +17 -0
  33. heph-0.0.49/src/ai/runtime/engine.py +1188 -0
  34. heph-0.0.49/src/ai/runtime/errors.py +56 -0
  35. heph-0.0.49/src/ai/runtime/events.py +129 -0
  36. heph-0.0.49/src/ai/runtime/messages.py +30 -0
  37. heph-0.0.49/src/ai/runtime/prompt_cache.py +134 -0
  38. heph-0.0.49/src/ai/runtime/py.typed +1 -0
  39. heph-0.0.49/src/ai/runtime/request_payload.py +51 -0
  40. heph-0.0.49/src/ai/runtime/resilience.py +131 -0
  41. heph-0.0.49/src/ai/runtime/thinking.py +39 -0
  42. heph-0.0.49/src/ai/runtime/tool_deltas.py +38 -0
  43. heph-0.0.49/src/ai/runtime/usage.py +232 -0
  44. heph-0.0.49/src/ai/runtime/usage_payload.py +64 -0
  45. heph-0.0.49/src/ai/types/__init__.py +46 -0
  46. heph-0.0.49/src/ai/types/py.typed +1 -0
  47. heph-0.0.49/src/extensions/__init__.py +1 -0
  48. heph-0.0.49/src/extensions/contracts.py +41 -0
  49. heph-0.0.49/src/extensions/py.typed +1 -0
  50. heph-0.0.49/src/heph/__init__.py +10 -0
  51. heph-0.0.49/src/heph/cli/__init__.py +7 -0
  52. heph-0.0.49/src/heph/cli/main.py +895 -0
  53. heph-0.0.49/src/heph/cli/py.typed +1 -0
  54. heph-0.0.49/src/heph/commands/__init__.py +145 -0
  55. heph-0.0.49/src/heph/commands/_base.py +72 -0
  56. heph-0.0.49/src/heph/commands/armory.py +166 -0
  57. heph-0.0.49/src/heph/commands/auth.py +246 -0
  58. heph-0.0.49/src/heph/commands/compact.py +36 -0
  59. heph-0.0.49/src/heph/commands/display.py +195 -0
  60. heph-0.0.49/src/heph/commands/help.py +81 -0
  61. heph-0.0.49/src/heph/commands/local.py +222 -0
  62. heph-0.0.49/src/heph/commands/memory.py +34 -0
  63. heph-0.0.49/src/heph/commands/model.py +124 -0
  64. heph-0.0.49/src/heph/commands/py.typed +1 -0
  65. heph-0.0.49/src/heph/commands/session.py +327 -0
  66. heph-0.0.49/src/heph/commands/settings.py +59 -0
  67. heph-0.0.49/src/heph/commands/study.py +341 -0
  68. heph-0.0.49/src/heph/commands/suggestions.py +12 -0
  69. heph-0.0.49/src/heph/commands/terminal_text.py +14 -0
  70. heph-0.0.49/src/heph/identity/README.md +6 -0
  71. heph-0.0.49/src/heph/local_llm.py +29 -0
  72. heph-0.0.49/src/heph/product/__init__.py +5 -0
  73. heph-0.0.49/src/heph/product/context.py +5 -0
  74. heph-0.0.49/src/heph/product/py.typed +1 -0
  75. heph-0.0.49/src/heph/prompts/README.md +6 -0
  76. heph-0.0.49/src/heph/py.typed +1 -0
  77. heph-0.0.49/src/heph/release_state.py +130 -0
  78. heph-0.0.49/src/heph/sdk/__init__.py +353 -0
  79. heph-0.0.49/src/heph/sdk/capabilities.py +1397 -0
  80. heph-0.0.49/src/heph/sdk/compatibility.py +449 -0
  81. heph-0.0.49/src/heph/sdk/config.py +170 -0
  82. heph-0.0.49/src/heph/sdk/events.py +351 -0
  83. heph-0.0.49/src/heph/sdk/factory.py +198 -0
  84. heph-0.0.49/src/heph/sdk/materials.py +123 -0
  85. heph-0.0.49/src/heph/sdk/method_validation.py +1175 -0
  86. heph-0.0.49/src/heph/sdk/methods.py +1610 -0
  87. heph-0.0.49/src/heph/sdk/models.py +90 -0
  88. heph-0.0.49/src/heph/sdk/operation_stream.py +64 -0
  89. heph-0.0.49/src/heph/sdk/providers.py +165 -0
  90. heph-0.0.49/src/heph/sdk/runtime.py +737 -0
  91. heph-0.0.49/src/heph/sdk/service.py +676 -0
  92. heph-0.0.49/src/heph/sdk/service_availability.py +237 -0
  93. heph-0.0.49/src/heph/sdk/service_contract.py +150 -0
  94. heph-0.0.49/src/heph/sdk/service_routes.py +445 -0
  95. heph-0.0.49/src/heph/sdk/settings.py +257 -0
  96. heph-0.0.49/src/heph/sdk/state.py +398 -0
  97. heph-0.0.49/src/heph/sdk/stdio.py +522 -0
  98. heph-0.0.49/src/heph/sdk/stdio_client.py +1403 -0
  99. heph-0.0.49/src/heph/sdk/stdio_contract.py +206 -0
  100. heph-0.0.49/src/heph/sdk/stdio_json.py +27 -0
  101. heph-0.0.49/src/heph/sdk/stdio_requests.py +122 -0
  102. heph-0.0.49/src/heph/sdk/stdio_routes.py +97 -0
  103. heph-0.0.49/src/heph/sdk/stdio_state.py +181 -0
  104. heph-0.0.49/src/heph/sdk/value_types.py +181 -0
  105. heph-0.0.49/src/heph/state/README.md +10 -0
  106. heph-0.0.49/src/heph/state/release.toml +13 -0
  107. heph-0.0.49/src/heph.egg-info/PKG-INFO +95 -0
  108. heph-0.0.49/src/heph.egg-info/SOURCES.txt +351 -0
  109. heph-0.0.49/src/heph.egg-info/dependency_links.txt +1 -0
  110. heph-0.0.49/src/heph.egg-info/entry_points.txt +3 -0
  111. heph-0.0.49/src/heph.egg-info/requires.txt +11 -0
  112. heph-0.0.49/src/heph.egg-info/top_level.txt +5 -0
  113. heph-0.0.49/src/hephaion/__init__.py +1 -0
  114. heph-0.0.49/src/hephaion/_types/__init__.py +19 -0
  115. heph-0.0.49/src/hephaion/_types/py.typed +1 -0
  116. heph-0.0.49/src/hephaion/agent/__init__.py +32 -0
  117. heph-0.0.49/src/hephaion/agent/armory_tools.py +441 -0
  118. heph-0.0.49/src/hephaion/agent/citation.py +164 -0
  119. heph-0.0.49/src/hephaion/agent/compact.py +365 -0
  120. heph-0.0.49/src/hephaion/agent/dispatch.py +467 -0
  121. heph-0.0.49/src/hephaion/agent/dispatch_support.py +294 -0
  122. heph-0.0.49/src/hephaion/agent/file_tools.py +375 -0
  123. heph-0.0.49/src/hephaion/agent/material_tools.py +181 -0
  124. heph-0.0.49/src/hephaion/agent/model_stream.py +269 -0
  125. heph-0.0.49/src/hephaion/agent/mutation_queue.py +97 -0
  126. heph-0.0.49/src/hephaion/agent/path_safety.py +73 -0
  127. heph-0.0.49/src/hephaion/agent/prompt.py +273 -0
  128. heph-0.0.49/src/hephaion/agent/py.typed +1 -0
  129. heph-0.0.49/src/hephaion/agent/runtime_notes.py +156 -0
  130. heph-0.0.49/src/hephaion/agent/shell_tools.py +239 -0
  131. heph-0.0.49/src/hephaion/agent/steering.py +42 -0
  132. heph-0.0.49/src/hephaion/agent/tool_execution.py +513 -0
  133. heph-0.0.49/src/hephaion/agent/tool_registry.py +129 -0
  134. heph-0.0.49/src/hephaion/agent/tool_schema.py +52 -0
  135. heph-0.0.49/src/hephaion/agent/tools.py +411 -0
  136. heph-0.0.49/src/hephaion/agent/web_tools.py +339 -0
  137. heph-0.0.49/src/hephaion/armory/__init__.py +37 -0
  138. heph-0.0.49/src/hephaion/armory/cli.py +105 -0
  139. heph-0.0.49/src/hephaion/armory/py.typed +1 -0
  140. heph-0.0.49/src/hephaion/armory/search.py +255 -0
  141. heph-0.0.49/src/hephaion/armory/state_files.py +218 -0
  142. heph-0.0.49/src/hephaion/armory/storage.py +130 -0
  143. heph-0.0.49/src/hephaion/armory/trust.py +35 -0
  144. heph-0.0.49/src/hephaion/chat/__init__.py +0 -0
  145. heph-0.0.49/src/hephaion/chat/agent_request.py +130 -0
  146. heph-0.0.49/src/hephaion/chat/armory_turn.py +222 -0
  147. heph-0.0.49/src/hephaion/chat/automation.py +37 -0
  148. heph-0.0.49/src/hephaion/chat/citation_patterns.py +22 -0
  149. heph-0.0.49/src/hephaion/chat/cli.py +51 -0
  150. heph-0.0.49/src/hephaion/chat/compaction.py +56 -0
  151. heph-0.0.49/src/hephaion/chat/conversation_context.py +70 -0
  152. heph-0.0.49/src/hephaion/chat/current_topic_planning.py +151 -0
  153. heph-0.0.49/src/hephaion/chat/events.py +61 -0
  154. heph-0.0.49/src/hephaion/chat/evidence.py +1012 -0
  155. heph-0.0.49/src/hephaion/chat/evidence_assessment.py +508 -0
  156. heph-0.0.49/src/hephaion/chat/evidence_format.py +21 -0
  157. heph-0.0.49/src/hephaion/chat/evidence_notices.py +120 -0
  158. heph-0.0.49/src/hephaion/chat/evidence_overview.py +172 -0
  159. heph-0.0.49/src/hephaion/chat/evidence_prompt.py +43 -0
  160. heph-0.0.49/src/hephaion/chat/evidence_text.py +100 -0
  161. heph-0.0.49/src/hephaion/chat/followup_intent_resolution.py +332 -0
  162. heph-0.0.49/src/hephaion/chat/followup_retrieval.py +706 -0
  163. heph-0.0.49/src/hephaion/chat/intent.py +143 -0
  164. heph-0.0.49/src/hephaion/chat/intent_resolution.py +373 -0
  165. heph-0.0.49/src/hephaion/chat/learning_reply.py +440 -0
  166. heph-0.0.49/src/hephaion/chat/learning_signals.py +256 -0
  167. heph-0.0.49/src/hephaion/chat/material_state.py +359 -0
  168. heph-0.0.49/src/hephaion/chat/message_delivery.py +84 -0
  169. heph-0.0.49/src/hephaion/chat/model_selection.py +86 -0
  170. heph-0.0.49/src/hephaion/chat/model_text.py +55 -0
  171. heph-0.0.49/src/hephaion/chat/orchestrator.py +46 -0
  172. heph-0.0.49/src/hephaion/chat/overview_cues.py +122 -0
  173. heph-0.0.49/src/hephaion/chat/overview_planning.py +89 -0
  174. heph-0.0.49/src/hephaion/chat/overview_reply.py +761 -0
  175. heph-0.0.49/src/hephaion/chat/overview_tables.py +121 -0
  176. heph-0.0.49/src/hephaion/chat/overview_topics.py +162 -0
  177. heph-0.0.49/src/hephaion/chat/overview_validation.py +295 -0
  178. heph-0.0.49/src/hephaion/chat/prior_answer.py +744 -0
  179. heph-0.0.49/src/hephaion/chat/priority_planning.py +63 -0
  180. heph-0.0.49/src/hephaion/chat/provider_selection.py +22 -0
  181. heph-0.0.49/src/hephaion/chat/py.typed +1 -0
  182. heph-0.0.49/src/hephaion/chat/replayability_planning.py +50 -0
  183. heph-0.0.49/src/hephaion/chat/reply_evidence.py +58 -0
  184. heph-0.0.49/src/hephaion/chat/reply_repair.py +549 -0
  185. heph-0.0.49/src/hephaion/chat/reply_text.py +232 -0
  186. heph-0.0.49/src/hephaion/chat/session.py +553 -0
  187. heph-0.0.49/src/hephaion/chat/session_persistence.py +78 -0
  188. heph-0.0.49/src/hephaion/chat/storage.py +224 -0
  189. heph-0.0.49/src/hephaion/chat/titles.py +49 -0
  190. heph-0.0.49/src/hephaion/chat/turn_contract.py +282 -0
  191. heph-0.0.49/src/hephaion/chat/turn_contract_checks.py +95 -0
  192. heph-0.0.49/src/hephaion/chat/turn_event_helpers.py +37 -0
  193. heph-0.0.49/src/hephaion/chat/turn_execution.py +700 -0
  194. heph-0.0.49/src/hephaion/chat/turn_finalization.py +945 -0
  195. heph-0.0.49/src/hephaion/chat/turn_history.py +160 -0
  196. heph-0.0.49/src/hephaion/chat/turn_lifecycle.py +319 -0
  197. heph-0.0.49/src/hephaion/chat/turn_orchestrator.py +36 -0
  198. heph-0.0.49/src/hephaion/chat/turn_outputs.py +61 -0
  199. heph-0.0.49/src/hephaion/chat/turn_planning.py +1020 -0
  200. heph-0.0.49/src/hephaion/chat/turn_predicates.py +86 -0
  201. heph-0.0.49/src/hephaion/chat/turn_query.py +257 -0
  202. heph-0.0.49/src/hephaion/chat/usage.py +146 -0
  203. heph-0.0.49/src/hephaion/diagnostics/__init__.py +0 -0
  204. heph-0.0.49/src/hephaion/diagnostics/crashes.py +328 -0
  205. heph-0.0.49/src/hephaion/diagnostics/events.py +143 -0
  206. heph-0.0.49/src/hephaion/diagnostics/py.typed +1 -0
  207. heph-0.0.49/src/hephaion/diagnostics/traces.py +102 -0
  208. heph-0.0.49/src/hephaion/learning/__init__.py +28 -0
  209. heph-0.0.49/src/hephaion/learning/actions.py +40 -0
  210. heph-0.0.49/src/hephaion/learning/automation.py +254 -0
  211. heph-0.0.49/src/hephaion/learning/cli.py +196 -0
  212. heph-0.0.49/src/hephaion/learning/constellation.py +231 -0
  213. heph-0.0.49/src/hephaion/learning/environment.py +131 -0
  214. heph-0.0.49/src/hephaion/learning/fixtures/__init__.py +3 -0
  215. heph-0.0.49/src/hephaion/learning/observation.py +392 -0
  216. heph-0.0.49/src/hephaion/learning/observation_audit.py +257 -0
  217. heph-0.0.49/src/hephaion/learning/policy.py +90 -0
  218. heph-0.0.49/src/hephaion/learning/policy_artifact.py +225 -0
  219. heph-0.0.49/src/hephaion/learning/puffer_backend.py +595 -0
  220. heph-0.0.49/src/hephaion/learning/reward.py +430 -0
  221. heph-0.0.49/src/hephaion/learning/storage.py +437 -0
  222. heph-0.0.49/src/hephaion/learning/training.py +726 -0
  223. heph-0.0.49/src/hephaion/matching/__init__.py +103 -0
  224. heph-0.0.49/src/hephaion/matching/py.typed +1 -0
  225. heph-0.0.49/src/hephaion/materials/__init__.py +426 -0
  226. heph-0.0.49/src/hephaion/materials/cli.py +97 -0
  227. heph-0.0.49/src/hephaion/materials/importing.py +168 -0
  228. heph-0.0.49/src/hephaion/materials/py.typed +1 -0
  229. heph-0.0.49/src/hephaion/memory/__init__.py +488 -0
  230. heph-0.0.49/src/hephaion/memory/extract.py +197 -0
  231. heph-0.0.49/src/hephaion/memory/py.typed +1 -0
  232. heph-0.0.49/src/hephaion/memory/workflow.py +48 -0
  233. heph-0.0.49/src/hephaion/parameters/__init__.py +0 -0
  234. heph-0.0.49/src/hephaion/parameters/cli.py +284 -0
  235. heph-0.0.49/src/hephaion/parameters/default.toml +14 -0
  236. heph-0.0.49/src/hephaion/parameters/py.typed +1 -0
  237. heph-0.0.49/src/hephaion/parameters/settings.py +337 -0
  238. heph-0.0.49/src/hephaion/privacy/__init__.py +0 -0
  239. heph-0.0.49/src/hephaion/privacy/consent.py +215 -0
  240. heph-0.0.49/src/hephaion/privacy/py.typed +1 -0
  241. heph-0.0.49/src/hephaion/privacy/release.py +9 -0
  242. heph-0.0.49/src/hephaion/py.typed +1 -0
  243. heph-0.0.49/src/hephaion/rag/__init__.py +111 -0
  244. heph-0.0.49/src/hephaion/rag/chunker.py +1191 -0
  245. heph-0.0.49/src/hephaion/rag/config.py +8 -0
  246. heph-0.0.49/src/hephaion/rag/context.py +263 -0
  247. heph-0.0.49/src/hephaion/rag/health.py +80 -0
  248. heph-0.0.49/src/hephaion/rag/hybrid.py +275 -0
  249. heph-0.0.49/src/hephaion/rag/index.py +1152 -0
  250. heph-0.0.49/src/hephaion/rag/index_state.py +36 -0
  251. heph-0.0.49/src/hephaion/rag/index_timeout.py +220 -0
  252. heph-0.0.49/src/hephaion/rag/optional_backends.py +168 -0
  253. heph-0.0.49/src/hephaion/rag/py.typed +1 -0
  254. heph-0.0.49/src/hephaion/rag/query_audit.py +99 -0
  255. heph-0.0.49/src/hephaion/rag/query_transform.py +398 -0
  256. heph-0.0.49/src/hephaion/rag/retrieval_types.py +69 -0
  257. heph-0.0.49/src/hephaion/rag/retrieve.py +832 -0
  258. heph-0.0.49/src/hephaion/rag/retrieve_compound.py +149 -0
  259. heph-0.0.49/src/hephaion/rag/scoring.py +251 -0
  260. heph-0.0.49/src/hephaion/rag/semantic.py +164 -0
  261. heph-0.0.49/src/hephaion/rag/source_mapping.py +123 -0
  262. heph-0.0.49/src/hephaion/rag/sparse.py +497 -0
  263. heph-0.0.49/src/hephaion/rag/vector.py +69 -0
  264. heph-0.0.49/src/hephaion/safety/__init__.py +36 -0
  265. heph-0.0.49/src/hephaion/safety/contracts.py +78 -0
  266. heph-0.0.49/src/hephaion/safety/local.py +41 -0
  267. heph-0.0.49/src/hephaion/safety/py.typed +1 -0
  268. heph-0.0.49/src/hephaion/study/__init__.py +135 -0
  269. heph-0.0.49/src/hephaion/study/artifacts.py +679 -0
  270. heph-0.0.49/src/hephaion/study/assessment.py +107 -0
  271. heph-0.0.49/src/hephaion/study/controller.py +842 -0
  272. heph-0.0.49/src/hephaion/study/exam.py +387 -0
  273. heph-0.0.49/src/hephaion/study/exam_bank.py +218 -0
  274. heph-0.0.49/src/hephaion/study/intent.py +1 -0
  275. heph-0.0.49/src/hephaion/study/knowledge.py +736 -0
  276. heph-0.0.49/src/hephaion/study/mastery.py +23 -0
  277. heph-0.0.49/src/hephaion/study/policy.py +882 -0
  278. heph-0.0.49/src/hephaion/study/priority.py +823 -0
  279. heph-0.0.49/src/hephaion/study/priority_analysis.py +92 -0
  280. heph-0.0.49/src/hephaion/study/priority_progress.py +77 -0
  281. heph-0.0.49/src/hephaion/study/priority_rendering.py +549 -0
  282. heph-0.0.49/src/hephaion/study/priority_report.py +731 -0
  283. heph-0.0.49/src/hephaion/study/priority_topics.py +24 -0
  284. heph-0.0.49/src/hephaion/study/priority_types.py +172 -0
  285. heph-0.0.49/src/hephaion/study/priority_web.py +116 -0
  286. heph-0.0.49/src/hephaion/study/prompt_plans.py +846 -0
  287. heph-0.0.49/src/hephaion/study/py.typed +1 -0
  288. heph-0.0.49/src/hephaion/study/schedule.py +744 -0
  289. heph-0.0.49/src/hephaion/study/state.py +208 -0
  290. heph-0.0.49/src/hephaion/version/__init__.py +5 -0
  291. heph-0.0.49/src/hephaion/version/py.typed +1 -0
  292. heph-0.0.49/src/hephaion/vocab/__init__.py +18 -0
  293. heph-0.0.49/src/hephaion/vocab/drill.py +261 -0
  294. heph-0.0.49/src/hephaion/vocab/parser.py +209 -0
  295. heph-0.0.49/src/hephaion/vocab/py.typed +1 -0
  296. heph-0.0.49/src/hephaion/vocab/scheduler.py +102 -0
  297. heph-0.0.49/src/hephaion/vocab/state.py +245 -0
  298. heph-0.0.49/src/interfaces/__init__.py +1 -0
  299. heph-0.0.49/src/interfaces/palette/__init__.py +94 -0
  300. heph-0.0.49/src/interfaces/palette/py.typed +1 -0
  301. heph-0.0.49/src/interfaces/py.typed +1 -0
  302. heph-0.0.49/src/interfaces/terminal/__init__.py +255 -0
  303. heph-0.0.49/src/interfaces/terminal/history.py +65 -0
  304. heph-0.0.49/src/interfaces/terminal/input.py +100 -0
  305. heph-0.0.49/src/interfaces/terminal/py.typed +1 -0
  306. heph-0.0.49/src/interfaces/terminal/source_open.py +60 -0
  307. heph-0.0.49/src/interfaces/terminal/theme_state.py +29 -0
  308. heph-0.0.49/src/interfaces/tui/__init__.py +382 -0
  309. heph-0.0.49/src/interfaces/tui/app_actions.py +622 -0
  310. heph-0.0.49/src/interfaces/tui/armory.py +841 -0
  311. heph-0.0.49/src/interfaces/tui/armory_browser.py +377 -0
  312. heph-0.0.49/src/interfaces/tui/auth_flows.py +318 -0
  313. heph-0.0.49/src/interfaces/tui/cell_text.py +39 -0
  314. heph-0.0.49/src/interfaces/tui/command_access.py +54 -0
  315. heph-0.0.49/src/interfaces/tui/command_output.py +63 -0
  316. heph-0.0.49/src/interfaces/tui/composer_controls.py +918 -0
  317. heph-0.0.49/src/interfaces/tui/dependencies.py +21 -0
  318. heph-0.0.49/src/interfaces/tui/display_text.py +558 -0
  319. heph-0.0.49/src/interfaces/tui/external_commands.py +320 -0
  320. heph-0.0.49/src/interfaces/tui/flow_state.py +19 -0
  321. heph-0.0.49/src/interfaces/tui/history.py +54 -0
  322. heph-0.0.49/src/interfaces/tui/ids.py +25 -0
  323. heph-0.0.49/src/interfaces/tui/inline_flows.py +1764 -0
  324. heph-0.0.49/src/interfaces/tui/inline_menu.py +819 -0
  325. heph-0.0.49/src/interfaces/tui/keybinds.py +109 -0
  326. heph-0.0.49/src/interfaces/tui/keyboard_protocol.py +111 -0
  327. heph-0.0.49/src/interfaces/tui/keymap.py +528 -0
  328. heph-0.0.49/src/interfaces/tui/local_flows.py +584 -0
  329. heph-0.0.49/src/interfaces/tui/materials.py +561 -0
  330. heph-0.0.49/src/interfaces/tui/model_flow.py +62 -0
  331. heph-0.0.49/src/interfaces/tui/model_flows.py +156 -0
  332. heph-0.0.49/src/interfaces/tui/option_list_layout.py +64 -0
  333. heph-0.0.49/src/interfaces/tui/py.typed +1 -0
  334. heph-0.0.49/src/interfaces/tui/render_state.py +37 -0
  335. heph-0.0.49/src/interfaces/tui/resize.py +517 -0
  336. heph-0.0.49/src/interfaces/tui/rich_transcript.py +380 -0
  337. heph-0.0.49/src/interfaces/tui/routing.py +128 -0
  338. heph-0.0.49/src/interfaces/tui/search_screen.py +320 -0
  339. heph-0.0.49/src/interfaces/tui/session_actions.py +211 -0
  340. heph-0.0.49/src/interfaces/tui/session_flows.py +381 -0
  341. heph-0.0.49/src/interfaces/tui/session_state.py +90 -0
  342. heph-0.0.49/src/interfaces/tui/shortcut_hints.py +27 -0
  343. heph-0.0.49/src/interfaces/tui/slash_command.py +82 -0
  344. heph-0.0.49/src/interfaces/tui/slash_completion.py +311 -0
  345. heph-0.0.49/src/interfaces/tui/startup_discovery.py +48 -0
  346. heph-0.0.49/src/interfaces/tui/status.py +199 -0
  347. heph-0.0.49/src/interfaces/tui/streaming.py +640 -0
  348. heph-0.0.49/src/interfaces/tui/style.py +446 -0
  349. heph-0.0.49/src/interfaces/tui/textual_compat.py +59 -0
  350. heph-0.0.49/src/interfaces/tui/transcript.py +807 -0
  351. heph-0.0.49/src/interfaces/tui/transparent.py +454 -0
  352. heph-0.0.49/src/interfaces/tui/turns.py +223 -0
  353. heph-0.0.49/src/interfaces/tui/widgets.py +430 -0
heph-0.0.49/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Gil
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
heph-0.0.49/PKG-INFO ADDED
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.4
2
+ Name: heph
3
+ Version: 0.0.49
4
+ Summary: Local-first document workspace for grounded answers, citations, memory, and recall practice with any LLM
5
+ Author-email: Gil <hi@gildrb.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/gildrb/heph
8
+ Project-URL: Repository, https://github.com/gildrb/heph
9
+ Project-URL: Issues, https://github.com/gildrb/heph/issues
10
+ Keywords: cli,documents,llm,rag,retrieval,learning
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Utilities
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.13
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: certifi==2026.2.25
23
+ Requires-Dist: keyring==25.7.0
24
+ Requires-Dist: openai==2.24.0
25
+ Requires-Dist: docling==2.94.0
26
+ Requires-Dist: gymnasium==0.29.1
27
+ Requires-Dist: numpy==1.26.4
28
+ Requires-Dist: pufferlib==3.0.0
29
+ Requires-Dist: torch==2.11.0
30
+ Requires-Dist: unicodeit==0.7.5
31
+ Requires-Dist: rich==14.3.3
32
+ Requires-Dist: textual==8.2.4
33
+ Dynamic: license-file
34
+
35
+ <p align="center">
36
+ <img alt="Hephaion" src="https://gildrb.github.io/heph/logo-auto.svg" width="128">
37
+ </p>
38
+
39
+ # Heph
40
+
41
+ Heph is the agent brain the user talks to.
42
+
43
+ It owns the user-facing agent identity, research/talking orchestration, the
44
+ command entrypoint, slash-command coordination, and the composition needed to
45
+ connect the harness, AI runtime, interfaces, and extension contracts.
46
+
47
+ Heph is not the validation harness. That is Hephaion.
48
+
49
+ ## Source Layout
50
+
51
+ ```text
52
+ src/
53
+ heph/
54
+ cli/ Console entrypoint and top-level command routing
55
+ commands/ Slash-command registry and command coordinators
56
+ sdk/ Programmatic runtime/session surface for native apps and automation
57
+ product/ Temporary bridge for Heph self-knowledge context
58
+ identity/ Stable agent identity target
59
+ prompts/ Prompt-program target for Heph-facing behavior
60
+ state/ Declarative state contract target
61
+ ```
62
+
63
+ The `product/` bridge exists for current self-knowledge routing. It should stay
64
+ thin and should not become a second harness or a place for domain behavior.
65
+
66
+ ## Boundaries
67
+
68
+ Heph is protected as the brain and composition layer. Lower packages must not
69
+ import it, and optional behavior should extend Heph through contracts or
70
+ composition instead of modifying harness or AI internals:
71
+
72
+ - Heph calls Hephaion for grounded answering, validation, citations, retrieval,
73
+ memory, and armory workflows.
74
+ - Heph calls AI for provider/model runtime.
75
+ - Heph calls Interfaces for terminal and TUI presentation.
76
+ - Heph calls Extensions for stable extension contracts.
77
+ - Heph exposes SDK wrappers for non-terminal clients; those wrappers must stay
78
+ UI-neutral and must not import `interfaces.*`.
79
+
80
+ Reusable validation behavior should move to Hephaion. Provider/API behavior
81
+ should move to AI. Conversational strategy, research orchestration, and
82
+ Heph-facing identity should stay here or move here as the migration continues.
83
+
84
+ ## Development
85
+
86
+ ```bash
87
+ uv run pytest --no-cov packages/heph/test
88
+ uv run heph --help
89
+ uv run python -m scripts.check_repo_policies
90
+ uv run lint-imports
91
+ ```
92
+
93
+ ## Related Docs
94
+
95
+ - [Root architecture guide](../../docs/architecture.md)
heph-0.0.49/README.md ADDED
@@ -0,0 +1,61 @@
1
+ <p align="center">
2
+ <img alt="Hephaion" src="https://gildrb.github.io/heph/logo-auto.svg" width="128">
3
+ </p>
4
+
5
+ # Heph
6
+
7
+ Heph is the agent brain the user talks to.
8
+
9
+ It owns the user-facing agent identity, research/talking orchestration, the
10
+ command entrypoint, slash-command coordination, and the composition needed to
11
+ connect the harness, AI runtime, interfaces, and extension contracts.
12
+
13
+ Heph is not the validation harness. That is Hephaion.
14
+
15
+ ## Source Layout
16
+
17
+ ```text
18
+ src/
19
+ heph/
20
+ cli/ Console entrypoint and top-level command routing
21
+ commands/ Slash-command registry and command coordinators
22
+ sdk/ Programmatic runtime/session surface for native apps and automation
23
+ product/ Temporary bridge for Heph self-knowledge context
24
+ identity/ Stable agent identity target
25
+ prompts/ Prompt-program target for Heph-facing behavior
26
+ state/ Declarative state contract target
27
+ ```
28
+
29
+ The `product/` bridge exists for current self-knowledge routing. It should stay
30
+ thin and should not become a second harness or a place for domain behavior.
31
+
32
+ ## Boundaries
33
+
34
+ Heph is protected as the brain and composition layer. Lower packages must not
35
+ import it, and optional behavior should extend Heph through contracts or
36
+ composition instead of modifying harness or AI internals:
37
+
38
+ - Heph calls Hephaion for grounded answering, validation, citations, retrieval,
39
+ memory, and armory workflows.
40
+ - Heph calls AI for provider/model runtime.
41
+ - Heph calls Interfaces for terminal and TUI presentation.
42
+ - Heph calls Extensions for stable extension contracts.
43
+ - Heph exposes SDK wrappers for non-terminal clients; those wrappers must stay
44
+ UI-neutral and must not import `interfaces.*`.
45
+
46
+ Reusable validation behavior should move to Hephaion. Provider/API behavior
47
+ should move to AI. Conversational strategy, research orchestration, and
48
+ Heph-facing identity should stay here or move here as the migration continues.
49
+
50
+ ## Development
51
+
52
+ ```bash
53
+ uv run pytest --no-cov packages/heph/test
54
+ uv run heph --help
55
+ uv run python -m scripts.check_repo_policies
56
+ uv run lint-imports
57
+ ```
58
+
59
+ ## Related Docs
60
+
61
+ - [Root architecture guide](../../docs/architecture.md)
@@ -0,0 +1,65 @@
1
+ [project]
2
+ name = "heph"
3
+ version = "0.0.49"
4
+ description = "Local-first document workspace for grounded answers, citations, memory, and recall practice with any LLM"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ authors = [
8
+ { name = "Gil", email = "hi@gildrb.com" },
9
+ ]
10
+ requires-python = ">=3.13"
11
+ keywords = [
12
+ "cli",
13
+ "documents",
14
+ "llm",
15
+ "rag",
16
+ "retrieval",
17
+ "learning",
18
+ ]
19
+ classifiers = [
20
+ "Development Status :: 3 - Alpha",
21
+ "Environment :: Console",
22
+ "Operating System :: OS Independent",
23
+ "Programming Language :: Python",
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Topic :: Utilities",
27
+ "Typing :: Typed",
28
+ ]
29
+ dependencies = [
30
+ "certifi==2026.2.25",
31
+ "keyring==25.7.0",
32
+ "openai==2.24.0",
33
+ "docling==2.94.0",
34
+ "gymnasium==0.29.1",
35
+ "numpy==1.26.4",
36
+ "pufferlib==3.0.0",
37
+ "torch==2.11.0",
38
+ "unicodeit==0.7.5",
39
+ "rich==14.3.3",
40
+ "textual==8.2.4",
41
+ ]
42
+
43
+ [project.urls]
44
+ Homepage = "https://github.com/gildrb/heph"
45
+ Repository = "https://github.com/gildrb/heph"
46
+ Issues = "https://github.com/gildrb/heph/issues"
47
+
48
+ [project.scripts]
49
+ heph = "heph.cli.main:main"
50
+ hephaion = "heph.cli.main:main"
51
+
52
+ [build-system]
53
+ requires = ["setuptools==81.0.0"]
54
+ build-backend = "setuptools.build_meta"
55
+
56
+ [tool.setuptools]
57
+ package-dir = {"" = "src"}
58
+ include-package-data = true
59
+
60
+ [tool.setuptools.packages.find]
61
+ where = ["src"]
62
+ include = ["ai*", "extensions*", "heph*", "hephaion*", "interfaces*"]
63
+
64
+ [tool.setuptools.package-data]
65
+ "*" = ["*.md", "*.toml", "py.typed"]
heph-0.0.49/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ from __future__ import annotations
@@ -0,0 +1,77 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Self
4
+
5
+
6
+ class NoopSpan:
7
+ __slots__ = ()
8
+
9
+ def set_attribute(self, key: str, value: object) -> object:
10
+ del key, value
11
+ return self
12
+
13
+ def end(self, end_time: float | None = None) -> None:
14
+ del end_time
15
+
16
+ def __enter__(self) -> Self:
17
+ return self
18
+
19
+ def __exit__(self, *args: object) -> None:
20
+ self.end()
21
+
22
+
23
+ class NoopTracer:
24
+ __slots__ = ()
25
+
26
+ def start_span(self, name: str, **kwargs: object) -> NoopSpan:
27
+ del name, kwargs
28
+ return NoopSpan()
29
+
30
+ def start_as_current_span(self, name: str, **kwargs: object) -> NoopSpan:
31
+ del name, kwargs
32
+ return NoopSpan()
33
+
34
+
35
+ class NoopInstrument:
36
+ __slots__ = ()
37
+
38
+ def record(self, value: float, _attributes: dict[str, str] | None = None) -> None:
39
+ del value, _attributes
40
+
41
+ def add(self, value: float, _attributes: dict[str, str] | None = None) -> None:
42
+ del value, _attributes
43
+
44
+ def set(self, value: float, _attributes: dict[str, str] | None = None) -> None:
45
+ del value, _attributes
46
+
47
+
48
+ class NoopMeter:
49
+ __slots__ = ()
50
+
51
+ def create_histogram(self, name: str, **kwargs: object) -> NoopInstrument:
52
+ del name, kwargs
53
+ return NoopInstrument()
54
+
55
+ def create_counter(self, name: str, **kwargs: object) -> NoopInstrument:
56
+ del name, kwargs
57
+ return NoopInstrument()
58
+
59
+ create_up_down_counter = create_counter
60
+
61
+ def create_gauge(self, name: str, **kwargs: object) -> NoopInstrument:
62
+ del name, kwargs
63
+ return NoopInstrument()
64
+
65
+
66
+ _NOOP_TRACER = NoopTracer()
67
+ _NOOP_METER = NoopMeter()
68
+
69
+
70
+ def get_tracer(name: str) -> NoopTracer:
71
+ del name
72
+ return _NOOP_TRACER
73
+
74
+
75
+ def get_meter(name: str) -> NoopMeter:
76
+ del name
77
+ return _NOOP_METER
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,216 @@
1
+ """Structured logging, redaction, and timers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import logging
7
+ import os
8
+ import re as _re
9
+ import sys
10
+ import time
11
+ from collections.abc import Mapping
12
+ from datetime import UTC, datetime
13
+ from pathlib import Path
14
+ from typing import ClassVar, Self
15
+
16
+ from ai.types import is_object_list, is_string_mapping
17
+
18
+ _LOG_TEXT_MUTED = "#6F6F6F"
19
+ _LOG_ACCENT = "#D06A4A"
20
+ _LOG_ERROR = "#FF6B5A"
21
+
22
+
23
+ def _ansi_fg(hex_color: str) -> str:
24
+ color = hex_color.lstrip("#")
25
+ red = int(color[0:2], 16)
26
+ green = int(color[2:4], 16)
27
+ blue = int(color[4:6], 16)
28
+ return f"\033[38;2;{red};{green};{blue}m"
29
+
30
+
31
+ # -- Redaction / scrubbing ---------------------------------------------------
32
+
33
+ # Patterns for dict keys whose values should always be redacted
34
+ _SENSITIVE_KEY_PATTERNS: list[_re.Pattern[str]] = [
35
+ _re.compile(r"(?i)(api.?key|secret|token(?!s)|password|auth(orization|entication))"),
36
+ _re.compile(r"(?i)(bearer|credential|private.?key)"),
37
+ ]
38
+
39
+ # Unanchored versions — find secrets embedded within longer text
40
+ _SENSITIVE_TEXT_PATTERNS: list[_re.Pattern[str]] = [
41
+ _re.compile(r"sk-or-v1-[a-zA-Z0-9\-_]{20,}"), # OpenRouter API keys
42
+ _re.compile(r"sk-proj-[a-zA-Z0-9\-_]{20,}"), # OpenAI project API keys
43
+ _re.compile(r"sk-[a-zA-Z0-9]{20,}"), # OpenAI-style API keys
44
+ _re.compile(r"sk-ant-[a-zA-Z0-9\-]{20,}"), # Provider API keys
45
+ _re.compile(r"\b(?:AKIA|ASIA)[0-9A-Z]{16}\b"), # AWS access keys
46
+ _re.compile(r"AIza[0-9A-Za-z\-_]{35}"), # Google API keys
47
+ _re.compile(r"ya29\.[0-9A-Za-z\-_]+"), # Google OAuth tokens
48
+ _re.compile(r"Bearer\s+\S+", _re.IGNORECASE), # Bearer tokens
49
+ _re.compile(r"\b[a-f0-9]{32,}\b"), # Long hex strings (potential tokens)
50
+ ]
51
+
52
+ _REDACTED = "***REDACTED***"
53
+
54
+
55
+ def redact_text(text: str) -> str:
56
+ if not text:
57
+ return text
58
+ for pattern in _SENSITIVE_TEXT_PATTERNS:
59
+ text = pattern.sub(_REDACTED, text)
60
+ return text
61
+
62
+
63
+ def _redact_dict(data: Mapping[str, object]) -> dict[str, object]:
64
+ return {key: _redact_value(key, value) for key, value in data.items()}
65
+
66
+
67
+ def redact_mapping(data: Mapping[str, object]) -> dict[str, object]:
68
+ return _redact_dict(data)
69
+
70
+
71
+ def _redact_value(key: str, value: object) -> object:
72
+ if any(pattern.search(key) for pattern in _SENSITIVE_KEY_PATTERNS):
73
+ return _REDACTED
74
+ return _redact_unkeyed_value(value)
75
+
76
+
77
+ def _redact_unkeyed_value(value: object) -> object:
78
+ if is_string_mapping(value):
79
+ return _redact_dict(value)
80
+ if is_object_list(value):
81
+ return [_redact_unkeyed_value(item) for item in value]
82
+ if isinstance(value, str):
83
+ return redact_text(value)
84
+ return value
85
+
86
+
87
+ class _JsonFormatter(logging.Formatter):
88
+ def format(self, record: logging.LogRecord) -> str:
89
+ entry: dict[str, object] = {
90
+ "ts": datetime.fromtimestamp(record.created, tz=UTC).isoformat(),
91
+ "level": record.levelname,
92
+ "logger": record.name,
93
+ "msg": record.getMessage(),
94
+ }
95
+ fields = getattr(record, "fields", None)
96
+ if is_string_mapping(fields):
97
+ entry.update(fields)
98
+ if record.exc_info and record.exc_info[1] is not None:
99
+ entry["exc"] = self.formatException(record.exc_info)
100
+ return json.dumps(_redact_dict(entry), default=str, ensure_ascii=False)
101
+
102
+
103
+ class _TextFormatter(logging.Formatter):
104
+ _LEVEL_COLOURS: ClassVar[dict[str, str]] = {
105
+ "DEBUG": _ansi_fg(_LOG_TEXT_MUTED),
106
+ "INFO": _ansi_fg(_LOG_ACCENT),
107
+ "WARNING": _ansi_fg(_LOG_ACCENT),
108
+ "ERROR": _ansi_fg(_LOG_ERROR),
109
+ "CRITICAL": f"\033[1m{_ansi_fg(_LOG_ERROR)}",
110
+ }
111
+ _RESET = "\033[0m"
112
+ _DIM = f"\033[2m{_ansi_fg(_LOG_TEXT_MUTED)}"
113
+
114
+ def format(self, record: logging.LogRecord) -> str:
115
+ ts = datetime.fromtimestamp(record.created, tz=UTC).strftime("%H:%M:%S")
116
+ colour = self._LEVEL_COLOURS.get(record.levelname, "")
117
+ level = f"{colour}{record.levelname:<8}{self._RESET}"
118
+ parts = [f"{self._DIM}{ts}{self._RESET} {level} {record.name}: {record.getMessage()}"]
119
+ parts.extend(self._field_lines(record))
120
+ parts.extend(self._exception_lines(record))
121
+ return "\n".join(parts)
122
+
123
+ def _field_lines(self, record: logging.LogRecord) -> list[str]:
124
+ fields = getattr(record, "fields", None)
125
+ if not is_string_mapping(fields):
126
+ return []
127
+ return [
128
+ f" {self._DIM}{key}={value}{self._RESET}"
129
+ for key, value in _redact_dict(fields).items()
130
+ ]
131
+
132
+ def _exception_lines(self, record: logging.LogRecord) -> list[str]:
133
+ if record.exc_info and record.exc_info[1] is not None:
134
+ return [self.formatException(record.exc_info)]
135
+ return []
136
+
137
+
138
+ LOG_LEVEL_ENV = "HEPHAION_LOG_LEVEL"
139
+ LOG_FILE_ENV = "HEPHAION_LOG_FILE"
140
+ LOG_FORMAT_ENV = "HEPHAION_LOG_FORMAT"
141
+
142
+ _hephaion_logger_initialised = False
143
+
144
+
145
+ def get_logger(name: str) -> logging.Logger:
146
+ _ensure_hephaion_logger()
147
+ if not name.startswith("hephaion"):
148
+ name = f"hephaion.{name}"
149
+ return logging.getLogger(name)
150
+
151
+
152
+ def _ensure_hephaion_logger() -> None:
153
+ global _hephaion_logger_initialised # noqa: PLW0603
154
+ if _hephaion_logger_initialised:
155
+ return
156
+ _hephaion_logger_initialised = True
157
+
158
+ level, fmt = _logging_config()
159
+ logger = logging.getLogger("hephaion")
160
+ logger.setLevel(level)
161
+ logger.propagate = False
162
+ if not logger.handlers:
163
+ logger.addHandler(_stderr_handler(level, fmt))
164
+ if log_file := os.environ.get(LOG_FILE_ENV):
165
+ logger.addHandler(_file_handler(Path(log_file), level))
166
+ _quiet_noisy_loggers()
167
+
168
+
169
+ def _logging_config() -> tuple[int, str]:
170
+ is_tty = sys.stderr.isatty()
171
+ default_level_name = "ERROR" if is_tty else "WARNING"
172
+ default_format = "text" if is_tty else "json"
173
+ level_name = os.environ.get(LOG_LEVEL_ENV, default_level_name).upper()
174
+ return getattr(logging, level_name, logging.WARNING), os.environ.get(
175
+ LOG_FORMAT_ENV,
176
+ default_format,
177
+ ).lower()
178
+
179
+
180
+ def _stderr_handler(level: int, fmt: str) -> logging.Handler:
181
+ handler = logging.StreamHandler(sys.stderr)
182
+ handler.setLevel(level)
183
+ handler.setFormatter(_JsonFormatter() if fmt == "json" else _TextFormatter())
184
+ return handler
185
+
186
+
187
+ def _file_handler(path: Path, level: int) -> logging.Handler:
188
+ path.parent.mkdir(parents=True, exist_ok=True)
189
+ handler = logging.FileHandler(path, encoding="utf-8")
190
+ handler.setLevel(level)
191
+ handler.setFormatter(_JsonFormatter())
192
+ return handler
193
+
194
+
195
+ def _quiet_noisy_loggers() -> None:
196
+ for noisy in ("openai", "httpx", "httpcore", "urllib3", "sentence_transformers"):
197
+ logging.getLogger(noisy).setLevel(logging.WARNING)
198
+
199
+
200
+ class Timer:
201
+ __slots__ = ("_end", "_start")
202
+
203
+ def __init__(self) -> None:
204
+ self._start: float = 0.0
205
+ self._end: float = 0.0
206
+
207
+ def __enter__(self) -> Self:
208
+ self._start = time.perf_counter()
209
+ return self
210
+
211
+ def __exit__(self, *args: object) -> None:
212
+ self._end = time.perf_counter()
213
+
214
+ @property
215
+ def ms(self) -> float:
216
+ return (self._end - self._start) * 1000
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,24 @@
1
+ """Multi-provider LLM configuration."""
2
+
3
+ from ai.providers.catalog import (
4
+ LiveProviderCatalog,
5
+ hydrate_provider_models,
6
+ prefetch_provider_model_catalogs,
7
+ )
8
+ from ai.providers.config import Provider, ProviderConfig, default_config, providers_dir
9
+ from ai.providers.keyring_store import mask_key, resolve_key
10
+ from ai.providers.registry import ModelInfo, get_registry
11
+
12
+ __all__ = [
13
+ "LiveProviderCatalog",
14
+ "ModelInfo",
15
+ "Provider",
16
+ "ProviderConfig",
17
+ "default_config",
18
+ "get_registry",
19
+ "hydrate_provider_models",
20
+ "mask_key",
21
+ "prefetch_provider_model_catalogs",
22
+ "providers_dir",
23
+ "resolve_key",
24
+ ]
@@ -0,0 +1,32 @@
1
+ """Provider access decisions independent of UI adapters."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ai.providers.catalog import hydrate_provider_models
6
+ from ai.providers.config import Provider, ProviderConfig
7
+ from ai.providers.endpoints import provider_uses_keyless_access
8
+ from ai.providers.keyring_store import resolve_key
9
+ from ai.providers.oauth import resolve_oauth_key
10
+
11
+
12
+ def provider_is_accessible(provider: Provider, *, refresh_oauth: bool = True) -> bool:
13
+ if provider.slug == "openai-codex":
14
+ return bool(resolve_oauth_key(provider.slug, refresh_expired=refresh_oauth))
15
+ if provider_uses_keyless_access(provider.slug, provider.endpoint):
16
+ return True
17
+ return bool(
18
+ resolve_key(
19
+ provider.slug,
20
+ provider.api_key_env,
21
+ refresh_oauth=refresh_oauth,
22
+ )
23
+ )
24
+
25
+
26
+ def activate_provider_config(pc: ProviderConfig, slug: str) -> Provider:
27
+ pc.set_active(slug)
28
+ hydrate_provider_models(pc, provider_slugs={slug})
29
+ provider = pc.providers[slug]
30
+ if not provider.current_model and provider.models:
31
+ provider.current_model = provider.models[0]
32
+ return provider