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