abstractcore 2.35.0__tar.gz → 2.35.2__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 (378) hide show
  1. {abstractcore-2.35.0 → abstractcore-2.35.2}/PKG-INFO +15 -12
  2. {abstractcore-2.35.0 → abstractcore-2.35.2}/README.md +3 -1
  3. abstractcore-2.35.2/abstractcore/config/hub_discovery.py +68 -0
  4. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/model_catalog.py +153 -68
  5. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/models_engines_cli.py +6 -2
  6. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/route_engines.py +1 -1
  7. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/download.py +3 -5
  8. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/embeddings/manager.py +29 -0
  9. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/huggingface_provider.py +17 -0
  10. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/install_settings.py +13 -0
  11. abstractcore-2.35.2/abstractcore/utils/llama_cuda_swap.py +277 -0
  12. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/version.py +1 -1
  13. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/windows_dll.py +9 -1
  14. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore.egg-info/PKG-INFO +15 -12
  15. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore.egg-info/SOURCES.txt +2 -0
  16. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore.egg-info/requires.txt +11 -10
  17. {abstractcore-2.35.0 → abstractcore-2.35.2}/pyproject.toml +12 -10
  18. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_install_hints_no_bare_packages.py +7 -0
  19. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_install_settings.py +2 -2
  20. {abstractcore-2.35.0 → abstractcore-2.35.2}/LICENSE +0 -0
  21. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/__init__.py +0 -0
  22. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/apps/__init__.py +0 -0
  23. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/apps/__main__.py +0 -0
  24. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/apps/app_config_utils.py +0 -0
  25. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/apps/deepsearch.py +0 -0
  26. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/apps/extractor.py +0 -0
  27. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/apps/intent.py +0 -0
  28. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/apps/judge.py +0 -0
  29. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/apps/summarizer.py +0 -0
  30. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/architectures/__init__.py +0 -0
  31. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/architectures/detection.py +0 -0
  32. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/architectures/enums.py +0 -0
  33. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/architectures/response_postprocessing.py +0 -0
  34. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/architectures/thinking_controls.py +0 -0
  35. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/assets/OCRA.ttf +0 -0
  36. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/assets/OCRB.ttf +0 -0
  37. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/assets/OCRBL.ttf +0 -0
  38. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/assets/README.md +0 -0
  39. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/assets/abstractframework_identity.json +0 -0
  40. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/assets/adapter_metadata.schema.json +0 -0
  41. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/assets/architecture_formats.json +0 -0
  42. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/assets/bloc-schema.jsonld +0 -0
  43. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/assets/llamacpp_known_architectures.json +0 -0
  44. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/assets/model_capabilities.json +0 -0
  45. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/assets/model_capabilities.schema.json +0 -0
  46. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/assets/model_downloads_catalog.json +0 -0
  47. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/assets/model_downloads_catalog.schema.json +0 -0
  48. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/assets/semantic_models.md +0 -0
  49. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/assets/session_schema.json +0 -0
  50. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/capabilities/__init__.py +0 -0
  51. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/capabilities/errors.py +0 -0
  52. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/capabilities/host.py +0 -0
  53. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/capabilities/music_selectors.py +0 -0
  54. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/capabilities/registry.py +0 -0
  55. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/capabilities/scene3d_selectors.py +0 -0
  56. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/capabilities/types.py +0 -0
  57. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/capabilities/vision_catalog.py +0 -0
  58. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/__init__.py +0 -0
  59. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/email/__init__.py +0 -0
  60. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/email/bodystructure.py +0 -0
  61. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/email/client.py +0 -0
  62. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/email/context.py +0 -0
  63. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/email/discovery.py +0 -0
  64. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/email/errors.py +0 -0
  65. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/email/imap_codec.py +0 -0
  66. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/email/legacy.py +0 -0
  67. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/email/limits.py +0 -0
  68. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/email/models.py +0 -0
  69. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/email/oauth.py +0 -0
  70. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/email/policy.py +0 -0
  71. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/email/search.py +0 -0
  72. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/email/store.py +0 -0
  73. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/comms/email/vault.py +0 -0
  74. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/compression/__init__.py +0 -0
  75. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/compression/analytics.py +0 -0
  76. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/compression/cache.py +0 -0
  77. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/compression/config.py +0 -0
  78. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/compression/exceptions.py +0 -0
  79. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/compression/glyph_processor.py +0 -0
  80. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/compression/optimizer.py +0 -0
  81. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/compression/orchestrator.py +0 -0
  82. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/compression/pil_text_renderer.py +0 -0
  83. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/compression/quality.py +0 -0
  84. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/compression/text_formatter.py +0 -0
  85. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/compression/vision_compressor.py +0 -0
  86. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/__init__.py +0 -0
  87. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/capability_defaults.py +0 -0
  88. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/email_cli.py +0 -0
  89. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/engines.py +0 -0
  90. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/host_jobs.py +0 -0
  91. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/main.py +0 -0
  92. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/manager.py +0 -0
  93. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/model_contexts.py +0 -0
  94. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/model_materializer.py +0 -0
  95. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/model_verify.py +0 -0
  96. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/provider_profiles.py +0 -0
  97. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/recommendations.py +0 -0
  98. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/config/vision_config.py +0 -0
  99. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/console/__init__.py +0 -0
  100. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/console/theme_sync.py +0 -0
  101. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/console/themes.py +0 -0
  102. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/console/web.py +0 -0
  103. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/__init__.py +0 -0
  104. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/bloc_kv.py +0 -0
  105. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/bloc_metadata.py +0 -0
  106. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/cached_session.py +0 -0
  107. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/degeneration.py +0 -0
  108. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/endpoint_damping.py +0 -0
  109. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/enums.py +0 -0
  110. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/factory.py +0 -0
  111. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/file_blocs.py +0 -0
  112. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/file_boxes.py +0 -0
  113. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/generate_contract.py +0 -0
  114. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/interface.py +0 -0
  115. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/multimodal_generation.py +0 -0
  116. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/output_specs.py +0 -0
  117. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/retry.py +0 -0
  118. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/session.py +0 -0
  119. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/core/types.py +0 -0
  120. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/__init__.py +0 -0
  121. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/backends/__init__.py +0 -0
  122. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/backends/base.py +0 -0
  123. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/backends/gguf.py +0 -0
  124. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/backends/hf.py +0 -0
  125. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/backends/logits.py +0 -0
  126. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/backends/mlx.py +0 -0
  127. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/convert.py +0 -0
  128. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/encoding.py +0 -0
  129. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/engines/__init__.py +0 -0
  130. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/engines/base.py +0 -0
  131. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/engines/labels.py +0 -0
  132. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/engines/laya.py +0 -0
  133. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/engines/remote.py +0 -0
  134. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/gguf_compat.py +0 -0
  135. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/head.py +0 -0
  136. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/kinds.py +0 -0
  137. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/loader.py +0 -0
  138. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/pool.py +0 -0
  139. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/render.py +0 -0
  140. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/runtime.py +0 -0
  141. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/tokenizers.py +0 -0
  142. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/decision/types.py +0 -0
  143. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/embeddings/__init__.py +0 -0
  144. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/embeddings/models.py +0 -0
  145. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/endpoint/__init__.py +0 -0
  146. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/endpoint/app.py +0 -0
  147. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/events/__init__.py +0 -0
  148. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/exceptions/__init__.py +0 -0
  149. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/mcp/__init__.py +0 -0
  150. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/mcp/client.py +0 -0
  151. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/mcp/factory.py +0 -0
  152. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/mcp/naming.py +0 -0
  153. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/mcp/stdio_client.py +0 -0
  154. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/mcp/tool_source.py +0 -0
  155. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/__init__.py +0 -0
  156. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/auto_handler.py +0 -0
  157. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/base.py +0 -0
  158. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/capabilities.py +0 -0
  159. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/delivery.py +0 -0
  160. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/enrichment.py +0 -0
  161. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/handlers/__init__.py +0 -0
  162. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/handlers/anthropic_handler.py +0 -0
  163. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/handlers/local_handler.py +0 -0
  164. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/handlers/openai_handler.py +0 -0
  165. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/openai_parts.py +0 -0
  166. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/pdf_routing.py +0 -0
  167. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/processors/__init__.py +0 -0
  168. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/processors/audio_processor.py +0 -0
  169. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/processors/direct_pdf_processor.py +0 -0
  170. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/processors/glyph_pdf_processor.py +0 -0
  171. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/processors/image_processor.py +0 -0
  172. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/processors/office_processor.py +0 -0
  173. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/processors/pdf_processor.py +0 -0
  174. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/processors/text_processor.py +0 -0
  175. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/processors/video_processor.py +0 -0
  176. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/types.py +0 -0
  177. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/utils/__init__.py +0 -0
  178. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/utils/image_scaler.py +0 -0
  179. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/utils/video_frames.py +0 -0
  180. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/media/vision_fallback.py +0 -0
  181. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/processing/__init__.py +0 -0
  182. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/processing/basic_deepsearch.py +0 -0
  183. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/processing/basic_extractor.py +0 -0
  184. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/processing/basic_intent.py +0 -0
  185. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/processing/basic_judge.py +0 -0
  186. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/processing/basic_summarizer.py +0 -0
  187. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/__init__.py +0 -0
  188. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/_http.py +0 -0
  189. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/anthropic_provider.py +0 -0
  190. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/base.py +0 -0
  191. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/capability_host.py +0 -0
  192. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/endpoint_context.py +0 -0
  193. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/generation_cancel.py +0 -0
  194. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/generation_progress.py +0 -0
  195. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/gguf_shared.py +0 -0
  196. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/gguf_slots.py +0 -0
  197. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/hf_residency.py +0 -0
  198. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/lmstudio_provider.py +0 -0
  199. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/mlx_hy_v3.py +0 -0
  200. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/mlx_model_rules.py +0 -0
  201. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/mlx_native_cache.py +0 -0
  202. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/mlx_native_session.py +0 -0
  203. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/mlx_prefill_observer.py +0 -0
  204. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/mlx_provider.py +0 -0
  205. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/mlx_qwen4.py +0 -0
  206. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/mlx_residency.py +0 -0
  207. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/mlx_runtime.py +0 -0
  208. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/mlx_vision_addon.py +0 -0
  209. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/model_capabilities.py +0 -0
  210. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/model_config_fingerprint.py +0 -0
  211. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/ollama_provider.py +0 -0
  212. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/openai_compatible_provider.py +0 -0
  213. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/openai_provider.py +0 -0
  214. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/openrouter_provider.py +0 -0
  215. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/portkey_provider.py +0 -0
  216. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/process_residency.py +0 -0
  217. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/registry.py +0 -0
  218. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/speculation.py +0 -0
  219. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/streaming.py +0 -0
  220. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/tokenizer_fingerprint.py +0 -0
  221. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/typesafe_provider.py +0 -0
  222. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/vllm_provider.py +0 -0
  223. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/providers/weights_fingerprint.py +0 -0
  224. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/py.typed +0 -0
  225. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/__init__.py +0 -0
  226. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/__main__.py +0 -0
  227. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/app.py +0 -0
  228. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/audio_endpoints.py +0 -0
  229. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/auth_policy.py +0 -0
  230. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/camera_endpoints.py +0 -0
  231. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/capability_generation.py +0 -0
  232. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/console_routes.py +0 -0
  233. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/credentials.py +0 -0
  234. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/email_routes.py +0 -0
  235. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/first_run.py +0 -0
  236. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/host_routes.py +0 -0
  237. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/managed.py +0 -0
  238. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/scene3d_endpoints.py +0 -0
  239. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/session_routes.py +0 -0
  240. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/server/vision_endpoints.py +0 -0
  241. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/structured/__init__.py +0 -0
  242. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/structured/handler.py +0 -0
  243. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/structured/json_schema.py +0 -0
  244. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/structured/retry.py +0 -0
  245. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/structured/schema_compat.py +0 -0
  246. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/testing/__init__.py +0 -0
  247. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/testing/mailserver.py +0 -0
  248. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/__init__.py +0 -0
  249. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/abstractignore.py +0 -0
  250. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/arg_canonicalizer.py +0 -0
  251. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/arg_coercion.py +0 -0
  252. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/browser_tools.py +0 -0
  253. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/code_analysis.py +0 -0
  254. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/common_tools.py +0 -0
  255. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/comms_tools.py +0 -0
  256. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/core.py +0 -0
  257. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/email_digests.py +0 -0
  258. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/fetch_url_ssrf.py +0 -0
  259. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/handler.py +0 -0
  260. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/inventory.py +0 -0
  261. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/parser.py +0 -0
  262. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/process_tree.py +0 -0
  263. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/registry.py +0 -0
  264. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/risk_facts.py +0 -0
  265. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/sandbox.py +0 -0
  266. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/shell_session.py +0 -0
  267. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/shell_tools.py +0 -0
  268. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/syntax_rewriter.py +0 -0
  269. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/tag_rewriter.py +0 -0
  270. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/telegram_tdlib.py +0 -0
  271. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/telegram_tools.py +0 -0
  272. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/tools/wire_naming.py +0 -0
  273. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/__init__.py +0 -0
  274. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/async_stream.py +0 -0
  275. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/cli.py +0 -0
  276. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/context_calibration.py +0 -0
  277. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/context_estimate.py +0 -0
  278. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/data_registry.py +0 -0
  279. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/display.py +0 -0
  280. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/file_filters.py +0 -0
  281. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/host_profile.py +0 -0
  282. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/hostinfo.py +0 -0
  283. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/identity.py +0 -0
  284. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/jsonish.py +0 -0
  285. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/kv_geometry.py +0 -0
  286. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/memory.py +0 -0
  287. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/message_preprocessor.py +0 -0
  288. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/model_cache.py +0 -0
  289. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/model_fit.py +0 -0
  290. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/residency.py +0 -0
  291. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/self_fixes.py +0 -0
  292. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/stdio.py +0 -0
  293. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/structured_logging.py +0 -0
  294. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/token_utils.py +0 -0
  295. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/trace_export.py +0 -0
  296. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/truncation.py +0 -0
  297. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/vlm_token_calculator.py +0 -0
  298. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore/utils/workspace_paths.py +0 -0
  299. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore.egg-info/dependency_links.txt +0 -0
  300. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore.egg-info/entry_points.txt +0 -0
  301. {abstractcore-2.35.0 → abstractcore-2.35.2}/abstractcore.egg-info/top_level.txt +0 -0
  302. {abstractcore-2.35.0 → abstractcore-2.35.2}/setup.cfg +0 -0
  303. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_abstractendpoint_singleton_provider.py +0 -0
  304. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_agentic_cli_compatibility.py +0 -0
  305. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_basic_session.py +0 -0
  306. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_bloc_kv.py +0 -0
  307. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_bloc_kv_cache_dtype.py +0 -0
  308. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_bloc_kv_endpoint.py +0 -0
  309. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_bloc_kv_engine_fingerprint.py +0 -0
  310. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_bloc_kv_model_config_fingerprint.py +0 -0
  311. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_bloc_kv_tokenizer_fingerprint.py +0 -0
  312. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_bloc_kv_weights_fingerprint.py +0 -0
  313. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_bloc_metadata_json_extract.py +0 -0
  314. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_cached_session_kv_mode.py +0 -0
  315. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_cached_session_kv_thinking_prefix.py +0 -0
  316. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_capabilities_registry.py +0 -0
  317. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_capabilities_registry_preferred_backends_from_config.py +0 -0
  318. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_changelog_fragments.py +0 -0
  319. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_complete_integration.py +0 -0
  320. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_comprehensive_events.py +0 -0
  321. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_console_about.py +0 -0
  322. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_console_email_tab.py +0 -0
  323. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_console_web.py +0 -0
  324. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_core_components.py +0 -0
  325. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_display_rule.py +0 -0
  326. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_email_digests.py +0 -0
  327. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_embedding_model_shared_unit.py +0 -0
  328. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_embedding_model_token_warnings.py +0 -0
  329. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_enhanced_prompt.py +0 -0
  330. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_environment_variable_tool_call_tags.py +0 -0
  331. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_factory.py +0 -0
  332. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_file_bloc_store_unit.py +0 -0
  333. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_file_boxes_cached_session.py +0 -0
  334. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_final_accuracy.py +0 -0
  335. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_final_comprehensive.py +0 -0
  336. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_final_graceful_errors.py +0 -0
  337. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_generate_contract.py +0 -0
  338. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_generate_response_reasoning_property_unit.py +0 -0
  339. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_generate_with_outputs.py +0 -0
  340. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_graceful_fallback.py +0 -0
  341. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_import_debug.py +0 -0
  342. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_import_safety.py +0 -0
  343. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_incremental_thinking_tag_stripper.py +0 -0
  344. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_install_scripts.py +0 -0
  345. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_integrated_functionality.py +0 -0
  346. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_llms_sources.py +0 -0
  347. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_mcp_handshake.py +0 -0
  348. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_mcp_integration.py +0 -0
  349. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_mcp_stdio_client.py +0 -0
  350. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_model_not_downloaded_error_unit.py +0 -0
  351. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_multimodal_generate_output.py +0 -0
  352. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_openai_prompt_cache_key_passthrough.py +0 -0
  353. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_output_specs.py +0 -0
  354. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_packaging_assets.py +0 -0
  355. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_packaging_extras.py +0 -0
  356. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_packaging_packages_listed.py +0 -0
  357. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_prompt_cache_api.py +0 -0
  358. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_prompt_cache_bloc_composition.py +0 -0
  359. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_prompt_cache_bloc_e2e_gate.py +0 -0
  360. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_prompt_cache_control_plane.py +0 -0
  361. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_prompt_cache_prepare_modules_thinking_http.py +0 -0
  362. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_retry_observability.py +0 -0
  363. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_retry_strategy.py +0 -0
  364. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_seed_determinism.py +0 -0
  365. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_seed_temperature_basic.py +0 -0
  366. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_sensory_prompting.py +0 -0
  367. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_server_prompt_cache_control_plane_proxy.py +0 -0
  368. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_server_prompt_cache_key_passthrough.py +0 -0
  369. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_session_reasoning_persistence_unit.py +0 -0
  370. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_sound_effect_seconds_route.py +0 -0
  371. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_sse_starlette_pin_unit.py +0 -0
  372. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_structured_output_truncation_retry.py +0 -0
  373. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_text_only_model_experience.py +0 -0
  374. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_timing_helper.py +0 -0
  375. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_unload_memory.py +0 -0
  376. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_user_scenario_validation.py +0 -0
  377. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_voice_host_credentials.py +0 -0
  378. {abstractcore-2.35.0 → abstractcore-2.35.2}/tests/test_windows_host_fixes.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractcore
