abstractcore 2.26.0__tar.gz → 2.26.3__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 (337) hide show
  1. {abstractcore-2.26.0 → abstractcore-2.26.3}/PKG-INFO +1 -1
  2. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/apps/deepsearch.py +3 -0
  3. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/apps/extractor.py +3 -0
  4. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/apps/intent.py +3 -0
  5. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/apps/judge.py +3 -0
  6. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/apps/summarizer.py +3 -0
  7. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/capabilities/vision_catalog.py +2 -0
  8. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/config/main.py +3 -0
  9. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/config/model_materializer.py +55 -2
  10. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/endpoint/app.py +3 -0
  11. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/huggingface_provider.py +12 -1
  12. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/process_tree.py +11 -1
  13. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/sandbox.py +8 -2
  14. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/cli.py +3 -0
  15. abstractcore-2.26.3/abstractcore/utils/stdio.py +29 -0
  16. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/version.py +1 -1
  17. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore.egg-info/PKG-INFO +1 -1
  18. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore.egg-info/SOURCES.txt +3 -1
  19. abstractcore-2.26.3/tests/test_windows_host_fixes.py +176 -0
  20. {abstractcore-2.26.0 → abstractcore-2.26.3}/LICENSE +0 -0
  21. {abstractcore-2.26.0 → abstractcore-2.26.3}/README.md +0 -0
  22. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/__init__.py +0 -0
  23. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/apps/__init__.py +0 -0
  24. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/apps/__main__.py +0 -0
  25. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/apps/app_config_utils.py +0 -0
  26. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/architectures/__init__.py +0 -0
  27. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/architectures/detection.py +0 -0
  28. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/architectures/enums.py +0 -0
  29. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/architectures/response_postprocessing.py +0 -0
  30. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/architectures/thinking_controls.py +0 -0
  31. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/assets/OCRA.ttf +0 -0
  32. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/assets/OCRB.ttf +0 -0
  33. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/assets/OCRBL.ttf +0 -0
  34. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/assets/README.md +0 -0
  35. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/assets/abstractframework_identity.json +0 -0
  36. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/assets/architecture_formats.json +0 -0
  37. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/assets/bloc-schema.jsonld +0 -0
  38. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/assets/model_capabilities.json +0 -0
  39. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/assets/model_capabilities.schema.json +0 -0
  40. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/assets/model_downloads_catalog.json +0 -0
  41. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/assets/model_downloads_catalog.schema.json +0 -0
  42. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/assets/semantic_models.md +0 -0
  43. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/assets/session_schema.json +0 -0
  44. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/capabilities/__init__.py +0 -0
  45. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/capabilities/errors.py +0 -0
  46. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/capabilities/host.py +0 -0
  47. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/capabilities/music_selectors.py +0 -0
  48. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/capabilities/registry.py +0 -0
  49. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/capabilities/scene3d_selectors.py +0 -0
  50. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/capabilities/types.py +0 -0
  51. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/__init__.py +0 -0
  52. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/email/__init__.py +0 -0
  53. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/email/bodystructure.py +0 -0
  54. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/email/client.py +0 -0
  55. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/email/context.py +0 -0
  56. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/email/discovery.py +0 -0
  57. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/email/errors.py +0 -0
  58. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/email/imap_codec.py +0 -0
  59. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/email/legacy.py +0 -0
  60. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/email/limits.py +0 -0
  61. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/email/models.py +0 -0
  62. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/email/oauth.py +0 -0
  63. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/email/policy.py +0 -0
  64. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/email/search.py +0 -0
  65. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/email/store.py +0 -0
  66. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/comms/email/vault.py +0 -0
  67. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/compression/__init__.py +0 -0
  68. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/compression/analytics.py +0 -0
  69. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/compression/cache.py +0 -0
  70. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/compression/config.py +0 -0
  71. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/compression/exceptions.py +0 -0
  72. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/compression/glyph_processor.py +0 -0
  73. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/compression/optimizer.py +0 -0
  74. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/compression/orchestrator.py +0 -0
  75. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/compression/pil_text_renderer.py +0 -0
  76. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/compression/quality.py +0 -0
  77. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/compression/text_formatter.py +0 -0
  78. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/compression/vision_compressor.py +0 -0
  79. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/config/__init__.py +0 -0
  80. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/config/capability_defaults.py +0 -0
  81. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/config/email_cli.py +0 -0
  82. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/config/engines.py +0 -0
  83. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/config/host_jobs.py +0 -0
  84. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/config/manager.py +0 -0
  85. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/config/model_catalog.py +0 -0
  86. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/config/model_verify.py +0 -0
  87. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/config/models_engines_cli.py +0 -0
  88. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/config/provider_profiles.py +0 -0
  89. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/config/recommendations.py +0 -0
  90. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/config/route_engines.py +0 -0
  91. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/config/vision_config.py +0 -0
  92. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/console/__init__.py +0 -0
  93. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/console/theme_sync.py +0 -0
  94. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/console/themes.py +0 -0
  95. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/console/web.py +0 -0
  96. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/__init__.py +0 -0
  97. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/bloc_kv.py +0 -0
  98. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/bloc_metadata.py +0 -0
  99. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/cached_session.py +0 -0
  100. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/degeneration.py +0 -0
  101. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/endpoint_damping.py +0 -0
  102. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/enums.py +0 -0
  103. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/factory.py +0 -0
  104. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/file_blocs.py +0 -0
  105. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/file_boxes.py +0 -0
  106. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/generate_contract.py +0 -0
  107. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/interface.py +0 -0
  108. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/multimodal_generation.py +0 -0
  109. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/output_specs.py +0 -0
  110. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/retry.py +0 -0
  111. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/session.py +0 -0
  112. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/core/types.py +0 -0
  113. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/download.py +0 -0
  114. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/embeddings/__init__.py +0 -0
  115. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/embeddings/manager.py +0 -0
  116. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/embeddings/models.py +0 -0
  117. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/endpoint/__init__.py +0 -0
  118. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/events/__init__.py +0 -0
  119. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/exceptions/__init__.py +0 -0
  120. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/mcp/__init__.py +0 -0
  121. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/mcp/client.py +0 -0
  122. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/mcp/factory.py +0 -0
  123. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/mcp/naming.py +0 -0
  124. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/mcp/stdio_client.py +0 -0
  125. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/mcp/tool_source.py +0 -0
  126. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/__init__.py +0 -0
  127. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/auto_handler.py +0 -0
  128. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/base.py +0 -0
  129. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/capabilities.py +0 -0
  130. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/delivery.py +0 -0
  131. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/enrichment.py +0 -0
  132. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/handlers/__init__.py +0 -0
  133. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/handlers/anthropic_handler.py +0 -0
  134. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/handlers/local_handler.py +0 -0
  135. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/handlers/openai_handler.py +0 -0
  136. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/openai_parts.py +0 -0
  137. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/pdf_routing.py +0 -0
  138. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/processors/__init__.py +0 -0
  139. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/processors/audio_processor.py +0 -0
  140. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/processors/direct_pdf_processor.py +0 -0
  141. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/processors/glyph_pdf_processor.py +0 -0
  142. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/processors/image_processor.py +0 -0
  143. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/processors/office_processor.py +0 -0
  144. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/processors/pdf_processor.py +0 -0
  145. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/processors/text_processor.py +0 -0
  146. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/processors/video_processor.py +0 -0
  147. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/types.py +0 -0
  148. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/utils/__init__.py +0 -0
  149. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/utils/image_scaler.py +0 -0
  150. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/utils/video_frames.py +0 -0
  151. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/media/vision_fallback.py +0 -0
  152. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/processing/__init__.py +0 -0
  153. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/processing/basic_deepsearch.py +0 -0
  154. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/processing/basic_extractor.py +0 -0
  155. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/processing/basic_intent.py +0 -0
  156. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/processing/basic_judge.py +0 -0
  157. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/processing/basic_summarizer.py +0 -0
  158. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/__init__.py +0 -0
  159. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/_http.py +0 -0
  160. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/anthropic_provider.py +0 -0
  161. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/base.py +0 -0
  162. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/capability_host.py +0 -0
  163. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/endpoint_context.py +0 -0
  164. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/generation_cancel.py +0 -0
  165. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/generation_progress.py +0 -0
  166. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/hf_residency.py +0 -0
  167. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/lmstudio_provider.py +0 -0
  168. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/mlx_model_rules.py +0 -0
  169. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/mlx_native_cache.py +0 -0
  170. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/mlx_native_session.py +0 -0
  171. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/mlx_prefill_observer.py +0 -0
  172. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/mlx_provider.py +0 -0
  173. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/mlx_qwen4.py +0 -0
  174. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/mlx_residency.py +0 -0
  175. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/mlx_runtime.py +0 -0
  176. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/mlx_vision_addon.py +0 -0
  177. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/model_capabilities.py +0 -0
  178. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/model_config_fingerprint.py +0 -0
  179. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/ollama_provider.py +0 -0
  180. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/openai_compatible_provider.py +0 -0
  181. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/openai_provider.py +0 -0
  182. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/openrouter_provider.py +0 -0
  183. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/portkey_provider.py +0 -0
  184. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/process_residency.py +0 -0
  185. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/registry.py +0 -0
  186. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/speculation.py +0 -0
  187. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/streaming.py +0 -0
  188. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/tokenizer_fingerprint.py +0 -0
  189. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/vllm_provider.py +0 -0
  190. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/providers/weights_fingerprint.py +0 -0
  191. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/py.typed +0 -0
  192. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/server/__init__.py +0 -0
  193. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/server/__main__.py +0 -0
  194. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/server/app.py +0 -0
  195. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/server/audio_endpoints.py +0 -0
  196. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/server/auth_policy.py +0 -0
  197. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/server/camera_endpoints.py +0 -0
  198. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/server/capability_generation.py +0 -0
  199. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/server/console_routes.py +0 -0
  200. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/server/credentials.py +0 -0
  201. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/server/email_routes.py +0 -0
  202. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/server/first_run.py +0 -0
  203. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/server/host_routes.py +0 -0
  204. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/server/scene3d_endpoints.py +0 -0
  205. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/server/session_routes.py +0 -0
  206. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/server/vision_endpoints.py +0 -0
  207. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/structured/__init__.py +0 -0
  208. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/structured/handler.py +0 -0
  209. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/structured/json_schema.py +0 -0
  210. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/structured/retry.py +0 -0
  211. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/structured/schema_compat.py +0 -0
  212. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/testing/__init__.py +0 -0
  213. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/testing/mailserver.py +0 -0
  214. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/__init__.py +0 -0
  215. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/abstractignore.py +0 -0
  216. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/arg_canonicalizer.py +0 -0
  217. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/arg_coercion.py +0 -0
  218. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/browser_tools.py +0 -0
  219. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/code_analysis.py +0 -0
  220. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/common_tools.py +0 -0
  221. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/comms_tools.py +0 -0
  222. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/core.py +0 -0
  223. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/email_digests.py +0 -0
  224. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/fetch_url_ssrf.py +0 -0
  225. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/handler.py +0 -0
  226. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/inventory.py +0 -0
  227. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/parser.py +0 -0
  228. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/registry.py +0 -0
  229. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/risk_facts.py +0 -0
  230. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/shell_session.py +0 -0
  231. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/shell_tools.py +0 -0
  232. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/syntax_rewriter.py +0 -0
  233. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/tag_rewriter.py +0 -0
  234. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/telegram_tdlib.py +0 -0
  235. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/telegram_tools.py +0 -0
  236. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/tools/wire_naming.py +0 -0
  237. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/__init__.py +0 -0
  238. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/async_stream.py +0 -0
  239. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/context_calibration.py +0 -0
  240. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/context_estimate.py +0 -0
  241. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/data_registry.py +0 -0
  242. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/display.py +0 -0
  243. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/file_filters.py +0 -0
  244. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/host_profile.py +0 -0
  245. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/hostinfo.py +0 -0
  246. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/identity.py +0 -0
  247. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/install_settings.py +0 -0
  248. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/jsonish.py +0 -0
  249. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/memory.py +0 -0
  250. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/message_preprocessor.py +0 -0
  251. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/model_cache.py +0 -0
  252. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/model_fit.py +0 -0
  253. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/residency.py +0 -0
  254. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/self_fixes.py +0 -0
  255. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/structured_logging.py +0 -0
  256. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/token_utils.py +0 -0
  257. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/trace_export.py +0 -0
  258. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/truncation.py +0 -0
  259. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/vlm_token_calculator.py +0 -0
  260. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/windows_dll.py +0 -0
  261. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore/utils/workspace_paths.py +0 -0
  262. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore.egg-info/dependency_links.txt +0 -0
  263. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore.egg-info/entry_points.txt +0 -0
  264. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore.egg-info/requires.txt +0 -0
  265. {abstractcore-2.26.0 → abstractcore-2.26.3}/abstractcore.egg-info/top_level.txt +0 -0
  266. {abstractcore-2.26.0 → abstractcore-2.26.3}/pyproject.toml +0 -0
  267. {abstractcore-2.26.0 → abstractcore-2.26.3}/setup.cfg +0 -0
  268. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_abstractendpoint_singleton_provider.py +0 -0
  269. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_agentic_cli_compatibility.py +0 -0
  270. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_basic_session.py +0 -0
  271. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_bloc_kv.py +0 -0
  272. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_bloc_kv_cache_dtype.py +0 -0
  273. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_bloc_kv_endpoint.py +0 -0
  274. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_bloc_kv_engine_fingerprint.py +0 -0
  275. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_bloc_kv_model_config_fingerprint.py +0 -0
  276. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_bloc_kv_tokenizer_fingerprint.py +0 -0
  277. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_bloc_kv_weights_fingerprint.py +0 -0
  278. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_bloc_metadata_json_extract.py +0 -0
  279. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_cached_session_kv_mode.py +0 -0
  280. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_cached_session_kv_thinking_prefix.py +0 -0
  281. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_capabilities_registry.py +0 -0
  282. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_capabilities_registry_preferred_backends_from_config.py +0 -0
  283. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_complete_integration.py +0 -0
  284. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_comprehensive_events.py +0 -0
  285. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_console_about.py +0 -0
  286. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_console_email_tab.py +0 -0
  287. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_console_web.py +0 -0
  288. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_core_components.py +0 -0
  289. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_display_rule.py +0 -0
  290. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_email_digests.py +0 -0
  291. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_embedding_model_token_warnings.py +0 -0
  292. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_enhanced_prompt.py +0 -0
  293. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_environment_variable_tool_call_tags.py +0 -0
  294. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_factory.py +0 -0
  295. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_file_bloc_store_unit.py +0 -0
  296. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_file_boxes_cached_session.py +0 -0
  297. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_final_accuracy.py +0 -0
  298. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_final_comprehensive.py +0 -0
  299. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_final_graceful_errors.py +0 -0
  300. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_generate_contract.py +0 -0
  301. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_generate_response_reasoning_property_unit.py +0 -0
  302. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_generate_with_outputs.py +0 -0
  303. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_graceful_fallback.py +0 -0
  304. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_import_debug.py +0 -0
  305. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_import_safety.py +0 -0
  306. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_incremental_thinking_tag_stripper.py +0 -0
  307. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_install_hints_no_bare_packages.py +0 -0
  308. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_install_settings.py +0 -0
  309. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_integrated_functionality.py +0 -0
  310. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_llms_sources.py +0 -0
  311. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_mcp_handshake.py +0 -0
  312. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_mcp_integration.py +0 -0
  313. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_mcp_stdio_client.py +0 -0
  314. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_multimodal_generate_output.py +0 -0
  315. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_openai_prompt_cache_key_passthrough.py +0 -0
  316. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_output_specs.py +0 -0
  317. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_packaging_assets.py +0 -0
  318. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_packaging_extras.py +0 -0
  319. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_prompt_cache_api.py +0 -0
  320. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_prompt_cache_bloc_composition.py +0 -0
  321. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_prompt_cache_bloc_e2e_gate.py +0 -0
  322. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_prompt_cache_control_plane.py +0 -0
  323. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_prompt_cache_prepare_modules_thinking_http.py +0 -0
  324. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_retry_observability.py +0 -0
  325. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_retry_strategy.py +0 -0
  326. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_seed_determinism.py +0 -0
  327. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_seed_temperature_basic.py +0 -0
  328. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_sensory_prompting.py +0 -0
  329. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_server_prompt_cache_control_plane_proxy.py +0 -0
  330. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_server_prompt_cache_key_passthrough.py +0 -0
  331. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_session_reasoning_persistence_unit.py +0 -0
  332. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_sound_effect_seconds_route.py +0 -0
  333. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_structured_output_truncation_retry.py +0 -0
  334. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_text_only_model_experience.py +0 -0
  335. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_unload_memory.py +0 -0
  336. {abstractcore-2.26.0 → abstractcore-2.26.3}/tests/test_user_scenario_validation.py +0 -0
  337. {abstractcore-2.26.0 → abstractcore-2.26.3}/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.26.0
