abstractcore 2.32.0__tar.gz → 2.34.0__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 (376) hide show
  1. {abstractcore-2.32.0 → abstractcore-2.34.0}/PKG-INFO +17 -15
  2. {abstractcore-2.32.0 → abstractcore-2.34.0}/README.md +13 -11
  3. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/manager.py +11 -2
  4. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/provider_profiles.py +15 -1
  5. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/route_engines.py +1 -1
  6. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/console/theme_sync.py +27 -3
  7. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/console/themes.py +2 -1
  8. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/pool.py +16 -0
  9. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/embeddings/manager.py +9 -4
  10. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/exceptions/__init__.py +14 -0
  11. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/base.py +80 -3
  12. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/generation_cancel.py +38 -9
  13. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/huggingface_provider.py +189 -21
  14. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/mlx_native_session.py +4 -3
  15. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/mlx_provider.py +17 -5
  16. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/openai_compatible_provider.py +41 -0
  17. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/openrouter_provider.py +2 -0
  18. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/process_residency.py +264 -10
  19. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/typesafe_provider.py +21 -1
  20. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/app.py +76 -26
  21. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/version.py +1 -1
  22. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore.egg-info/PKG-INFO +17 -15
  23. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore.egg-info/SOURCES.txt +2 -0
  24. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore.egg-info/requires.txt +3 -3
  25. {abstractcore-2.32.0 → abstractcore-2.34.0}/pyproject.toml +3 -3
  26. abstractcore-2.34.0/tests/test_changelog_fragments.py +177 -0
  27. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_console_web.py +35 -0
  28. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_integrated_functionality.py +4 -9
  29. abstractcore-2.34.0/tests/test_model_not_downloaded_error_unit.py +85 -0
  30. abstractcore-2.34.0/tests/test_timing_helper.py +36 -0
  31. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_windows_host_fixes.py +2 -1
  32. abstractcore-2.32.0/tests/test_changelog_fragments.py +0 -78
  33. {abstractcore-2.32.0 → abstractcore-2.34.0}/LICENSE +0 -0
  34. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/__init__.py +0 -0
  35. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/apps/__init__.py +0 -0
  36. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/apps/__main__.py +0 -0
  37. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/apps/app_config_utils.py +0 -0
  38. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/apps/deepsearch.py +0 -0
  39. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/apps/extractor.py +0 -0
  40. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/apps/intent.py +0 -0
  41. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/apps/judge.py +0 -0
  42. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/apps/summarizer.py +0 -0
  43. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/architectures/__init__.py +0 -0
  44. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/architectures/detection.py +0 -0
  45. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/architectures/enums.py +0 -0
  46. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/architectures/response_postprocessing.py +0 -0
  47. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/architectures/thinking_controls.py +0 -0
  48. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/assets/OCRA.ttf +0 -0
  49. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/assets/OCRB.ttf +0 -0
  50. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/assets/OCRBL.ttf +0 -0
  51. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/assets/README.md +0 -0
  52. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/assets/abstractframework_identity.json +0 -0
  53. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/assets/adapter_metadata.schema.json +0 -0
  54. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/assets/architecture_formats.json +0 -0
  55. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/assets/bloc-schema.jsonld +0 -0
  56. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/assets/llamacpp_known_architectures.json +0 -0
  57. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/assets/model_capabilities.json +0 -0
  58. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/assets/model_capabilities.schema.json +0 -0
  59. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/assets/model_downloads_catalog.json +0 -0
  60. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/assets/model_downloads_catalog.schema.json +0 -0
  61. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/assets/semantic_models.md +0 -0
  62. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/assets/session_schema.json +0 -0
  63. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/capabilities/__init__.py +0 -0
  64. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/capabilities/errors.py +0 -0
  65. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/capabilities/host.py +0 -0
  66. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/capabilities/music_selectors.py +0 -0
  67. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/capabilities/registry.py +0 -0
  68. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/capabilities/scene3d_selectors.py +0 -0
  69. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/capabilities/types.py +0 -0
  70. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/capabilities/vision_catalog.py +0 -0
  71. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/__init__.py +0 -0
  72. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/email/__init__.py +0 -0
  73. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/email/bodystructure.py +0 -0
  74. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/email/client.py +0 -0
  75. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/email/context.py +0 -0
  76. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/email/discovery.py +0 -0
  77. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/email/errors.py +0 -0
  78. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/email/imap_codec.py +0 -0
  79. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/email/legacy.py +0 -0
  80. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/email/limits.py +0 -0
  81. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/email/models.py +0 -0
  82. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/email/oauth.py +0 -0
  83. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/email/policy.py +0 -0
  84. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/email/search.py +0 -0
  85. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/email/store.py +0 -0
  86. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/comms/email/vault.py +0 -0
  87. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/compression/__init__.py +0 -0
  88. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/compression/analytics.py +0 -0
  89. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/compression/cache.py +0 -0
  90. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/compression/config.py +0 -0
  91. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/compression/exceptions.py +0 -0
  92. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/compression/glyph_processor.py +0 -0
  93. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/compression/optimizer.py +0 -0
  94. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/compression/orchestrator.py +0 -0
  95. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/compression/pil_text_renderer.py +0 -0
  96. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/compression/quality.py +0 -0
  97. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/compression/text_formatter.py +0 -0
  98. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/compression/vision_compressor.py +0 -0
  99. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/__init__.py +0 -0
  100. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/capability_defaults.py +0 -0
  101. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/email_cli.py +0 -0
  102. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/engines.py +0 -0
  103. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/host_jobs.py +0 -0
  104. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/main.py +0 -0
  105. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/model_catalog.py +0 -0
  106. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/model_contexts.py +0 -0
  107. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/model_materializer.py +0 -0
  108. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/model_verify.py +0 -0
  109. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/models_engines_cli.py +0 -0
  110. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/recommendations.py +0 -0
  111. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/config/vision_config.py +0 -0
  112. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/console/__init__.py +0 -0
  113. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/console/web.py +0 -0
  114. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/__init__.py +0 -0
  115. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/bloc_kv.py +0 -0
  116. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/bloc_metadata.py +0 -0
  117. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/cached_session.py +0 -0
  118. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/degeneration.py +0 -0
  119. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/endpoint_damping.py +0 -0
  120. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/enums.py +0 -0
  121. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/factory.py +0 -0
  122. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/file_blocs.py +0 -0
  123. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/file_boxes.py +0 -0
  124. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/generate_contract.py +0 -0
  125. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/interface.py +0 -0
  126. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/multimodal_generation.py +0 -0
  127. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/output_specs.py +0 -0
  128. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/retry.py +0 -0
  129. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/session.py +0 -0
  130. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/core/types.py +0 -0
  131. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/__init__.py +0 -0
  132. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/backends/__init__.py +0 -0
  133. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/backends/base.py +0 -0
  134. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/backends/gguf.py +0 -0
  135. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/backends/hf.py +0 -0
  136. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/backends/logits.py +0 -0
  137. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/backends/mlx.py +0 -0
  138. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/convert.py +0 -0
  139. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/encoding.py +0 -0
  140. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/engines/__init__.py +0 -0
  141. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/engines/base.py +0 -0
  142. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/engines/labels.py +0 -0
  143. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/engines/laya.py +0 -0
  144. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/engines/remote.py +0 -0
  145. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/gguf_compat.py +0 -0
  146. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/head.py +0 -0
  147. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/kinds.py +0 -0
  148. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/loader.py +0 -0
  149. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/render.py +0 -0
  150. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/runtime.py +0 -0
  151. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/tokenizers.py +0 -0
  152. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/decision/types.py +0 -0
  153. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/download.py +0 -0
  154. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/embeddings/__init__.py +0 -0
  155. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/embeddings/models.py +0 -0
  156. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/endpoint/__init__.py +0 -0
  157. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/endpoint/app.py +0 -0
  158. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/events/__init__.py +0 -0
  159. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/mcp/__init__.py +0 -0
  160. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/mcp/client.py +0 -0
  161. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/mcp/factory.py +0 -0
  162. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/mcp/naming.py +0 -0
  163. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/mcp/stdio_client.py +0 -0
  164. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/mcp/tool_source.py +0 -0
  165. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/__init__.py +0 -0
  166. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/auto_handler.py +0 -0
  167. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/base.py +0 -0
  168. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/capabilities.py +0 -0
  169. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/delivery.py +0 -0
  170. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/enrichment.py +0 -0
  171. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/handlers/__init__.py +0 -0
  172. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/handlers/anthropic_handler.py +0 -0
  173. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/handlers/local_handler.py +0 -0
  174. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/handlers/openai_handler.py +0 -0
  175. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/openai_parts.py +0 -0
  176. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/pdf_routing.py +0 -0
  177. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/processors/__init__.py +0 -0
  178. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/processors/audio_processor.py +0 -0
  179. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/processors/direct_pdf_processor.py +0 -0
  180. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/processors/glyph_pdf_processor.py +0 -0
  181. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/processors/image_processor.py +0 -0
  182. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/processors/office_processor.py +0 -0
  183. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/processors/pdf_processor.py +0 -0
  184. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/processors/text_processor.py +0 -0
  185. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/processors/video_processor.py +0 -0
  186. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/types.py +0 -0
  187. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/utils/__init__.py +0 -0
  188. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/utils/image_scaler.py +0 -0
  189. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/utils/video_frames.py +0 -0
  190. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/media/vision_fallback.py +0 -0
  191. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/processing/__init__.py +0 -0
  192. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/processing/basic_deepsearch.py +0 -0
  193. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/processing/basic_extractor.py +0 -0
  194. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/processing/basic_intent.py +0 -0
  195. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/processing/basic_judge.py +0 -0
  196. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/processing/basic_summarizer.py +0 -0
  197. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/__init__.py +0 -0
  198. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/_http.py +0 -0
  199. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/anthropic_provider.py +0 -0
  200. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/capability_host.py +0 -0
  201. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/endpoint_context.py +0 -0
  202. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/generation_progress.py +0 -0
  203. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/gguf_shared.py +0 -0
  204. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/gguf_slots.py +0 -0
  205. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/hf_residency.py +0 -0
  206. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/lmstudio_provider.py +0 -0
  207. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/mlx_hy_v3.py +0 -0
  208. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/mlx_model_rules.py +0 -0
  209. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/mlx_native_cache.py +0 -0
  210. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/mlx_prefill_observer.py +0 -0
  211. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/mlx_qwen4.py +0 -0
  212. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/mlx_residency.py +0 -0
  213. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/mlx_runtime.py +0 -0
  214. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/mlx_vision_addon.py +0 -0
  215. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/model_capabilities.py +0 -0
  216. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/model_config_fingerprint.py +0 -0
  217. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/ollama_provider.py +0 -0
  218. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/openai_provider.py +0 -0
  219. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/portkey_provider.py +0 -0
  220. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/registry.py +0 -0
  221. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/speculation.py +0 -0
  222. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/streaming.py +0 -0
  223. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/tokenizer_fingerprint.py +0 -0
  224. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/vllm_provider.py +0 -0
  225. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/providers/weights_fingerprint.py +0 -0
  226. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/py.typed +0 -0
  227. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/__init__.py +0 -0
  228. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/__main__.py +0 -0
  229. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/audio_endpoints.py +0 -0
  230. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/auth_policy.py +0 -0
  231. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/camera_endpoints.py +0 -0
  232. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/capability_generation.py +0 -0
  233. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/console_routes.py +0 -0
  234. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/credentials.py +0 -0
  235. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/email_routes.py +0 -0
  236. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/first_run.py +0 -0
  237. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/host_routes.py +0 -0
  238. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/managed.py +0 -0
  239. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/scene3d_endpoints.py +0 -0
  240. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/session_routes.py +0 -0
  241. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/server/vision_endpoints.py +0 -0
  242. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/structured/__init__.py +0 -0
  243. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/structured/handler.py +0 -0
  244. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/structured/json_schema.py +0 -0
  245. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/structured/retry.py +0 -0
  246. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/structured/schema_compat.py +0 -0
  247. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/testing/__init__.py +0 -0
  248. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/testing/mailserver.py +0 -0
  249. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/__init__.py +0 -0
  250. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/abstractignore.py +0 -0
  251. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/arg_canonicalizer.py +0 -0
  252. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/arg_coercion.py +0 -0
  253. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/browser_tools.py +0 -0
  254. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/code_analysis.py +0 -0
  255. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/common_tools.py +0 -0
  256. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/comms_tools.py +0 -0
  257. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/core.py +0 -0
  258. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/email_digests.py +0 -0
  259. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/fetch_url_ssrf.py +0 -0
  260. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/handler.py +0 -0
  261. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/inventory.py +0 -0
  262. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/parser.py +0 -0
  263. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/process_tree.py +0 -0
  264. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/registry.py +0 -0
  265. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/risk_facts.py +0 -0
  266. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/sandbox.py +0 -0
  267. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/shell_session.py +0 -0
  268. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/shell_tools.py +0 -0
  269. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/syntax_rewriter.py +0 -0
  270. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/tag_rewriter.py +0 -0
  271. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/telegram_tdlib.py +0 -0
  272. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/telegram_tools.py +0 -0
  273. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/tools/wire_naming.py +0 -0
  274. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/__init__.py +0 -0
  275. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/async_stream.py +0 -0
  276. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/cli.py +0 -0
  277. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/context_calibration.py +0 -0
  278. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/context_estimate.py +0 -0
  279. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/data_registry.py +0 -0
  280. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/display.py +0 -0
  281. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/file_filters.py +0 -0
  282. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/host_profile.py +0 -0
  283. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/hostinfo.py +0 -0
  284. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/identity.py +0 -0
  285. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/install_settings.py +0 -0
  286. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/jsonish.py +0 -0
  287. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/kv_geometry.py +0 -0
  288. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/memory.py +0 -0
  289. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/message_preprocessor.py +0 -0
  290. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/model_cache.py +0 -0
  291. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/model_fit.py +0 -0
  292. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/residency.py +0 -0
  293. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/self_fixes.py +0 -0
  294. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/stdio.py +0 -0
  295. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/structured_logging.py +0 -0
  296. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/token_utils.py +0 -0
  297. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/trace_export.py +0 -0
  298. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/truncation.py +0 -0
  299. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/vlm_token_calculator.py +0 -0
  300. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/windows_dll.py +0 -0
  301. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore/utils/workspace_paths.py +0 -0
  302. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore.egg-info/dependency_links.txt +0 -0
  303. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore.egg-info/entry_points.txt +0 -0
  304. {abstractcore-2.32.0 → abstractcore-2.34.0}/abstractcore.egg-info/top_level.txt +0 -0
  305. {abstractcore-2.32.0 → abstractcore-2.34.0}/setup.cfg +0 -0
  306. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_abstractendpoint_singleton_provider.py +0 -0
  307. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_agentic_cli_compatibility.py +0 -0
  308. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_basic_session.py +0 -0
  309. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_bloc_kv.py +0 -0
  310. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_bloc_kv_cache_dtype.py +0 -0
  311. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_bloc_kv_endpoint.py +0 -0
  312. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_bloc_kv_engine_fingerprint.py +0 -0
  313. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_bloc_kv_model_config_fingerprint.py +0 -0
  314. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_bloc_kv_tokenizer_fingerprint.py +0 -0
  315. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_bloc_kv_weights_fingerprint.py +0 -0
  316. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_bloc_metadata_json_extract.py +0 -0
  317. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_cached_session_kv_mode.py +0 -0
  318. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_cached_session_kv_thinking_prefix.py +0 -0
  319. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_capabilities_registry.py +0 -0
  320. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_capabilities_registry_preferred_backends_from_config.py +0 -0
  321. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_complete_integration.py +0 -0
  322. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_comprehensive_events.py +0 -0
  323. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_console_about.py +0 -0
  324. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_console_email_tab.py +0 -0
  325. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_core_components.py +0 -0
  326. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_display_rule.py +0 -0
  327. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_email_digests.py +0 -0
  328. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_embedding_model_shared_unit.py +0 -0
  329. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_embedding_model_token_warnings.py +0 -0
  330. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_enhanced_prompt.py +0 -0
  331. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_environment_variable_tool_call_tags.py +0 -0
  332. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_factory.py +0 -0
  333. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_file_bloc_store_unit.py +0 -0
  334. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_file_boxes_cached_session.py +0 -0
  335. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_final_accuracy.py +0 -0
  336. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_final_comprehensive.py +0 -0
  337. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_final_graceful_errors.py +0 -0
  338. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_generate_contract.py +0 -0
  339. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_generate_response_reasoning_property_unit.py +0 -0
  340. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_generate_with_outputs.py +0 -0
  341. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_graceful_fallback.py +0 -0
  342. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_import_debug.py +0 -0
  343. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_import_safety.py +0 -0
  344. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_incremental_thinking_tag_stripper.py +0 -0
  345. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_install_hints_no_bare_packages.py +0 -0
  346. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_install_settings.py +0 -0
  347. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_llms_sources.py +0 -0
  348. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_mcp_handshake.py +0 -0
  349. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_mcp_integration.py +0 -0
  350. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_mcp_stdio_client.py +0 -0
  351. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_multimodal_generate_output.py +0 -0
  352. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_openai_prompt_cache_key_passthrough.py +0 -0
  353. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_output_specs.py +0 -0
  354. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_packaging_assets.py +0 -0
  355. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_packaging_extras.py +0 -0
  356. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_packaging_packages_listed.py +0 -0
  357. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_prompt_cache_api.py +0 -0
  358. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_prompt_cache_bloc_composition.py +0 -0
  359. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_prompt_cache_bloc_e2e_gate.py +0 -0
  360. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_prompt_cache_control_plane.py +0 -0
  361. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_prompt_cache_prepare_modules_thinking_http.py +0 -0
  362. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_retry_observability.py +0 -0
  363. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_retry_strategy.py +0 -0
  364. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_seed_determinism.py +0 -0
  365. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_seed_temperature_basic.py +0 -0
  366. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_sensory_prompting.py +0 -0
  367. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_server_prompt_cache_control_plane_proxy.py +0 -0
  368. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_server_prompt_cache_key_passthrough.py +0 -0
  369. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_session_reasoning_persistence_unit.py +0 -0
  370. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_sound_effect_seconds_route.py +0 -0
  371. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_sse_starlette_pin_unit.py +0 -0
  372. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_structured_output_truncation_retry.py +0 -0
  373. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_text_only_model_experience.py +0 -0
  374. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_unload_memory.py +0 -0
  375. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_user_scenario_validation.py +0 -0
  376. {abstractcore-2.32.0 → abstractcore-2.34.0}/tests/test_voice_host_credentials.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractcore
3
- Version: 2.32.0
3
+ Version: 2.34.0
4
4
  Summary: Unified interface to all LLM providers with essential infrastructure for tool calling, streaming, and model management
5
5
  Author-email: Laurent-Philippe Albou <contact@abstractcore.ai>
6
6
  Maintainer-email: Laurent-Philippe Albou <contact@abstractcore.ai>
@@ -49,7 +49,7 @@ Requires-Dist: uvicorn[standard]<1.0.0,>=0.23.0
49
49
  Requires-Dist: python-multipart<1.0.0,>=0.0.6
50
50
  Requires-Dist: sse-starlette<4,>=1.8
51
51
  Requires-Dist: cryptography>=41.0.0
52
- Requires-Dist: abstractvoice>=0.19.5
52
+ Requires-Dist: abstractvoice>=0.20.0
53
53
  Requires-Dist: abstractvision>=0.3.34
54
54
  Requires-Dist: abstractmusic>=0.1.17; python_version >= "3.10"
55
55
  Requires-Dist: abstract3d>=0.3.2; python_version >= "3.10"
@@ -69,7 +69,7 @@ Requires-Dist: accelerate>=1.0.0; extra == "apple"
69
69
  Requires-Dist: sentence-transformers<6.0.0,>=5.1.0; extra == "apple"