3
- Version: 2.35.0
3
+ Version: 2.35.2
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>
@@ -36,10 +36,11 @@ Requires-Dist: anthropic<1.0.0,>=0.25.0
36
36
  Requires-Dist: tiktoken<1.0.0,>=0.5.0
37
37
  Requires-Dist: requests<3.0.0,>=2.25.0
38
38
  Requires-Dist: beautifulsoup4<5.0.0,>=4.12.0
39
- Requires-Dist: lxml<6.0.0,>=4.9.0
39
+ Requires-Dist: lxml<7.0.0,>=4.9.0
40
40
  Requires-Dist: ddgs<10.0.0,>=9.10.0; python_version >= "3.10"
41
41
  Requires-Dist: duckduckgo-search<4.0.0,>=3.8.0; python_version < "3.10"
42
42
  Requires-Dist: psutil<8,>=5.9
43
+ Requires-Dist: huggingface_hub<2.0.0,>=0.20.0
43
44
  Requires-Dist: Pillow<13.0.0,>=10.0.0
44
45
  Requires-Dist: pypdf<7.0.0,>=6.0.0
45
46
  Requires-Dist: unstructured[docx,odt,pptx,rtf,xlsx]<1.0.0,>=0.10.0
@@ -49,8 +50,8 @@ Requires-Dist: uvicorn[standard]<1.0.0,>=0.23.0
49
50
  Requires-Dist: python-multipart<1.0.0,>=0.0.6
