distributed-llm 0.4.1__py3-none-any.whl
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.
- distllm/__init__.py +47 -0
- distllm/api/__init__.py +11 -0
- distllm/api/api_settings.py +126 -0
- distllm/api/api_state.py +85 -0
- distllm/api/api_versioning.py +984 -0
- distllm/api/app_state.py +29 -0
- distllm/api/auth/__init__.py +181 -0
- distllm/api/auth/models.py +37 -0
- distllm/api/auth/oauth2.py +123 -0
- distllm/api/auth/oidc.py +317 -0
- distllm/api/auth/saml.py +107 -0
- distllm/api/auth/store.py +39 -0
- distllm/api/auth_deps.py +68 -0
- distllm/api/authz/__init__.py +30 -0
- distllm/api/authz/opa.py +297 -0
- distllm/api/background_tasks.py +295 -0
- distllm/api/body_cache_middleware.py +117 -0
- distllm/api/circuit_breaker_middleware.py +175 -0
- distllm/api/cost_middleware.py +186 -0
- distllm/api/csrf_middleware.py +196 -0
- distllm/api/dedup.py +180 -0
- distllm/api/errors.py +256 -0
- distllm/api/grpc_bridge.py +296 -0
- distllm/api/ip_utils.py +58 -0
- distllm/api/middleware.py +706 -0
- distllm/api/observability_middleware.py +169 -0
- distllm/api/persistent_store.py +295 -0
- distllm/api/prompt_injection.py +418 -0
- distllm/api/quota_middleware.py +186 -0
- distllm/api/rate_limit_middleware.py +13 -0
- distllm/api/rate_limiter.py +250 -0
- distllm/api/rate_limiter_unified.py +975 -0
- distllm/api/redis_rate_limiter.py +164 -0
- distllm/api/routes/__init__.py +42 -0
- distllm/api/routes/admin.py +599 -0
- distllm/api/routes/api_keys.py +115 -0
- distllm/api/routes/assistants.py +443 -0
- distllm/api/routes/batch.py +1094 -0
- distllm/api/routes/chat.py +875 -0
- distllm/api/routes/completion.py +141 -0
- distllm/api/routes/debug.py +185 -0
- distllm/api/routes/defrag.py +66 -0
- distllm/api/routes/embeddings.py +399 -0
- distllm/api/routes/eval.py +254 -0
- distllm/api/routes/exchange.py +367 -0
- distllm/api/routes/experiments.py +293 -0
- distllm/api/routes/federated.py +155 -0
- distllm/api/routes/files.py +243 -0
- distllm/api/routes/fine_tuning.py +381 -0
- distllm/api/routes/gossip.py +72 -0
- distllm/api/routes/health.py +395 -0
- distllm/api/routes/leaderboard.py +377 -0
- distllm/api/routes/marketplace.py +320 -0
- distllm/api/routes/metrics_history.py +415 -0
- distllm/api/routes/model_registry.py +255 -0
- distllm/api/routes/moderation.py +420 -0
- distllm/api/routes/plugins.py +184 -0
- distllm/api/routes/prompts.py +347 -0
- distllm/api/routes/router_admin.py +187 -0
- distllm/api/routes/scheduler.py +168 -0
- distllm/api/routes/tokenize.py +77 -0
- distllm/api/routes/tools.py +89 -0
- distllm/api/routes/webhooks.py +336 -0
- distllm/api/routes/webrtc.py +194 -0
- distllm/api/semantic_cache.py +337 -0
- distllm/api/server.py +2038 -0
- distllm/api/server_config.py +140 -0
- distllm/api/server_coordinator.py +125 -0
- distllm/api/server_errors.py +79 -0
- distllm/api/server_lifespan.py +184 -0
- distllm/api/server_routes_api.py +456 -0
- distllm/api/server_routes_dashboard.py +246 -0
- distllm/api/server_state.py +29 -0
- distllm/api/services/__init__.py +2 -0
- distllm/api/services/chat_service.py +677 -0
- distllm/api/services/completion_service.py +227 -0
- distllm/api/services/embedding_service.py +451 -0
- distllm/api/services/eval_service.py +198 -0
- distllm/api/sso_auth.py +644 -0
- distllm/api/sso_middleware.py +808 -0
- distllm/api/streaming.py +479 -0
- distllm/api/token_estimator.py +86 -0
- distllm/api/tool_calling.py +240 -0
- distllm/api/tracing_middleware.py +579 -0
- distllm/api/validation.py +58 -0
- distllm/api/waf.py +344 -0
- distllm/api/webhooks/__init__.py +17 -0
- distllm/api/webhooks/delivery.py +452 -0
- distllm/api/ws_chat.py +495 -0
- distllm/backends/__init__.py +121 -0
- distllm/backends/config.py +85 -0
- distllm/backends/exllama_backend.py +229 -0
- distllm/backends/llamacpp_backend.py +266 -0
- distllm/backends/mlx_backend.py +199 -0
- distllm/backends/nim_backend.py +838 -0
- distllm/backends/ollama_backend.py +48 -0
- distllm/backends/onnx_backend.py +239 -0
- distllm/backends/paged_attention.py +547 -0
- distllm/backends/paged_attention_quantized.py +319 -0
- distllm/backends/protocol.py +203 -0
- distllm/backends/pytorch_backend.py +157 -0
- distllm/backends/registry.py +428 -0
- distllm/backends/tensorrt_backend.py +333 -0
- distllm/backends/tgi_backend.py +120 -0
- distllm/backends/triton_backend.py +875 -0
- distllm/backends/vllm_backend.py +489 -0
- distllm/backends/webgpu_backend.py +255 -0
- distllm/benchmarks/__init__.py +10 -0
- distllm/benchmarks/evaluation_harness.py +69 -0
- distllm/benchmarks/scaling.py +298 -0
- distllm/cache/__init__.py +19 -0
- distllm/cache/crdt.py +507 -0
- distllm/cache/cross_cluster_prefix_index.py +709 -0
- distllm/cli/__init__.py +17 -0
- distllm/cli/adapters.py +93 -0
- distllm/cli/autopsy.py +287 -0
- distllm/cli/backup.py +118 -0
- distllm/cli/benchmark.py +241 -0
- distllm/cli/cert.py +86 -0
- distllm/cli/chat.py +79 -0
- distllm/cli/chat_stream.py +494 -0
- distllm/cli/client.py +233 -0
- distllm/cli/cluster.py +405 -0
- distllm/cli/completion.py +79 -0
- distllm/cli/compress.py +98 -0
- distllm/cli/config_commands.py +71 -0
- distllm/cli/cost_avoid.py +168 -0
- distllm/cli/daas_commands.py +123 -0
- distllm/cli/defrag_commands.py +128 -0
- distllm/cli/deploy.py +252 -0
- distllm/cli/doctor.py +688 -0
- distllm/cli/draft_commands.py +82 -0
- distllm/cli/error_handler.py +192 -0
- distllm/cli/eval.py +168 -0
- distllm/cli/federate_commands.py +125 -0
- distllm/cli/install.py +198 -0
- distllm/cli/logs.py +85 -0
- distllm/cli/main.py +1795 -0
- distllm/cli/models.py +74 -0
- distllm/cli/notify.py +91 -0
- distllm/cli/observe.py +314 -0
- distllm/cli/onboard.py +671 -0
- distllm/cli/output.py +437 -0
- distllm/cli/profile.py +152 -0
- distllm/cli/profile_presets.py +129 -0
- distllm/cli/prompts.py +139 -0
- distllm/cli/quota.py +227 -0
- distllm/cli/router.py +131 -0
- distllm/cli/run.py +59 -0
- distllm/cli/setup.py +104 -0
- distllm/cli/setup_wizard.py +191 -0
- distllm/cli/status.py +65 -0
- distllm/cli/system_commands.py +255 -0
- distllm/cli/tune.py +488 -0
- distllm/cli/tutorial.py +128 -0
- distllm/cli/verify.py +37 -0
- distllm/cli/webhook.py +85 -0
- distllm/client/__init__.py +38 -0
- distllm/client/client.py +503 -0
- distllm/cloud/__init__.py +53 -0
- distllm/cloud/aws.py +199 -0
- distllm/cloud/azure.py +179 -0
- distllm/cloud/common.py +217 -0
- distllm/cloud/gcp.py +238 -0
- distllm/cloud/spot_orchestrator.py +1642 -0
- distllm/cloud/worker_agent.py +344 -0
- distllm/compliance/__init__.py +10 -0
- distllm/compliance/evidence_pack.py +699 -0
- distllm/config/__init__.py +81 -0
- distllm/config/_application.py +41 -0
- distllm/config/_backends.py +80 -0
- distllm/config/_cache.py +121 -0
- distllm/config/_deployment.py +95 -0
- distllm/config/_generation.py +36 -0
- distllm/config/_hardware.py +42 -0
- distllm/config/_model.py +270 -0
- distllm/config/_network.py +235 -0
- distllm/config/_observability.py +42 -0
- distllm/config/_parallelism.py +172 -0
- distllm/config/_performance.py +47 -0
- distllm/config/loader.py +168 -0
- distllm/config/model_heuristics.py +169 -0
- distllm/config/profiles.py +168 -0
- distllm/config/reference.py +104 -0
- distllm/config/resolver.py +240 -0
- distllm/config/settings.py +463 -0
- distllm/constants.py +117 -0
- distllm/core/__init__.py +111 -0
- distllm/core/_advanced_scheduling_original.py +1892 -0
- distllm/core/ab_test_coordinator.py +178 -0
- distllm/core/activation_profiler.py +172 -0
- distllm/core/adaptive_batching.py +234 -0
- distllm/core/adaptive_cache_compressor.py +168 -0
- distllm/core/adaptive_compression.py +427 -0
- distllm/core/adaptive_compression_hierarchy.py +402 -0
- distllm/core/advanced_scheduling/__init__.py +39 -0
- distllm/core/advanced_scheduling/cost_aware.py +55 -0
- distllm/core/advanced_scheduling/disaggregated.py +352 -0
- distllm/core/advanced_scheduling/energy.py +58 -0
- distllm/core/advanced_scheduling/federated.py +59 -0
- distllm/core/advanced_scheduling/heterogeneous.py +84 -0
- distllm/core/advanced_scheduling/multi_objective.py +372 -0
- distllm/core/advanced_scheduling/policy.py +95 -0
- distllm/core/advanced_scheduling/predictive.py +47 -0
- distllm/core/advanced_scheduling/preemption.py +43 -0
- distllm/core/advanced_scheduling/tiered_store.py +604 -0
- distllm/core/advanced_scheduling/token_bank.py +62 -0
- distllm/core/advanced_scheduling/wan.py +85 -0
- distllm/core/aegis_compliance.py +890 -0
- distllm/core/aether_federated.py +1326 -0
- distllm/core/agentic_router.py +749 -0
- distllm/core/api_key_store.py +478 -0
- distllm/core/arbitrage_engine.py +613 -0
- distllm/core/aria_autoscaler.py +1339 -0
- distllm/core/async_connection_pool.py +187 -0
- distllm/core/async_pipelined_speculative.py +543 -0
- distllm/core/atlas_mesh.py +1437 -0
- distllm/core/auto_discovery.py +351 -0
- distllm/core/auto_partitioner.py +396 -0
- distllm/core/autonomous_healer.py +466 -0
- distllm/core/autoq.py +1545 -0
- distllm/core/backup_manager.py +344 -0
- distllm/core/bargaining_engine.py +450 -0
- distllm/core/batch_builder.py +480 -0
- distllm/core/batch_scheduler.py +1342 -0
- distllm/core/block_affinity_tracker.py +162 -0
- distllm/core/block_eviction_policy.py +363 -0
- distllm/core/cache_aware_router.py +116 -0
- distllm/core/cache_bench.py +239 -0
- distllm/core/cache_coherence.py +139 -0
- distllm/core/cache_doctor.py +220 -0
- distllm/core/cache_eviction.py +181 -0
- distllm/core/cache_index.py +91 -0
- distllm/core/cache_manager.py +703 -0
- distllm/core/cache_migration.py +134 -0
- distllm/core/cache_persistence.py +200 -0
- distllm/core/cache_query_log.py +195 -0
- distllm/core/cache_snapshot.py +178 -0
- distllm/core/cache_template_warmer.py +112 -0
- distllm/core/cache_warming.py +188 -0
- distllm/core/calibration.py +223 -0
- distllm/core/carbon_migration.py +338 -0
- distllm/core/cert_rotation.py +402 -0
- distllm/core/certificate_manager.py +414 -0
- distllm/core/cluster_manager.py +212 -0
- distllm/core/cluster_predictive_prefetcher.py +239 -0
- distllm/core/cluster_registry.py +227 -0
- distllm/core/cluster_state_store.py +442 -0
- distllm/core/compliance_evidence.py +503 -0
- distllm/core/compressed_speculative.py +399 -0
- distllm/core/connection_pool.py +387 -0
- distllm/core/constrained_decoder.py +678 -0
- distllm/core/coordinator.py +1594 -0
- distllm/core/coordinator_cli.py +130 -0
- distllm/core/coordinator_config.py +203 -0
- distllm/core/coordinator_config_wiring.py +316 -0
- distllm/core/coordinator_election.py +246 -0
- distllm/core/coordinator_failover.py +197 -0
- distllm/core/coordinator_health.py +135 -0
- distllm/core/coordinator_lifecycle.py +272 -0
- distllm/core/coordinator_metrics.py +155 -0
- distllm/core/coordinator_multi_model.py +41 -0
- distllm/core/coordinator_request.py +246 -0
- distllm/core/coordinator_state.py +215 -0
- distllm/core/coordinator_subsystem.py +614 -0
- distllm/core/correctness_cert.py +133 -0
- distllm/core/correctness_harness.py +421 -0
- distllm/core/cortex_multimodel.py +939 -0
- distllm/core/cost_comparison.py +250 -0
- distllm/core/cost_dashboard.py +363 -0
- distllm/core/cost_optimizer.py +317 -0
- distllm/core/cost_tracker.py +569 -0
- distllm/core/cross_cloud_router.py +924 -0
- distllm/core/cross_model_prefix_sharing.py +237 -0
- distllm/core/cuda_graph.py +165 -0
- distllm/core/debug.py +84 -0
- distllm/core/device_registry.py +289 -0
- distllm/core/di.py +153 -0
- distllm/core/differential_privacy.py +183 -0
- distllm/core/distributed_distillation.py +402 -0
- distllm/core/distributed_speculative.py +1563 -0
- distllm/core/dp_inference/__init__.py +657 -0
- distllm/core/dp_inference/accounting.py +144 -0
- distllm/core/dp_inference/config.py +139 -0
- distllm/core/dp_inference/mechanisms.py +137 -0
- distllm/core/dp_inference.py +1016 -0
- distllm/core/draft_model_router.py +435 -0
- distllm/core/draft_quality_scorer.py +157 -0
- distllm/core/draft_tree.py +232 -0
- distllm/core/dynamic_memory_budget.py +179 -0
- distllm/core/dynamic_sharder.py +364 -0
- distllm/core/dynamic_speculation.py +131 -0
- distllm/core/evaluation/__init__.py +57 -0
- distllm/core/evaluation/constants.py +86 -0
- distllm/core/evaluation/db.py +185 -0
- distllm/core/evaluation/formatters.py +82 -0
- distllm/core/evaluation/loaders.py +320 -0
- distllm/core/evaluation/models.py +66 -0
- distllm/core/evaluation/report.py +79 -0
- distllm/core/evaluation/runner.py +518 -0
- distllm/core/evaluation/scorers.py +278 -0
- distllm/core/evaluation/worker.py +123 -0
- distllm/core/evaluation_harness.py +1530 -0
- distllm/core/event_bus.py +516 -0
- distllm/core/faas_7b.py +201 -0
- distllm/core/feature_flags.py +245 -0
- distllm/core/federated_finetuner.py +377 -0
- distllm/core/federated_incentives.py +267 -0
- distllm/core/framework_adapters.py +390 -0
- distllm/core/gaia_cache.py +1330 -0
- distllm/core/gossip_cache_bridge.py +202 -0
- distllm/core/gpu_power_manager.py +173 -0
- distllm/core/gpu_profiler.py +59 -0
- distllm/core/gpu_resource_manager.py +338 -0
- distllm/core/graceful_degradation.py +264 -0
- distllm/core/grammar_constrained.py +549 -0
- distllm/core/grammar_constrained_draft.py +91 -0
- distllm/core/grammar_decoder.py +285 -0
- distllm/core/ha_coordinator.py +452 -0
- distllm/core/hardware_plugin_registry.py +126 -0
- distllm/core/hashing.py +29 -0
- distllm/core/health_manager.py +320 -0
- distllm/core/heterogeneous_scheduler.py +663 -0
- distllm/core/hibernation_manager.py +250 -0
- distllm/core/hierarchical_digest.py +168 -0
- distllm/core/hybrid_cache.py +253 -0
- distllm/core/hydra_diffusion.py +177 -0
- distllm/core/inference_engine.py +1072 -0
- distllm/core/intelligent_autoscaler.py +219 -0
- distllm/core/kraken_chaos.py +1407 -0
- distllm/core/kv_backup.py +217 -0
- distllm/core/kv_cache.py +1271 -0
- distllm/core/kv_cache_adaptive_quantizer.py +168 -0
- distllm/core/kv_cache_compressor.py +290 -0
- distllm/core/kv_cache_manager.py +142 -0
- distllm/core/kv_cache_marketplace.py +319 -0
- distllm/core/kv_cache_metrics.py +205 -0
- distllm/core/kv_cache_migration.py +137 -0
- distllm/core/kv_cache_paged.py +78 -0
- distllm/core/kv_cache_replication.py +107 -0
- distllm/core/kv_cache_serialization.py +182 -0
- distllm/core/latency_tracker.py +17 -0
- distllm/core/leaky_bucket_limiter.py +194 -0
- distllm/core/learning_router.py +652 -0
- distllm/core/llamacpp_backend.py +23 -0
- distllm/core/load_balancer.py +286 -0
- distllm/core/media_pipeline.py +387 -0
- distllm/core/memory_defragmenter.py +666 -0
- distllm/core/metering.py +449 -0
- distllm/core/metrics_collector.py +39 -0
- distllm/core/micro_batch_scheduler.py +170 -0
- distllm/core/model_hub_enhanced.py +382 -0
- distllm/core/model_registry.py +9 -0
- distllm/core/model_router.py +930 -0
- distllm/core/model_selector.py +245 -0
- distllm/core/model_sizing.py +334 -0
- distllm/core/model_version_manager.py +414 -0
- distllm/core/moe_orchestrator.py +205 -0
- distllm/core/money.py +53 -0
- distllm/core/monitor.py +141 -0
- distllm/core/mtp_head.py +444 -0
- distllm/core/multi_draft_verifier.py +346 -0
- distllm/core/multi_model_serving.py +624 -0
- distllm/core/multimodal_engine.py +194 -0
- distllm/core/neural_partition_optimizer.py +1398 -0
- distllm/core/node_recovery.py +27 -0
- distllm/core/notification_manager.py +317 -0
- distllm/core/param_update_channel.py +104 -0
- distllm/core/pareto_optimizer.py +227 -0
- distllm/core/performance_alerts.py +198 -0
- distllm/core/performance_baseline.py +252 -0
- distllm/core/persistence.py +947 -0
- distllm/core/persistent_store.py +485 -0
- distllm/core/pipeline_composer.py +211 -0
- distllm/core/pipeline_executor.py +500 -0
- distllm/core/pipeline_orchestrator.py +17 -0
- distllm/core/pipeline_overlap.py +177 -0
- distllm/core/placement.py +442 -0
- distllm/core/plugin_marketplace.py +483 -0
- distllm/core/plugin_sandbox.py +751 -0
- distllm/core/plugin_system.py +601 -0
- distllm/core/plugins/__init__.py +26 -0
- distllm/core/plugins/sandbox.py +38 -0
- distllm/core/predictive_cache.py +29 -0
- distllm/core/predictive_cache_warming.py +323 -0
- distllm/core/predictive_failure.py +163 -0
- distllm/core/preemptible_scheduler.py +343 -0
- distllm/core/preference_learning.py +606 -0
- distllm/core/prefix_cache.py +17 -0
- distllm/core/pricing_providers.py +576 -0
- distllm/core/priority_heap.py +62 -0
- distllm/core/privacy_budget.py +177 -0
- distllm/core/prompt_caching_service.py +228 -0
- distllm/core/prompt_library.py +838 -0
- distllm/core/protocols.py +213 -0
- distllm/core/provider_health.py +234 -0
- distllm/core/provisioning.py +329 -0
- distllm/core/pulse_performance_model.py +1558 -0
- distllm/core/quantization_selector.py +210 -0
- distllm/core/radix_tree_cache.py +306 -0
- distllm/core/rebalancer.py +25 -0
- distllm/core/redis_prompt_cache.py +311 -0
- distllm/core/replication_controller.py +128 -0
- distllm/core/request_auditor.py +215 -0
- distllm/core/request_fingerprinting.py +231 -0
- distllm/core/request_latency.py +211 -0
- distllm/core/request_pipeline.py +810 -0
- distllm/core/request_replay.py +253 -0
- distllm/core/request_tracker.py +184 -0
- distllm/core/resource_manager.py +657 -0
- distllm/core/route_audit.py +175 -0
- distllm/core/routing_extensions.py +604 -0
- distllm/core/schedule_simulator.py +289 -0
- distllm/core/schedule_viz.py +233 -0
- distllm/core/scheduler/__init__.py +34 -0
- distllm/core/scheduler/budget.py +25 -0
- distllm/core/scheduler/budget_computer.py +322 -0
- distllm/core/scheduler/chunked_prefill.py +52 -0
- distllm/core/scheduler/kv_cache_manager.py +313 -0
- distllm/core/scheduler/preemption_manager.py +251 -0
- distllm/core/scheduler/pressure.py +48 -0
- distllm/core/scheduler/sequence.py +179 -0
- distllm/core/secret_manager.py +315 -0
- distllm/core/semantic_cache.py +369 -0
- distllm/core/sentinel_qos.py +833 -0
- distllm/core/shadow_eval_runner.py +194 -0
- distllm/core/shared_layer_pool.py +279 -0
- distllm/core/smart_model_router.py +309 -0
- distllm/core/spec_equivalence.py +140 -0
- distllm/core/spec_verify.py +225 -0
- distllm/core/speculative_adaptor.py +108 -0
- distllm/core/speculative_dashboard.py +134 -0
- distllm/core/speculative_decoder.py +1286 -0
- distllm/core/speculative_profiler.py +132 -0
- distllm/core/split_brain.py +236 -0
- distllm/core/spot_failover.py +292 -0
- distllm/core/spot_forecasting.py +463 -0
- distllm/core/starvation_monitor.py +70 -0
- distllm/core/state_replication.py +261 -0
- distllm/core/stats_collector.py +64 -0
- distllm/core/step_processor.py +115 -0
- distllm/core/straggler_alerts.py +174 -0
- distllm/core/straggler_aware_scheduler.py +255 -0
- distllm/core/straggler_detector.py +29 -0
- distllm/core/streaming_cost.py +286 -0
- distllm/core/streaming_generator.py +228 -0
- distllm/core/structured_output/__init__.py +348 -0
- distllm/core/structured_output/config.py +38 -0
- distllm/core/structured_output/engine.py +367 -0
- distllm/core/structured_output/streaming.py +183 -0
- distllm/core/structured_output/validator.py +199 -0
- distllm/core/subsystem_registry.py +171 -0
- distllm/core/synapse_debugger.py +226 -0
- distllm/core/synth_data_generator.py +122 -0
- distllm/core/telemetry.py +240 -0
- distllm/core/tenant_billing.py +446 -0
- distllm/core/tenant_cost_attribution.py +314 -0
- distllm/core/token_generator.py +368 -0
- distllm/core/token_streaming_buffer.py +147 -0
- distllm/core/topology_aware_lb.py +324 -0
- distllm/core/topology_aware_tiering.py +344 -0
- distllm/core/tree_speculative_decoder.py +405 -0
- distllm/core/types.py +17 -0
- distllm/core/unified_router.py +439 -0
- distllm/core/unified_sla_router.py +242 -0
- distllm/core/usage_meter.py +915 -0
- distllm/core/vectorstore/__init__.py +30 -0
- distllm/core/vectorstore/base.py +106 -0
- distllm/core/vectorstore/chroma_store.py +99 -0
- distllm/core/vectorstore/factory.py +97 -0
- distllm/core/vectorstore/pgvector_store.py +123 -0
- distllm/core/vectorstore/providers/__init__.py +7 -0
- distllm/core/vectorstore/providers/milvus.py +219 -0
- distllm/core/vectorstore/providers/pinecone.py +142 -0
- distllm/core/vectorstore/providers/qdrant.py +176 -0
- distllm/core/vectorstore/providers/weaviate.py +185 -0
- distllm/core/vectorstore/qdrant_store.py +148 -0
- distllm/core/vectorstore/rag.py +151 -0
- distllm/core/vllm_backend.py +21 -0
- distllm/core/vllm_node.py +174 -0
- distllm/core/voyager_multimodal.py +1354 -0
- distllm/core/webgpu_manager.py +382 -0
- distllm/core/webhook_formatters.py +243 -0
- distllm/core/webhook_manager.py +433 -0
- distllm/core/wisp_wasm.py +934 -0
- distllm/core/workload_classifier.py +137 -0
- distllm/dashboard/__init__.py +17 -0
- distllm/dashboard/verification_history.py +192 -0
- distllm/dashboard/ws_handler.py +586 -0
- distllm/dist/__init__.py +85 -0
- distllm/dist/__init__.pyi +132 -0
- distllm/dist/_pipeline_original.py +2916 -0
- distllm/dist/adaptive_speculator.py +384 -0
- distllm/dist/admin_cli.py +293 -0
- distllm/dist/api_docs.py +594 -0
- distllm/dist/async_pipeline.py +578 -0
- distllm/dist/attention.py +1699 -0
- distllm/dist/attention_block_pool.py +634 -0
- distllm/dist/attention_distributed.py +138 -0
- distllm/dist/autoscaler.py +196 -0
- distllm/dist/backends/__init__.py +29 -0
- distllm/dist/backends/backend_profiles.py +357 -0
- distllm/dist/backends/graceful_degradation.py +409 -0
- distllm/dist/backends/health_monitor.py +518 -0
- distllm/dist/backends/llamacpp.py +125 -0
- distllm/dist/backends/ray.py +89 -0
- distllm/dist/backends/vllm.py +177 -0
- distllm/dist/block_pool.py +662 -0
- distllm/dist/block_transfer_service.py +267 -0
- distllm/dist/byzantine.py +2193 -0
- distllm/dist/cache.py +305 -0
- distllm/dist/cache_digest.py +405 -0
- distllm/dist/chaos/__init__.py +52 -0
- distllm/dist/chaos/chaos_orchestrator.py +229 -0
- distllm/dist/chaos/fault_injector.py +1273 -0
- distllm/dist/chunked_prefill.py +136 -0
- distllm/dist/cloud_selector.py +257 -0
- distllm/dist/config.py +70 -0
- distllm/dist/cross_cluster.py +231 -0
- distllm/dist/daas/__init__.py +46 -0
- distllm/dist/daas/load_balancer.py +275 -0
- distllm/dist/daas/marketplace_integration.py +374 -0
- distllm/dist/daas/resource_isolation.py +280 -0
- distllm/dist/daas/tenant_dispatcher.py +252 -0
- distllm/dist/daas/usage_meter.py +360 -0
- distllm/dist/daas_server.py +514 -0
- distllm/dist/disagg/__init__.py +162 -0
- distllm/dist/disagg/pool.py +214 -0
- distllm/dist/disagg/transfer.py +77 -0
- distllm/dist/disagg/transfer_compression.py +793 -0
- distllm/dist/disagg/transfer_engine.py +998 -0
- distllm/dist/discovery.py +166 -0
- distllm/dist/draft_bank.py +344 -0
- distllm/dist/draft_migration.py +382 -0
- distllm/dist/ebpf_transport.py +364 -0
- distllm/dist/edge_cloud.py +544 -0
- distllm/dist/edge_federation.py +426 -0
- distllm/dist/execution_planner.py +148 -0
- distllm/dist/federated_merge.py +560 -0
- distllm/dist/federation.py +1114 -0
- distllm/dist/flash_attention.py +237 -0
- distllm/dist/fsdp.py +295 -0
- distllm/dist/geo.py +361 -0
- distllm/dist/ice_transport.py +2968 -0
- distllm/dist/kv_migration.py +237 -0
- distllm/dist/latency.py +60 -0
- distllm/dist/lifecycle.py +467 -0
- distllm/dist/marketplace.py +891 -0
- distllm/dist/merkle.py +178 -0
- distllm/dist/metrics.py +456 -0
- distllm/dist/model_store.py +107 -0
- distllm/dist/multi_tenant.py +339 -0
- distllm/dist/multimodal.py +392 -0
- distllm/dist/nat.py +934 -0
- distllm/dist/nccl.py +733 -0
- distllm/dist/network.py +126 -0
- distllm/dist/node_client.py +568 -0
- distllm/dist/node_pb2.py +66 -0
- distllm/dist/node_pb2.pyi +236 -0
- distllm/dist/node_pb2_grpc.py +312 -0
- distllm/dist/node_pb2_grpc.pyi +41 -0
- distllm/dist/node_registrar.py +247 -0
- distllm/dist/node_service.py +572 -0
- distllm/dist/observability.py +258 -0
- distllm/dist/otel.py +158 -0
- distllm/dist/p2p/__init__.py +22 -0
- distllm/dist/p2p/discovery.py +132 -0
- distllm/dist/p2p/gossip.py +1339 -0
- distllm/dist/p2p/kademlia_dht.py +1058 -0
- distllm/dist/p2p/load_balancer.py +131 -0
- distllm/dist/p2p/quic_transport.py +700 -0
- distllm/dist/p2p/router.py +607 -0
- distllm/dist/p2p/transport.py +399 -0
- distllm/dist/p2p_model_distributor.py +160 -0
- distllm/dist/paged_attention_kernel.py +281 -0
- distllm/dist/parallel.py +1061 -0
- distllm/dist/parallel_planner.py +586 -0
- distllm/dist/parallel_topology.py +231 -0
- distllm/dist/partition/__init__.py +146 -0
- distllm/dist/partition/adaptive.py +532 -0
- distllm/dist/partition/benchmark_suite.py +479 -0
- distllm/dist/partition/cli.py +189 -0
- distllm/dist/partition/cloud_arbitrage.py +523 -0
- distllm/dist/partition/config.py +97 -0
- distllm/dist/partition/cost_model.py +553 -0
- distllm/dist/partition/learned_cost.py +653 -0
- distllm/dist/partition/mixed_precision_tuner.py +310 -0
- distllm/dist/partition/network_cost_model.py +664 -0
- distllm/dist/partition/optimizer.py +650 -0
- distllm/dist/partition/pareto_optimizer.py +466 -0
- distllm/dist/partition/partitioner.py +363 -0
- distllm/dist/partition/persistence.py +392 -0
- distllm/dist/partition/plugins.py +270 -0
- distllm/dist/partition/profiles.py +659 -0
- distllm/dist/partition/quant_bench.py +373 -0
- distllm/dist/partition/quant_calibrate.py +229 -0
- distllm/dist/partition/quant_coordinator.py +260 -0
- distllm/dist/partition/quant_cost.py +293 -0
- distllm/dist/partition/quant_partition.py +432 -0
- distllm/dist/partition/quant_report.py +301 -0
- distllm/dist/partition/quantization_metrics.py +355 -0
- distllm/dist/partition/quantization_search.py +290 -0
- distllm/dist/partition/quantization_tuner.py +1383 -0
- distllm/dist/partition/topology.py +218 -0
- distllm/dist/partition/validator.py +405 -0
- distllm/dist/partition/visualizer.py +275 -0
- distllm/dist/pipeline/__init__.py +71 -0
- distllm/dist/pipeline/bandwidth_controller.py +780 -0
- distllm/dist/pipeline/compression_negotiation.py +771 -0
- distllm/dist/pipeline/context.py +80 -0
- distllm/dist/pipeline/continuous_batching.py +435 -0
- distllm/dist/pipeline/disagg_orchestrator.py +284 -0
- distllm/dist/pipeline/orchestrator.py +727 -0
- distllm/dist/pipeline/pipeline_reconfig.py +1395 -0
- distllm/dist/pipeline/profiler.py +84 -0
- distllm/dist/pipeline/serialization.py +176 -0
- distllm/dist/pipeline/simulator.py +172 -0
- distllm/dist/pipeline/strategy.py +193 -0
- distllm/dist/pipeline/token_accumulator.py +211 -0
- distllm/dist/pipeline/transport.py +230 -0
- distllm/dist/pipeline/wan_disagg_orchestrator.py +564 -0
- distllm/dist/power_cap.py +358 -0
- distllm/dist/predictive_cache.py +399 -0
- distllm/dist/preemption.py +198 -0
- distllm/dist/prefix_cache.py +461 -0
- distllm/dist/prefix_clustering.py +305 -0
- distllm/dist/privacy/__init__.py +411 -0
- distllm/dist/privacy/privacy_accountant.py +344 -0
- distllm/dist/privacy.py +240 -0
- distllm/dist/provisioning.py +338 -0
- distllm/dist/quality.py +118 -0
- distllm/dist/quic_transport.py +335 -0
- distllm/dist/quota_enforcer.py +265 -0
- distllm/dist/rebalancer.py +234 -0
- distllm/dist/recovery.py +827 -0
- distllm/dist/recovery_drill.py +281 -0
- distllm/dist/redis_cache.py +265 -0
- distllm/dist/redundant.py +308 -0
- distllm/dist/redundant_executor.py +1536 -0
- distllm/dist/reputation.py +231 -0
- distllm/dist/router.py +382 -0
- distllm/dist/routing/__init__.py +19 -0
- distllm/dist/routing/arbitrage_engine.py +575 -0
- distllm/dist/routing/composite.py +179 -0
- distllm/dist/routing/consistent_hash.py +144 -0
- distllm/dist/routing/latency_aware.py +116 -0
- distllm/dist/routing/load_aware.py +114 -0
- distllm/dist/scheduling/__init__.py +38 -0
- distllm/dist/scheduling/batcher.py +95 -0
- distllm/dist/scheduling/classifier.py +153 -0
- distllm/dist/scheduling/deadline_scheduler.py +958 -0
- distllm/dist/scheduling/iteration.py +404 -0
- distllm/dist/scheduling/profiler.py +57 -0
- distllm/dist/simulation/__init__.py +54 -0
- distllm/dist/simulation/chaos_simulator.py +901 -0
- distllm/dist/simulation/cluster_simulator.py +667 -0
- distllm/dist/simulation/cost_aware_provisioning.py +370 -0
- distllm/dist/simulation/digital_twin.py +617 -0
- distllm/dist/simulation/topology_optimizer.py +747 -0
- distllm/dist/simulation/what_if.py +492 -0
- distllm/dist/speculative/__init__.py +47 -0
- distllm/dist/speculative/adaptive_spec.py +291 -0
- distllm/dist/speculative/draft_cache.py +246 -0
- distllm/dist/speculative/draft_orchestrator.py +634 -0
- distllm/dist/speculative/draft_registry.py +439 -0
- distllm/dist/speculative/multi_draft.py +304 -0
- distllm/dist/speculative/online_sisd.py +523 -0
- distllm/dist/straggler.py +819 -0
- distllm/dist/streaming_kv_transfer.py +174 -0
- distllm/dist/structured_output/__init__.py +7 -0
- distllm/dist/structured_output/engine.py +144 -0
- distllm/dist/tensor_pool.py +120 -0
- distllm/dist/tgi_compat.py +254 -0
- distllm/dist/topology.py +141 -0
- distllm/dist/topology_consensus.py +1961 -0
- distllm/dist/topology_dynamic.py +270 -0
- distllm/dist/tp_inprocess.py +214 -0
- distllm/dist/tp_launcher.py +310 -0
- distllm/dist/tracing.py +325 -0
- distllm/dist/transport.py +493 -0
- distllm/dist/wan_speculative.py +295 -0
- distllm/dist/wandb_integration.py +252 -0
- distllm/dist/webrtc.py +846 -0
- distllm/dist/wide_area.py +515 -0
- distllm/dist/worker.py +635 -0
- distllm/dist/zero_copy.py +307 -0
- distllm/errors/__init__.py +105 -0
- distllm/errors/policies.py +106 -0
- distllm/errors/retry.py +168 -0
- distllm/errors/types.py +721 -0
- distllm/health/__init__.py +6 -0
- distllm/health/failover.py +81 -0
- distllm/health/prober.py +31 -0
- distllm/health/service.py +173 -0
- distllm/health/state.py +76 -0
- distllm/integrations/__init__.py +72 -0
- distllm/integrations/_common/__init__.py +7 -0
- distllm/integrations/_common/base_tool_provider.py +189 -0
- distllm/integrations/_common/cost_tracker.py +144 -0
- distllm/integrations/_common/model_router.py +148 -0
- distllm/integrations/ci/__init__.py +24 -0
- distllm/integrations/ci/_common.py +92 -0
- distllm/integrations/ci/gitlab.py +345 -0
- distllm/integrations/ci/jenkins.py +273 -0
- distllm/integrations/mlflow_tracking.py +372 -0
- distllm/integrations/spark_connector.py +909 -0
- distllm/integrations/wandb/__init__.py +81 -0
- distllm/models/__init__.py +40 -0
- distllm/models/_trust_remote.py +48 -0
- distllm/models/adapter.py +768 -0
- distllm/models/adapter_router.py +198 -0
- distllm/models/cache.py +172 -0
- distllm/models/model_hub.py +650 -0
- distllm/models/partition_planner.py +384 -0
- distllm/models/partitioner.py +784 -0
- distllm/models/rope_scaling.py +119 -0
- distllm/models/safetensors_index.py +179 -0
- distllm/observability/__init__.py +107 -0
- distllm/observability/alerting_config.py +881 -0
- distllm/observability/capacity_models.py +398 -0
- distllm/observability/capacity_planning.py +2870 -0
- distllm/observability/exporter.py +228 -0
- distllm/observability/exporter_datadog.py +1362 -0
- distllm/observability/health_config.py +1282 -0
- distllm/observability/incident_response.py +779 -0
- distllm/observability/logging.py +211 -0
- distllm/observability/logging_config.py +864 -0
- distllm/observability/loki_sink.py +178 -0
- distllm/observability/metrics.py +111 -0
- distllm/observability/metrics_completeness.py +393 -0
- distllm/observability/otel_logging.py +290 -0
- distllm/observability/self_healing_config.py +1308 -0
- distllm/observability/slo_config.py +1520 -0
- distllm/observability/spans.py +235 -0
- distllm/observability/tracing.py +268 -0
- distllm/observability/tracing_config.py +676 -0
- distllm/observability/wandb_monitor.py +66 -0
- distllm/plugins/__init__.py +14 -0
- distllm/plugins/airflow.py +110 -0
- distllm/plugins/auth_plugin.py +576 -0
- distllm/plugins/builtin.py +376 -0
- distllm/plugins/cache_plugin.py +576 -0
- distllm/plugins/health_plugin.py +396 -0
- distllm/plugins/kubeflow.py +84 -0
- distllm/plugins/mlflow_plugin.py +71 -0
- distllm/plugins/registry.py +135 -0
- distllm/prompts/__init__.py +39 -0
- distllm/prompts/analysis.py +103 -0
- distllm/prompts/code.py +172 -0
- distllm/prompts/creative.py +115 -0
- distllm/prompts/education.py +91 -0
- distllm/prompts/engine.py +119 -0
- distllm/prompts/exchange.py +705 -0
- distllm/prompts/language.py +81 -0
- distllm/prompts/library.py +1198 -0
- distllm/prompts/management.py +35 -0
- distllm/prompts/productivity.py +86 -0
- distllm/prompts/professional.py +111 -0
- distllm/prompts/prompt_def.py +33 -0
- distllm/prompts/reasoning.py +108 -0
- distllm/prompts/specialized.py +140 -0
- distllm/prompts/templates.py +128 -0
- distllm/prompts/writing.py +140 -0
- distllm/py.typed +0 -0
- distllm/sdk/__init__.py +82 -0
- distllm/sdk/circuit_breaker.py +139 -0
- distllm/sdk/client.py +1191 -0
- distllm/sdk/observability.py +42 -0
- distllm/sdk/streaming.py +228 -0
- distllm/sdk/transport.py +224 -0
- distllm/sdk/types.py +288 -0
- distllm/security/__init__.py +63 -0
- distllm/security/content_moderation/__init__.py +44 -0
- distllm/security/content_moderation/base.py +405 -0
- distllm/security/content_moderation/jailbreak.py +192 -0
- distllm/security/content_moderation/pii.py +262 -0
- distllm/security/content_moderation/pipeline.py +272 -0
- distllm/security/content_moderation/topics.py +153 -0
- distllm/security/content_moderation/toxicity.py +131 -0
- distllm/security/e2e.py +437 -0
- distllm/security/edge_attestation.py +381 -0
- distllm/security/log_redaction.py +244 -0
- distllm/security/quantum_safe_tls.py +314 -0
- distllm/security/spiffe.py +347 -0
- distllm/security/tee.py +461 -0
- distllm/security/utils.py +139 -0
- distllm/security/watermark.py +779 -0
- distllm/terraform/__init__.py +260 -0
- distllm/ui/__init__.py +7 -0
- distllm/ui/app.py +98 -0
- distllm/utils/__init__.py +0 -0
- distllm/utils/gbnf_grammar.py +99 -0
- distllm/utils/lazy_imports.py +90 -0
- distllm/utils/scheduling.py +38 -0
- distllm/verification/__init__.py +49 -0
- distllm/verification/comparator.py +263 -0
- distllm/verification/hash_registry.py +220 -0
- distllm/verification/report.py +240 -0
- distllm/verification/runner.py +822 -0
- distllm/verification/test_verify.py +205 -0
- distributed_llm-0.4.1.dist-info/METADATA +315 -0
- distributed_llm-0.4.1.dist-info/RECORD +806 -0
- distributed_llm-0.4.1.dist-info/WHEEL +5 -0
- distributed_llm-0.4.1.dist-info/entry_points.txt +5 -0
- distributed_llm-0.4.1.dist-info/licenses/LICENSE +97 -0
- distributed_llm-0.4.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
"""GPU memory defragmentation for PagedAttention blocks.
|
|
2
|
+
|
|
3
|
+
Compacts fragmented KV cache blocks to free contiguous GPU memory.
|
|
4
|
+
Prevents OOM errors caused by memory fragmentation during long-running
|
|
5
|
+
inference sessions with many concurrent requests.
|
|
6
|
+
|
|
7
|
+
Supports multiple compaction policies and async execution.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import enum
|
|
14
|
+
import threading
|
|
15
|
+
import time
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
import torch
|
|
20
|
+
from loguru import logger
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DefragPolicy(str, enum.Enum):
|
|
24
|
+
"""Compaction aggressiveness policy."""
|
|
25
|
+
|
|
26
|
+
LAZY = "lazy"
|
|
27
|
+
"""Only compact when fragmentation exceeds 50%. Max 64 blocks per pass."""
|
|
28
|
+
|
|
29
|
+
BALANCED = "balanced"
|
|
30
|
+
"""Compact when fragmentation exceeds 30%. Max 128 blocks per pass."""
|
|
31
|
+
|
|
32
|
+
AGGRESSIVE = "aggressive"
|
|
33
|
+
"""Compact when fragmentation exceeds 15%. Compact all fragmented blocks."""
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def threshold(self) -> float:
|
|
37
|
+
if self == DefragPolicy.LAZY:
|
|
38
|
+
return 0.50
|
|
39
|
+
if self == DefragPolicy.AGGRESSIVE:
|
|
40
|
+
return 0.15
|
|
41
|
+
return 0.30
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def max_blocks_per_pass(self) -> int:
|
|
45
|
+
if self == DefragPolicy.LAZY:
|
|
46
|
+
return 64
|
|
47
|
+
if self == DefragPolicy.AGGRESSIVE:
|
|
48
|
+
return 0 # unlimited
|
|
49
|
+
return 128
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class TieredCompactionLevel(str, enum.Enum):
|
|
53
|
+
L1_HOT = "l1_hot"
|
|
54
|
+
"""Compact within GPU memory. Fast, no offloading."""
|
|
55
|
+
|
|
56
|
+
L2_WARM = "l2_warm"
|
|
57
|
+
"""Swap cold sequences to CPU, recompact GPU, remap page table."""
|
|
58
|
+
|
|
59
|
+
L3_COLD = "l3_cold"
|
|
60
|
+
"""Offload to NVMe for extreme memory pressure."""
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class DefragConfig:
|
|
65
|
+
"""Configuration for the memory defragmenter.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
enabled: Whether defragmentation is active.
|
|
69
|
+
policy: Compaction aggressiveness policy.
|
|
70
|
+
interval_seconds: Seconds between background defrag checks.
|
|
71
|
+
max_blocks_per_pass: Override max blocks to move per pass (0 = unlimited).
|
|
72
|
+
tiered_compaction: Enable tiered (L1/L2/L3) compaction.
|
|
73
|
+
l2_cpu_swap_threshold: Fragmentation threshold to trigger L2 warm swap.
|
|
74
|
+
l3_nvme_swap_threshold: Fragmentation threshold to trigger L3 cold swap.
|
|
75
|
+
cuda_stream_priority: CUDA stream priority for copy operations.
|
|
76
|
+
enable_predictive: Enable predictive (preemptive) defragmentation.
|
|
77
|
+
enable_prometheus: Export Prometheus metrics.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
enabled: bool = True
|
|
81
|
+
policy: DefragPolicy = DefragPolicy.BALANCED
|
|
82
|
+
interval_seconds: float = 60.0
|
|
83
|
+
max_blocks_per_pass: int = 0
|
|
84
|
+
tiered_compaction: bool = False
|
|
85
|
+
l2_cpu_swap_threshold: float = 0.60
|
|
86
|
+
l3_nvme_swap_threshold: float = 0.80
|
|
87
|
+
cuda_stream_priority: int = -1
|
|
88
|
+
enable_predictive: bool = False
|
|
89
|
+
enable_prometheus: bool = False
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass
|
|
93
|
+
class FragmentInfo:
|
|
94
|
+
"""Information about a fragmented memory region."""
|
|
95
|
+
|
|
96
|
+
block_id: int
|
|
97
|
+
device: str
|
|
98
|
+
size_bytes: int
|
|
99
|
+
is_free: bool
|
|
100
|
+
adjacent_free: int
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass
|
|
104
|
+
class DefragResult:
|
|
105
|
+
"""Result of a single defragmentation pass."""
|
|
106
|
+
|
|
107
|
+
blocks_moved: int = 0
|
|
108
|
+
bytes_compacted: int = 0
|
|
109
|
+
time_ms: float = 0.0
|
|
110
|
+
fragmentation_before: float = 0.0
|
|
111
|
+
fragmentation_after: float = 0.0
|
|
112
|
+
tier_used: TieredCompactionLevel | None = None
|
|
113
|
+
error: str | None = None
|
|
114
|
+
|
|
115
|
+
def to_dict(self) -> dict:
|
|
116
|
+
result = {
|
|
117
|
+
"blocks_moved": self.blocks_moved,
|
|
118
|
+
"bytes_compacted": self.bytes_compacted,
|
|
119
|
+
"time_ms": self.time_ms,
|
|
120
|
+
"fragmentation_before": round(self.fragmentation_before, 4),
|
|
121
|
+
"fragmentation_after": round(self.fragmentation_after, 4),
|
|
122
|
+
}
|
|
123
|
+
if self.tier_used:
|
|
124
|
+
result["tier_used"] = self.tier_used.value
|
|
125
|
+
if self.error:
|
|
126
|
+
result["error"] = self.error
|
|
127
|
+
return result
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class MemoryDefragmenter:
|
|
131
|
+
"""Defragments PagedAttention KV cache blocks.
|
|
132
|
+
|
|
133
|
+
Identifies fragmented regions where free blocks are scattered
|
|
134
|
+
between allocated blocks, then compacts by moving allocated
|
|
135
|
+
blocks to fill gaps.
|
|
136
|
+
|
|
137
|
+
Thread-safe: uses a reentrant lock around all mutation operations.
|
|
138
|
+
Async-safe: provides async ``defragment_async()`` for use in event loops.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
config: Defragmentation configuration.
|
|
142
|
+
metrics_collector: Optional Prometheus-style metrics collector.
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
def __init__(
|
|
146
|
+
self,
|
|
147
|
+
config: DefragConfig | None = None,
|
|
148
|
+
metrics_collector: Any = None,
|
|
149
|
+
):
|
|
150
|
+
self._config = config or DefragConfig()
|
|
151
|
+
self._lock = threading.RLock()
|
|
152
|
+
self._async_lock = asyncio.Lock()
|
|
153
|
+
self._cuda_stream: torch.cuda.Stream | None = None
|
|
154
|
+
self._metrics = metrics_collector
|
|
155
|
+
self._stats = {
|
|
156
|
+
"defrag_count": 0,
|
|
157
|
+
"blocks_moved": 0,
|
|
158
|
+
"bytes_compacted": 0,
|
|
159
|
+
"total_time_ms": 0.0,
|
|
160
|
+
"last_fragmentation_ratio": 0.0,
|
|
161
|
+
"peak_fragmentation_ratio": 0.0,
|
|
162
|
+
"l1_count": 0,
|
|
163
|
+
"l2_count": 0,
|
|
164
|
+
"l3_count": 0,
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
# Predictive state
|
|
168
|
+
self._fragmentation_history: list[float] = []
|
|
169
|
+
self._history_max_len = 100
|
|
170
|
+
# M-10: Block cooldown — prevents moving the same block repeatedly.
|
|
171
|
+
# A block moved within _block_cooldown_seconds is skipped on next pass.
|
|
172
|
+
self._block_cooldown_seconds: float = 30.0
|
|
173
|
+
self._block_last_moved: dict[int, float] = {}
|
|
174
|
+
|
|
175
|
+
# Prometheus-style gauges (lazy init)
|
|
176
|
+
self._metric_frag_ratio: Any = None
|
|
177
|
+
self._metric_blocks_moved: Any = None
|
|
178
|
+
self._metric_duration: Any = None
|
|
179
|
+
|
|
180
|
+
# ── Configuration ──
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def config(self) -> DefragConfig:
|
|
184
|
+
return self._config
|
|
185
|
+
|
|
186
|
+
def reconfigure(self, config: DefragConfig) -> None:
|
|
187
|
+
with self._lock:
|
|
188
|
+
self._config = config
|
|
189
|
+
|
|
190
|
+
# ── Fragmentation Analysis ──
|
|
191
|
+
|
|
192
|
+
def analyze_fragmentation(self, blocks: list[Any]) -> list[FragmentInfo]:
|
|
193
|
+
"""Analyze fragmentation in a block pool.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
blocks: List of KVCacheBlock objects from PagedAttentionManager.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
List of FragmentInfo for each block.
|
|
200
|
+
"""
|
|
201
|
+
fragments = []
|
|
202
|
+
for i, block in enumerate(blocks):
|
|
203
|
+
adjacent_free = 0
|
|
204
|
+
if i > 0 and not blocks[i - 1].is_allocated:
|
|
205
|
+
adjacent_free += 1
|
|
206
|
+
if i < len(blocks) - 1 and not blocks[i + 1].is_allocated:
|
|
207
|
+
adjacent_free += 1
|
|
208
|
+
|
|
209
|
+
size_bytes = 0
|
|
210
|
+
if block.is_allocated and block.key_cache is not None:
|
|
211
|
+
size_bytes = (
|
|
212
|
+
block.key_cache.element_size() * block.key_cache.numel()
|
|
213
|
+
+ block.value_cache.element_size() * block.value_cache.numel()
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
fragments.append(
|
|
217
|
+
FragmentInfo(
|
|
218
|
+
block_id=block.block_id,
|
|
219
|
+
device=str(block.key_cache.device) if block.key_cache is not None else "cpu",
|
|
220
|
+
size_bytes=size_bytes,
|
|
221
|
+
is_free=not block.is_allocated,
|
|
222
|
+
adjacent_free=adjacent_free,
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
return fragments
|
|
227
|
+
|
|
228
|
+
def compute_compaction_plan(self, blocks: list[Any]) -> list[tuple[int, int, int]]:
|
|
229
|
+
"""Compute which blocks need to move to eliminate fragmentation.
|
|
230
|
+
|
|
231
|
+
Returns list of (source_index, dest_index, ref_count) tuples.
|
|
232
|
+
Respects copy-on-write: blocks with ref_count > 1 are not moved.
|
|
233
|
+
"""
|
|
234
|
+
moves = []
|
|
235
|
+
free_slots = []
|
|
236
|
+
allocated = []
|
|
237
|
+
|
|
238
|
+
for i, block in enumerate(blocks):
|
|
239
|
+
if not block.is_allocated:
|
|
240
|
+
free_slots.append(i)
|
|
241
|
+
elif free_slots:
|
|
242
|
+
if block.ref_count <= 1:
|
|
243
|
+
allocated.append(i)
|
|
244
|
+
|
|
245
|
+
max_blocks = self._config.max_blocks_per_pass
|
|
246
|
+
if max_blocks > 0 and len(allocated) > max_blocks:
|
|
247
|
+
allocated = allocated[:max_blocks]
|
|
248
|
+
free_slots = free_slots[:max_blocks]
|
|
249
|
+
|
|
250
|
+
for alloc_idx, free_idx in zip(reversed(allocated), free_slots):
|
|
251
|
+
if alloc_idx > free_idx:
|
|
252
|
+
moves.append((alloc_idx, free_idx, blocks[alloc_idx].ref_count))
|
|
253
|
+
|
|
254
|
+
return moves
|
|
255
|
+
|
|
256
|
+
@staticmethod
|
|
257
|
+
def compute_fragmentation_ratio(blocks: list[Any]) -> float:
|
|
258
|
+
"""Compute the fragmentation ratio.
|
|
259
|
+
|
|
260
|
+
Ratio = fragmented free blocks / total blocks.
|
|
261
|
+
A free block is "fragmented" if it has at least one allocated neighbor
|
|
262
|
+
(i.e. it's scattered among allocated blocks, not contiguous).
|
|
263
|
+
"""
|
|
264
|
+
total = len(blocks)
|
|
265
|
+
if total == 0:
|
|
266
|
+
return 0.0
|
|
267
|
+
|
|
268
|
+
fragmented = 0
|
|
269
|
+
for i, block in enumerate(blocks):
|
|
270
|
+
if not block.is_allocated:
|
|
271
|
+
has_alloc_neighbor = False
|
|
272
|
+
if i > 0 and blocks[i - 1].is_allocated:
|
|
273
|
+
has_alloc_neighbor = True
|
|
274
|
+
if i < len(blocks) - 1 and blocks[i + 1].is_allocated:
|
|
275
|
+
has_alloc_neighbor = True
|
|
276
|
+
if has_alloc_neighbor:
|
|
277
|
+
fragmented += 1
|
|
278
|
+
|
|
279
|
+
return fragmented / total
|
|
280
|
+
|
|
281
|
+
_compute_fragmentation_ratio = compute_fragmentation_ratio
|
|
282
|
+
|
|
283
|
+
# ── Defragmentation Trigger ──
|
|
284
|
+
|
|
285
|
+
def should_defragment(self, blocks: list[Any]) -> bool:
|
|
286
|
+
"""Check if defragmentation is needed based on policy threshold."""
|
|
287
|
+
ratio = self._compute_fragmentation_ratio(blocks)
|
|
288
|
+
return ratio >= self._config.policy.threshold
|
|
289
|
+
|
|
290
|
+
# ── Synchronous Defragmentation ──
|
|
291
|
+
|
|
292
|
+
def defragment(self, paged_attention_mgr: Any) -> DefragResult:
|
|
293
|
+
"""Defragment the PagedAttention block pool.
|
|
294
|
+
|
|
295
|
+
Thread-safe: acquires internal lock around all mutation.
|
|
296
|
+
Also acquires the mgr's lock to prevent concurrent free_sequence()
|
|
297
|
+
or allocate_sequence() calls from racing with block moves.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
paged_attention_mgr: A PagedAttentionManager (or any object
|
|
301
|
+
conforming to the Defragmentable protocol).
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
DefragResult with statistics.
|
|
305
|
+
"""
|
|
306
|
+
# Acquire both locks (self first, then mgr) to prevent deadlock
|
|
307
|
+
with self._lock:
|
|
308
|
+
mgr_lock = getattr(paged_attention_mgr, '_lock', None)
|
|
309
|
+
if mgr_lock is not None:
|
|
310
|
+
mgr_lock.acquire()
|
|
311
|
+
try:
|
|
312
|
+
return self._defragment_impl(paged_attention_mgr)
|
|
313
|
+
finally:
|
|
314
|
+
if mgr_lock is not None:
|
|
315
|
+
mgr_lock.release()
|
|
316
|
+
|
|
317
|
+
def _defragment_impl_locked(
|
|
318
|
+
self,
|
|
319
|
+
mgr: Any,
|
|
320
|
+
tier: TieredCompactionLevel | None = None,
|
|
321
|
+
) -> DefragResult:
|
|
322
|
+
"""Run ``_defragment_impl`` under both ``_lock`` and ``mgr._lock``.
|
|
323
|
+
|
|
324
|
+
Mirrors the lock acquisition in the synchronous ``defragment()``
|
|
325
|
+
(self first, then mgr) so a defrag task running in an executor
|
|
326
|
+
thread cannot race with a concurrent synchronous defrag or with
|
|
327
|
+
``allocate_sequence()``/``free_sequence()`` on the manager.
|
|
328
|
+
Intended for the executor-worker path used by the async variants.
|
|
329
|
+
"""
|
|
330
|
+
with self._lock:
|
|
331
|
+
mgr_lock = getattr(mgr, '_lock', None)
|
|
332
|
+
if mgr_lock is not None:
|
|
333
|
+
mgr_lock.acquire()
|
|
334
|
+
try:
|
|
335
|
+
return self._defragment_impl(mgr, tier)
|
|
336
|
+
finally:
|
|
337
|
+
if mgr_lock is not None:
|
|
338
|
+
mgr_lock.release()
|
|
339
|
+
|
|
340
|
+
@staticmethod
|
|
341
|
+
def _resolve_blocks(mgr: Any) -> tuple[list[Any], dict[str, Any]]:
|
|
342
|
+
"""Get blocks and seq_blocks from mgr via protocol or fallback."""
|
|
343
|
+
try:
|
|
344
|
+
blocks = mgr.get_blocks()
|
|
345
|
+
if not isinstance(blocks, list):
|
|
346
|
+
raise TypeError
|
|
347
|
+
except (AttributeError, TypeError):
|
|
348
|
+
blocks = mgr._blocks
|
|
349
|
+
try:
|
|
350
|
+
seq_blocks = mgr.get_seq_blocks()
|
|
351
|
+
if not isinstance(seq_blocks, dict):
|
|
352
|
+
raise TypeError
|
|
353
|
+
except (AttributeError, TypeError):
|
|
354
|
+
seq_blocks = mgr._seq_blocks
|
|
355
|
+
return blocks, seq_blocks
|
|
356
|
+
|
|
357
|
+
@staticmethod
|
|
358
|
+
def _set_free_blocks(mgr: Any, free_ids: list[int]) -> None:
|
|
359
|
+
try:
|
|
360
|
+
mgr.set_free_blocks(free_ids)
|
|
361
|
+
except (AttributeError, TypeError):
|
|
362
|
+
mgr._free_blocks = free_ids
|
|
363
|
+
|
|
364
|
+
def _defragment_impl(
|
|
365
|
+
self,
|
|
366
|
+
mgr: Any,
|
|
367
|
+
tier: TieredCompactionLevel | None = None,
|
|
368
|
+
) -> DefragResult:
|
|
369
|
+
"""Internal defrag implementation (caller must hold _lock)."""
|
|
370
|
+
start = time.monotonic()
|
|
371
|
+
result = DefragResult()
|
|
372
|
+
|
|
373
|
+
blocks, seq_blocks = self._resolve_blocks(mgr)
|
|
374
|
+
if not blocks:
|
|
375
|
+
return result
|
|
376
|
+
|
|
377
|
+
result.fragmentation_before = self._compute_fragmentation_ratio(blocks)
|
|
378
|
+
|
|
379
|
+
self._fragmentation_history.append(result.fragmentation_before)
|
|
380
|
+
if len(self._fragmentation_history) > self._history_max_len:
|
|
381
|
+
self._fragmentation_history.pop(0)
|
|
382
|
+
|
|
383
|
+
moves = self.compute_compaction_plan(blocks)
|
|
384
|
+
if not moves:
|
|
385
|
+
result.fragmentation_after = result.fragmentation_before
|
|
386
|
+
return result
|
|
387
|
+
|
|
388
|
+
# M-10: Filter out blocks moved recently (cooldown).
|
|
389
|
+
# Prevents thrashing where the same block is moved on every pass.
|
|
390
|
+
now = time.monotonic()
|
|
391
|
+
filtered_moves = []
|
|
392
|
+
for src_idx, dst_idx, ref_count in moves:
|
|
393
|
+
src_block = blocks[src_idx]
|
|
394
|
+
last_moved = self._block_last_moved.get(src_block.block_id, 0)
|
|
395
|
+
if now - last_moved > self._block_cooldown_seconds:
|
|
396
|
+
filtered_moves.append((src_idx, dst_idx, ref_count))
|
|
397
|
+
moves = filtered_moves
|
|
398
|
+
if not moves:
|
|
399
|
+
result.fragmentation_after = result.fragmentation_before
|
|
400
|
+
return result
|
|
401
|
+
|
|
402
|
+
# L2/L3 tier: swap cold sequences to CPU/NVMe before compacting
|
|
403
|
+
if tier in (TieredCompactionLevel.L2_WARM, TieredCompactionLevel.L3_COLD):
|
|
404
|
+
self._offload_cold_sequences(mgr, tier)
|
|
405
|
+
|
|
406
|
+
# Initialize CUDA stream if needed
|
|
407
|
+
stream = self._get_cuda_stream()
|
|
408
|
+
|
|
409
|
+
bytes_moved = 0
|
|
410
|
+
for src_idx, dst_idx, ref_count in moves:
|
|
411
|
+
src_block = blocks[src_idx]
|
|
412
|
+
dst_block = blocks[dst_idx]
|
|
413
|
+
|
|
414
|
+
if src_block.key_cache is None:
|
|
415
|
+
continue
|
|
416
|
+
|
|
417
|
+
# Allocate destination block if it hasn't been allocated yet
|
|
418
|
+
if dst_block.key_cache is None:
|
|
419
|
+
dst_block.allocate(
|
|
420
|
+
num_heads=src_block.key_cache.shape[1],
|
|
421
|
+
head_dim=src_block.key_cache.shape[-1],
|
|
422
|
+
device=str(src_block.key_cache.device),
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
if stream is not None:
|
|
426
|
+
with torch.cuda.stream(stream):
|
|
427
|
+
dst_block.key_cache.copy_(src_block.key_cache)
|
|
428
|
+
dst_block.value_cache.copy_(src_block.value_cache)
|
|
429
|
+
else:
|
|
430
|
+
dst_block.key_cache.copy_(src_block.key_cache)
|
|
431
|
+
dst_block.value_cache.copy_(src_block.value_cache)
|
|
432
|
+
|
|
433
|
+
dst_block.num_tokens = src_block.num_tokens
|
|
434
|
+
dst_block.is_allocated = True
|
|
435
|
+
dst_block.ref_count = ref_count
|
|
436
|
+
|
|
437
|
+
src_block.key_cache = None
|
|
438
|
+
src_block.value_cache = None
|
|
439
|
+
src_block.is_allocated = False
|
|
440
|
+
src_block.num_tokens = 0
|
|
441
|
+
src_block.ref_count = 0
|
|
442
|
+
|
|
443
|
+
bytes_moved += (
|
|
444
|
+
dst_block.key_cache.element_size() * dst_block.key_cache.numel()
|
|
445
|
+
+ dst_block.value_cache.element_size() * dst_block.value_cache.numel()
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
# Synchronize CUDA stream before updating bookkeeping
|
|
449
|
+
if stream is not None:
|
|
450
|
+
stream.synchronize()
|
|
451
|
+
|
|
452
|
+
# Update sequence block references using block_ids (not list indices).
|
|
453
|
+
# This is robust even if block_id != list index in future implementations.
|
|
454
|
+
for src_idx, dst_idx, _ in moves:
|
|
455
|
+
src_block_id = blocks[src_idx].block_id
|
|
456
|
+
dst_block_id = blocks[dst_idx].block_id
|
|
457
|
+
for seq in seq_blocks.values():
|
|
458
|
+
for i, bid in enumerate(seq.block_ids):
|
|
459
|
+
if bid == src_block_id:
|
|
460
|
+
seq.block_ids[i] = dst_block_id
|
|
461
|
+
|
|
462
|
+
# Rebuild free list
|
|
463
|
+
self._set_free_blocks(mgr, [b.block_id for b in blocks if not b.is_allocated])
|
|
464
|
+
|
|
465
|
+
# M-10: Record when each block was last moved (for cooldown)
|
|
466
|
+
now_cd = time.monotonic()
|
|
467
|
+
for src_idx, _dst_idx, _ref_count in moves:
|
|
468
|
+
src_block = blocks[src_idx]
|
|
469
|
+
self._block_last_moved[src_block.block_id] = now_cd
|
|
470
|
+
|
|
471
|
+
result.fragmentation_after = self._compute_fragmentation_ratio(blocks)
|
|
472
|
+
result.blocks_moved = len(moves)
|
|
473
|
+
result.bytes_compacted = bytes_moved
|
|
474
|
+
result.time_ms = (time.monotonic() - start) * 1000
|
|
475
|
+
|
|
476
|
+
# Only reclaim GPU memory when blocks were actually moved.
|
|
477
|
+
# torch.cuda.empty_cache() causes 10-100ms stalls and should not
|
|
478
|
+
# be called on every defrag pass. See backends/paged_attention.py
|
|
479
|
+
# line 471 for the same rationale.
|
|
480
|
+
if bytes_moved > 0:
|
|
481
|
+
try:
|
|
482
|
+
torch.cuda.empty_cache()
|
|
483
|
+
except Exception:
|
|
484
|
+
pass
|
|
485
|
+
|
|
486
|
+
result.tier_used = tier or TieredCompactionLevel.L1_HOT
|
|
487
|
+
|
|
488
|
+
# Update stats
|
|
489
|
+
self._stats["defrag_count"] += 1
|
|
490
|
+
self._stats["blocks_moved"] += result.blocks_moved
|
|
491
|
+
self._stats["bytes_compacted"] += result.bytes_compacted
|
|
492
|
+
self._stats["total_time_ms"] += result.time_ms
|
|
493
|
+
self._stats["last_fragmentation_ratio"] = result.fragmentation_after
|
|
494
|
+
self._stats["peak_fragmentation_ratio"] = max(
|
|
495
|
+
self._stats["peak_fragmentation_ratio"],
|
|
496
|
+
result.fragmentation_before,
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
tier_key = f"{tier or TieredCompactionLevel.L1_HOT}_count".replace("-", "_")
|
|
500
|
+
ts = tier or TieredCompactionLevel.L1_HOT
|
|
501
|
+
if ts == TieredCompactionLevel.L1_HOT:
|
|
502
|
+
self._stats["l1_count"] += 1
|
|
503
|
+
elif ts == TieredCompactionLevel.L2_WARM:
|
|
504
|
+
self._stats["l2_count"] += 1
|
|
505
|
+
elif ts == TieredCompactionLevel.L3_COLD:
|
|
506
|
+
self._stats["l3_count"] += 1
|
|
507
|
+
|
|
508
|
+
self._emit_prometheus(result)
|
|
509
|
+
|
|
510
|
+
logger.info(
|
|
511
|
+
f"Defrag [{tier or TieredCompactionLevel.L1_HOT.value}]: "
|
|
512
|
+
f"moved {result.blocks_moved} blocks, "
|
|
513
|
+
f"{result.bytes_compacted / 1024 / 1024:.1f}MB compacted, "
|
|
514
|
+
f"frag {result.fragmentation_before:.1%} -> {result.fragmentation_after:.1%}, "
|
|
515
|
+
f"{result.time_ms:.1f}ms"
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
return result
|
|
519
|
+
|
|
520
|
+
def _offload_cold_sequences(self, mgr: Any, tier: TieredCompactionLevel) -> int:
|
|
521
|
+
"""Offload cold sequences to CPU (L2) or NVMe (L3).
|
|
522
|
+
|
|
523
|
+
Returns number of sequences offloaded.
|
|
524
|
+
"""
|
|
525
|
+
offloaded = 0
|
|
526
|
+
_, seq_blocks = self._resolve_blocks(mgr)
|
|
527
|
+
seq_ids = list(seq_blocks.keys())
|
|
528
|
+
for seq_id in seq_ids:
|
|
529
|
+
if tier == TieredCompactionLevel.L2_WARM:
|
|
530
|
+
offloaded += mgr.swap_blocks_to_cpu(seq_id)
|
|
531
|
+
elif tier == TieredCompactionLevel.L3_COLD:
|
|
532
|
+
logger.warning(
|
|
533
|
+
"L3 NVMe offload not yet implemented; "
|
|
534
|
+
"falling back to CPU swap for seq_id={}",
|
|
535
|
+
seq_id,
|
|
536
|
+
)
|
|
537
|
+
try:
|
|
538
|
+
mgr.swap_blocks_to_cpu(seq_id)
|
|
539
|
+
offloaded += 1
|
|
540
|
+
except Exception:
|
|
541
|
+
pass
|
|
542
|
+
return offloaded
|
|
543
|
+
|
|
544
|
+
def _get_cuda_stream(self) -> torch.cuda.Stream | None:
|
|
545
|
+
"""Get or create a CUDA stream for async copies."""
|
|
546
|
+
if not torch.cuda.is_available():
|
|
547
|
+
return None
|
|
548
|
+
if self._cuda_stream is None:
|
|
549
|
+
self._cuda_stream = torch.cuda.Stream(
|
|
550
|
+
priority=self._config.cuda_stream_priority
|
|
551
|
+
)
|
|
552
|
+
return self._cuda_stream
|
|
553
|
+
|
|
554
|
+
# ── Async Defragmentation ──
|
|
555
|
+
|
|
556
|
+
async def defragment_async(self, paged_attention_mgr: Any) -> DefragResult:
|
|
557
|
+
"""Async variant that yields the event loop during compaction.
|
|
558
|
+
|
|
559
|
+
Thread-safe: uses asyncio.Lock for cooperative concurrency and
|
|
560
|
+
re-acquires the same threading locks as the synchronous path
|
|
561
|
+
inside the executor worker (self first, then the manager's lock).
|
|
562
|
+
"""
|
|
563
|
+
async with self._async_lock:
|
|
564
|
+
loop = asyncio.get_running_loop()
|
|
565
|
+
return await loop.run_in_executor(
|
|
566
|
+
None, self._defragment_impl_locked, paged_attention_mgr, None,
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
async def defragment_with_tier_async(
|
|
570
|
+
self,
|
|
571
|
+
paged_attention_mgr: Any,
|
|
572
|
+
tier: TieredCompactionLevel,
|
|
573
|
+
) -> DefragResult:
|
|
574
|
+
"""Async defragmentation with explicit tier selection."""
|
|
575
|
+
async with self._async_lock:
|
|
576
|
+
loop = asyncio.get_running_loop()
|
|
577
|
+
return await loop.run_in_executor(
|
|
578
|
+
None, self._defragment_impl_locked, paged_attention_mgr, tier,
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
# ── Predictive Defragmentation ──
|
|
582
|
+
|
|
583
|
+
def predict_fragmentation(self, steps_ahead: int = 5) -> float:
|
|
584
|
+
"""Predict future fragmentation using exponential smoothing.
|
|
585
|
+
|
|
586
|
+
Args:
|
|
587
|
+
steps_ahead: Number of inference steps to forecast.
|
|
588
|
+
|
|
589
|
+
Returns:
|
|
590
|
+
Predicted fragmentation ratio (0.0-1.0).
|
|
591
|
+
"""
|
|
592
|
+
history = self._fragmentation_history
|
|
593
|
+
if len(history) < 3:
|
|
594
|
+
return history[-1] if history else 0.0
|
|
595
|
+
|
|
596
|
+
alpha = 0.3
|
|
597
|
+
smoothed = history[0]
|
|
598
|
+
for val in history[1:]:
|
|
599
|
+
smoothed = alpha * val + (1 - alpha) * smoothed
|
|
600
|
+
|
|
601
|
+
trend = (history[-1] - history[0]) / max(len(history) - 1, 1)
|
|
602
|
+
predicted = smoothed + trend * steps_ahead
|
|
603
|
+
return max(0.0, min(1.0, predicted))
|
|
604
|
+
|
|
605
|
+
# ── Prometheus Metrics ──
|
|
606
|
+
|
|
607
|
+
def _ensure_prometheus(self) -> None:
|
|
608
|
+
if not self._config.enable_prometheus:
|
|
609
|
+
return
|
|
610
|
+
if self._metric_frag_ratio is not None:
|
|
611
|
+
return
|
|
612
|
+
try:
|
|
613
|
+
from prometheus_client import Gauge, Histogram
|
|
614
|
+
|
|
615
|
+
self._metric_frag_ratio = Gauge(
|
|
616
|
+
"kv_cache_fragmentation_ratio",
|
|
617
|
+
"Current KV cache fragmentation ratio (0-1)",
|
|
618
|
+
)
|
|
619
|
+
self._metric_blocks_moved = Gauge(
|
|
620
|
+
"defrag_blocks_moved_total",
|
|
621
|
+
"Total blocks moved by defragmentation",
|
|
622
|
+
)
|
|
623
|
+
self._metric_duration = Histogram(
|
|
624
|
+
"defrag_duration_ms",
|
|
625
|
+
"Defragmentation pass duration in ms",
|
|
626
|
+
buckets=[10, 50, 100, 500, 1000, 5000],
|
|
627
|
+
)
|
|
628
|
+
except ImportError:
|
|
629
|
+
pass
|
|
630
|
+
|
|
631
|
+
def _emit_prometheus(self, result: DefragResult) -> None:
|
|
632
|
+
if not self._config.enable_prometheus:
|
|
633
|
+
return
|
|
634
|
+
self._ensure_prometheus()
|
|
635
|
+
try:
|
|
636
|
+
if self._metric_frag_ratio is not None:
|
|
637
|
+
self._metric_frag_ratio.set(result.fragmentation_after)
|
|
638
|
+
if self._metric_blocks_moved is not None:
|
|
639
|
+
self._metric_blocks_moved.inc(result.blocks_moved)
|
|
640
|
+
if self._metric_duration is not None:
|
|
641
|
+
self._metric_duration.observe(result.time_ms)
|
|
642
|
+
except Exception:
|
|
643
|
+
pass
|
|
644
|
+
|
|
645
|
+
# ── Properties ──
|
|
646
|
+
|
|
647
|
+
@property
|
|
648
|
+
def stats(self) -> dict:
|
|
649
|
+
with self._lock:
|
|
650
|
+
return dict(self._stats)
|
|
651
|
+
|
|
652
|
+
@property
|
|
653
|
+
def fragmentation_history(self) -> list[float]:
|
|
654
|
+
return list(self._fragmentation_history)
|
|
655
|
+
|
|
656
|
+
@property
|
|
657
|
+
def needs_tier_upgrade(self) -> bool:
|
|
658
|
+
"""Check if fragmentation warrants an upgrade to L2 or L3."""
|
|
659
|
+
if not self._fragmentation_history:
|
|
660
|
+
return False
|
|
661
|
+
current = self._fragmentation_history[-1]
|
|
662
|
+
return current > self._config.l2_cpu_swap_threshold
|
|
663
|
+
|
|
664
|
+
@property
|
|
665
|
+
def defrag_count(self) -> int:
|
|
666
|
+
return self._stats["defrag_count"]
|