kiapi 0.1.0__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.
- kiapi/__init__.py +3 -0
- kiapi/__main__.py +4 -0
- kiapi/api/__init__.py +26 -0
- kiapi/api/_constants/require_auth.py +14 -0
- kiapi/api/_helpers/build_job_responses.py +73 -0
- kiapi/api/_helpers/build_openapi.py +55 -0
- kiapi/api/_helpers/build_train_responses.py +21 -0
- kiapi/api/_helpers/get_accept.py +20 -0
- kiapi/api/_helpers/get_ctx.py +7 -0
- kiapi/api/_helpers/get_worker.py +7 -0
- kiapi/api/_helpers/register_capability_endpoints.py +70 -0
- kiapi/api/_helpers/submit_and_maybe_wait.py +113 -0
- kiapi/api/_settings.py +50 -0
- kiapi/api/_views/async_job_response.py +21 -0
- kiapi/api/_views/capability_model_spec.py +49 -0
- kiapi/api/app.py +150 -0
- kiapi/api/audio/acestep/_views/extract_response.py +57 -0
- kiapi/api/audio/acestep/_views/track_response.py +57 -0
- kiapi/api/audio/acestep/router.py +190 -0
- kiapi/api/audio/audiogen/_views/audio_response.py +46 -0
- kiapi/api/audio/audiogen/router.py +77 -0
- kiapi/api/chat/_views/chat_completion_response.py +71 -0
- kiapi/api/chat/router.py +253 -0
- kiapi/api/embedding/_views/embedding_response.py +35 -0
- kiapi/api/embedding/router.py +82 -0
- kiapi/api/files/_operations/get_file.py +14 -0
- kiapi/api/files/_operations/get_file_id.py +18 -0
- kiapi/api/files/_views/file_delete_response.py +14 -0
- kiapi/api/files/_views/file_list_response.py +15 -0
- kiapi/api/files/router.py +105 -0
- kiapi/api/health/_views/health_response.py +26 -0
- kiapi/api/health/router.py +28 -0
- kiapi/api/image/depthpro/_views/estimate_response.py +70 -0
- kiapi/api/image/depthpro/router.py +81 -0
- kiapi/api/image/ernie/_views/image_response.py +50 -0
- kiapi/api/image/ernie/router.py +181 -0
- kiapi/api/image/flux2/_views/image_response.py +53 -0
- kiapi/api/image/flux2/router.py +197 -0
- kiapi/api/image/ideogram4/_views/image_response.py +55 -0
- kiapi/api/image/ideogram4/router.py +87 -0
- kiapi/api/image/qwen/_views/image_response.py +47 -0
- kiapi/api/image/qwen/router.py +141 -0
- kiapi/api/image/seedvr2/_views/image_response.py +50 -0
- kiapi/api/image/seedvr2/router.py +83 -0
- kiapi/api/image/zimage/_views/image_response.py +49 -0
- kiapi/api/image/zimage/router.py +131 -0
- kiapi/api/jobs/_operations/get_job_id.py +18 -0
- kiapi/api/jobs/_views/job_delete_response.py +19 -0
- kiapi/api/jobs/_views/job_list_response.py +15 -0
- kiapi/api/jobs/router.py +62 -0
- kiapi/api/models/_schemas/openai_model_spec.py +26 -0
- kiapi/api/models/_views/model_list_response.py +15 -0
- kiapi/api/models/router.py +29 -0
- kiapi/api/video/ltx2/_views/__init__.py +1 -0
- kiapi/api/video/ltx2/_views/video_response.py +59 -0
- kiapi/api/video/ltx2/router.py +89 -0
- kiapi/api/web/_views/__init__.py +7 -0
- kiapi/api/web/_views/fetch_api_request.py +26 -0
- kiapi/api/web/_views/fetch_error_response.py +25 -0
- kiapi/api/web/router.py +302 -0
- kiapi/capabilities/__init__.py +24 -0
- kiapi/capabilities/_exceptions/CapabilityError.py +2 -0
- kiapi/capabilities/_exceptions/ValidationError.py +2 -0
- kiapi/capabilities/_helpers/attach_mflux_progress.py +50 -0
- kiapi/capabilities/_helpers/get_file_path.py +20 -0
- kiapi/capabilities/_helpers/resolve_file_ref.py +16 -0
- kiapi/capabilities/acestep/README.ja.md +174 -0
- kiapi/capabilities/acestep/README.md +167 -0
- kiapi/capabilities/acestep/__init__.py +37 -0
- kiapi/capabilities/acestep/_constants/description.py +122 -0
- kiapi/capabilities/acestep/_exceptions/ace_step_engine_error.py +2 -0
- kiapi/capabilities/acestep/_helpers/register.py +96 -0
- kiapi/capabilities/acestep/_models/acestep.py +105 -0
- kiapi/capabilities/acestep/_operations/handle_cover.py +23 -0
- kiapi/capabilities/acestep/_operations/handle_extract.py +38 -0
- kiapi/capabilities/acestep/_operations/handle_generate.py +18 -0
- kiapi/capabilities/acestep/_operations/handle_repaint.py +25 -0
- kiapi/capabilities/acestep/_operations/resolve_ace_step_paths.py +30 -0
- kiapi/capabilities/acestep/_operations/resolve_cover_params.py +21 -0
- kiapi/capabilities/acestep/_operations/resolve_engine.py +10 -0
- kiapi/capabilities/acestep/_operations/resolve_extract_params.py +24 -0
- kiapi/capabilities/acestep/_operations/resolve_generate_params.py +18 -0
- kiapi/capabilities/acestep/_operations/resolve_repaint_params.py +22 -0
- kiapi/capabilities/acestep/_schemas/ace_step_paths.py +29 -0
- kiapi/capabilities/acestep/_services/ace_step_engine.py +143 -0
- kiapi/capabilities/acestep/_settings.py +82 -0
- kiapi/capabilities/acestep/_views/ace_step_base.py +61 -0
- kiapi/capabilities/acestep/_views/cover_params.py +26 -0
- kiapi/capabilities/acestep/_views/cover_request.py +49 -0
- kiapi/capabilities/acestep/_views/extract_params.py +24 -0
- kiapi/capabilities/acestep/_views/extract_request.py +33 -0
- kiapi/capabilities/acestep/_views/generate_params.py +25 -0
- kiapi/capabilities/acestep/_views/generate_request.py +54 -0
- kiapi/capabilities/acestep/_views/repaint_params.py +27 -0
- kiapi/capabilities/acestep/_views/repaint_request.py +51 -0
- kiapi/capabilities/acestep/_views/track_params.py +7 -0
- kiapi/capabilities/acestep/worker_subprocess.py +247 -0
- kiapi/capabilities/audiogen/README.ja.md +49 -0
- kiapi/capabilities/audiogen/README.md +48 -0
- kiapi/capabilities/audiogen/__init__.py +25 -0
- kiapi/capabilities/audiogen/_constants/description.py +60 -0
- kiapi/capabilities/audiogen/_helpers/attach_audiogen_progress.py +27 -0
- kiapi/capabilities/audiogen/_helpers/register.py +42 -0
- kiapi/capabilities/audiogen/_helpers/validate_generate.py +14 -0
- kiapi/capabilities/audiogen/_models/audiogen.py +98 -0
- kiapi/capabilities/audiogen/_operations/handle_generate.py +27 -0
- kiapi/capabilities/audiogen/_operations/resolve_generate_params.py +25 -0
- kiapi/capabilities/audiogen/_settings.py +34 -0
- kiapi/capabilities/audiogen/_views/generate_params.py +14 -0
- kiapi/capabilities/audiogen/_views/generate_request.py +96 -0
- kiapi/capabilities/chat/README.ja.md +822 -0
- kiapi/capabilities/chat/README.md +806 -0
- kiapi/capabilities/chat/__init__.py +25 -0
- kiapi/capabilities/chat/_constants/description.py +163 -0
- kiapi/capabilities/chat/_helpers/register.py +67 -0
- kiapi/capabilities/chat/_models/qwen3_5.py +183 -0
- kiapi/capabilities/chat/_models/qwen3_omni.py +242 -0
- kiapi/capabilities/chat/_operations/apply_template.py +74 -0
- kiapi/capabilities/chat/_operations/completed_hermes_tool_call_text.py +9 -0
- kiapi/capabilities/chat/_operations/emit_streaming_response.py +297 -0
- kiapi/capabilities/chat/_operations/ensure_streaming_detokenizer_compat.py +49 -0
- kiapi/capabilities/chat/_operations/format_response.py +72 -0
- kiapi/capabilities/chat/_operations/handle_chat.py +25 -0
- kiapi/capabilities/chat/_operations/parse_hermes_tool_calls.py +63 -0
- kiapi/capabilities/chat/_operations/parse_json_tool_calls.py +66 -0
- kiapi/capabilities/chat/_operations/parse_messages.py +326 -0
- kiapi/capabilities/chat/_operations/resolve_chat_params.py +50 -0
- kiapi/capabilities/chat/_operations/stream_text_from_tokens.py +32 -0
- kiapi/capabilities/chat/_operations/strip_tool_calls.py +15 -0
- kiapi/capabilities/chat/_settings.py +74 -0
- kiapi/capabilities/chat/_utils/apply_parallel_tool_call_policy.py +17 -0
- kiapi/capabilities/chat/_utils/apply_seed.py +8 -0
- kiapi/capabilities/chat/_utils/load_audio_mono.py +31 -0
- kiapi/capabilities/chat/_utils/load_mlx_vlm.py +12 -0
- kiapi/capabilities/chat/_utils/warmup_params.py +26 -0
- kiapi/capabilities/chat/_views/chat_params.py +34 -0
- kiapi/capabilities/chat/_views/chat_request.py +153 -0
- kiapi/capabilities/depthpro/README.ja.md +131 -0
- kiapi/capabilities/depthpro/README.md +124 -0
- kiapi/capabilities/depthpro/__init__.py +23 -0
- kiapi/capabilities/depthpro/_constants/description.py +39 -0
- kiapi/capabilities/depthpro/_helpers/register.py +46 -0
- kiapi/capabilities/depthpro/_helpers/validate_estimate.py +32 -0
- kiapi/capabilities/depthpro/_models/depthpro.py +144 -0
- kiapi/capabilities/depthpro/_operations/handle_estimate.py +50 -0
- kiapi/capabilities/depthpro/_operations/is_quantize_override.py +12 -0
- kiapi/capabilities/depthpro/_operations/resolve_estimate_params.py +24 -0
- kiapi/capabilities/depthpro/_settings.py +46 -0
- kiapi/capabilities/depthpro/_views/estimate_params.py +21 -0
- kiapi/capabilities/depthpro/_views/estimate_request.py +63 -0
- kiapi/capabilities/embedding/README.ja.md +137 -0
- kiapi/capabilities/embedding/README.md +128 -0
- kiapi/capabilities/embedding/__init__.py +24 -0
- kiapi/capabilities/embedding/_constants/description.py +47 -0
- kiapi/capabilities/embedding/_helpers/register.py +66 -0
- kiapi/capabilities/embedding/_models/qwen3_text.py +53 -0
- kiapi/capabilities/embedding/_models/qwen3_vl.py +97 -0
- kiapi/capabilities/embedding/_operations/format_response.py +24 -0
- kiapi/capabilities/embedding/_operations/handle_embed.py +24 -0
- kiapi/capabilities/embedding/_operations/parse_inputs.py +108 -0
- kiapi/capabilities/embedding/_operations/resolve_embed_params.py +18 -0
- kiapi/capabilities/embedding/_settings.py +27 -0
- kiapi/capabilities/embedding/_utils/to_vector.py +20 -0
- kiapi/capabilities/embedding/_views/embed_params.py +17 -0
- kiapi/capabilities/embedding/_views/embed_request.py +39 -0
- kiapi/capabilities/ernie/README.ja.md +242 -0
- kiapi/capabilities/ernie/README.md +223 -0
- kiapi/capabilities/ernie/__init__.py +34 -0
- kiapi/capabilities/ernie/_constants/description.py +71 -0
- kiapi/capabilities/ernie/_helpers/register.py +60 -0
- kiapi/capabilities/ernie/_helpers/validate_edit.py +19 -0
- kiapi/capabilities/ernie/_helpers/validate_generate.py +8 -0
- kiapi/capabilities/ernie/_models/ernie.py +356 -0
- kiapi/capabilities/ernie/_operations/handle_edit.py +39 -0
- kiapi/capabilities/ernie/_operations/handle_generate.py +38 -0
- kiapi/capabilities/ernie/_operations/handle_train.py +77 -0
- kiapi/capabilities/ernie/_operations/is_quantize_override.py +16 -0
- kiapi/capabilities/ernie/_operations/resolve_edit_params.py +44 -0
- kiapi/capabilities/ernie/_operations/resolve_generate_params.py +40 -0
- kiapi/capabilities/ernie/_operations/resolve_lora_params.py +23 -0
- kiapi/capabilities/ernie/_operations/resolve_train_params.py +43 -0
- kiapi/capabilities/ernie/_operations/validate_common.py +33 -0
- kiapi/capabilities/ernie/_schemas/lora_ref.py +27 -0
- kiapi/capabilities/ernie/_settings.py +151 -0
- kiapi/capabilities/ernie/_views/edit_params.py +36 -0
- kiapi/capabilities/ernie/_views/edit_request.py +142 -0
- kiapi/capabilities/ernie/_views/generate_params.py +32 -0
- kiapi/capabilities/ernie/_views/generate_request.py +127 -0
- kiapi/capabilities/ernie/_views/train_params.py +28 -0
- kiapi/capabilities/ernie/_views/train_request.py +96 -0
- kiapi/capabilities/flux2/README.ja.md +260 -0
- kiapi/capabilities/flux2/README.md +241 -0
- kiapi/capabilities/flux2/__init__.py +36 -0
- kiapi/capabilities/flux2/_constants/description.py +81 -0
- kiapi/capabilities/flux2/_helpers/register.py +98 -0
- kiapi/capabilities/flux2/_helpers/validate_edit.py +12 -0
- kiapi/capabilities/flux2/_helpers/validate_generate.py +8 -0
- kiapi/capabilities/flux2/_models/flux2.py +396 -0
- kiapi/capabilities/flux2/_operations/handle_edit.py +44 -0
- kiapi/capabilities/flux2/_operations/handle_generate.py +46 -0
- kiapi/capabilities/flux2/_operations/handle_train.py +105 -0
- kiapi/capabilities/flux2/_operations/is_quantize_override.py +16 -0
- kiapi/capabilities/flux2/_operations/resolve_edit_params.py +43 -0
- kiapi/capabilities/flux2/_operations/resolve_generate_params.py +44 -0
- kiapi/capabilities/flux2/_operations/resolve_lora_params.py +23 -0
- kiapi/capabilities/flux2/_operations/resolve_train_params.py +42 -0
- kiapi/capabilities/flux2/_operations/role_spec.py +13 -0
- kiapi/capabilities/flux2/_operations/split_repo.py +11 -0
- kiapi/capabilities/flux2/_operations/validate_common.py +35 -0
- kiapi/capabilities/flux2/_schemas/lora_ref.py +27 -0
- kiapi/capabilities/flux2/_settings.py +181 -0
- kiapi/capabilities/flux2/_views/edit_params.py +36 -0
- kiapi/capabilities/flux2/_views/edit_request.py +145 -0
- kiapi/capabilities/flux2/_views/generate_params.py +36 -0
- kiapi/capabilities/flux2/_views/generate_request.py +150 -0
- kiapi/capabilities/flux2/_views/train_params.py +32 -0
- kiapi/capabilities/flux2/_views/train_request.py +102 -0
- kiapi/capabilities/ideogram4/README.ja.md +230 -0
- kiapi/capabilities/ideogram4/README.md +219 -0
- kiapi/capabilities/ideogram4/__init__.py +24 -0
- kiapi/capabilities/ideogram4/_constants/description.py +65 -0
- kiapi/capabilities/ideogram4/_helpers/register.py +44 -0
- kiapi/capabilities/ideogram4/_helpers/validate_generate.py +31 -0
- kiapi/capabilities/ideogram4/_models/ideogram4.py +120 -0
- kiapi/capabilities/ideogram4/_operations/handle_generate.py +30 -0
- kiapi/capabilities/ideogram4/_operations/is_quantize_override.py +12 -0
- kiapi/capabilities/ideogram4/_operations/resolve_generate_params.py +30 -0
- kiapi/capabilities/ideogram4/_settings.py +89 -0
- kiapi/capabilities/ideogram4/_views/generate_params.py +27 -0
- kiapi/capabilities/ideogram4/_views/generate_request.py +118 -0
- kiapi/capabilities/ltx2/README.ja.md +287 -0
- kiapi/capabilities/ltx2/README.md +274 -0
- kiapi/capabilities/ltx2/__init__.py +26 -0
- kiapi/capabilities/ltx2/_constants/description.py +81 -0
- kiapi/capabilities/ltx2/_helpers/register.py +58 -0
- kiapi/capabilities/ltx2/_helpers/validate_generate.py +42 -0
- kiapi/capabilities/ltx2/_models/ltx2.py +106 -0
- kiapi/capabilities/ltx2/_operations/detect_mode.py +19 -0
- kiapi/capabilities/ltx2/_operations/handle_generate.py +52 -0
- kiapi/capabilities/ltx2/_operations/resolve_generate_params.py +31 -0
- kiapi/capabilities/ltx2/_settings.py +93 -0
- kiapi/capabilities/ltx2/_views/generate_params.py +34 -0
- kiapi/capabilities/ltx2/_views/generate_request.py +151 -0
- kiapi/capabilities/qwen/README.ja.md +251 -0
- kiapi/capabilities/qwen/README.md +235 -0
- kiapi/capabilities/qwen/__init__.py +30 -0
- kiapi/capabilities/qwen/_constants/description.py +53 -0
- kiapi/capabilities/qwen/_helpers/register.py +60 -0
- kiapi/capabilities/qwen/_helpers/validate_edit.py +12 -0
- kiapi/capabilities/qwen/_helpers/validate_generate.py +12 -0
- kiapi/capabilities/qwen/_models/qwen.py +224 -0
- kiapi/capabilities/qwen/_operations/handle_edit.py +44 -0
- kiapi/capabilities/qwen/_operations/handle_generate.py +46 -0
- kiapi/capabilities/qwen/_operations/is_quantize_override.py +15 -0
- kiapi/capabilities/qwen/_operations/resolve_edit_params.py +35 -0
- kiapi/capabilities/qwen/_operations/resolve_generate_params.py +39 -0
- kiapi/capabilities/qwen/_operations/resolve_lora_params.py +23 -0
- kiapi/capabilities/qwen/_operations/validate_common.py +32 -0
- kiapi/capabilities/qwen/_schemas/lora_ref.py +27 -0
- kiapi/capabilities/qwen/_settings.py +107 -0
- kiapi/capabilities/qwen/_views/edit_params.py +36 -0
- kiapi/capabilities/qwen/_views/edit_request.py +138 -0
- kiapi/capabilities/qwen/_views/generate_params.py +37 -0
- kiapi/capabilities/qwen/_views/generate_request.py +149 -0
- kiapi/capabilities/seedvr2/README.ja.md +234 -0
- kiapi/capabilities/seedvr2/README.md +219 -0
- kiapi/capabilities/seedvr2/__init__.py +24 -0
- kiapi/capabilities/seedvr2/_constants/description.py +46 -0
- kiapi/capabilities/seedvr2/_helpers/register.py +60 -0
- kiapi/capabilities/seedvr2/_helpers/validate_upscale.py +52 -0
- kiapi/capabilities/seedvr2/_models/seedvr2.py +140 -0
- kiapi/capabilities/seedvr2/_operations/handle_upscale.py +42 -0
- kiapi/capabilities/seedvr2/_operations/is_quantize_override.py +12 -0
- kiapi/capabilities/seedvr2/_operations/resolve_upscale_params.py +30 -0
- kiapi/capabilities/seedvr2/_settings.py +74 -0
- kiapi/capabilities/seedvr2/_views/upscale_params.py +27 -0
- kiapi/capabilities/seedvr2/_views/upscale_request.py +94 -0
- kiapi/capabilities/web/README.ja.md +187 -0
- kiapi/capabilities/web/README.md +171 -0
- kiapi/capabilities/web/__init__.py +30 -0
- kiapi/capabilities/web/_constants/description.py +38 -0
- kiapi/capabilities/web/_exceptions/FetchError.py +24 -0
- kiapi/capabilities/web/_exceptions/SearchBackendError.py +11 -0
- kiapi/capabilities/web/_helpers/register.py +59 -0
- kiapi/capabilities/web/_models/crawl4ai.py +32 -0
- kiapi/capabilities/web/_models/searxng.py +44 -0
- kiapi/capabilities/web/_operations/handle_fetch.py +145 -0
- kiapi/capabilities/web/_operations/handle_search.py +56 -0
- kiapi/capabilities/web/_operations/resolve_fetch_params.py +19 -0
- kiapi/capabilities/web/_operations/resolve_search_params.py +34 -0
- kiapi/capabilities/web/_operations/resolve_searxng_config_dir.py +21 -0
- kiapi/capabilities/web/_services/__init__.py +1 -0
- kiapi/capabilities/web/_services/web_backend_process.py +144 -0
- kiapi/capabilities/web/_settings.py +138 -0
- kiapi/capabilities/web/_views/fetch_params.py +27 -0
- kiapi/capabilities/web/_views/fetch_request.py +29 -0
- kiapi/capabilities/web/_views/fetch_result.py +20 -0
- kiapi/capabilities/web/_views/search_params.py +43 -0
- kiapi/capabilities/web/_views/search_request.py +93 -0
- kiapi/capabilities/web/_views/search_response.py +49 -0
- kiapi/capabilities/web/resources/__init__.py +0 -0
- kiapi/capabilities/web/resources/searxng/__init__.py +0 -0
- kiapi/capabilities/web/resources/searxng/settings.yml +11 -0
- kiapi/capabilities/zimage/README.ja.md +249 -0
- kiapi/capabilities/zimage/README.md +231 -0
- kiapi/capabilities/zimage/__init__.py +34 -0
- kiapi/capabilities/zimage/_constants/description.py +73 -0
- kiapi/capabilities/zimage/_helpers/register.py +62 -0
- kiapi/capabilities/zimage/_helpers/validate_generate.py +37 -0
- kiapi/capabilities/zimage/_models/zimage.py +278 -0
- kiapi/capabilities/zimage/_operations/handle_generate.py +38 -0
- kiapi/capabilities/zimage/_operations/handle_train.py +78 -0
- kiapi/capabilities/zimage/_operations/is_quantize_override.py +11 -0
- kiapi/capabilities/zimage/_operations/resolve_generate_params.py +39 -0
- kiapi/capabilities/zimage/_operations/resolve_lora_params.py +23 -0
- kiapi/capabilities/zimage/_operations/resolve_train_params.py +38 -0
- kiapi/capabilities/zimage/_schemas/lora_ref.py +27 -0
- kiapi/capabilities/zimage/_settings.py +166 -0
- kiapi/capabilities/zimage/_views/generate_params.py +23 -0
- kiapi/capabilities/zimage/_views/generate_request.py +127 -0
- kiapi/capabilities/zimage/_views/train_params.py +22 -0
- kiapi/capabilities/zimage/_views/train_request.py +101 -0
- kiapi/cli/__init__.py +11 -0
- kiapi/cli/_helpers/dedupe_resources.py +12 -0
- kiapi/cli/_helpers/register_all_capabilities.py +36 -0
- kiapi/cli/_helpers/run_resources.py +24 -0
- kiapi/cli/_helpers/select_specs.py +67 -0
- kiapi/cli/activate/_operations/preflight_hf_access.py +129 -0
- kiapi/cli/activate/cli.py +37 -0
- kiapi/cli/check/_helpers/check_audio_acestep_turbo.py +22 -0
- kiapi/cli/check/_helpers/check_audio_acestep_xl_base.py +22 -0
- kiapi/cli/check/_helpers/check_audio_audiogen_medium.py +24 -0
- kiapi/cli/check/_helpers/check_chat_chat_qwen3_6_27b.py +19 -0
- kiapi/cli/check/_helpers/check_chat_chat_qwen3_omni.py +19 -0
- kiapi/cli/check/_helpers/check_embedding_embedding_qwen3_embedding_8b.py +14 -0
- kiapi/cli/check/_helpers/check_embedding_embedding_qwen3_vl_embedding_2b.py +19 -0
- kiapi/cli/check/_helpers/check_image_depthpro_base.py +21 -0
- kiapi/cli/check/_helpers/check_image_ernie_base.py +23 -0
- kiapi/cli/check/_helpers/check_image_ernie_turbo.py +23 -0
- kiapi/cli/check/_helpers/check_image_flux2_klein_9b.py +23 -0
- kiapi/cli/check/_helpers/check_image_flux2_klein_base_4b.py +23 -0
- kiapi/cli/check/_helpers/check_image_flux2_klein_base_9b.py +23 -0
- kiapi/cli/check/_helpers/check_image_ideogram4_fp8.py +23 -0
- kiapi/cli/check/_helpers/check_image_qwen_edit_2509.py +27 -0
- kiapi/cli/check/_helpers/check_image_qwen_image.py +23 -0
- kiapi/cli/check/_helpers/check_image_seedvr2_3b.py +23 -0
- kiapi/cli/check/_helpers/check_image_seedvr2_7b.py +23 -0
- kiapi/cli/check/_helpers/check_image_zimage_base.py +23 -0
- kiapi/cli/check/_helpers/check_image_zimage_turbo.py +23 -0
- kiapi/cli/check/_helpers/check_video_ltx2_distilled.py +26 -0
- kiapi/cli/check/_helpers/check_web_web_fetch.py +14 -0
- kiapi/cli/check/_helpers/check_web_web_search.py +14 -0
- kiapi/cli/check/_operations/build_check_result.py +20 -0
- kiapi/cli/check/_operations/check_context.py +10 -0
- kiapi/cli/check/_operations/create_sample_image.py +46 -0
- kiapi/cli/check/_schemas/check_result.py +12 -0
- kiapi/cli/check/cli.py +89 -0
- kiapi/cli/cli.py +29 -0
- kiapi/cli/config/cli.py +17 -0
- kiapi/cli/config/edit/cli.py +32 -0
- kiapi/cli/config/init/cli.py +33 -0
- kiapi/cli/config/show/cli.py +13 -0
- kiapi/cli/config/template/cli.py +36 -0
- kiapi/cli/deactivate/cli.py +49 -0
- kiapi/cli/run/asgi.py +16 -0
- kiapi/cli/run/cli.py +59 -0
- kiapi/cli/service/__init__.py +5 -0
- kiapi/cli/service/_services/service_manager.py +107 -0
- kiapi/cli/service/cli.py +19 -0
- kiapi/cli/service/install/cli.py +17 -0
- kiapi/cli/service/start/cli.py +22 -0
- kiapi/cli/service/status/cli.py +81 -0
- kiapi/cli/service/stop/cli.py +19 -0
- kiapi/cli/service/uninstall/cli.py +16 -0
- kiapi/cli/status/cli.py +226 -0
- kiapi/core/app/__init__.py +16 -0
- kiapi/core/app/_schemas/app_context.py +28 -0
- kiapi/core/app/_services/user_directory.py +59 -0
- kiapi/core/app/_settings.py +46 -0
- kiapi/core/capability/__init__.py +11 -0
- kiapi/core/capability/_schemas/capability_spec.py +17 -0
- kiapi/core/capability/_services/capability_spec_registry.py +21 -0
- kiapi/core/config/__init__.py +11 -0
- kiapi/core/config/_exceptions/user_config_error.py +2 -0
- kiapi/core/config/_helpers/get_user_settings_path.py +7 -0
- kiapi/core/config/_helpers/load_user_settings.py +54 -0
- kiapi/core/file/__init__.py +37 -0
- kiapi/core/file/_helpers/create_file_store.py +7 -0
- kiapi/core/file/_helpers/resolve_file_ref.py +90 -0
- kiapi/core/file/_schemas/file_data_url_ref.py +19 -0
- kiapi/core/file/_schemas/file_id_ref.py +16 -0
- kiapi/core/file/_schemas/file_record.py +46 -0
- kiapi/core/file/_schemas/file_url_ref.py +16 -0
- kiapi/core/file/_services/file_store.py +138 -0
- kiapi/core/file/_settings.py +22 -0
- kiapi/core/file/_types/file_id.py +2 -0
- kiapi/core/file/_types/file_ref.py +12 -0
- kiapi/core/file/_views/resolved_file_ref.py +16 -0
- kiapi/core/job/__init__.py +24 -0
- kiapi/core/job/_enums/job_status.py +9 -0
- kiapi/core/job/_helpers/creep_progress.py +123 -0
- kiapi/core/job/_models/job.py +121 -0
- kiapi/core/job/_services/job_store.py +33 -0
- kiapi/core/job/_services/progress_reporter.py +79 -0
- kiapi/core/job/_types/job_id.py +3 -0
- kiapi/core/job/_types/job_result.py +5 -0
- kiapi/core/job/_types/job_type.py +4 -0
- kiapi/core/logging/__init__.py +14 -0
- kiapi/core/logging/_helpers/get_log_level.py +8 -0
- kiapi/core/logging/_helpers/setup_logger.py +14 -0
- kiapi/core/logging/_settings.py +34 -0
- kiapi/core/logging/_types/log_level.py +3 -0
- kiapi/core/memory/__init__.py +21 -0
- kiapi/core/memory/_exceptions/memory_budget_error.py +2 -0
- kiapi/core/memory/_helpers/create_memory_manager.py +17 -0
- kiapi/core/memory/_operations/log_memory_limit.py +29 -0
- kiapi/core/memory/_operations/resolve_effective_memory_limit_gb.py +13 -0
- kiapi/core/memory/_operations/resolve_framework_strategy.py +42 -0
- kiapi/core/memory/_schemas/framework_strategy.py +14 -0
- kiapi/core/memory/_schemas/memory_stats.py +24 -0
- kiapi/core/memory/_schemas/resident_model.py +14 -0
- kiapi/core/memory/_schemas/resident_model_stats.py +49 -0
- kiapi/core/memory/_services/memory_manager.py +257 -0
- kiapi/core/memory/_settings.py +36 -0
- kiapi/core/memory/_types/framework_name.py +4 -0
- kiapi/core/memory/_types/resident_key.py +4 -0
- kiapi/core/model/__init__.py +36 -0
- kiapi/core/model/_exceptions/unknown_model_error.py +2 -0
- kiapi/core/model/_schemas/model_spec.py +145 -0
- kiapi/core/model/_services/model_registry.py +85 -0
- kiapi/core/model/_types/model_alias.py +2 -0
- kiapi/core/model/_types/model_domain.py +6 -0
- kiapi/core/model/_types/model_family.py +5 -0
- kiapi/core/model/_types/model_key.py +8 -0
- kiapi/core/model/_types/model_name.py +4 -0
- kiapi/core/model/_types/model_repo.py +3 -0
- kiapi/core/net/__init__.py +16 -0
- kiapi/core/net/_exceptions/unsafe_url_error.py +7 -0
- kiapi/core/net/_helpers/verify_public_url.py +68 -0
- kiapi/core/net/_settings.py +28 -0
- kiapi/core/setup/__init__.py +25 -0
- kiapi/core/setup/_exceptions/setup_required_error.py +2 -0
- kiapi/core/setup/_schemas/docker_image_resource.py +37 -0
- kiapi/core/setup/_schemas/hf_snapshot_resource.py +50 -0
- kiapi/core/setup/_schemas/local_path_resource.py +43 -0
- kiapi/core/setup/_schemas/python_package_resource.py +58 -0
- kiapi/core/setup/_schemas/python_venv_resource.py +63 -0
- kiapi/core/setup/_schemas/setup_status.py +7 -0
- kiapi/core/setup/_schemas/url_file_resource.py +43 -0
- kiapi/core/setup/_services/setup_manager.py +331 -0
- kiapi/core/setup/_types/setup_resource.py +21 -0
- kiapi/core/setup/_types/setup_target.py +11 -0
- kiapi/core/workdir/__init__.py +8 -0
- kiapi/core/workdir/_helpers/create_work_dir.py +11 -0
- kiapi/core/workdir/_settings.py +21 -0
- kiapi/core/worker/__init__.py +23 -0
- kiapi/core/worker/_helpers/create_worker.py +9 -0
- kiapi/core/worker/_services/worker.py +165 -0
- kiapi/core/worker/_settings.py +36 -0
- kiapi/core/worker/_types/job_thunk.py +6 -0
- kiapi-0.1.0.dist-info/METADATA +337 -0
- kiapi-0.1.0.dist-info/RECORD +465 -0
- kiapi-0.1.0.dist-info/WHEEL +4 -0
- kiapi-0.1.0.dist-info/entry_points.txt +2 -0
- kiapi-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
import tempfile
|
|
5
|
+
import urllib.request
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from huggingface_hub import scan_cache_dir, snapshot_download
|
|
9
|
+
|
|
10
|
+
from .._exceptions.setup_required_error import SetupRequiredError
|
|
11
|
+
from .._schemas.docker_image_resource import DockerImageResource
|
|
12
|
+
from .._schemas.hf_snapshot_resource import HfSnapshotResource
|
|
13
|
+
from .._schemas.local_path_resource import LocalPathResource
|
|
14
|
+
from .._schemas.python_package_resource import PythonPackageResource
|
|
15
|
+
from .._schemas.python_venv_resource import PythonVenvResource
|
|
16
|
+
from .._schemas.setup_status import SetupStatus
|
|
17
|
+
from .._schemas.url_file_resource import UrlFileResource
|
|
18
|
+
from .._types.setup_resource import SetupResource
|
|
19
|
+
from .._types.setup_target import SetupTarget
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SetupManager:
|
|
23
|
+
def status(self, resource: SetupResource) -> SetupStatus:
|
|
24
|
+
if isinstance(resource, HfSnapshotResource):
|
|
25
|
+
return self._hf_status(resource)
|
|
26
|
+
if isinstance(resource, DockerImageResource):
|
|
27
|
+
return self._docker_status(resource)
|
|
28
|
+
if isinstance(resource, LocalPathResource):
|
|
29
|
+
path = Path(resource.path).expanduser()
|
|
30
|
+
if path.exists():
|
|
31
|
+
return SetupStatus(True, str(path))
|
|
32
|
+
return SetupStatus(False, f"missing: {path}")
|
|
33
|
+
if isinstance(resource, UrlFileResource):
|
|
34
|
+
path = Path(resource.path).expanduser()
|
|
35
|
+
if path.exists() and path.stat().st_size > 0:
|
|
36
|
+
return SetupStatus(True, str(path))
|
|
37
|
+
return SetupStatus(False, f"missing: {path}")
|
|
38
|
+
if isinstance(resource, PythonVenvResource):
|
|
39
|
+
return self._python_venv_status(resource)
|
|
40
|
+
if isinstance(resource, PythonPackageResource):
|
|
41
|
+
return self._python_package_status(resource)
|
|
42
|
+
|
|
43
|
+
def activate(self, resource: SetupResource) -> SetupStatus:
|
|
44
|
+
if isinstance(resource, HfSnapshotResource):
|
|
45
|
+
snapshot_path = snapshot_download(
|
|
46
|
+
repo_id=resource.repo,
|
|
47
|
+
revision=resource.revision,
|
|
48
|
+
local_dir=resource.local_dir,
|
|
49
|
+
)
|
|
50
|
+
return SetupStatus(True, str(snapshot_path))
|
|
51
|
+
if isinstance(resource, DockerImageResource):
|
|
52
|
+
self._docker("pull", resource.image)
|
|
53
|
+
return self.status(resource)
|
|
54
|
+
if isinstance(resource, LocalPathResource):
|
|
55
|
+
return self.status(resource)
|
|
56
|
+
if isinstance(resource, UrlFileResource):
|
|
57
|
+
file_path = Path(resource.path).expanduser()
|
|
58
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
59
|
+
if not file_path.exists():
|
|
60
|
+
with tempfile.NamedTemporaryFile(
|
|
61
|
+
dir=file_path.parent, delete=False
|
|
62
|
+
) as tmp_file:
|
|
63
|
+
tmp_path = Path(tmp_file.name)
|
|
64
|
+
try:
|
|
65
|
+
urllib.request.urlretrieve(resource.url, tmp_path)
|
|
66
|
+
tmp_path.replace(file_path)
|
|
67
|
+
finally:
|
|
68
|
+
tmp_path.unlink(missing_ok=True)
|
|
69
|
+
return self.status(resource)
|
|
70
|
+
if isinstance(resource, PythonVenvResource):
|
|
71
|
+
return self._python_venv_activate(resource)
|
|
72
|
+
if isinstance(resource, PythonPackageResource):
|
|
73
|
+
return self._python_package_activate(resource)
|
|
74
|
+
|
|
75
|
+
def deactivate(self, resource: SetupResource) -> SetupStatus:
|
|
76
|
+
if isinstance(resource, HfSnapshotResource):
|
|
77
|
+
return self._hf_deactivate(resource)
|
|
78
|
+
if isinstance(resource, DockerImageResource):
|
|
79
|
+
self._docker("image", "rm", resource.image)
|
|
80
|
+
return self.status(resource)
|
|
81
|
+
if isinstance(resource, LocalPathResource):
|
|
82
|
+
path = Path(resource.path).expanduser()
|
|
83
|
+
if path.exists():
|
|
84
|
+
if path.is_dir():
|
|
85
|
+
shutil.rmtree(path)
|
|
86
|
+
else:
|
|
87
|
+
path.unlink()
|
|
88
|
+
return self.status(resource)
|
|
89
|
+
if isinstance(resource, UrlFileResource):
|
|
90
|
+
Path(resource.path).expanduser().unlink(missing_ok=True)
|
|
91
|
+
return self.status(resource)
|
|
92
|
+
if isinstance(resource, PythonVenvResource):
|
|
93
|
+
path = Path(resource.path).expanduser()
|
|
94
|
+
if path.exists():
|
|
95
|
+
shutil.rmtree(path)
|
|
96
|
+
return self.status(resource)
|
|
97
|
+
if isinstance(resource, PythonPackageResource):
|
|
98
|
+
return self._python_package_deactivate(resource)
|
|
99
|
+
|
|
100
|
+
def ensure_ready(self, spec: SetupTarget) -> None:
|
|
101
|
+
missing = [
|
|
102
|
+
resource
|
|
103
|
+
for resource in spec.setup_resources
|
|
104
|
+
if not self.status(resource).ready
|
|
105
|
+
]
|
|
106
|
+
if not missing:
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
labels = ", ".join(resource.label for resource in missing)
|
|
110
|
+
repos = " ".join(f"--repo {resource.label}" for resource in missing)
|
|
111
|
+
raise SetupRequiredError(
|
|
112
|
+
f"model {spec.name!r} is not activated; missing: {labels}. "
|
|
113
|
+
f"Run: kiapi activate {repos}"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def _hf_status(self, resource: HfSnapshotResource) -> SetupStatus:
|
|
117
|
+
if resource.local_dir is not None:
|
|
118
|
+
local_path = Path(resource.local_dir).expanduser()
|
|
119
|
+
if local_path.exists() and any(local_path.iterdir()):
|
|
120
|
+
return SetupStatus(True, str(local_path))
|
|
121
|
+
return SetupStatus(False, f"missing: {local_path}")
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
snapshot_path = snapshot_download(
|
|
125
|
+
repo_id=resource.repo,
|
|
126
|
+
revision=resource.revision,
|
|
127
|
+
local_dir=resource.local_dir,
|
|
128
|
+
local_files_only=True,
|
|
129
|
+
)
|
|
130
|
+
except Exception as exc:
|
|
131
|
+
return SetupStatus(False, str(exc))
|
|
132
|
+
return SetupStatus(True, str(snapshot_path))
|
|
133
|
+
|
|
134
|
+
def _hf_deactivate(self, resource: HfSnapshotResource) -> SetupStatus:
|
|
135
|
+
if resource.local_dir is not None:
|
|
136
|
+
path = Path(resource.local_dir).expanduser()
|
|
137
|
+
if path.exists():
|
|
138
|
+
shutil.rmtree(path)
|
|
139
|
+
return self.status(resource)
|
|
140
|
+
|
|
141
|
+
info = scan_cache_dir()
|
|
142
|
+
revisions: list[str] = []
|
|
143
|
+
for repo in info.repos:
|
|
144
|
+
if repo.repo_id != resource.repo:
|
|
145
|
+
continue
|
|
146
|
+
for revision in repo.revisions:
|
|
147
|
+
revisions.append(revision.commit_hash)
|
|
148
|
+
|
|
149
|
+
if revisions:
|
|
150
|
+
strategy = info.delete_revisions(*revisions)
|
|
151
|
+
strategy.execute()
|
|
152
|
+
|
|
153
|
+
return self.status(resource)
|
|
154
|
+
|
|
155
|
+
def _docker_status(self, resource: DockerImageResource) -> SetupStatus:
|
|
156
|
+
try:
|
|
157
|
+
self._docker("image", "inspect", resource.image)
|
|
158
|
+
except subprocess.CalledProcessError as exc:
|
|
159
|
+
detail = (exc.stderr or exc.stdout or str(exc)).strip()
|
|
160
|
+
return SetupStatus(False, detail)
|
|
161
|
+
return SetupStatus(True, "present")
|
|
162
|
+
|
|
163
|
+
def _docker(self, *args: str) -> subprocess.CompletedProcess[str]:
|
|
164
|
+
return subprocess.run(
|
|
165
|
+
["docker", *args],
|
|
166
|
+
check=True,
|
|
167
|
+
text=True,
|
|
168
|
+
capture_output=True,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def _python_venv_status(self, resource: PythonVenvResource) -> SetupStatus:
|
|
172
|
+
python_path = self._python_venv_python_path(resource)
|
|
173
|
+
if not python_path.exists():
|
|
174
|
+
return SetupStatus(False, f"missing: {python_path}")
|
|
175
|
+
try:
|
|
176
|
+
self._run_python_import(python_path, resource.import_name)
|
|
177
|
+
except subprocess.CalledProcessError as exc:
|
|
178
|
+
detail = (exc.stderr or exc.stdout or str(exc)).strip()
|
|
179
|
+
return SetupStatus(False, detail)
|
|
180
|
+
return SetupStatus(True, str(python_path))
|
|
181
|
+
|
|
182
|
+
def _python_venv_activate(self, resource: PythonVenvResource) -> SetupStatus:
|
|
183
|
+
state = self.status(resource)
|
|
184
|
+
if state.ready:
|
|
185
|
+
return state
|
|
186
|
+
|
|
187
|
+
path = Path(resource.path).expanduser()
|
|
188
|
+
python_path = self._python_venv_python_path(resource)
|
|
189
|
+
if not python_path.exists():
|
|
190
|
+
self._create_python_venv(path, resource.python)
|
|
191
|
+
self._install_python_packages(python_path, resource.packages)
|
|
192
|
+
return self.status(resource)
|
|
193
|
+
|
|
194
|
+
def _python_venv_python_path(self, resource: PythonVenvResource) -> Path:
|
|
195
|
+
return Path(resource.path).expanduser() / "bin" / "python"
|
|
196
|
+
|
|
197
|
+
def _run_python_import(self, python_path: Path, import_name: str) -> None:
|
|
198
|
+
subprocess.run(
|
|
199
|
+
[str(python_path), "-c", f"import {import_name}"],
|
|
200
|
+
check=True,
|
|
201
|
+
text=True,
|
|
202
|
+
capture_output=True,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
def _python_package_status(self, resource: PythonPackageResource) -> SetupStatus:
|
|
206
|
+
try:
|
|
207
|
+
self._run_python_package_verify(resource)
|
|
208
|
+
except subprocess.CalledProcessError as exc:
|
|
209
|
+
detail = (exc.stderr or exc.stdout or str(exc)).strip()
|
|
210
|
+
return SetupStatus(False, detail)
|
|
211
|
+
return SetupStatus(True, resource.import_name)
|
|
212
|
+
|
|
213
|
+
def _python_package_activate(self, resource: PythonPackageResource) -> SetupStatus:
|
|
214
|
+
state = self.status(resource)
|
|
215
|
+
if state.ready:
|
|
216
|
+
return state
|
|
217
|
+
|
|
218
|
+
self._install_python_packages(Path(sys.executable), (resource.spec,))
|
|
219
|
+
return self.status(resource)
|
|
220
|
+
|
|
221
|
+
def _python_package_deactivate(
|
|
222
|
+
self, resource: PythonPackageResource
|
|
223
|
+
) -> SetupStatus:
|
|
224
|
+
self._uninstall_python_package(Path(sys.executable), resource.package)
|
|
225
|
+
return self.status(resource)
|
|
226
|
+
|
|
227
|
+
def _run_python_package_verify(self, resource: PythonPackageResource) -> None:
|
|
228
|
+
code = "\n".join(
|
|
229
|
+
[
|
|
230
|
+
"import importlib",
|
|
231
|
+
f"module = importlib.import_module({resource.import_name!r})",
|
|
232
|
+
f"attrs = {list(resource.verify_attrs)!r}",
|
|
233
|
+
"missing = [attr for attr in attrs if not hasattr(module, attr)]",
|
|
234
|
+
"if missing:",
|
|
235
|
+
" raise RuntimeError(f'missing attrs: {missing}')",
|
|
236
|
+
]
|
|
237
|
+
)
|
|
238
|
+
subprocess.run(
|
|
239
|
+
[sys.executable, "-c", code],
|
|
240
|
+
check=True,
|
|
241
|
+
text=True,
|
|
242
|
+
capture_output=True,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
def _create_python_venv(self, path: Path, python: str) -> None:
|
|
246
|
+
if shutil.which("uv"):
|
|
247
|
+
subprocess.run(
|
|
248
|
+
["uv", "venv", "--python", python, str(path)],
|
|
249
|
+
check=True,
|
|
250
|
+
text=True,
|
|
251
|
+
capture_output=True,
|
|
252
|
+
)
|
|
253
|
+
return
|
|
254
|
+
|
|
255
|
+
python_bin = self._resolve_python_executable(python)
|
|
256
|
+
subprocess.run(
|
|
257
|
+
[str(python_bin), "-m", "venv", str(path)],
|
|
258
|
+
check=True,
|
|
259
|
+
text=True,
|
|
260
|
+
capture_output=True,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
def _install_python_packages(
|
|
264
|
+
self, python_path: Path, packages: tuple[str, ...]
|
|
265
|
+
) -> None:
|
|
266
|
+
if shutil.which("uv"):
|
|
267
|
+
subprocess.run(
|
|
268
|
+
["uv", "pip", "install", "--python", str(python_path), *packages],
|
|
269
|
+
check=True,
|
|
270
|
+
text=True,
|
|
271
|
+
capture_output=True,
|
|
272
|
+
)
|
|
273
|
+
return
|
|
274
|
+
|
|
275
|
+
subprocess.run(
|
|
276
|
+
[str(python_path), "-m", "pip", "install", *packages],
|
|
277
|
+
check=True,
|
|
278
|
+
text=True,
|
|
279
|
+
capture_output=True,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
def _uninstall_python_package(self, python_path: Path, package: str) -> None:
|
|
283
|
+
if shutil.which("uv"):
|
|
284
|
+
subprocess.run(
|
|
285
|
+
["uv", "pip", "uninstall", "--python", str(python_path), package],
|
|
286
|
+
check=True,
|
|
287
|
+
text=True,
|
|
288
|
+
capture_output=True,
|
|
289
|
+
)
|
|
290
|
+
return
|
|
291
|
+
|
|
292
|
+
subprocess.run(
|
|
293
|
+
[str(python_path), "-m", "pip", "uninstall", "-y", package],
|
|
294
|
+
check=True,
|
|
295
|
+
text=True,
|
|
296
|
+
capture_output=True,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
def _resolve_python_executable(self, python: str) -> Path:
|
|
300
|
+
candidates = [python]
|
|
301
|
+
if python.startswith("3."):
|
|
302
|
+
candidates.append(f"python{python}")
|
|
303
|
+
candidates.extend(["python3", sys.executable])
|
|
304
|
+
|
|
305
|
+
for candidate in candidates:
|
|
306
|
+
executable = (
|
|
307
|
+
shutil.which(candidate) if candidate != sys.executable else candidate
|
|
308
|
+
)
|
|
309
|
+
if executable and self._python_matches_requirement(
|
|
310
|
+
Path(executable), python
|
|
311
|
+
):
|
|
312
|
+
return Path(executable)
|
|
313
|
+
|
|
314
|
+
raise RuntimeError(f"Python {python} executable was not found")
|
|
315
|
+
|
|
316
|
+
def _python_matches_requirement(self, python_path: Path, python: str) -> bool:
|
|
317
|
+
if not python.startswith("3."):
|
|
318
|
+
return True
|
|
319
|
+
|
|
320
|
+
code = "\n".join(
|
|
321
|
+
[
|
|
322
|
+
"import sys",
|
|
323
|
+
f"raise SystemExit(0 if sys.version_info[:2] == {tuple(map(int, python.split('.')))!r} else 1)",
|
|
324
|
+
]
|
|
325
|
+
)
|
|
326
|
+
result = subprocess.run(
|
|
327
|
+
[str(python_path), "-c", code],
|
|
328
|
+
text=True,
|
|
329
|
+
capture_output=True,
|
|
330
|
+
)
|
|
331
|
+
return result.returncode == 0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
from .._schemas.docker_image_resource import DockerImageResource
|
|
6
|
+
from .._schemas.hf_snapshot_resource import HfSnapshotResource
|
|
7
|
+
from .._schemas.local_path_resource import LocalPathResource
|
|
8
|
+
from .._schemas.python_package_resource import PythonPackageResource
|
|
9
|
+
from .._schemas.python_venv_resource import PythonVenvResource
|
|
10
|
+
from .._schemas.url_file_resource import UrlFileResource
|
|
11
|
+
|
|
12
|
+
SetupResource = Annotated[
|
|
13
|
+
HfSnapshotResource
|
|
14
|
+
| DockerImageResource
|
|
15
|
+
| LocalPathResource
|
|
16
|
+
| UrlFileResource
|
|
17
|
+
| PythonPackageResource
|
|
18
|
+
| PythonVenvResource,
|
|
19
|
+
Field(discriminator="kind"),
|
|
20
|
+
]
|
|
21
|
+
"""A resource that must be activated before a model can run, tagged by ``kind``."""
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import tempfile
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from .._settings import settings_manager
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def create_work_dir(purpose: str) -> Path:
|
|
8
|
+
root = Path(settings_manager.get_settings().tmp_root).expanduser()
|
|
9
|
+
parent = root / purpose
|
|
10
|
+
parent.mkdir(parents=True, exist_ok=True)
|
|
11
|
+
return Path(tempfile.mkdtemp(dir=parent))
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from pydantic import Field
|
|
2
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
3
|
+
from pydantic_settings_manager import SettingsManager
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class WorkDirSettings(BaseSettings):
|
|
7
|
+
"""Settings for per-request temporary work directories."""
|
|
8
|
+
|
|
9
|
+
model_config = SettingsConfigDict(env_prefix="KIAPI_")
|
|
10
|
+
|
|
11
|
+
tmp_root: str = Field(
|
|
12
|
+
default="/tmp/kiapi/work",
|
|
13
|
+
title="Temporary work directory root",
|
|
14
|
+
description=(
|
|
15
|
+
"Root directory for temporary work files created while handling requests.\n"
|
|
16
|
+
"Each operation creates a purpose-specific child directory under this root."
|
|
17
|
+
),
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
settings_manager = SettingsManager(WorkDirSettings)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""The single-flight worker: one dedicated thread runs every job, one at a time
|
|
2
|
+
(MLX thread-affinity + sound memory budgeting). See :mod:`._services.worker`.
|
|
3
|
+
|
|
4
|
+
Public surface (unchanged from the former ``core/worker.py``): :class:`Worker`,
|
|
5
|
+
:data:`JobThunk`.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from ._helpers.create_worker import create_worker
|
|
9
|
+
from ._services.worker import Worker
|
|
10
|
+
from ._settings import WorkerSettings, settings_manager
|
|
11
|
+
from ._types.job_thunk import JobThunk
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
# ._types
|
|
15
|
+
"JobThunk",
|
|
16
|
+
# ._services
|
|
17
|
+
"Worker",
|
|
18
|
+
# ._settings
|
|
19
|
+
"WorkerSettings",
|
|
20
|
+
# ._helpers
|
|
21
|
+
"create_worker",
|
|
22
|
+
"settings_manager",
|
|
23
|
+
]
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""The single-flight worker — kiapi's one hard rule: only one job runs at a time.
|
|
2
|
+
|
|
3
|
+
All heavy work (model load, eviction, warmup, every generation, across every
|
|
4
|
+
capability) runs on ONE dedicated thread (``ThreadPoolExecutor(max_workers=1)``).
|
|
5
|
+
This is required for MLX correctness (arrays/streams are thread-affine) AND it is
|
|
6
|
+
what makes the global memory budget sound: the model being acquired for the
|
|
7
|
+
running job is the only one generating, so budgeting just its peak headroom is
|
|
8
|
+
correct (see core/memory). Concurrent HTTP requests are accepted and simply queue.
|
|
9
|
+
|
|
10
|
+
If you need real parallelism, run multiple kiapi processes, each scoped to a
|
|
11
|
+
subset of capabilities with its own slice of the memory budget.
|
|
12
|
+
|
|
13
|
+
A job's work is supplied as a JobThunk. The worker drives the job's lifecycle
|
|
14
|
+
(running → succeeded/failed) and resolves the caller's future. Sync callers await
|
|
15
|
+
the future (with a timeout); async callers drop it and poll the job store.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
import logging
|
|
20
|
+
import traceback
|
|
21
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
22
|
+
|
|
23
|
+
from kiapi.core.app import AppContext
|
|
24
|
+
from kiapi.core.job import Job, ProgressReporter
|
|
25
|
+
from kiapi.core.model import ModelKey
|
|
26
|
+
from kiapi.core.setup import SetupRequiredError
|
|
27
|
+
|
|
28
|
+
from .._settings import WorkerSettings
|
|
29
|
+
from .._types.job_thunk import JobThunk
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Worker:
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
settings: WorkerSettings,
|
|
38
|
+
ctx: AppContext,
|
|
39
|
+
) -> None:
|
|
40
|
+
self.settings: WorkerSettings = settings
|
|
41
|
+
self.ctx: AppContext = ctx
|
|
42
|
+
self.queue: asyncio.Queue[tuple[Job, JobThunk, asyncio.Future]] = (
|
|
43
|
+
asyncio.Queue()
|
|
44
|
+
)
|
|
45
|
+
self.executor = ThreadPoolExecutor(max_workers=1, thread_name_prefix="kiapi")
|
|
46
|
+
self.warm = False
|
|
47
|
+
self._task: asyncio.Task | None = None
|
|
48
|
+
self._ttl_task: asyncio.Task | None = None
|
|
49
|
+
|
|
50
|
+
def start(self) -> None:
|
|
51
|
+
self._task = asyncio.create_task(self._run(), name="kiapi-worker")
|
|
52
|
+
if self.settings.ttl_sweep_interval_s > 0:
|
|
53
|
+
self._ttl_task = asyncio.create_task(
|
|
54
|
+
self._ttl_loop(), name="kiapi-ttl-sweep"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
async def stop(self) -> None:
|
|
58
|
+
if self._task is not None:
|
|
59
|
+
self._task.cancel()
|
|
60
|
+
if self._ttl_task is not None:
|
|
61
|
+
self._ttl_task.cancel()
|
|
62
|
+
# Free resident models on the worker thread, then tear it down.
|
|
63
|
+
try:
|
|
64
|
+
loop = asyncio.get_running_loop()
|
|
65
|
+
await loop.run_in_executor(self.executor, self.ctx.memory_manager.shutdown)
|
|
66
|
+
except Exception:
|
|
67
|
+
traceback.print_exc()
|
|
68
|
+
self.executor.shutdown(wait=False, cancel_futures=True)
|
|
69
|
+
|
|
70
|
+
async def submit(self, job: Job, fn: JobThunk) -> asyncio.Future:
|
|
71
|
+
loop = asyncio.get_running_loop()
|
|
72
|
+
fut: asyncio.Future = loop.create_future()
|
|
73
|
+
await self.queue.put((job, fn, fut))
|
|
74
|
+
return fut
|
|
75
|
+
|
|
76
|
+
async def _run(self) -> None:
|
|
77
|
+
loop = asyncio.get_running_loop()
|
|
78
|
+
while True:
|
|
79
|
+
job, fn, fut = await self.queue.get()
|
|
80
|
+
job.mark_running()
|
|
81
|
+
# Bind a reporter for this job so capability code can push coarse
|
|
82
|
+
# progress via ProgressReporter.current(); run_bound installs it on
|
|
83
|
+
# the worker thread, where the thunk actually executes.
|
|
84
|
+
reporter = ProgressReporter(job)
|
|
85
|
+
try:
|
|
86
|
+
result, artifacts = await loop.run_in_executor(
|
|
87
|
+
self.executor, reporter.run_bound, fn
|
|
88
|
+
)
|
|
89
|
+
job.mark_succeeded(result, artifacts)
|
|
90
|
+
if not fut.done():
|
|
91
|
+
fut.set_result(result)
|
|
92
|
+
except Exception as exc:
|
|
93
|
+
traceback.print_exc()
|
|
94
|
+
job.mark_failed(str(exc))
|
|
95
|
+
if not fut.done():
|
|
96
|
+
fut.set_exception(exc)
|
|
97
|
+
|
|
98
|
+
async def _ttl_loop(self) -> None:
|
|
99
|
+
"""The sweep runs on the worker thread (shared executor), so it serializes
|
|
100
|
+
with generations and respects MLX thread-affinity — it frees models past
|
|
101
|
+
their TTL even when no requests arrive."""
|
|
102
|
+
loop = asyncio.get_running_loop()
|
|
103
|
+
interval = self.settings.ttl_sweep_interval_s
|
|
104
|
+
while True:
|
|
105
|
+
await asyncio.sleep(interval)
|
|
106
|
+
try:
|
|
107
|
+
freed = await loop.run_in_executor(
|
|
108
|
+
self.executor, self.ctx.memory_manager.sweep_expired
|
|
109
|
+
)
|
|
110
|
+
if freed:
|
|
111
|
+
logger.info("idle sweep freed: %s", freed)
|
|
112
|
+
except Exception:
|
|
113
|
+
traceback.print_exc()
|
|
114
|
+
|
|
115
|
+
async def warmup(self) -> None:
|
|
116
|
+
"""Each model is loaded + primed on the worker thread; acquire evicts as
|
|
117
|
+
needed to respect the memory budget."""
|
|
118
|
+
model_names = self.settings.warmup_models
|
|
119
|
+
if not model_names:
|
|
120
|
+
self.warm = True
|
|
121
|
+
return
|
|
122
|
+
loop = asyncio.get_running_loop()
|
|
123
|
+
for name in model_names:
|
|
124
|
+
try:
|
|
125
|
+
await loop.run_in_executor(self.executor, self._warm_one, name)
|
|
126
|
+
except Exception:
|
|
127
|
+
traceback.print_exc()
|
|
128
|
+
self.warm = True
|
|
129
|
+
|
|
130
|
+
def _warm_one(self, name: ModelKey) -> None:
|
|
131
|
+
"""Runs on the worker thread."""
|
|
132
|
+
from kiapi.core.model import model_registry
|
|
133
|
+
|
|
134
|
+
# A warmup name is a model name; find which family owns it.
|
|
135
|
+
spec = None
|
|
136
|
+
for family in model_registry.families():
|
|
137
|
+
try:
|
|
138
|
+
spec = model_registry.resolve(family, name)
|
|
139
|
+
break
|
|
140
|
+
except Exception:
|
|
141
|
+
continue
|
|
142
|
+
if spec is None:
|
|
143
|
+
logger.warning("unknown warmup model %r; skipping", name)
|
|
144
|
+
return
|
|
145
|
+
if not spec.resident:
|
|
146
|
+
# Transient providers (e.g. video) load/free per call — nothing to
|
|
147
|
+
# keep resident. Prime via the module's own warmup if it has one.
|
|
148
|
+
warm = getattr(spec.module, "warmup", None)
|
|
149
|
+
if warm is not None:
|
|
150
|
+
try:
|
|
151
|
+
self.ctx.ensure_model_ready(spec)
|
|
152
|
+
except SetupRequiredError as exc:
|
|
153
|
+
logger.warning("skipping warmup for %s: %s", spec.name, exc)
|
|
154
|
+
return
|
|
155
|
+
warm(None)
|
|
156
|
+
return
|
|
157
|
+
try:
|
|
158
|
+
self.ctx.ensure_model_ready(spec)
|
|
159
|
+
except SetupRequiredError as exc:
|
|
160
|
+
logger.warning("skipping warmup for %s: %s", spec.name, exc)
|
|
161
|
+
return
|
|
162
|
+
payload = self.ctx.memory_manager.acquire(spec)
|
|
163
|
+
warm = getattr(spec.module, "warmup", None)
|
|
164
|
+
if warm is not None:
|
|
165
|
+
warm(payload)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from pydantic import Field
|
|
2
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
3
|
+
from pydantic_settings_manager import SettingsManager
|
|
4
|
+
|
|
5
|
+
from kiapi.core.model import ModelKey
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class WorkerSettings(BaseSettings):
|
|
9
|
+
"""Settings for the single worker queue, TTL sweeping, and startup warmup."""
|
|
10
|
+
|
|
11
|
+
model_config = SettingsConfigDict(
|
|
12
|
+
env_prefix="KIAPI_",
|
|
13
|
+
extra="ignore",
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
ttl_sweep_interval_s: float = Field(
|
|
17
|
+
default=60.0,
|
|
18
|
+
title="TTL sweep interval seconds",
|
|
19
|
+
description=(
|
|
20
|
+
"Interval for background checks of resident models that exceeded their idle TTL.\n"
|
|
21
|
+
"Set to 0.0 to completely disable the background sweep."
|
|
22
|
+
),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
warmup_models: list[ModelKey] = Field(
|
|
26
|
+
default_factory=list,
|
|
27
|
+
title="Startup warmup models",
|
|
28
|
+
description=(
|
|
29
|
+
"List of model keys to preload when the server starts.\n"
|
|
30
|
+
"Each item uses the model registry key format. An empty list keeps "
|
|
31
|
+
"models lazily loaded."
|
|
32
|
+
),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
settings_manager = SettingsManager(WorkerSettings)
|