50
51
  Requires-Dist: sse-starlette<4,>=1.8
51
52
  Requires-Dist: cryptography>=41.0.0
52
- Requires-Dist: abstractvoice>=0.20.0
53
- Requires-Dist: abstractvision>=0.3.34
53
+ Requires-Dist: abstractvoice>=0.21.1
54
+ Requires-Dist: abstractvision>=0.3.36
54
55
  Requires-Dist: abstractmusic>=0.1.17; python_version >= "3.10"
55
56
  Requires-Dist: abstract3d>=0.3.2; python_version >= "3.10"
56
57
  Provides-Extra: apple
@@ -69,9 +70,9 @@ Requires-Dist: accelerate>=1.0.0; extra == "apple"
69
70
  Requires-Dist: sentence-transformers<6.0.0,>=5.1.0; extra == "apple"
70
71
  Requires-Dist: numpy<3.0.0,>=2.1.0; extra == "apple"
71
72
  Requires-Dist: Pillow<13.0.0,>=12.1.1; extra == "apple"
72
- Requires-Dist: abstractvoice[all-apple]>=0.20.0; extra == "apple"
73
+ Requires-Dist: abstractvoice[all-apple]>=0.21.1; extra == "apple"
73
74
  Requires-Dist: omnivoice>=0.1.5; extra == "apple"
74
- Requires-Dist: abstractvision[all-apple]>=0.3.34; extra == "apple"
75
+ Requires-Dist: abstractvision[all-apple]>=0.3.36; extra == "apple"
75
76
  Requires-Dist: abstractmusic[all-apple]>=0.1.17; extra == "apple"
76
77
  Requires-Dist: playwright<2.0.0,>=1.49.0; extra == "apple"
77
78
  Provides-Extra: gpu
@@ -87,9 +88,9 @@ Requires-Dist: gguf<1.0.0,>=0.10.0; extra == "gpu"
87
88
  Requires-Dist: sentence-transformers<6.0.0,>=5.1.0; extra == "gpu"
88
89
  Requires-Dist: numpy<3.0.0,>=1.20.0; python_version < "3.13" and extra == "gpu"
89
90
  Requires-Dist: numpy<3.0.0,>=2.1.0; python_version >= "3.13" and extra == "gpu"