3
+ Version: 2.26.3
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>
@@ -348,6 +348,9 @@ def print_report(report, format_type: str) -> None:
348
348
 
349
349
  def main():
350
350
  """Main CLI function"""
351
+ from abstractcore.utils.stdio import safe_stdio
352
+
353
+ safe_stdio()
351
354
  parser = argparse.ArgumentParser(
352
355
  description="AbstractCore Deep Search - Autonomous research agent",
353
356
  formatter_class=argparse.RawDescriptionHelpFormatter,
@@ -145,6 +145,9 @@ def parse_extraction_length(length_str: Optional[str]) -> str:
145
145
 
146
146
  def main():
147
147
  """Main CLI function"""
148
+ from abstractcore.utils.stdio import safe_stdio
149
+
150
+ safe_stdio()
148
151
  parser = argparse.ArgumentParser(
149
152
  description="AbstractCore Entity & Relationship Extractor - Default: qwen3:4b-instruct-2507-q4_K_M (requires Ollama)",
150
153
  formatter_class=argparse.RawDescriptionHelpFormatter,
@@ -344,6 +344,9 @@ def _format_single_analysis_plain(analysis, analysis_time: float = None) -> list
344
344
 
345
345
  def main():
346
346
  """Main CLI function"""
347
+ from abstractcore.utils.stdio import safe_stdio
348
+
349
+ safe_stdio()
347
350
  parser = argparse.ArgumentParser(
348
351
  description="AbstractCore Intent Analyzer - Identify and analyze intents behind text",
349
352
  formatter_class=argparse.RawDescriptionHelpFormatter,
@@ -283,6 +283,9 @@ def format_assessment_plain(assessment: dict) -> str:
283
283
 
284
284
  def main():
285
285
  """Main CLI function"""
286
+ from abstractcore.utils.stdio import safe_stdio
287
+
288
+ safe_stdio()
286
289
  parser = argparse.ArgumentParser(
287
290
  description="AbstractCore Basic Judge - LLM-as-a-judge for objective evaluation (Default: qwen3:4b-instruct-2507-q4_K_M)",
288
291
  formatter_class=argparse.RawDescriptionHelpFormatter,
@@ -207,6 +207,9 @@ def format_summary_output(result) -> str:
207
207
 
208
208
  def main():
209
209
  """Main CLI function"""
210
+ from abstractcore.utils.stdio import safe_stdio
211
+
212
+ safe_stdio()
210
213
  parser = argparse.ArgumentParser(
211
214
  description="AbstractCore Document Summarizer - Default: gemma3:1b-it-qat (requires Ollama)",
212
215
  formatter_class=argparse.RawDescriptionHelpFormatter,
@@ -308,6 +308,8 @@ def _default_lmstudio_model_dirs() -> list[Path]:
308
308
 
309
309
  sysname = platform.system().lower()
310
310
  home = Path.home()
311
+ # LM Studio 0.3+ keeps its models in ~/.lmstudio/models on every OS (as utils/model_cache).
312
+ dirs.append(str(home / ".lmstudio" / "models"))
311
313
  if sysname == "darwin":
312
314
  dirs.append(str(home / "Library" / "Application Support" / "LM Studio" / "models"))
313
315
  elif sysname == "linux":
@@ -3122,6 +3122,9 @@ _CONFIG_SUBCOMMANDS = {
3122
3122
 
3123
3123
  def main(argv: List[str] = None):
3124
3124
  """Main CLI entry point."""
3125
+ from abstractcore.utils.stdio import safe_stdio
3126
+
3127
+ safe_stdio()
3125
3128
  if argv is None:
3126
3129
  argv = sys.argv[1:]
3127
3130
 
@@ -3170,8 +3170,11 @@ def _hf_marker_unfinished(repo_dir: Path) -> Optional[int]:
3170
3170
  etag = entry.get("etag") if isinstance(entry, dict) else None
3171
3171
  size = entry.get("size") if isinstance(entry, dict) else None
3172
3172
  blob = repo_dir / "blobs" / str(etag or "")
3173
+ name = entry.get("name") if isinstance(entry, dict) else None
3173
3174
  try:
3174
3175
  whole = bool(etag) and blob.is_file() and (not isinstance(size, int) or blob.stat().st_size == size)
3176
+ if not whole and etag and isinstance(name, str):
3177
+ whole = _hf_snapshot_copy_size(repo_dir, name, size if isinstance(size, int) else None) is not None
3175
3178
  except Exception:
3176
3179
  whole = False
3177
3180
  if not whole:
@@ -3389,8 +3392,37 @@ def repair_hf_refs(*, apply: bool = False, cache_dirs: Optional[Iterable[Path]]
3389
3392
  return {"apply": bool(apply), "cache_dirs": [str(d) for d in dirs], "rows": rows, "counts": counts}
3390
3393
 
3391
3394
 
3395
+ def _hf_snapshot_copy_size(repo_dir: Path, name: str, size: Optional[int]) -> Optional[int]:
3396
+ """The size of a whole COPY of `name` in a snapshot folder, else None.
3397
+
3398
+ Where the cache cannot hold symbolic links (Windows without Developer Mode),
3399
+ huggingface_hub downloads into `blobs/<etag>.incomplete` and then MOVES the
3400
+ file into `snapshots/<rev>/<name>`: no blob is left behind, and the snapshot
3401
+ entry is a regular file, not a link to a blob. A copy of the planned size
3402
+ (any size when the plan has none) is that file, whole."""
3403
+
3404
+ if not name:
3405
+ return None
3406
+ try:
3407
+ snaps = [d for d in (repo_dir / "snapshots").iterdir() if d.is_dir()]
3408
+ except Exception:
3409
+ return None
3410
+ for snap in snaps:
3411
+ path = snap / name
3412
+ try:
3413
+ if path.is_symlink() or not path.is_file():
3414
+ continue
3415
+ found = path.stat().st_size
3416
+ except Exception:
3417
+ continue
3418
+ if not isinstance(size, int) or found == size:
3419
+ return found
3420
+ return None
3421
+
3422
+
3392
3423
  class _HfBlobWatcher:
3393
- """Per-file progress from `blobs/<etag>(.incomplete)` sizes."""
3424
+ """Per-file progress from `blobs/<etag>(.incomplete)` sizes, or from the
3425
+ snapshot copy when the cache holds no links (`_hf_snapshot_copy_size`)."""
3394
3426
 
3395
3427
  def __init__(self, repo_dir: Path, plan: Optional[List[Dict[str, Any]]], emit: ProgressCallback, repo_id: str):
3396
3428
  self.repo_dir = repo_dir
@@ -3435,6 +3467,11 @@ class _HfBlobWatcher:
3435
3467
  row["bytes_done"] = whole.stat().st_size
3436
3468
  row["state"] = "done"
3437
3469
  continue
3470
+ copied = _hf_snapshot_copy_size(self.repo_dir, str(row.get("name") or ""), row.get("bytes_total"))
3471
+ if copied is not None and not _hf_partials(self.blobs, str(etag)):
3472
+ row["bytes_done"] = copied
3473
+ row["state"] = "done"
3474
+ continue
3438
3475
  partial = _hf_partial_size(self.blobs, str(etag))
3439
3476
  if partial is not None:
3440
3477
  row["bytes_done"] = partial
@@ -3560,10 +3597,26 @@ import json, os, sys, threading, time
3560
3597
  # The owner passes its pid (argv[2]): reading getppid() here could already
3561
3598
  # see the re-parented value if the owner died during this interpreter's start.
3562
3599
  _owner = int(sys.argv[2]) if len(sys.argv) > 2 else os.getppid()
3600
+ def _owner_gone_check():
3601
+ if os.name != "nt":
3602
+ return lambda: os.getppid() != _owner
3603
+ # Windows: a venv's python.exe is a launcher that starts the real
3604
+ # interpreter as ITS child, so getppid() is the launcher, never the owner.
3605
+ # Hold a handle on the owner instead (a handle pins the identity, unlike a
3606
+ # pid number) and ask whether that process has exited.
3607
+ import ctypes
3608
+ k32 = ctypes.WinDLL("kernel32", use_last_error=True)
3609
+ k32.OpenProcess.restype = ctypes.c_void_p
3610
+ k32.WaitForSingleObject.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
3611
+ handle = k32.OpenProcess(0x00100000, False, _owner) # SYNCHRONIZE
3612
+ if not handle:
3613
+ return lambda: True
3614
+ return lambda: k32.WaitForSingleObject(handle, 0) == 0 # WAIT_OBJECT_0: exited
3615
+ _owner_gone = _owner_gone_check()
3563
3616
  def _watch_owner():
3564
3617
  while True:
3565
3618
  time.sleep(1.0)
3566
- if os.getppid() != _owner:
3619
+ if _owner_gone():
3567
3620
  os._exit(75) # no goodbye line: the owner's end of the pipe is gone
3568
3621
  threading.Thread(target=_watch_owner, daemon=True).start()
3569
3622
  from huggingface_hub import snapshot_download
@@ -1172,6 +1172,9 @@ def _parse_args(argv: Optional[List[str]] = None) -> EndpointConfig:
1172
1172
 
1173
1173
 
1174
1174
  def main(argv: Optional[List[str]] = None) -> None:
1175
+ from abstractcore.utils.stdio import safe_stdio
1176
+
1177
+ safe_stdio()
1175
1178
  cfg = _parse_args(argv)
1176
1179
  app = create_app(provider_name=cfg.provider, model=cfg.model)
1177
1180
  import uvicorn
@@ -4531,8 +4531,19 @@ class HuggingFaceProvider(BaseProvider):
4531
4531
  )
4532
4532
  return False
4533
4533
 
4534
+ # `crop(n)` with a positive `n` (keep n tokens) was deprecated and is gone in
4535
+ # transformers 5.20; a negative `n` (drop |n| tokens) works on every version.
4536
+ keep = int(keep_tokens)
4534
4537
  try:
4535
- crop(int(keep_tokens))
4538
+ lengths = self._transformers_layer_seq_lengths(cache)
4539
+ except Exception:
4540
+ lengths = []
4541
+ current = max(lengths) if lengths else None
4542
+ try:
4543
+ if current is None:
4544
+ crop(keep) # length unknown: the legacy form is the only one available
4545
+ elif current > keep:
4546
+ crop(keep - current)
4536
4547
  except Exception:
4537
4548
  return False
4538
4549
 
@@ -71,13 +71,23 @@ def hard_kill_tree(proc: "subprocess.Popen") -> None:
71
71
  POSIX: enumerate descendants first (while the tree is intact), kill the
72
72
  worker's own group, then SIGKILL each descendant's process group and the
73
73
  descendant itself — so a `setsid()`'d grandchild's group and its children
74
- die too. Windows: `proc.kill()` (no group-escape there; best-effort).
74
+ die too. Windows: `taskkill /T /F` ends the worker and every process it started
75
+ (a browser driver's Chrome, a shell's children), then `proc.kill()` as the backstop.
75
76
 
76
77
  Guarded throughout: a descendant reaped between enumeration and kill (or a
77
78
  `getpgid` on a dead pid) must never raise — the point is to leave nothing
78
79
  alive, not to account for every pid.
79
80
  """
80
81
  if os.name != "posix":
82
+ if os.name == "nt":
83
+ try:
84
+ subprocess.run(
85
+ ["taskkill", "/T", "/F", "/PID", str(proc.pid)],
86
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=15,
87
+ creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
88
+ )
89
+ except Exception:
90
+ pass
81
91
  try:
82
92
  proc.kill()
83
93
  except Exception:
@@ -507,7 +507,10 @@ def macos_profile(spec: SandboxSpec) -> str:
507
507
  while parent and parent != "/":
508
508
  if parent not in ancestors:
509
509
  ancestors.append(parent)
510
- parent = os.path.dirname(parent)
510
+ up = os.path.dirname(parent)
511
+ if up == parent: # a filesystem root that is not "/" (C:\ on Windows)
512
+ break
513
+ parent = up
511
514
  if xcrun_rule and xcrun_tmp:
512
515
  # Creating the cache file looks up its folder: metadata of the folder and its
513
516
  # ancestors only (never a listing or a read of anything else in the temp root).
@@ -515,7 +518,10 @@ def macos_profile(spec: SandboxSpec) -> str:
515
518
  while parent and parent != "/":
516
519
  if parent not in ancestors:
517
520
  ancestors.append(parent)
518
- parent = os.path.dirname(parent)
521
+ up = os.path.dirname(parent)
522
+ if up == parent:
523
+ break
524
+ parent = up
519
525
  if ancestors:
520
526
  lines.append("(allow file-read-metadata " + " ".join(f"(literal {_sbpl_str(a)})" for a in ancestors) + ")")
521
527
  return "\n".join(lines) + "\n"
@@ -2671,6 +2671,9 @@ class SimpleCLI:
2671
2671
 
2672
2672
 
2673
2673
  def main():
2674
+ from abstractcore.utils.stdio import safe_stdio
2675
+
2676
+ safe_stdio()
2674
2677
  parser = argparse.ArgumentParser(
2675
2678
  description="Simplified CLI REPL for AbstractCore",
2676
2679
  formatter_class=argparse.RawDescriptionHelpFormatter,
@@ -0,0 +1,29 @@
1
+ """Console output that never crashes on a character the terminal's code page lacks.
2
+
3
+ Windows sends a redirected or piped stream (``> file``, ``| Select-String``, CI logs) through
4
+ the ANSI code page, often cp1252, which has no emoji, arrows or box-drawing characters; a
5
+ print of one raised ``UnicodeEncodeError`` and ended the command. The command-line entry
6
+ points call `safe_stdio()` first: an encoding error then writes a replacement character.
7
+ Interactive consoles (UTF-16 through the console API) and UTF-8 streams are left as they are.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import sys
13
+
14
+
15
+ def safe_stdio() -> None:
16
+ """Make stdout/stderr replace characters their encoding cannot represent."""
17
+
18
+ for name in ("stdout", "stderr"):
19
+ stream = getattr(sys, name, None)
20
+ reconfigure = getattr(stream, "reconfigure", None)
21
+ if reconfigure is None:
22
+ continue
23
+ encoding = str(getattr(stream, "encoding", "") or "").lower().replace("_", "-")
24
+ if encoding in {"utf-8", "utf8", "utf-8-sig"}:
25
+ continue
26
+ try:
27
+ reconfigure(errors="replace")
28
+ except Exception: # a stream that cannot be reconfigured keeps its behavior
29
+ pass
@@ -10,4 +10,4 @@ including when the package is installed from PyPI where pyproject.toml is not av
10
10
  """
11
11
 
12
12
  # Package version - update this when releasing new versions
13
- __version__ = "2.26.0"
13
+ __version__ = "2.26.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractcore
3
- Version: 2.26.0
3
+ Version: 2.26.3
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>
@@ -253,6 +253,7 @@ abstractcore/utils/model_cache.py
253
253
  abstractcore/utils/model_fit.py
254
254
  abstractcore/utils/residency.py
255
255
  abstractcore/utils/self_fixes.py
256
+ abstractcore/utils/stdio.py
256
257
  abstractcore/utils/structured_logging.py
257
258
  abstractcore/utils/token_utils.py
258
259
  abstractcore/utils/trace_export.py
@@ -330,4 +331,5 @@ tests/test_structured_output_truncation_retry.py
330
331
  tests/test_text_only_model_experience.py
331
332
  tests/test_unload_memory.py
332
333
  tests/test_user_scenario_validation.py
333
- tests/test_voice_host_credentials.py
334
+ tests/test_voice_host_credentials.py
335
+ tests/test_windows_host_fixes.py
@@ -0,0 +1,176 @@
1
+ """Windows host behavior: console output, process trees and LM Studio's model folder."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import io
6
+ import os
7
+ import subprocess
8
+ import sys
9
+ import time
10
+ from pathlib import Path
11
+
12
+ import pytest
13
+
14
+ from abstractcore.utils.stdio import safe_stdio
15
+
16
+
17
+ def test_safe_stdio_replaces_what_a_cp1252_stream_cannot_encode(monkeypatch) -> None:
18
+ raw = io.BytesIO()
19
+ stream = io.TextIOWrapper(raw, encoding="cp1252", errors="strict")
20
+ monkeypatch.setattr(sys, "stdout", stream)
21
+ safe_stdio()
22
+ print("\U0001f4cb status → ok │", file=sys.stdout)
23
+ sys.stdout.flush()
24
+ assert raw.getvalue().decode("cp1252").strip() == "? status ? ok ?"
25
+
26
+
27
+ def test_safe_stdio_leaves_a_utf8_stream_as_it_is(monkeypatch) -> None:
28
+ raw = io.BytesIO()
29
+ stream = io.TextIOWrapper(raw, encoding="utf-8", errors="strict")
30
+ monkeypatch.setattr(sys, "stdout", stream)
31
+ safe_stdio()
32
+ assert sys.stdout.errors == "strict"
33
+ print("→", file=sys.stdout)
34
+ sys.stdout.flush()
35
+ assert raw.getvalue().decode("utf-8").strip() == "→"
36
+
37
+
38
+ def test_safe_stdio_tolerates_streams_without_reconfigure(monkeypatch) -> None:
39
+ monkeypatch.setattr(sys, "stdout", io.StringIO())
40
+ monkeypatch.setattr(sys, "stderr", None)
41
+ safe_stdio()
42
+
43
+
44
+ @pytest.mark.parametrize(
45
+ "module",
46
+ ["abstractcore.config.main", "abstractcore.utils.cli", "abstractcore.endpoint.app", "abstractcore.apps.summarizer",
47
+ "abstractcore.apps.extractor", "abstractcore.apps.judge", "abstractcore.apps.intent", "abstractcore.apps.deepsearch"],
48
+ )
49
+ def test_every_console_entry_point_makes_stdio_safe_first(module) -> None:
50
+ import importlib.util
51
+ import inspect
52
+
53
+ spec = importlib.util.find_spec(module)
54
+ source = Path(spec.origin).read_text(encoding="utf-8")
55
+ body = source[source.index("\ndef main("):]
56
+ assert "safe_stdio()" in body.split("\ndef ", 2)[1], f"{module}.main does not call safe_stdio()"
57
+
58
+
59
+ def _alive(pid: int) -> bool:
60
+ out = subprocess.run(["tasklist", "/FI", f"PID eq {pid}", "/NH"], capture_output=True, text=True).stdout
61
+ return str(pid) in out
62
+
63
+
64
+ @pytest.mark.skipif(os.name != "nt", reason="Windows process tree")
65
+ def test_hard_kill_tree_ends_a_windows_workers_children() -> None:
66
+ from abstractcore.tools.process_tree import hard_kill_tree
67
+
68
+ code = ("import subprocess, sys, time; "
69
+ "p = subprocess.Popen([sys.executable, '-c', 'import time; time.sleep(60)']); "
70
+ "print(p.pid, flush=True); time.sleep(60)")
71
+ proc = subprocess.Popen([sys.executable, "-c", code], stdout=subprocess.PIPE, text=True)
72
+ grandchild = int(proc.stdout.readline())
73
+ assert _alive(proc.pid) and _alive(grandchild)
74
+ hard_kill_tree(proc)
75
+ deadline = time.time() + 10
76
+ while time.time() < deadline and (_alive(proc.pid) or _alive(grandchild)):
77
+ time.sleep(0.2)
78
+ assert not _alive(proc.pid) and not _alive(grandchild)
79
+
80
+
81
+ @pytest.mark.parametrize("system", ["Windows", "Darwin", "Linux"])
82
+ def test_lm_studio_models_in_the_home_folder_are_found_on_every_os(tmp_path, monkeypatch, system) -> None:
83
+ from abstractcore.capabilities import vision_catalog
84
+
85
+ models = tmp_path / ".lmstudio" / "models"
86
+ models.mkdir(parents=True)
87
+ monkeypatch.setattr(vision_catalog.platform, "system", lambda: system)
88
+ monkeypatch.setattr(vision_catalog.Path, "home", classmethod(lambda cls: tmp_path))
89
+ for key in ("LMSTUDIO_MODELS_DIR", "LMSTUDIO_MODEL_DIR", "LM_STUDIO_MODELS_DIR", "LOCALAPPDATA", "APPDATA"):
90
+ monkeypatch.delenv(key, raising=False)
91
+ assert models in vision_catalog._default_lmstudio_model_dirs()
92
+
93
+
94
+ def _copy_cache(tmp_path: Path) -> tuple[Path, list]:
95
+ """A Hugging Face cache without links (Windows without Developer Mode): huggingface_hub moved
96
+ each finished file from blobs/ into snapshots/<rev>/ and left no blob behind."""
97
+ repo = tmp_path / "models--org--tiny"
98
+ snap = repo / "snapshots" / "abc123"
99
+ snap.mkdir(parents=True)
100
+ (repo / "blobs").mkdir()
101
+ (snap / "config.json").write_bytes(b"{}" * 10)
102
+ (snap / "model.safetensors").write_bytes(b"\0" * 300)
103
+ plan = [{"name": "config.json", "size": 20, "etag": "e1"}, {"name": "model.safetensors", "size": 300, "etag": "e2"}]
104
+ return repo, plan
105
+
106
+
107
+ def test_a_download_into_a_cache_without_links_is_whole(tmp_path) -> None:
108
+ from abstractcore.config import model_materializer as mm
109
+
110
+ repo, plan = _copy_cache(tmp_path)
111
+ watcher = mm._HfBlobWatcher(repo, plan, lambda *_a, **_k: None, "org/tiny")
112
+ assert watcher.missing() == []
113
+ assert watcher.bytes_done() == 320
114
+
115
+
116
+ def test_a_snapshot_copy_of_the_wrong_size_is_not_whole(tmp_path) -> None:
117
+ from abstractcore.config import model_materializer as mm
118
+
119
+ repo, plan = _copy_cache(tmp_path)
120
+ (repo / "snapshots" / "abc123" / "model.safetensors").write_bytes(b"\0" * 10)
121
+ watcher = mm._HfBlobWatcher(repo, plan, lambda *_a, **_k: None, "org/tiny")
122
+ assert watcher.missing() == ["model.safetensors"]
123
+
124
+
125
+ def test_the_download_marker_counts_snapshot_copies_as_whole(tmp_path) -> None:
126
+ import json
127
+ from abstractcore.config import model_materializer as mm
128
+
129
+ repo, plan = _copy_cache(tmp_path)
130
+ (repo / mm._HF_MARKER).write_text(json.dumps({"files": plan}), encoding="utf-8")
131
+ assert mm._hf_marker_unfinished(repo) == 0
132
+
133
+
134
+ def test_a_blob_layout_is_read_as_before(tmp_path) -> None:
135
+ from abstractcore.config import model_materializer as mm
136
+
137
+ repo = tmp_path / "models--org--tiny"
138
+ (repo / "blobs").mkdir(parents=True)
139
+ (repo / "snapshots" / "abc123").mkdir(parents=True)
140
+ (repo / "blobs" / "e1").write_bytes(b"x" * 20)
141
+ plan = [{"name": "config.json", "size": 20, "etag": "e1"}, {"name": "model.safetensors", "size": 300, "etag": "e2"}]
142
+ watcher = mm._HfBlobWatcher(repo, plan, lambda *_a, **_k: None, "org/tiny")
143
+ assert watcher.missing() == ["model.safetensors"]
144
+
145
+
146
+ def _owner_watch_header() -> str:
147
+ from abstractcore.config.model_materializer import _HF_CHILD
148
+
149
+ head, sep, _ = _HF_CHILD.partition("from huggingface_hub import snapshot_download")
150
+ assert sep, "the download child no longer has the expected shape"
151
+ return head
152
+
153
+
154
+ def test_the_download_child_keeps_running_while_its_owner_lives() -> None:
155
+ # On Windows sys.executable in a venv is a launcher: the child's parent pid is the
156
+ # launcher, not the owner. The child must not take that for a dead owner.
157
+ code = _owner_watch_header() + "time.sleep(3.5)\nprint('alive', flush=True)\n"
158
+ out = subprocess.run(
159
+ [sys.executable, "-c", code, "{}", str(os.getpid())],
160
+ capture_output=True, text=True, timeout=60,
161
+ )
162
+ assert out.returncode == 0, (out.returncode, out.stderr)
163
+ assert out.stdout.strip() == "alive"
164
+
165
+
166
+ def test_the_download_child_stops_when_its_owner_exits() -> None:
167
+ owner = subprocess.Popen([sys.executable, "-c", "import time; time.sleep(1.5)"])
168
+ code = _owner_watch_header() + "time.sleep(30)\nprint('outlived', flush=True)\n"
169
+ started = time.monotonic()
170
+ out = subprocess.run(
171
+ [sys.executable, "-c", code, "{}", str(owner.pid)],
172
+ capture_output=True, text=True, timeout=60,
173
+ )
174
+ owner.wait(timeout=30)
175
+ assert out.returncode == 75, (out.returncode, out.stdout, out.stderr)
176
+ assert time.monotonic() - started < 20
File without changes
File without changes