70
70
  Requires-Dist: numpy<3.0.0,>=2.1.0; extra == "apple"
71
71
  Requires-Dist: Pillow<13.0.0,>=12.1.1; extra == "apple"
72
- Requires-Dist: abstractvoice[all-apple]>=0.19.5; extra == "apple"
72
+ Requires-Dist: abstractvoice[all-apple]>=0.20.0; extra == "apple"
73
73
  Requires-Dist: omnivoice>=0.1.5; extra == "apple"
74
74
  Requires-Dist: abstractvision[all-apple]>=0.3.34; extra == "apple"
75
75
  Requires-Dist: abstractmusic[all-apple]>=0.1.17; extra == "apple"
@@ -87,7 +87,7 @@ Requires-Dist: gguf<1.0.0,>=0.10.0; extra == "gpu"
87
87
  Requires-Dist: sentence-transformers<6.0.0,>=5.1.0; extra == "gpu"
88
88
  Requires-Dist: numpy<3.0.0,>=1.20.0; python_version < "3.13" and extra == "gpu"
89
89
  Requires-Dist: numpy<3.0.0,>=2.1.0; python_version >= "3.13" and extra == "gpu"
90
- Requires-Dist: abstractvoice[all-gpu]>=0.19.5; extra == "gpu"
90
+ Requires-Dist: abstractvoice[all-gpu]>=0.20.0; extra == "gpu"
91
91
  Requires-Dist: omnivoice>=0.1.5; extra == "gpu"
92
92
  Requires-Dist: abstractvision[all-gpu]>=0.3.34; extra == "gpu"
93
93
  Requires-Dist: abstractmusic[all-gpu]>=0.1.17; extra == "gpu"
@@ -196,17 +196,19 @@ Dynamic: license-file
196
196
  Unified LLM Interface
197
197
  > Write once, run everywhere
198
198
 
199
- What's new in 2.32.0: decision models, text in and calibrated typed decisions out
200
- (`generate(document, output="decision", questions=...)`, `POST /v1/decisions`) over hosted Jev, any
201
- System One-compatible endpoint and local Kev / Lev / Laya checkpoints; one llama.cpp weights instance
202
- per GGUF with a per-model slot pool for concurrent requests; the reserved context computed from free
203
- memory and settable per model; and every cached asset (adapters included) listed in the catalogue.
204
- See the [changelog](CHANGELOG.md#2320---2026-11-02).
205
-
206
- What's new in 2.31.1: `/v1/chat/completions` reuses a model already resident in the process (a host's
207
- runtime pool) instead of loading a second llama.cpp copy per request; the download catalog lists the
208
- LFM2 and LFM2.5 families (GGUF and MLX, the 24B-A2B models included). See the
209
- [changelog](CHANGELOG.md#2311---2026-10-28).
199
+ What's new in 2.34.0: a local model that no call has used for 60 minutes is unloaded from memory and loads
200
+ again on its next call; a model kept loaded (Keep loaded) never is, and no running call is interrupted
201
+ (`process_residency.set_idle_unload(seconds)`, `0` turns it off). A provider key you send to the server's discovery
202
+ routes now reaches only the provider you name, never every provider those routes check. The web console's themes
203
+ match ui-kit 0.9.14. See the [changelog](CHANGELOG.md#2340---2026-11-03).
204
+
205
+ What's new in 2.33.0: GGUF prompt-cache snapshots drop an unused 0.5 GB copy each, stay under a byte budget
206
+ (`gguf_prompt_cache_max_gb`), and are never evicted under a prefill that is reading them; a model that is not
207
+ downloaded raises `ModelNotDownloadedError` naming it; an OpenAI-compatible key check refuses a wrong key even where
208
+ the model list is public (OVH); hosted decision models (`typesafe`, `systemone`) are connection families, listed and
209
+ routed as decision models, and a local decision model answers its own decisions; a provider profile this version
210
+ cannot read is kept and reported instead of resetting the whole config. See the
211
+ [changelog](CHANGELOG.md#2330---2026-11-03).
210
212
 
211
213
  AbstractCore is an offline-capable, open-source-first LLM infrastructure layer
212
214
  for Python applications. It gives you one `create_llm(...)` API across local
@@ -9,17 +9,19 @@
9
9
  Unified LLM Interface
10
10
  > Write once, run everywhere
11
11
 
12
- What's new in 2.32.0: decision models, text in and calibrated typed decisions out
13
- (`generate(document, output="decision", questions=...)`, `POST /v1/decisions`) over hosted Jev, any
14
- System One-compatible endpoint and local Kev / Lev / Laya checkpoints; one llama.cpp weights instance
15
- per GGUF with a per-model slot pool for concurrent requests; the reserved context computed from free
16
- memory and settable per model; and every cached asset (adapters included) listed in the catalogue.
17
- See the [changelog](CHANGELOG.md#2320---2026-11-02).
18
-
19
- What's new in 2.31.1: `/v1/chat/completions` reuses a model already resident in the process (a host's
20
- runtime pool) instead of loading a second llama.cpp copy per request; the download catalog lists the
21
- LFM2 and LFM2.5 families (GGUF and MLX, the 24B-A2B models included). See the
22
- [changelog](CHANGELOG.md#2311---2026-10-28).
12
+ What's new in 2.34.0: a local model that no call has used for 60 minutes is unloaded from memory and loads
13
+ again on its next call; a model kept loaded (Keep loaded) never is, and no running call is interrupted
14
+ (`process_residency.set_idle_unload(seconds)`, `0` turns it off). A provider key you send to the server's discovery
15
+ routes now reaches only the provider you name, never every provider those routes check. The web console's themes
16
+ match ui-kit 0.9.14. See the [changelog](CHANGELOG.md#2340---2026-11-03).
17
+
18
+ What's new in 2.33.0: GGUF prompt-cache snapshots drop an unused 0.5 GB copy each, stay under a byte budget
19
+ (`gguf_prompt_cache_max_gb`), and are never evicted under a prefill that is reading them; a model that is not
20
+ downloaded raises `ModelNotDownloadedError` naming it; an OpenAI-compatible key check refuses a wrong key even where
21
+ the model list is public (OVH); hosted decision models (`typesafe`, `systemone`) are connection families, listed and
22
+ routed as decision models, and a local decision model answers its own decisions; a provider profile this version
23
+ cannot read is kept and reported instead of resetting the whole config. See the
24
+ [changelog](CHANGELOG.md#2330---2026-11-03).
23
25
 
24
26
  AbstractCore is an offline-capable, open-source-first LLM infrastructure layer
25
27
  for Python applications. It gives you one `create_llm(...)` API across local
@@ -36,6 +36,7 @@ from .provider_profiles import (
36
36
  normalize_provider_family,
37
37
  normalize_string_list,
38
38
  profile_id_from_virtual_provider,
39
+ provider_profile_from_dict,
39
40
  provider_profiles_from_dict,
40
41
  split_api_key_value,
41
42
  )
@@ -1289,8 +1290,10 @@ class ConfigurationManager:
1289
1290
  - `capability_defaults` keys other than version/routes/seeded, and
1290
1291
  route keys this version cannot parse (a future modality); a route
1291
1292
  row's unknown fields already survive as `options`;
1292
- - `provider_profiles` keys other than `profiles`, and unknown columns
1293
- of a profile row that is still present (a deleted row stays deleted).
1293
+ - `provider_profiles` keys other than `profiles`, profile rows this
1294
+ version cannot load (a provider family it does not know), and
1295
+ unknown columns of a profile row that is still present (a deleted
1296
+ row stays deleted).
1294
1297
 
1295
1298
  Known keys are never carried: a known row or field absent from `mine`
1296
1299
  is a deliberate delete (`clear-default`, `delete-provider`) or a value
@@ -1345,6 +1348,12 @@ class ConfigurationManager:
1345
1348
  if isinstance(mine_rows, dict):
1346
1349
  known_columns = {f.name for f in fields(ProviderProfile)}
1347
1350
  for profile_id, raw_row in raw_profiles["profiles"].items():
1351
+ if profile_id not in mine_rows:
1352
+ try:
1353
+ provider_profile_from_dict(str(profile_id), raw_row)
1354
+ except (ValueError, TypeError):
1355
+ mine_rows[profile_id] = copy.deepcopy(raw_row) # not loaded: kept as stored
1356
+ continue
1348
1357
  mine_row = mine_rows.get(profile_id)
1349
1358
  if not isinstance(raw_row, dict) or not isinstance(mine_row, dict):
1350
1359
  continue
@@ -276,6 +276,20 @@ def provider_profiles_from_dict(data: Any) -> ProviderProfilesConfig:
276
276
 
277
277
  profiles: Dict[str, ProviderProfile] = {}
278
278
  for raw_id, raw_profile in rows.items():
279
- profile = provider_profile_from_dict(str(raw_id), raw_profile)
279
+ try:
280
+ profile = provider_profile_from_dict(str(raw_id), raw_profile)
281
+ except (ValueError, TypeError) as exc:
282
+ # A row this Core cannot load (a provider family it does not know, written by a
283
+ # newer Core, 2026-11-03: `typesafe` read by 2.31) is skipped and said, never fatal:
284
+ # raising here made the whole config read as corrupt (a `.corrupt-*.bak` copy and
285
+ # DEFAULTS for the session). The save keeps the row as stored
286
+ # (`ConfigurationManager._carry_unknown_store_keys`).
287
+ import logging
288
+
289
+ logging.getLogger(__name__).warning(
290
+ "provider profile %r is not loaded by this AbstractCore (%s); it stays in the config file unchanged",
291
+ str(raw_id), exc,
292
+ )
293
+ continue
280
294
  profiles[profile.id.lower()] = profile
281
295
  return ProviderProfilesConfig(profiles=profiles)
@@ -63,7 +63,7 @@ __all__ = [
63
63
  # AbstractCore's AbstractVoice floor (it has `abstractvoice.engine_runtime`, the
64
64
  # public runtime probe, since 0.15.0). Mirrors the abstractvoice pins in
65
65
  # pyproject.toml; the release stager raises both together.
66
- ABSTRACTVOICE_ENGINE_RUNTIME_FLOOR = "0.19.5"
66
+ ABSTRACTVOICE_ENGINE_RUNTIME_FLOOR = "0.20.0"
67
67
 
68
68
  # Voice providers that run remotely: AbstractVoice needs nothing beyond its
69
69
  # core install for them, and whether they are configured is not a runtime
@@ -26,6 +26,24 @@ from typing import Any, Dict, List, Optional, Sequence, Tuple
26
26
 
27
27
  GENERATED_MODULE = "themes.py"
28
28
 
29
+ #: The ui-kit release the console's theme copy must come from at least (backlog 0869).
30
+ #: `themes.py` records the kit version it was generated from; a test compares the two
31
+ #: without the abstractuic checkout, so a stale copy fails CI everywhere. Raise this
32
+ #: with every re-sync.
33
+ KIT_THEME_FLOOR = "0.9.14"
34
+
35
+
36
+ def kit_version(kit_src: Path) -> str:
37
+ """The ui-kit version beside ``ui-kit/src`` (``ui-kit/package.json``); refuses to guess."""
38
+ package = kit_src.parent / "package.json"
39
+ try:
40
+ version = str(json.loads(package.read_text(encoding="utf-8"))["version"]).strip()
41
+ except (OSError, ValueError, KeyError) as e:
42
+ raise ValueError(f"cannot read the ui-kit version from {package}: {e}") from e
43
+ if not version:
44
+ raise ValueError(f"{package} has an empty version")
45
+ return version
46
+
29
47
  _SPEC_RE = re.compile(
30
48
  r"\{\s*id:\s*\"(?P<id>[a-z0-9-]+)\",\s*label:\s*\"(?P<label>[^\"]+)\",\s*"
31
49
  r"group:\s*\"(?P<group>dark|light)\",\s*swatches:\s*\[(?P<swatches>[^\]]*)\]",
@@ -102,14 +120,19 @@ def parse_theme_css_blocks(theme_css_text: str) -> List[Tuple[str, str]]:
102
120
  return blocks
103
121
 
104
122
 
123
+ # `:root` alone or with attribute qualifiers (`:root[data-density="dense"]`): a token rule.
124
+ # `:root .af-session-card` (a descendant) or `:root.theme-x` is a component or theme rule.
125
+ _TOKEN_ROOT_RE = re.compile(r":root(\[[^\]]*\])*")
126
+
127
+
105
128
  def _is_token_block(selector: str, body: str) -> bool:
106
- """A responsive-token block: ``html {...}``, a non-theme ``:root...{...}``,
129
+ """A responsive-token block: ``html {...}``, a bare ``:root`` (or ``:root[attr]``) ``{...}``,
107
130
  or an ``@media``/``@supports`` block holding only such ``:root`` rules."""
108
- if selector == "html" or (selector.startswith(":root") and ".theme-" not in selector):
131
+ if selector == "html" or _TOKEN_ROOT_RE.fullmatch(selector):
109
132
  return "{" not in body
110
133
  if selector.startswith(("@media", "@supports")):
111
134
  inner = [sel.strip() for sel in re.findall(r"([^{}]+)\{", body)]
112
- return bool(inner) and all(sel.startswith(":root") and ".theme-" not in sel for sel in inner)
135
+ return bool(inner) and all(_TOKEN_ROOT_RE.fullmatch(sel) for sel in inner)
113
136
  return False
114
137
 
115
138
 
@@ -240,6 +263,7 @@ def generate_themes_module(kit_src: Path) -> str:
240
263
  light_ids = [s["id"] for s in specs if s["group"] == "light"]
241
264
  css_text = "\n\n".join(block for _, block in blocks)
242
265
  provenance = {
266
+ "kit_version": kit_version(kit_src),
243
267
  "theme_ts_sha256": hashlib.sha256(theme_ts.encode("utf-8")).hexdigest(),
244
268
  "theme_css_sha256": hashlib.sha256(theme_css.encode("utf-8")).hexdigest(),
245
269
  }
@@ -289,8 +289,9 @@ KIT_THEME_SPECS = [
289
289
  KIT_LIGHT_THEME_IDS = ["catppuccin-latte", "rose-pine-dawn", "one-light", "everforest-light", "solarized-light", "light"]
290
290
 
291
291
  KIT_SOURCE_PROVENANCE = {
292
+ "kit_version": "0.9.14",
292
293
  "theme_ts_sha256": "5ae99c5b4c018983702cdbef688428ebe36972136d20d9269fdd5cf4d5ec48cc",
293
- "theme_css_sha256": "2f1d216d5c21126972058dd60686a9a098623bd9b59188cc95cbcee5cdcd8444"
294
+ "theme_css_sha256": "83fe3a16b95177d39f7d5394c1d62fe127960b843d53c5deae143fa86a5745f7"
294
295
  }
295
296
 
296
297
  KIT_ROOT_CSS = ':root {\n color-scheme: dark;\n\n /* Colors */\n --bg-primary: #1a1a2e;\n --bg-secondary: #16213e;\n --bg-tertiary: #0f3460;\n --text-primary: #eee;\n --text-secondary: #aaa;\n --text-muted: #666;\n --accent: #e94560;\n --success: #27ae60;\n --warning: #f39c12;\n --error: #e74c3c;\n --info: #60a5fa;\n\n /* Fonts (no remote imports; prefer system fonts) */\n --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;\n --font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;\n\n /* Typography / density (controlled by thin-client Settings; avoid per-app drift) */\n --font-scale: 1;\n --header-density: 1;\n\n --font-size-xxs: calc(10px * var(--font-scale));\n --font-size-xs: calc(11px * var(--font-scale));\n --font-size-sm: calc(12px * var(--font-scale));\n --font-size-md: calc(13px * var(--font-scale));\n --font-size-base: calc(14px * var(--font-scale));\n /* Headings are FLUID (responsive workstream 2026-09-30): they reach their\n * desktop size at ~1000px wide and shrink slightly below, so a phone is\n * never bigger than a laptop. Sizes up to lg stay fixed px (legibility\n * floor; apps use lg as their 16px touch-input size, so it must not dip).\n * Everything still multiplies by the user\'s --font-scale. */\n --font-size-lg: calc(16px * var(--font-scale));\n --font-size-xl: calc(clamp(16px, 14px + 0.4vw, 18px) * var(--font-scale));\n --font-size-2xl: calc(clamp(18px, 15px + 0.7vw, 22px) * var(--font-scale));\n /* Role sizes: reading text, form fields, monospace. Coarse pointers raise\n * their floors (see the Responsive tokens block). */\n --font-size-body: var(--font-size-base);\n --font-size-input: var(--font-size-md);\n --font-size-code: var(--font-size-sm);\n /* Select arrow drawn on touch, where native select appearance is off\n * (see the RESPONSIVE LAYER). In the base token block so consoles that\n * sync only :root tokens receive it by name; mid-grey reads on light and\n * dark themes alike, so no per-theme variant. */\n --af-select-chevron: url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'12\' height=\'8\' viewBox=\'0 0 12 8\'%3E%3Cpath d=\'M1 1.5l5 5 5-5\' fill=\'none\' stroke=\'%238a93a3\' stroke-width=\'1.6\' stroke-linecap=\'round\' stroke-linejoin=\'round\'/%3E%3C/svg%3E");\n\n --line-height-base: 1.5;\n\n /* Sizes */\n --header-height: calc(48px * var(--header-density));\n --footer-height: calc(32px * var(--header-density));\n --header-pad-x: 16px;\n --header-pad-y: calc(8px * var(--header-density));\n\n /* Radius */\n --radius-sm: 4px;\n --radius-md: 8px;\n --radius-lg: 10px;\n\n /* General UI surfaces */\n --ui-surface-1: rgba(0, 0, 0, 0.16);\n --ui-surface-2: rgba(255, 255, 255, 0.06);\n --ui-surface-3: rgba(0, 0, 0, 0.25);\n --ui-border-1: rgba(255, 255, 255, 0.10);\n --ui-border-2: rgba(255, 255, 255, 0.14);\n --ui-shadow-1: 0 10px 30px rgba(0, 0, 0, 0.35);\n\n /* Entity-semantic tokens (one visual language for summoned-entity surfaces\n * across the entity app, gateway console, and flow — c594 contract).\n * Dark values are the shipped truth operators learned in the Observer\n * entity view (abstractobserver/src/entity/entity.css); the light-theme\n * variants live in the grouped light block below. Named for MEANING, not\n * color — values may tune per theme, the vocabulary is the contract.\n * Consumption rule: these are MARK colors (node rings, lanes, diamonds,\n * chrome); as text they clear 4.5:1 only on primary/secondary surfaces\n * (scar reads below AA as text on several dark themes) — entity-colored\n * labels pair the mark with --text-primary text, and identity/bond/accent\n * are one warm family: never color-alone coding between them. */\n /* Code/JSON syntax tokens (dark set = the VSCode-dark palette the shared\n * JSON viewers shipped as literals; promoted to tokens 2026-07-11 so light\n * themes can override — the literals stay as fallbacks in component CSS\n * for consumers loading components without theme.css). */\n --syntax-key: #9cdcfe;\n --syntax-string: #ce9178;\n --syntax-number: #b5cea8;\n --syntax-keyword: #569cd6;\n\n --entity-identity: #e7b45a;\n --entity-memory: #6ea8d8;\n --entity-diary: #7bc98c;\n --entity-standing: #c084dd;\n --entity-scar: #e05555;\n --entity-bond: #f0c060;\n --entity-accent: #e8a54a;\n\n /* Account-kind row tints (.af-row--admin|user|entity, .af-kind-chip--*):\n * faint washes over whatever surface the row sits on. Users stay neutral,\n * admin takes the theme accent, entities the entity-standing violet. Text\n * on a tinted row keeps >= 4.5:1 in every theme (check_modal_rows.mjs\n * computes it). The light-theme strengths live in the grouped light block. */\n --af-row-tint-admin: color-mix(in srgb, var(--accent) 7%, transparent);\n --af-row-tint-user: transparent;\n --af-row-tint-entity: color-mix(in srgb, var(--entity-standing) 8%, transparent);\n /* Text on a tinted row: the theme\'s primary, and a secondary nudged toward\n * the foreground so it keeps 4.5:1 over the wash (.af-row__muted). */\n --af-row-text: var(--text-primary);\n --af-row-text-muted: color-mix(in srgb, var(--text-secondary) 65%, #ffffff);\n --af-row-mark-admin: var(--accent);\n --af-row-mark-user: var(--ui-border-2);\n --af-row-mark-entity: var(--entity-standing);\n\n /* Semantic tints (hue-matched to the base colors above — subtle/border\n * variants must never drift to a different hue family than their base) */\n --accent-subtle: rgba(233, 69, 96, 0.12);\n --info-subtle: rgba(96, 165, 250, 0.12);\n --success-subtle: rgba(39, 174, 96, 0.12);\n --warning-subtle: rgba(243, 156, 18, 0.12);\n --error-subtle: rgba(231, 76, 60, 0.12);\n\n /* Semantic borders (used for selection + pills) */\n --accent-border: rgba(233, 69, 96, 0.35);\n --info-border: rgba(96, 165, 250, 0.35);\n --success-border: rgba(39, 174, 96, 0.35);\n --warning-border: rgba(243, 156, 18, 0.35);\n --error-border: rgba(231, 76, 60, 0.35);\n --muted-border: rgba(148, 163, 184, 0.22);\n\n /* Chips / badges */\n --ui-chip-bg: rgba(0, 0, 0, 0.22);\n --ui-chip-border: rgba(255, 255, 255, 0.12);\n --ui-chip-text: rgba(255, 255, 255, 0.92);\n\n /* Small overlay surfaces (tabs, pills) */\n --ui-overlay-bg: rgba(0, 0, 0, 0.18);\n --ui-overlay-bg-hover: rgba(255, 255, 255, 0.08);\n --ui-overlay-border: rgba(255, 255, 255, 0.14);\n --ui-pill-bg: rgba(0, 0, 0, 0.22);\n --ui-pill-border: rgba(255, 255, 255, 0.12);\n\n /* Markdown/code surfaces */\n --ui-code-bg: rgba(0, 0, 0, 0.18);\n --ui-code-border: rgba(255, 255, 255, 0.08);\n --ui-code-block-bg: rgba(0, 0, 0, 0.25);\n --ui-code-block-border: rgba(255, 255, 255, 0.08);\n\n /* Compatibility aliases used by multiple thin clients */\n --bg: var(--bg-primary);\n --bg-surface: var(--bg-secondary);\n --bg-elevated: var(--bg-tertiary);\n --bg-card: var(--bg-secondary);\n --panel: var(--bg-secondary);\n --panel2: var(--bg-tertiary);\n --text: var(--text-primary);\n --muted: var(--text-secondary);\n --danger: var(--error);\n --ok: var(--success);\n\n /* Strokes */\n --border: var(--bg-tertiary);\n --border-default: rgba(255, 255, 255, 0.12);\n --border-accent: rgba(233, 69, 96, 0.55);\n}'
@@ -37,6 +37,22 @@ _LOAD_LOCKS: Dict[Key, threading.Lock] = {}
37
37
  _LOCK = threading.Lock()
38
38
 
39
39
 
40
+ def serves_decisions_only(provider: Optional[str]) -> bool:
41
+ """Whether a provider serves decision models only (``typesafe``, ``systemone`` and endpoint
42
+ profiles of those families). Its model names (``jev-latest``) are the endpoint's own: never
43
+ one of the ``ALIASES`` of a resident model, and always decision models."""
44
+ name = str(provider or "").strip().lower()
45
+ if not name:
46
+ return False
47
+ try:
48
+ from ..providers.registry import get_provider_info
49
+
50
+ info = get_provider_info(name)
51
+ except Exception:
52
+ return False
53
+ return info is not None and list(info.supported_features or []) == ["decision"]
54
+
55
+
40
56
  def split_handle(handle: str) -> Tuple[Optional[str], str]:
41
57
  """``(provider, model)`` of a decision handle: the provider when the handle starts with
42
58
  one (``mlx/jaredpalmer/kev-4b``), else ``None`` and the handle whole."""
@@ -936,6 +936,8 @@ class EmbeddingManager:
936
936
  self._load_lock = threading.RLock()
937
937
  self._model_cv = threading.Condition(self._load_lock)
938
938
  self._inflight_encodes = 0
939
+ # When an encode last began or ended on this manager: the idle unload's clock (ADR 0012).
940
+ self._last_used_at: Optional[float] = None
939
941
  if self.provider == "huggingface":
940
942
  self._load_model()
941
943
 
@@ -1287,11 +1289,13 @@ class EmbeddingManager:
1287
1289
  with self._model_cv:
1288
1290
  model = self._ensure_local_model()
1289
1291
  self._inflight_encodes += 1
1292
+ self._last_used_at = time.time()
1290
1293
  try:
1291
1294
  yield model
1292
1295
  finally:
1293
1296
  with self._model_cv:
1294
1297
  self._inflight_encodes -= 1
1298
+ self._last_used_at = time.time()
1295
1299
  self._model_cv.notify_all()
1296
1300
 
1297
1301
  def _sentence_transformers_source(self) -> "tuple[str, Dict[str, Any]]":
@@ -1304,7 +1308,7 @@ class EmbeddingManager:
1304
1308
  so nothing asks the Hub -- not even for a `refs/main` a pinned download
1305
1309
  may lack. A bare legacy name (`all-MiniLM-L6-v2`) is also looked up as
1306
1310
  `sentence-transformers/<name>`, as sentence-transformers itself does.
1307
- Not cached -> ModelNotFoundError with the plain "download it first".
1311
+ Not cached -> ModelNotDownloadedError (a ModelNotFoundError) with the plain "download it first".
1308
1312
  Otherwise the id is passed through and may download on first use.
1309
1313
  """
1310
1314
  try:
@@ -1334,13 +1338,14 @@ class EmbeddingManager:
1334
1338
  return str(snapshot), {"local_files_only": True}
1335
1339
 
1336
1340
  from ..config.manager import hf_download_first_hint
1337
- from ..exceptions import ModelNotFoundError
1341
+ from ..exceptions import ModelNotDownloadedError
1338
1342
 
1339
1343
  looked_in = ", ".join(str(d) for d in cache_dirs) or "no cache directory found"
1340
- raise ModelNotFoundError(
1344
+ raise ModelNotDownloadedError(
1341
1345
  f"Embedding model {names[0]!r} is not in the local Hugging Face cache (looked in: {looked_in}); "
1342
1346
  + hf_download_first_hint(names[0])
1343
- + "."
1347
+ + ".",
1348
+ model=names[0], provider=self.provider,
1344
1349
  )
1345
1350
 
1346
1351
  def _select_backend(self) -> EmbeddingBackend:
@@ -187,6 +187,19 @@ class ModelNotFoundError(ProviderError):
187
187
  pass
188
188
 
189
189
 
190
+ class ModelNotDownloadedError(ModelNotFoundError):
191
+ """The named model is not on this computer, and loading never downloads (`offline.offline_first`).
192
+
193
+ A state, not a fault: the remedy is a download. `model` is the id to download and `provider`
194
+ the provider that serves it (`abstractcore models download <provider> <model>`). Callers read
195
+ these attributes; the message is for people, and its wording is not a contract."""
196
+
197
+ def __init__(self, message: str, *, model: "str | None" = None, provider: "str | None" = None):
198
+ super().__init__(message)
199
+ self.model = model
200
+ self.provider = provider
201
+
202
+
190
203
  class UnknownProviderError(ModelNotFoundError, ValueError):
191
204
  """The provider part of a provider/model id names no registered provider.
192
205
 
@@ -314,6 +327,7 @@ __all__ = [
314
327
  'SessionError',
315
328
  'ConfigurationError',
316
329
  'ModelNotFoundError',
330
+ 'ModelNotDownloadedError',
317
331
  'UnknownProviderError',
318
332
  'ModelArtifactMismatchError',
319
333
  'format_model_error',
@@ -127,6 +127,7 @@ class PromptCacheStore:
127
127
  max_entries: int = 32,
128
128
  default_ttl_s: Optional[float] = None,
129
129
  on_evict: Optional[Any] = None,
130
+ on_set: Optional[Any] = None,
130
131
  ):
131
132
  self._max_entries = int(max_entries) if max_entries and int(max_entries) > 0 else 32
132
133
  self._default_ttl_s = default_ttl_s if default_ttl_s is None else float(default_ttl_s)
@@ -141,11 +142,19 @@ class PromptCacheStore:
141
142
  # deepcopy per key for the provider's lifetime once the key was
142
143
  # LRU-evicted, since nothing told the provider the key was gone).
143
144
  self._on_evict = on_evict
145
+ # Called as on_set(key, value) after every set(), outside the lock: a provider that bounds
146
+ # its entries by a measure the store cannot compute (GGUF snapshot BYTES) enforces it here,
147
+ # on every insert path at once.
148
+ self._on_set = on_set
144
149
 
145
150
  def set_on_evict(self, callback: Optional[Any]) -> None:
146
151
  """Register the eviction callback (see __init__). One callback, last wins."""
147
152
  self._on_evict = callback
148
153
 
154
+ def set_on_set(self, callback: Optional[Any]) -> None:
155
+ """Register the after-set callback (see __init__). One callback, last wins."""
156
+ self._on_set = callback
157
+
149
158
  def _fire_evictions(self, evicted: List[tuple]) -> None:
150
159
  """Invoke the eviction callback OUTSIDE self._lock (callbacks may take
151
160
  provider-side locks; never nest them under the store lock)."""
@@ -216,6 +225,12 @@ class PromptCacheStore:
216
225
  evicted_key, evicted_entry = self._entries.popitem(last=False)
217
226
  evicted.append((evicted_key, evicted_entry.value))
218
227
  self._fire_evictions(evicted)
228
+ cb = self._on_set
229
+ if cb is not None:
230
+ try:
231
+ cb(key, value)
232
+ except Exception as exc:
233
+ logger.warning(f"prompt cache on_set callback failed for key '{key}': {type(exc).__name__}: {exc}")
219
234
 
220
235
  def delete(self, key: str) -> bool:
221
236
  if not isinstance(key, str) or not key.strip():
@@ -224,6 +239,19 @@ class PromptCacheStore:
224
239
  with self._lock:
225
240
  return self._entries.pop(key, None) is not None
226
241
 
242
+ def evict(self, key: str) -> bool:
243
+ """Drop `key` as a capacity eviction: unlike delete(), the on_evict callback fires (the
244
+ provider frees what it keyed to the entry). For budgets the store cannot measure itself."""
245
+ if not isinstance(key, str) or not key.strip():
246
+ return False
247
+ key = key.strip()
248
+ with self._lock:
249
+ entry = self._entries.pop(key, None)
250
+ if entry is None:
251
+ return False
252
+ self._fire_evictions([(key, entry.value)])
253
+ return True
254
+
227
255
  def clear(self) -> None:
228
256
  with self._lock:
229
257
  self._entries.clear()
@@ -872,6 +900,7 @@ class BaseProvider(AbstractCoreInterface, ABC):
872
900
  # provider so per-key side state (snapshots, device pools) can be
873
901
  # released — silent eviction is a leak (2026-08-03 audit).
874
902
  on_evict=self._prompt_cache_store_evicted,
903
+ on_set=self._prompt_cache_store_after_set,
875
904
  )
876
905
 
877
906
  # Provider created successfully - no event emission needed
@@ -2953,6 +2982,45 @@ class BaseProvider(AbstractCoreInterface, ABC):
2953
2982
  if state is not None and "state" not in spec:
2954
2983
  spec["state"] = state
2955
2984
 
2985
+ def _answers_decisions_itself(self) -> bool:
2986
+ """True when this instance's own model is a decision model (a local Kev / Lev / Laya
2987
+ checkpoint on huggingface or mlx, or a decisions-only provider such as `typesafe`)."""
2988
+ if getattr(self, "_decision_model", None) is not None or callable(getattr(self, "_build_decision_model", None)):
2989
+ return True
2990
+ from ..decision import is_decision_model
2991
+ from ..decision.pool import serves_decisions_only
2992
+
2993
+ provider = str(getattr(self, "provider", "") or "").strip().lower()
2994
+ if serves_decisions_only(provider):
2995
+ return True
2996
+ model = str(getattr(self, "model", "") or "").strip()
2997
+ return provider in {"huggingface", "mlx"} and bool(model) and is_decision_model(model, provider)
2998
+
2999
+ def _decisions_stay_on_this_model(self, specs: List[Dict[str, Any]], output_routes: List[Any]) -> None:
3000
+ """A decision model answers its own decisions: the `output.decision` default is for
3001
+ instances that cannot decide (a text model). When the request names no decision
3002
+ provider or model and this instance's model is a decision model, the default's
3003
+ provider, model, endpoint and options are taken back off the spec, so
3004
+ `_decision_route` picks this model (a `typesafe` default no longer sends a local Kev
3005
+ instance's document to the hosted service). A route the request names still wins."""
3006
+ decision_specs = [s for s in specs if str(s.get("modality") or "").lower() == "decision"]
3007
+ decision_routes = [r for r in output_routes if getattr(r, "route_key", None) == "output.decision"]
3008
+ if not decision_specs or len(decision_specs) != len(decision_routes):
3009
+ return
3010
+ if not self._answers_decisions_itself():
3011
+ return
3012
+ for spec, route in zip(decision_specs, decision_routes):
3013
+ sources = dict(getattr(route, "field_sources", {}) or {})
3014
+ if sources.get("provider") == "explicit" or sources.get("model") == "explicit":
3015
+ continue
3016
+ for key in ("provider", "model", "base_url"):
3017
+ if sources.get(key) not in (None, "explicit"):
3018
+ spec.pop(key, None)
3019
+ if sources.get("options") not in (None, "explicit"):
3020
+ for key, value in dict(getattr(route, "options", {}) or {}).items():
3021
+ if spec.get(key) == value: # the default's option, applied to the spec
3022
+ spec.pop(key, None)
3023
+
2956
3024
  def _decision_load_kwargs(self) -> Dict[str, Any]:
2957
3025
  """Provider-specific options for ``load_decision_model`` (device, precision...)."""
2958
3026
  return {}
@@ -2999,15 +3067,17 @@ class BaseProvider(AbstractCoreInterface, ABC):
2999
3067
  A `model` written as `provider/model` names its own provider (it wins over the
3000
3068
  provider the route resolver copied from the `output.decision` default); a bare
3001
3069
  model picks the local engine (`decision.pool.parse_handle`); `kev-latest` /
3002
- `jev-latest` reuse the most recently used resident model.
3070
+ `jev-latest` reuse the most recently used resident model, unless the route names a
3071
+ provider that serves decisions only (`typesafe`, `systemone`): there they are the
3072
+ hosted model's own name (`typesafe` + `jev-latest` is TypeSafe's Jev).
3003
3073
  """
3004
- from ..decision.pool import ALIASES, latest, parse_handle, split_handle
3074
+ from ..decision.pool import ALIASES, latest, parse_handle, serves_decisions_only, split_handle
3005
3075
 
3006
3076
  route_provider = str(spec.get("provider") or "").strip().lower()
3007
3077
  route_model = str(spec.get("model") or "").strip()
3008
3078
  own_provider = str(getattr(self, "provider", "") or "").strip().lower()
3009
3079
  own_model = str(getattr(self, "model", "") or "").strip()
3010
- if route_model.lower() in ALIASES:
3080
+ if route_model.lower() in ALIASES and not serves_decisions_only(route_provider):
3011
3081
  resident = latest()
3012
3082
  if resident is None:
3013
3083
  raise ValueError(
@@ -3819,6 +3889,7 @@ class BaseProvider(AbstractCoreInterface, ABC):
3819
3889
  is_acore_output = False
3820
3890
  else:
3821
3891
  self._bind_decision_questions(maybe_specs, kwargs)
3892
+ self._decisions_stay_on_this_model(maybe_specs, resolved_generate_route.output_routes)
3822
3893
  kwargs["output"] = maybe_specs[0] if len(maybe_specs) == 1 else maybe_specs
3823
3894
 
3824
3895
  if is_acore_output:
@@ -7425,6 +7496,11 @@ class BaseProvider(AbstractCoreInterface, ABC):
7425
7496
  def _prompt_cache_backend_create(self) -> Optional[Any]:
7426
7497
  return None
7427
7498
 
7499
+ def _prompt_cache_store_after_set(self, key: str, cache_value: Any) -> None:
7500
+ """Hook: `key` was just stored. Providers bounding the store by bytes enforce it here.
7501
+ Default: nothing."""
7502
+ _ = (key, cache_value)
7503
+
7428
7504
  def _prompt_cache_store_evicted(self, key: str, cache_value: Any) -> None:
7429
7505
  """Hook: `key` left the prompt-cache store WITHOUT an explicit clear
7430
7506
  (LRU capacity eviction or TTL expiry).
@@ -9657,6 +9733,7 @@ Please provide a structured response."""
9657
9733
  is_acore_output = False
9658
9734
  else:
9659
9735
  self._bind_decision_questions(maybe_specs, kwargs)
9736
+ self._decisions_stay_on_this_model(maybe_specs, resolved_generate_route.output_routes)
9660
9737
  kwargs["output"] = maybe_specs[0] if len(maybe_specs) == 1 else maybe_specs
9661
9738
 
9662
9739
  if is_acore_output:
@@ -239,11 +239,16 @@ import weakref as _weakref
239
239
  #: expiry is logged at ERROR and refuses the unload.
240
240
  DEFAULT_EJECT_DRAIN_TIMEOUT_S = 30.0
241
241
 
242
+ #: How long a call that begins while an idle unload holds this instance's gate
243
+ #: waits for it (core ADR 0012). The unload itself takes seconds; reaching this
244
+ #: bound means something is stuck, and the call fails loudly instead of hanging.
245
+ IDLE_GATE_WAIT_S = 120.0
246
+
242
247
 
243
248
  class _InflightCall:
244
249
  __slots__ = ("event", "started", "done", "__weakref__")
245
250
 
246
- def __init__(self, event: threading.Event) -> None:
251
+ def __init__(self, event: Optional[threading.Event]) -> None:
247
252
  self.event = event
248
253
  self.started = _time.monotonic()
249
254
  self.done = threading.Event()
@@ -252,27 +257,49 @@ class _InflightCall:
252
257
  class InflightGenerations:
253
258
  """Registry of the generations currently running on one provider instance.
254
259
 
255
- Only calls that carry a cancel event are registered (the host's, or a
256
- private one the provider creates, see BaseProvider). Entries are weakly
260
+ Every call is registered and stamps `last_used_at`, with or without a cancel
261
+ event (the host's, or a private one the provider creates, see BaseProvider);
262
+ only a call with an event can be cancelled by an eject. Entries are weakly
257
263
  held: an abandoned, garbage-collected stream disappears by itself.
264
+
265
+ The idle gate (core ADR 0012): an idle unload closes it (`close_for_idle`), then
266
+ checks again that nothing runs before it ejects, so it never cancels a call; a
267
+ call that begins while it is closed waits for `reopen_after_idle` and then runs
268
+ (the provider reloads its model on demand).
258
269
  """
259
270
 
260
271
  def __init__(self) -> None:
261
272
  self._lock = threading.Lock()
273
+ self._gate = threading.Condition(self._lock)
274
+ self._closed_for_idle = False
262
275
  self._calls: "_weakref.WeakSet[_InflightCall]" = _weakref.WeakSet()
263
276
  # Wall-clock time a generation last began or ended on this instance: the
264
- # "least recently used" of the residency failsafe (core ADR 0010).
277
+ # "least recently used" of the residency failsafe (core ADR 0010) and of
278
+ # the idle unload (ADR 0012).
265
279
  self.last_used_at: Optional[float] = None
266
280
 
267
- def begin(self, event: Optional[threading.Event]) -> Optional[_InflightCall]:
268
- if event is None:
269
- return None
281
+ def begin(self, event: Optional[threading.Event]) -> _InflightCall:
270
282
  call = _InflightCall(event)
271
- with self._lock:
283
+ with self._gate:
284
+ if self._closed_for_idle and not self._gate.wait_for(lambda: not self._closed_for_idle,
285
+ timeout=IDLE_GATE_WAIT_S):
286
+ raise RuntimeError(
287
+ f"this model has been unloading for idleness for more than {IDLE_GATE_WAIT_S:.0f}s; "
288
+ "the call was not started (send it again)")
272
289
  self._calls.add(call)
273
290
  self.last_used_at = _time.time()
274
291
  return call
275
292
 
293
+ def close_for_idle(self) -> None:
294
+ """Close the gate for an idle unload: calls that begin from now wait for the reopen."""
295
+ with self._gate:
296
+ self._closed_for_idle = True
297
+
298
+ def reopen_after_idle(self) -> None:
299
+ with self._gate:
300
+ self._closed_for_idle = False
301
+ self._gate.notify_all()
302
+
276
303
  def end(self, call: Optional[_InflightCall]) -> None:
277
304
  if call is None:
278
305
  return
@@ -301,7 +328,8 @@ class InflightGenerations:
301
328
  "drain_timeout_s": timeout}
302
329
  t0 = _time.monotonic()
303
330
  for call in calls:
304
- call.event.set()
331
+ if call.event is not None: # a call without an event cannot be stopped: it is waited for
332
+ call.event.set()
305
333
  _log.warning(
306
334
  "%s/%s: %s — cancelled %d in-flight generation(s); waiting up to %.1fs for them to stop",
307
335
  provider, model, reason, len(calls), timeout,
@@ -327,6 +355,7 @@ class InflightGenerations:
327
355
  __all__ = [
328
356
  "CANCEL_KWARG",
329
357
  "DEFAULT_EJECT_DRAIN_TIMEOUT_S",
358
+ "IDLE_GATE_WAIT_S",
330
359
  "HttpCancelGuard",
331
360
  "InflightGenerations",
332
361
  "WATCH_EXIT_SLICE_S",