90
- Requires-Dist: abstractvoice[all-gpu]>=0.20.0; extra == "gpu"
91
+ Requires-Dist: abstractvoice[all-gpu]>=0.21.1; extra == "gpu"
91
92
  Requires-Dist: omnivoice>=0.1.5; extra == "gpu"
92
- Requires-Dist: abstractvision[all-gpu]>=0.3.34; extra == "gpu"
93
+ Requires-Dist: abstractvision[all-gpu]>=0.3.36; extra == "gpu"
93
94
  Requires-Dist: abstractmusic[all-gpu]>=0.1.17; extra == "gpu"
94
95
  Requires-Dist: playwright<2.0.0,>=1.49.0; extra == "gpu"
95
96
  Provides-Extra: remote
@@ -135,11 +136,11 @@ Requires-Dist: sentence-transformers<6.0.0,>=5.1.0; extra == "embeddings"
135
136
  Requires-Dist: numpy<3.0.0,>=1.20.0; python_version < "3.13" and extra == "embeddings"
136
137
  Requires-Dist: numpy<3.0.0,>=2.1.0; python_version >= "3.13" and extra == "embeddings"
137
138
  Provides-Extra: vision-diffusers
138
- Requires-Dist: abstractvision[huggingface]>=0.3.34; extra == "vision-diffusers"
139
+ Requires-Dist: abstractvision[huggingface]>=0.3.36; extra == "vision-diffusers"
139
140
  Provides-Extra: vision-sdcpp
140
- Requires-Dist: abstractvision[sdcpp]>=0.3.34; extra == "vision-sdcpp"
141
+ Requires-Dist: abstractvision[sdcpp]>=0.3.36; extra == "vision-sdcpp"
141
142
  Provides-Extra: vision-local
142
- Requires-Dist: abstractvision[local]>=0.3.34; extra == "vision-local"
143
+ Requires-Dist: abstractvision[local]>=0.3.36; extra == "vision-local"
143
144
  Provides-Extra: browser
144
145
  Requires-Dist: playwright<2.0.0,>=1.49.0; extra == "browser"
145
146
  Provides-Extra: pdf-pymupdf-commercial
@@ -196,7 +197,9 @@ Dynamic: license-file
196
197
  Unified LLM Interface
197
198
  > Write once, run everywhere
198
199
 
199
- What's new in 2.35.0: the model capability registry covers the current vendor lineups (GPT-6 and GPT-6.1, Claude 5.5 and Fable 5.1, Grok 4.3 to 4.7, Qwen 3.8, DeepSeek V4.1 Flash, GLM 5.3, MiniMax M3.1 Flash, Kimi K3 and K3.1 Pro, LFM3, Spark X2.5/X2.6, Nemotron 3.5, Bonsai 2 and more), so an image, video or voice route can name them; and a GGUF or MLX re-packaging of a registered model (`unsloth/...-GGUF:Q4_K_M`, `mlx-community/...-4bit`, bartowski's `Org_Model-GGUF`) resolves to that model's entry instead of the text-only default. Transcriptions report `language` and `detected_language`. See the [changelog](CHANGELOG.md#2350---2026-11-07).
200
+ What's new in 2.35.2: Hugging Face searches use bounded summary pages and a shared quota cooldown. Full repository metadata is an explicit selected-repo action; missing sizes stay unknown or estimated. See [model discovery](docs/models.md#the-catalog).
201
+
202
+ What's new in 2.35.1: a plain `pip install "abstractcore[gpu]"` on a Linux NVIDIA machine now runs GGUF models on the GPU (AbstractCore installs llama.cpp's CUDA build itself, once, the first time it is needed). In 2.35.0: the model capability registry covers the current vendor lineups (GPT-6 and GPT-6.1, Claude 5.5 and Fable 5.1, Grok 4.3 to 4.7, Qwen 3.8, DeepSeek V4.1 Flash, GLM 5.3, MiniMax M3.1 Flash, Kimi K3 and K3.1 Pro, LFM3, Spark X2.5/X2.6, Nemotron 3.5, Bonsai 2 and more), so an image, video or voice route can name them; and a GGUF or MLX re-packaging of a registered model (`unsloth/...-GGUF:Q4_K_M`, `mlx-community/...-4bit`, bartowski's `Org_Model-GGUF`) resolves to that model's entry instead of the text-only default. Transcriptions report `language` and `detected_language`. See the [changelog](CHANGELOG.md#2351---2026-11-10).
200
203
 
201
204
  What's new in 2.34.0: a local model that no call has used for 60 minutes is unloaded from memory and loads
202
205
  again on its next call; a model kept loaded (Keep loaded) never is, and no running call is interrupted
@@ -9,7 +9,9 @@
9
9
  Unified LLM Interface
10
10
  > Write once, run everywhere
11
11
 
12
- What's new in 2.35.0: the model capability registry covers the current vendor lineups (GPT-6 and GPT-6.1, Claude 5.5 and Fable 5.1, Grok 4.3 to 4.7, Qwen 3.8, DeepSeek V4.1 Flash, GLM 5.3, MiniMax M3.1 Flash, Kimi K3 and K3.1 Pro, LFM3, Spark X2.5/X2.6, Nemotron 3.5, Bonsai 2 and more), so an image, video or voice route can name them; and a GGUF or MLX re-packaging of a registered model (`unsloth/...-GGUF:Q4_K_M`, `mlx-community/...-4bit`, bartowski's `Org_Model-GGUF`) resolves to that model's entry instead of the text-only default. Transcriptions report `language` and `detected_language`. See the [changelog](CHANGELOG.md#2350---2026-11-07).
12
+ What's new in 2.35.2: Hugging Face searches use bounded summary pages and a shared quota cooldown. Full repository metadata is an explicit selected-repo action; missing sizes stay unknown or estimated. See [model discovery](docs/models.md#the-catalog).
13
+
14
+ What's new in 2.35.1: a plain `pip install "abstractcore[gpu]"` on a Linux NVIDIA machine now runs GGUF models on the GPU (AbstractCore installs llama.cpp's CUDA build itself, once, the first time it is needed). In 2.35.0: the model capability registry covers the current vendor lineups (GPT-6 and GPT-6.1, Claude 5.5 and Fable 5.1, Grok 4.3 to 4.7, Qwen 3.8, DeepSeek V4.1 Flash, GLM 5.3, MiniMax M3.1 Flash, Kimi K3 and K3.1 Pro, LFM3, Spark X2.5/X2.6, Nemotron 3.5, Bonsai 2 and more), so an image, video or voice route can name them; and a GGUF or MLX re-packaging of a registered model (`unsloth/...-GGUF:Q4_K_M`, `mlx-community/...-4bit`, bartowski's `Org_Model-GGUF`) resolves to that model's entry instead of the text-only default. Transcriptions report `language` and `detected_language`. See the [changelog](CHANGELOG.md#2351---2026-11-10).
13
15
 
14
16
  What's new in 2.34.0: a local model that no call has used for 60 minutes is unloaded from memory and loads
15
17
  again on its next call; a model kept loaded (Keep loaded) never is, and no running call is interrupted
@@ -0,0 +1,68 @@
1
+ """Bounded, read-only Hub discovery. Download clients keep their own transport."""
2
+ from __future__ import annotations
3
+
4
+ import hashlib
5
+ import json
6
+ import time
7
+ from types import SimpleNamespace
8
+ from urllib.parse import quote, urlparse
9
+
10
+ import httpx
11
+
12
+ _EXPAND = ['siblings', 'gguf', 'safetensors', 'gated', 'downloads', 'library_name', 'pipeline_tag', 'tags']
13
+
14
+
15
+ def _info(value):
16
+ value = dict(value)
17
+ value['siblings'] = [SimpleNamespace(rfilename=s.get('rfilename'), size=s.get('size')) for s in value.get('siblings') or []]
18
+ return SimpleNamespace(**value)
19
+
20
+
21
+ class HubDiscovery:
22
+ """One HTTP attempt per page/detail, no redirects/retries; bounded body/deadline.
23
+
24
+ Pagination follows only same-origin model-list links. Callers persist the next
25
+ URL so advancing a page never downloads its preceding prefix again.
26
+ """
27
+ def __init__(self, *, endpoint=None, token=None, transport=None):
28
+ from huggingface_hub import constants
29
+ from huggingface_hub.utils._headers import get_token_to_send
30
+ self.endpoint = (endpoint or constants.ENDPOINT).rstrip('/')
31
+ # Older SDKs resolve the cached token before checking this setting.
32
+ # Anonymous discovery must not even read that credential when disabled.
33
+ self.token = '' if token is None and constants.HF_HUB_DISABLE_IMPLICIT_TOKEN else (get_token_to_send(token) or '')
34
+ # SDK user-agent construction can fetch an agent registry. Discovery
35
+ # owns every HTTP attempt, so do not invoke that telemetry path.
36
+ self.headers = {'user-agent': 'abstractcore-hub-discovery'}
37
+ if self.token:
38
+ self.headers['authorization'] = 'Bearer ' + self.token
39
+ self.scope = hashlib.sha256((self.endpoint + '\0' + (self.token or '')).encode()).hexdigest()
40
+ self.transport = transport
41
+
42
+ def _get(self, url, *, params=None):
43
+ target, origin = urlparse(url), urlparse(self.endpoint)
44
+ if (target.scheme, target.netloc) != (origin.scheme, origin.netloc) or (target.path != '/api/models' and params is None):
45
+ raise ValueError('Invalid Hub discovery pagination URL')
46
+ headers = self.headers
47
+ deadline = time.monotonic() + 20
48
+ with httpx.Client(transport=self.transport, timeout=httpx.Timeout(5, connect=3), follow_redirects=False, headers=headers) as client:
49
+ with client.stream('GET', url, params=params) as response:
50
+ response.raise_for_status()
51
+ parts, size = [], 0
52
+ for chunk in response.iter_bytes():
53
+ size += len(chunk)
54
+ if size > 16 * 1024 * 1024 or time.monotonic() > deadline:
55
+ raise TimeoutError('Hub discovery response exceeded its size or time budget')
56
+ parts.append(chunk)
57
+ return json.loads(b''.join(parts)), response.links.get('next', {}).get('url')
58
+
59
+ def list_models_page(self, *, search, filter, limit, cursor=None):
60
+ params = None if cursor else {'search': search, 'filter': filter, 'sort': 'downloads', 'direction': -1, 'limit': limit, 'expand': _EXPAND}
61
+ values, next_url = self._get(cursor or self.endpoint + '/api/models', params=params)
62
+ if not isinstance(values, list):
63
+ raise ValueError('Unexpected Hub search response')
64
+ return [_info(value) for value in values[:limit]], next_url
65
+
66
+ def model_info(self, repo_id, *, files_metadata=True):
67
+ value, _ = self._get(self.endpoint + '/api/models/' + quote(repo_id, safe='/'), params={'blobs': 'true'})
68
+ return _info(value)
@@ -9,19 +9,21 @@ exist elsewhere and must not be re-derived by any surface:
9
9
  fit utils.model_fit.estimate_fit() against utils.host_profile()
10
10
  hub (opt-in) Hugging Face sizes/params (+ free-text search rows)
11
11
 
12
- THE HUB IS OPT-IN AND CACHED. `hub=False` (the default) never touches the
13
- network: sizes come from the seed (observed on real engine stores) or are
14
- labelled `estimate` from parameters x bits. `hub=True` asks the Hugging Face
15
- API for exact file sizes and parameter counts, caches every answer on disk
16
- for 24 hours under the AbstractCore config directory, stops at the first 429
17
- (rate limit) and reports it in `hub.errors`, and degrades to the offline
18
- answer when the hub is unreachable. A browser that hammered the hub on every
19
- keystroke would be rate-limited within minutes.
12
+ THE HUB IS OPT-IN AND CACHED. `hub=False` never contacts the network. Search
13
+ uses bounded bulk summary pages, not per-result enrichment. Full file metadata
14
+ requires one explicit `detail_repo` (or an exact handle). Discovery shares a
15
+ credential-scoped rate-limit cooldown, follows bounded cursor pages without SDK
16
+ retries, and uses its own finite transport limits. Missing sizes stay unknown
17
+ or labelled estimates. Successful answers are cached for 24 hours.
18
+
20
19
  """
21
20
 
22
21
  from __future__ import annotations
23
22
 
24
23
  import functools
24
+ import hashlib
25
+ import math
26
+ from email.utils import parsedate_to_datetime
25
27
  import json
26
28
  import os
27
29
  import re
@@ -1289,6 +1291,8 @@ class _HubCache:
1289
1291
  if isinstance(loaded, dict):
1290
1292
  self.data["models"] = dict(loaded.get("models") or {})
1291
1293
  self.data["searches"] = dict(loaded.get("searches") or {})
1294
+ self.data["scope"] = loaded.get("scope")
1295
+ self.data["retry_at"] = loaded.get("retry_at")
1292
1296
  except Exception:
1293
1297
  pass
1294
1298
 
@@ -1302,6 +1306,9 @@ class _HubCache:
1302
1306
 
1303
1307
  def put(self, bucket: str, key: str, value: Dict[str, Any]) -> None:
1304
1308
  self.data[bucket][key] = dict(value, fetched_at=time.time())
1309
+ while len(self.data[bucket]) > 512:
1310
+ oldest = min(self.data[bucket], key=lambda k: self.data[bucket][k].get("fetched_at", 0))
1311
+ del self.data[bucket][oldest]
1305
1312
  self.dirty = True
1306
1313
 
1307
1314
  def save(self) -> None:
@@ -1317,7 +1324,52 @@ class _HubCache:
1317
1324
 
1318
1325
 
1319
1326
  class _RateLimited(Exception):
1320
- pass
1327
+ def __init__(self, message: str, retry_at: Optional[float] = None):
1328
+ super().__init__(message)
1329
+ self.retry_at = retry_at or time.time() + 60
1330
+
1331
+
1332
+ def _rate_limit(exc: BaseException, api: Any) -> _RateLimited:
1333
+ now = time.time()
1334
+ headers = getattr(getattr(exc, "response", None), "headers", {}) or {}
1335
+ retry = headers.get("Retry-After")
1336
+ delay = None
1337
+ try:
1338
+ candidate = float(retry)
1339
+ if math.isfinite(candidate) and candidate > 0:
1340
+ delay = candidate
1341
+ except (TypeError, ValueError, OverflowError):
1342
+ try:
1343
+ candidate = parsedate_to_datetime(retry).timestamp() - now
1344
+ if math.isfinite(candidate) and candidate > 0:
1345
+ delay = candidate
1346
+ except (TypeError, ValueError, OverflowError):
1347
+ pass
1348
+ if delay is None:
1349
+ reset = re.search(r"(?:^|;)\s*t\s*=\s*(\d+)", str(headers.get("RateLimit") or ""), re.I)
1350
+ try:
1351
+ if reset:
1352
+ candidate = float(reset.group(1))
1353
+ else:
1354
+ raw = float(headers.get("X-RateLimit-Reset") or headers.get("RateLimit-Reset"))
1355
+ candidate = raw - now if raw > now else raw
1356
+ if math.isfinite(candidate) and candidate > 0:
1357
+ delay = candidate
1358
+ except (TypeError, ValueError, OverflowError):
1359
+ pass
1360
+ deadline = now + max(1, delay if delay is not None else 60)
1361
+ for key in list(_hub_cooldowns):
1362
+ if _hub_cooldowns[key] <= now:
1363
+ del _hub_cooldowns[key]
1364
+ _hub_cooldowns[_hub_scope(api)] = deadline
1365
+ return _RateLimited(str(exc), deadline)
1366
+
1367
+
1368
+ _hub_cooldowns: Dict[str, float] = {}
1369
+
1370
+
1371
+ def _hub_scope(api: Any) -> str:
1372
+ return str(getattr(api, "scope", "") or hashlib.sha256((str(getattr(api, "endpoint", "https://huggingface.co")) + "\0" + str(getattr(api, "token", "") or "")).encode()).hexdigest())
1321
1373
 
1322
1374
 
1323
1375
  def _status_code(exc: BaseException) -> Optional[int]:
@@ -1330,10 +1382,10 @@ def _status_code(exc: BaseException) -> Optional[int]:
1330
1382
 
1331
1383
 
1332
1384
  def _default_hf_api() -> Any:
1333
- from huggingface_hub import HfApi # type: ignore
1385
+ from .hub_discovery import HubDiscovery
1334
1386
 
1335
1387
  token = os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN") or None
1336
- return HfApi(token=token)
1388
+ return HubDiscovery(token=token)
1337
1389
 
1338
1390
 
1339
1391
  def _hub_model_facts(api: Any, cache: _HubCache, repo_id: str) -> Dict[str, Any]:
@@ -1348,9 +1400,15 @@ def _hub_model_facts(api: Any, cache: _HubCache, repo_id: str) -> Dict[str, Any]
1348
1400
  info = api.model_info(repo_id, files_metadata=True)
1349
1401
  except Exception as exc:
1350
1402
  if _status_code(exc) == 429:
1351
- raise _RateLimited(str(exc)) from exc
1403
+ raise _rate_limit(exc, api) from exc
1352
1404
  cache.put("models", repo_id, {"error": f"{type(exc).__name__}: {str(exc)[:200]}"})
1353
1405
  raise LookupError(str(exc)) from exc
1406
+ facts = _hub_info_facts(info)
1407
+ cache.put("models", repo_id, facts)
1408
+ return facts
1409
+
1410
+
1411
+ def _hub_info_facts(info: Any) -> Dict[str, Any]:
1354
1412
  siblings = []
1355
1413
  for s in getattr(info, "siblings", None) or []:
1356
1414
  name = getattr(s, "rfilename", None)
@@ -1374,7 +1432,6 @@ def _hub_model_facts(api: Any, cache: _HubCache, repo_id: str) -> Dict[str, Any]
1374
1432
  "gated": bool(getattr(info, "gated", False)),
1375
1433
  "gguf_architecture": str(arch).strip() if isinstance(arch, str) and arch.strip() else None,
1376
1434
  }
1377
- cache.put("models", repo_id, facts)
1378
1435
  return facts
1379
1436
 
1380
1437
 
@@ -1386,6 +1443,8 @@ def _size_for(facts: Mapping[str, Any], patterns: Optional[List[str]]) -> Option
1386
1443
  for name, size in facts.get("siblings") or []:
1387
1444
  if patterns and not _matches_any(str(name), patterns):
1388
1445
  continue
1446
+ if not isinstance(size, int):
1447
+ return None
1389
1448
  if isinstance(size, int):
1390
1449
  total += size
1391
1450
  counted = True
@@ -1401,17 +1460,14 @@ def _hub_search(
1401
1460
  *,
1402
1461
  page: int = 1,
1403
1462
  page_size: int = _HUB_SEARCH_LIMIT,
1463
+ cache_only: bool = False,
1404
1464
  ) -> Tuple[List[Dict[str, Any]], bool]:
1405
- """Free-text hub search, one page -> (`[{repo_id, provider, downloads}]`, more pages exist).
1406
-
1407
- Each lane (GGUF; MLX on Apple silicon) is asked for the results up to the end of `page`
1408
- plus one, most downloaded first, and cached per depth; the page is that slice. A handle
1409
- (`Qwen/Qwen3.5-9B`) searches its name (`Qwen3.5-9B`): the GGUF and MLX builds of a model
1410
- live under other organisations (`catalog` looks the handle itself up)."""
1411
-
1465
+ """Cached summary pages: one listing per lane, never per-hit metadata."""
1412
1466
  term = query.split("/", 1)[1] if _HUB_HANDLE_RE.match(query) else query
1413
1467
  start = (page - 1) * page_size
1414
- want = min(_HUB_SEARCH_MAX_RESULTS, start + page_size + 1)
1468
+ if start >= _HUB_SEARCH_MAX_RESULTS:
1469
+ return [], False
1470
+ limit = min(page_size, _HUB_SEARCH_MAX_RESULTS - start)
1415
1471
  lanes: List[Tuple[str, str]] = []
1416
1472
  if engine in (None, "", "mlx") and (accelerator == "metal" or engine == "mlx"):
1417
1473
  lanes.append(("mlx", "mlx"))
@@ -1420,25 +1476,33 @@ def _hub_search(
1420
1476
  out: List[Dict[str, Any]] = []
1421
1477
  more = False
1422
1478
  for tag, provider in lanes:
1423
- key = f"{tag}:{term.lower()}" + ("" if want == _HUB_SEARCH_LIMIT + 1 else f"#{want}")
1479
+ prefix = f"summary-v2:{tag}:{term.lower()}:{page_size}:"
1480
+ key = prefix + str(page)
1424
1481
  hit = cache.get("searches", key)
1482
+ if hit is None and cache_only:
1483
+ continue
1425
1484
  if hit is None:
1485
+ previous = cache.get("searches", prefix + str(page - 1)) if page > 1 else None
1486
+ if page > 1 and previous is not None and not previous.get("next"):
1487
+ cache.put("searches", key, {"results": [], "next": None})
1488
+ continue # this lane is exhausted; other lanes can still advance
1489
+ if page > 1 and previous is None:
1490
+ raise LookupError("Search page expired; start the search again before requesting the next page")
1426
1491
  try:
1427
- results = api.list_models(search=term, filter=tag, sort="downloads", limit=want)
1428
- rows = [
1429
- {"repo_id": str(getattr(m, "id", None) or getattr(m, "modelId", "")), "downloads": getattr(m, "downloads", None)}
1430
- for m in results
1431
- ]
1492
+ results, cursor = api.list_models_page(search=term, filter=tag, limit=limit,
1493
+ cursor=previous.get("next") if previous else None)
1494
+ rows = [{"repo_id": str(getattr(m, "id", None) or getattr(m, "modelId", "")),
1495
+ "downloads": getattr(m, "downloads", None), "facts": _hub_info_facts(m)} for m in results]
1432
1496
  except Exception as exc:
1433
1497
  if _status_code(exc) == 429:
1434
- raise _RateLimited(str(exc)) from exc
1498
+ raise _rate_limit(exc, api) from exc
1435
1499
  raise LookupError(str(exc)) from exc
1436
- cache.put("searches", key, {"results": rows})
1437
- hit = {"results": rows}
1438
- rows = [r for r in hit.get("results") or [] if r.get("repo_id")]
1439
- more = more or (len(rows) > start + page_size and start + page_size < _HUB_SEARCH_MAX_RESULTS)
1440
- for r in rows[start : start + page_size]:
1441
- out.append({"repo_id": r["repo_id"], "provider": provider, "downloads": r.get("downloads")})
1500
+ hit = {"results": rows, "next": cursor}
1501
+ cache.put("searches", key, hit)
1502
+ more = more or (bool(hit.get("next")) and start + limit < _HUB_SEARCH_MAX_RESULTS)
1503
+ for row in hit.get("results") or []:
1504
+ if row.get("repo_id"):
1505
+ out.append(dict(row, provider=provider))
1442
1506
  return out, more
1443
1507
 
1444
1508
 
@@ -1920,6 +1984,7 @@ def catalog(
1920
1984
  hub_page: int = 1,
1921
1985
  hub_page_size: int = _HUB_SEARCH_LIMIT,
1922
1986
  extended: bool = False,
1987
+ detail_repo: Optional[str] = None,
1923
1988
  ) -> Dict[str, Any]:
1924
1989
  """Contract C: the `model_catalog_v1` payload.
1925
1990
 
@@ -1929,18 +1994,19 @@ def catalog(
1929
1994
  fits keep only artifacts that fit this host (`FITS_FILTER_VERDICTS`:
1930
1995
  `fits`, `tight`, or `needs_gpu_limit` -- fits once the Mac's GPU
1931
1996
  memory limit is raised, the command in `fit.gpu_limit`)
1932
- hub enrich HF-hosted artifacts with exact sizes from the Hugging Face
1933
- API and, when `q` is given, append `hf_search` rows
1997
+ hub append bounded Hugging Face summary search rows when `q` is given;
1998
+ curated rows keep their catalog sizes (no eager metadata enrichment)
1999
+ detail_repo
2000
+ one exact org/repo whose full metadata is explicitly requested (with hub)
1934
2001
  tags keep only rows carrying every tag (e.g. ["embedding"])
1935
2002
  hub_page, hub_page_size
1936
2003
  which page of Hugging Face search results to append (1-based; 20 per lane
1937
2004
  by default). `hub.search` reports the page, how many rows it added and
1938
2005
  whether another page exists. A query that is an exact handle
1939
2006
  (`Qwen/Qwen3.5-9B`) also looks that repo up and lists it first (page 1).
1940
- extended with `hub`: every quant the row's own GGUF repos ship (UD-IQ1_S ... Q8_0,
1941
- BF16), each with its exact size from the hub's file list (cached 24 h) and
1942
- its fit, marked `extended: true`; a label the download pattern cannot
1943
- fetch exactly is left out. Never the pre-selection.
2007
+ extended lists verified quant labels from available summary/detail facts;
2008
+ it never causes additional repo lookups. Sizes may be unknown until
2009
+ explicit details. Variants are marked `extended`, never preselected.
1944
2010
  """
1945
2011
 
1946
2012
  from ..utils.host_profile import host_profile, utc_now_iso
@@ -1958,7 +2024,11 @@ def catalog(
1958
2024
  page_size = min(100, max(1, int(hub_page_size)))
1959
2025
  except (TypeError, ValueError):
1960
2026
  page_size = _HUB_SEARCH_LIMIT
1961
- handle = query if _HUB_HANDLE_RE.match(query) else None
2027
+ if detail_repo and not hub:
2028
+ raise ValueError("detail_repo requires hub=True")
2029
+ if detail_repo and not _HUB_HANDLE_RE.fullmatch(detail_repo):
2030
+ raise ValueError("detail_repo must be one exact org/repo handle")
2031
+ handle = detail_repo or (query if _HUB_HANDLE_RE.match(query) else None)
1962
2032
  search_more = False
1963
2033
  want_tags = [_norm(t) for t in (tags or []) if _norm(t)]
1964
2034
  seed = load_seed()
@@ -1999,42 +2069,53 @@ def catalog(
1999
2069
  hub_block.update(ok=False)
2000
2070
  hub_block["errors"].append(f"huggingface_hub unavailable: {exc}")
2001
2071
  if api is not None:
2002
- repos: List[str] = []
2003
- for r in seed_rows:
2004
- for a in r["artifacts"]:
2005
- if a["provider"] in _HF_REPO_PROVIDERS and (eng is None or _artifact_matches_engine(a, eng) or a["provider"] == eng):
2006
- repo = mm.hf_artifact_parts(a["artifact"])[0]
2007
- if repo not in repos:
2008
- repos.append(repo)
2072
+ # Partition cache by endpoint and effective credentials; no token is serialized.
2073
+ scope = _hub_scope(api)
2074
+ if cache.data.get("scope") != scope:
2075
+ cache.data = {"models": {}, "searches": {}, "scope": scope}
2009
2076
  try:
2077
+ cooldown = max(float(cache.data.get("retry_at") or 0), _hub_cooldowns.get(scope, 0))
2078
+ if cooldown > time.time():
2079
+ raise _RateLimited("discovery cooldown active", cooldown)
2010
2080
  if handle and page == 1:
2011
2081
  try:
2012
- exact = _hub_handle_hit(_hub_model_facts(api, cache, handle), handle)
2082
+ was_cached = cache.get("models", handle) is not None
2083
+ facts = _hub_model_facts(api, cache, handle)
2084
+ hub_ctx["facts"][handle] = facts
2085
+ hub_block["cached" if was_cached else "fetched"] += 1
2086
+ exact = _hub_handle_hit(facts, handle)
2013
2087
  except LookupError:
2014
- exact = None # no such repo (or private): the name search still runs
2088
+ exact = None
2015
2089
  if exact is not None:
2016
2090
  search_hits.append(exact)
2017
- if query:
2091
+ if query and not detail_repo:
2018
2092
  hits, search_more = _hub_search(api, cache, query, eng, accelerator, page=page, page_size=page_size)
2019
2093
  search_hits.extend(hits)
2020
- for hit in search_hits:
2021
- if hit["repo_id"] not in repos:
2022
- repos.append(hit["repo_id"])
2023
- for repo in repos:
2024
- was_cached = cache.get("models", repo) is not None
2025
- try:
2026
- hub_ctx["facts"][repo] = _hub_model_facts(api, cache, repo)
2027
- hub_block["cached" if was_cached else "fetched"] += 1
2028
- except LookupError as exc:
2029
- hub_block["errors"].append(f"{repo}: {str(exc)[:160]}")
2094
+ for hit in hits:
2095
+ repo = hit["repo_id"]
2096
+ # An explicitly fetched detail may enrich a listing, never trigger another request.
2097
+ hub_ctx["facts"].setdefault(repo, cache.get("models", repo) or hit.get("facts") or {})
2098
+ # Curated rows keep their verified catalog sizes. Enrichment is explicit and single-repo.
2099
+ if not query and not detail_repo:
2100
+ hub_block["mode"] = "offline"
2030
2101
  except _RateLimited as exc:
2031
- hub_block.update(ok=False)
2032
- hub_block["errors"].append(f"rate limited by the Hugging Face API (429); showing cached/offline data: {str(exc)[:160]}")
2102
+ cache.data["retry_at"] = exc.retry_at
2103
+ cache.dirty = True
2104
+ hub_block.update(ok=False, rate_limited=True, retry_at=exc.retry_at,
2105
+ retry_after_seconds=max(1, math.ceil(exc.retry_at - time.time())))
2106
+ hub_block["errors"].append("rate limited by the Hugging Face API (429); wait until the retry deadline")
2107
+ if query and not detail_repo:
2108
+ cached_hits, search_more = _hub_search(api, cache, query, eng, accelerator,
2109
+ page=page, page_size=page_size, cache_only=True)
2110
+ seen = {hit["repo_id"] for hit in search_hits}
2111
+ for hit in cached_hits:
2112
+ if hit["repo_id"] not in seen:
2113
+ search_hits.append(hit)
2114
+ hub_ctx["facts"].setdefault(hit["repo_id"], hit.get("facts") or {})
2115
+ hub_block["cached_results"] = bool(cached_hits)
2033
2116
  except LookupError as exc:
2034
2117
  hub_block.update(ok=False)
2035
- hub_block["errors"].append(f"hub search failed (offline?): {str(exc)[:200]}")
2036
- if repos and not (hub_block["fetched"] or hub_block["cached"]):
2037
- hub_block["ok"] = False # every lookup failed: offline or blocked
2118
+ hub_block["errors"].append(f"hub search failed: {str(exc)[:200]}")
2038
2119
  cache.save()
2039
2120
 
2040
2121
  try:
@@ -2159,8 +2240,7 @@ def catalog(
2159
2240
  quant = _pick_gguf_quant(facts)
2160
2241
  if quant:
2161
2242
  artifact = f"{artifact}:{quant}"
2162
- elif not hit.get("exact"):
2163
- continue # a GGUF repo with no recognisable quant file is not one click
2243
+ # Missing summary siblings remain discoverable; require explicit details before download.
2164
2244
  # else: the exact handle of a Transformers checkpoint, fetched whole
2165
2245
  elif provider == "mlx":
2166
2246
  import re as _re
@@ -2179,6 +2259,8 @@ def catalog(
2179
2259
  hub_ctx,
2180
2260
  installed_sizes,
2181
2261
  )
2262
+ if provider == "huggingface" and not quant and not hit.get("exact"):
2263
+ art.update(downloadable=False, recommended=False, details_required=True)
2182
2264
  name = hit["repo_id"].rsplit("/", 1)[-1]
2183
2265
  row = {
2184
2266
  "id": "hf:" + hit["repo_id"].lower(),
@@ -2191,6 +2273,7 @@ def catalog(
2191
2273
  "license": None,
2192
2274
  "capabilities": caps,
2193
2275
  "source": "hf_search",
2276
+ "detail_repo": hit["repo_id"],
2194
2277
  "tags": tags_hit,
2195
2278
  "starter": False,
2196
2279
  "notes": (
@@ -2202,6 +2285,8 @@ def catalog(
2202
2285
  "exact": bool(hit.get("exact")),
2203
2286
  "artifacts": [art],
2204
2287
  }
2288
+ if extended and detail_repo and quant:
2289
+ row["artifacts"] += _extended_quant_artifacts(row, row["artifacts"], caps, profile, support, hub_ctx, installed_sizes)
2205
2290
  if asset_kind == "adapter":
2206
2291
  art["asset_kind"] = "adapter"
2207
2292
  art["fit"] = {"verdict":"unknown", "notes":[]}
@@ -296,7 +296,9 @@ def _handle_repair_refs(args: argparse.Namespace) -> int:
296
296
  def _catalog_args(parser: argparse.ArgumentParser) -> None:
297
297
  parser.add_argument("--engine", default=None, help="Only artifacts for this engine/provider (ollama, lmstudio, mlx, huggingface, llamacpp)")
298
298
  parser.add_argument("--fits", action="store_true", help="Only artifacts that fit this machine (fits or tight)")
299
- parser.add_argument("--hub", action="store_true", help="Ask Hugging Face for exact sizes (and search results); cached 24 h")
299
+ parser.add_argument("--hub", action="store_true", help="Search Hugging Face summary pages; cached 24 h")
300
+ parser.add_argument("--detail-repo", default=None, help="Read full metadata for one selected org/repo (implies --hub)")
301
+ parser.add_argument("--extended", action="store_true", help="List verified quant variants from available metadata")
300
302
  parser.add_argument("--tag", action="append", default=None, help="Only rows with this tag (repeatable): chat, coding, vision, embedding...")
301
303
  parser.add_argument("--json", action="store_true", help="Emit model_catalog_v1 JSON")
302
304
 
@@ -337,8 +339,10 @@ def _handle_catalog(args: argparse.Namespace) -> int:
337
339
  getattr(args, "query", None),
338
340
  engine=args.engine,
339
341
  fits=bool(args.fits),
340
- hub=bool(args.hub),
342
+ hub=bool(args.hub or getattr(args, "detail_repo", None)),
341
343
  tags=args.tag,
344
+ **({"detail_repo": args.detail_repo} if getattr(args, "detail_repo", None) else {}),
345
+ **({"extended": True} if getattr(args, "extended", False) else {}),
342
346
  )
343
347
  if args.json:
344
348
  _print_json(payload)
@@ -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.20.0"
66
+ ABSTRACTVOICE_ENGINE_RUNTIME_FLOOR = "0.21.1"
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
@@ -12,7 +12,7 @@ from enum import Enum
12
12
  from typing import Any, AsyncIterator, Dict, List, Optional
13
13
 
14
14
  import httpx
15
- from .utils.install_settings import local_engines_hint
15
+ from .utils.install_settings import light_repair_hint, local_engines_hint
16
16
 
17
17
 
18
18
  class DownloadStatus(Enum):
@@ -226,10 +226,8 @@ async def _download_huggingface(
226
226
  except ImportError:
227
227
  yield DownloadProgress(
228
228
  status=DownloadStatus.ERROR,
229
- message=(
230
- "huggingface_hub is not installed. "
231
- f"{local_engines_hint('Downloading Hugging Face models')}"
232
- ),
229
+ # huggingface_hub is part of the light set (2.35.1): missing means a broken install.
230
+ message=f"huggingface_hub is not installed: {light_repair_hint()}",
233
231
  )
234
232
  return
235
233
 
@@ -250,6 +250,35 @@ def _refuse_resident_gguf_embedding(path: Any) -> None:
250
250
  )
251
251
 
252
252
 
253
+ def release_shared_gguf_embeddings_at_exit() -> int:
254
+ """Close every resident GGUF embedding encoder (llama.cpp) before the interpreter tears modules
255
+ down. Without this, a process that embedded with a GGUF model on Apple silicon aborted at exit
256
+ with ``ggml-metal-device.m: GGML_ASSERT([rsets->data count] == 0)`` (exit code 134) after every
257
+ call had succeeded: the llama.cpp context was freed during module teardown, after the Metal
258
+ device, instead of before it (found on the Mac during the 0.14.0 profile proof, 2.35.1).
259
+ SentenceTransformer models need nothing here. Returns the number of encoders closed."""
260
+ closed = 0
261
+ with _SHARED_EMBEDDING_LOCK:
262
+ entries = list(_SHARED_EMBEDDING_MODELS.values())
263
+ for entry in entries:
264
+ model = entry.model
265
+ close = getattr(model, "close", None)
266
+ if model is None or not callable(close) or not hasattr(model, "_llm"):
267
+ continue
268
+ try:
269
+ close()
270
+ closed += 1
271
+ except Exception:
272
+ pass
273
+ entry.model = None
274
+ return closed
275
+
276
+
277
+ # Registered once at import: atexit handlers run before module teardown, in reverse order of
278
+ # registration, so the encoders go first (see release_shared_gguf_embeddings_at_exit).
279
+ atexit.register(release_shared_gguf_embeddings_at_exit)
280
+
281
+
253
282
  def _atexit_save_caches(ref: "weakref.ReferenceType") -> None:
254
283
  manager = ref()
255
284
  if manager is None: