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,90 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import binascii
|
|
3
|
+
import mimetypes
|
|
4
|
+
import urllib.error
|
|
5
|
+
import urllib.parse
|
|
6
|
+
import urllib.request
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from kiapi.core.net import UnsafeURLError, verify_public_url
|
|
10
|
+
|
|
11
|
+
from .._schemas.file_data_url_ref import FileDataURLRef
|
|
12
|
+
from .._schemas.file_id_ref import FileIDRef
|
|
13
|
+
from .._schemas.file_record import FileRecord
|
|
14
|
+
from .._schemas.file_url_ref import FileURLRef
|
|
15
|
+
from .._services.file_store import FileStore
|
|
16
|
+
from .._types.file_ref import FileRef
|
|
17
|
+
from .._views.resolved_file_ref import ResolvedFileRef
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def resolve_file_ref(
|
|
21
|
+
file_store: FileStore,
|
|
22
|
+
ref: FileRef,
|
|
23
|
+
*,
|
|
24
|
+
kind: str,
|
|
25
|
+
) -> ResolvedFileRef:
|
|
26
|
+
if isinstance(ref, FileIDRef):
|
|
27
|
+
rec = file_store.get(ref.file_id)
|
|
28
|
+
if rec is None:
|
|
29
|
+
raise FileNotFoundError(f"unknown {kind} file_id {ref.file_id!r}")
|
|
30
|
+
return ResolvedFileRef(rec)
|
|
31
|
+
|
|
32
|
+
if isinstance(ref, FileURLRef):
|
|
33
|
+
return ResolvedFileRef(_store_url(file_store, ref, kind=kind))
|
|
34
|
+
|
|
35
|
+
if isinstance(ref, FileDataURLRef):
|
|
36
|
+
return ResolvedFileRef(_store_data_url(file_store, ref, kind=kind))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _store_url(file_store: FileStore, ref: FileURLRef, *, kind: str) -> FileRecord:
|
|
40
|
+
parsed = urllib.parse.urlparse(ref.url)
|
|
41
|
+
if parsed.scheme not in ("http", "https"):
|
|
42
|
+
raise ValueError(f"{kind} url must be an absolute http:// or https:// URL")
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
verify_public_url(ref.url, kind=kind)
|
|
46
|
+
except UnsafeURLError as exc:
|
|
47
|
+
raise ValueError(str(exc)) from exc
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
with urllib.request.urlopen(ref.url, timeout=30) as response:
|
|
51
|
+
data = response.read()
|
|
52
|
+
content_type = response.headers.get_content_type()
|
|
53
|
+
except urllib.error.URLError as exc:
|
|
54
|
+
raise ValueError(f"failed to fetch {kind} url {ref.url!r}: {exc}") from exc
|
|
55
|
+
|
|
56
|
+
filename = (
|
|
57
|
+
Path(parsed.path).name or f"{kind}{_suffix_for_content_type(content_type)}"
|
|
58
|
+
)
|
|
59
|
+
return file_store.put_bytes(
|
|
60
|
+
data,
|
|
61
|
+
filename=filename,
|
|
62
|
+
content_type=content_type,
|
|
63
|
+
meta={"source": "url", "url": ref.url, "kind": kind},
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _store_data_url(
|
|
68
|
+
file_store: FileStore, ref: FileDataURLRef, *, kind: str
|
|
69
|
+
) -> FileRecord:
|
|
70
|
+
header, sep, raw = ref.data_url.partition(",")
|
|
71
|
+
if sep != "," or not header.startswith("data:"):
|
|
72
|
+
raise ValueError(f"{kind} data_url must be a data URL")
|
|
73
|
+
|
|
74
|
+
media_type = header[5:].split(";", 1)[0] or "application/octet-stream"
|
|
75
|
+
is_base64 = ";base64" in header
|
|
76
|
+
try:
|
|
77
|
+
data = base64.b64decode(raw, validate=True) if is_base64 else raw.encode()
|
|
78
|
+
except (binascii.Error, ValueError) as exc:
|
|
79
|
+
raise ValueError(f"invalid {kind} data_url payload: {exc}") from exc
|
|
80
|
+
|
|
81
|
+
return file_store.put_bytes(
|
|
82
|
+
data,
|
|
83
|
+
filename=f"{kind}{_suffix_for_content_type(media_type)}",
|
|
84
|
+
content_type=media_type,
|
|
85
|
+
meta={"source": "data_url", "kind": kind},
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _suffix_for_content_type(content_type: str | None) -> str:
|
|
90
|
+
return mimetypes.guess_extension(content_type or "") or ".bin"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FileDataURLRef(BaseModel):
|
|
7
|
+
model_config = {
|
|
8
|
+
"json_schema_extra": {
|
|
9
|
+
"examples": [
|
|
10
|
+
{
|
|
11
|
+
"type": "data_url",
|
|
12
|
+
"data_url": "data:image/png;base64,iVBORw0KGgo...",
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type: Literal["data_url"] = "data_url"
|
|
19
|
+
data_url: str = Field(..., min_length=1)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FileIDRef(BaseModel):
|
|
7
|
+
model_config = {
|
|
8
|
+
"json_schema_extra": {
|
|
9
|
+
"examples": [
|
|
10
|
+
{"type": "file_id", "file_id": "file_0123456789abcdef"},
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type: Literal["file_id"] = "file_id"
|
|
16
|
+
file_id: str = Field(..., min_length=1)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
from .._types.file_id import FileID
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FileRecord(BaseModel):
|
|
9
|
+
"""A stored file's metadata. Doubles as the Files API response schema.
|
|
10
|
+
|
|
11
|
+
``path`` is the on-disk location and is internal to the file store; it is
|
|
12
|
+
excluded from serialization so ``model_dump()`` yields the API/sidecar shape.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
file_id: FileID = Field(
|
|
16
|
+
...,
|
|
17
|
+
description="Persistent file id. Use this in FileRef inputs or download it with GET /v1/files/{file_id}/download.",
|
|
18
|
+
examples=["file_0123456789abcdef"],
|
|
19
|
+
)
|
|
20
|
+
filename: str = Field(
|
|
21
|
+
...,
|
|
22
|
+
description="Original or generated filename associated with the stored bytes.",
|
|
23
|
+
examples=["image_1766200000.png"],
|
|
24
|
+
)
|
|
25
|
+
content_type: str = Field(
|
|
26
|
+
...,
|
|
27
|
+
description="Media type of the stored file.",
|
|
28
|
+
examples=["image/png"],
|
|
29
|
+
)
|
|
30
|
+
size: int = Field(
|
|
31
|
+
...,
|
|
32
|
+
ge=0,
|
|
33
|
+
description="File size in bytes.",
|
|
34
|
+
examples=[123456],
|
|
35
|
+
)
|
|
36
|
+
created_at: float = Field(
|
|
37
|
+
...,
|
|
38
|
+
description="Unix timestamp when the file was stored.",
|
|
39
|
+
examples=[1766200000.0],
|
|
40
|
+
)
|
|
41
|
+
meta: dict[str, Any] = Field(
|
|
42
|
+
default_factory=dict,
|
|
43
|
+
description="Capability-specific metadata such as seed, request params, timings, dimensions, or artifact kind.",
|
|
44
|
+
examples=[{"model": "turbo", "width": 1024, "height": 1024}],
|
|
45
|
+
)
|
|
46
|
+
path: str = Field(..., exclude=True)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FileURLRef(BaseModel):
|
|
7
|
+
model_config = {
|
|
8
|
+
"json_schema_extra": {
|
|
9
|
+
"examples": [
|
|
10
|
+
{"type": "url", "url": "https://example.com/input.png"},
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type: Literal["url"] = "url"
|
|
16
|
+
url: str = Field(..., min_length=1)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Unified file store. Artifacts (and uploads) are addressed by ``file_id``.
|
|
2
|
+
|
|
3
|
+
Files outlive jobs while their files remain on disk: jobs are in-memory and reset
|
|
4
|
+
on process restart, while stored files stay self-describing via a sidecar
|
|
5
|
+
``<file_id>.json`` holding the original filename, content type, size, and an
|
|
6
|
+
optional ``meta`` blob. This
|
|
7
|
+
mirrors the per-server Files APIs (ace-step / audiocraft / video) unified into
|
|
8
|
+
one store under ``files_root``.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import mimetypes
|
|
13
|
+
import shutil
|
|
14
|
+
import time
|
|
15
|
+
import uuid
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from .._schemas.file_record import FileRecord
|
|
20
|
+
from .._settings import FileSettings
|
|
21
|
+
from .._types.file_id import FileID
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class FileStore:
|
|
25
|
+
def __init__(self, settings: FileSettings) -> None:
|
|
26
|
+
self.root = Path(settings.files_root).expanduser()
|
|
27
|
+
self.root.mkdir(parents=True, exist_ok=True)
|
|
28
|
+
|
|
29
|
+
def _blob_path(self, file_id: FileID, suffix: str) -> Path:
|
|
30
|
+
return self.root / f"{file_id}{suffix}"
|
|
31
|
+
|
|
32
|
+
def _sidecar(self, file_id: FileID) -> Path:
|
|
33
|
+
return self.root / f"{file_id}.json"
|
|
34
|
+
|
|
35
|
+
# -- write ----------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
def put_bytes(
|
|
38
|
+
self,
|
|
39
|
+
data: bytes,
|
|
40
|
+
*,
|
|
41
|
+
filename: str,
|
|
42
|
+
content_type: str | None = None,
|
|
43
|
+
meta: dict[str, Any] | None = None,
|
|
44
|
+
) -> FileRecord:
|
|
45
|
+
file_id = _generate_file_id()
|
|
46
|
+
suffix = Path(filename).suffix
|
|
47
|
+
path = self._blob_path(file_id, suffix)
|
|
48
|
+
path.write_bytes(data)
|
|
49
|
+
rec = FileRecord(
|
|
50
|
+
file_id=file_id,
|
|
51
|
+
filename=filename,
|
|
52
|
+
content_type=_resolve_content_type(filename, content_type),
|
|
53
|
+
size=len(data),
|
|
54
|
+
created_at=time.time(),
|
|
55
|
+
meta=meta or {},
|
|
56
|
+
path=str(path),
|
|
57
|
+
)
|
|
58
|
+
self._write_sidecar(rec, suffix)
|
|
59
|
+
return rec
|
|
60
|
+
|
|
61
|
+
def put_path(
|
|
62
|
+
self,
|
|
63
|
+
src: str | Path,
|
|
64
|
+
*,
|
|
65
|
+
filename: str | None = None,
|
|
66
|
+
content_type: str | None = None,
|
|
67
|
+
meta: dict[str, Any] | None = None,
|
|
68
|
+
move: bool = False,
|
|
69
|
+
) -> FileRecord:
|
|
70
|
+
src = Path(src)
|
|
71
|
+
file_id = _generate_file_id()
|
|
72
|
+
suffix = src.suffix
|
|
73
|
+
filename = filename or src.name
|
|
74
|
+
path = self._blob_path(file_id, suffix)
|
|
75
|
+
if move:
|
|
76
|
+
shutil.move(str(src), str(path))
|
|
77
|
+
else:
|
|
78
|
+
shutil.copyfile(src, path)
|
|
79
|
+
rec = FileRecord(
|
|
80
|
+
file_id=file_id,
|
|
81
|
+
filename=filename,
|
|
82
|
+
content_type=_resolve_content_type(filename, content_type),
|
|
83
|
+
size=path.stat().st_size,
|
|
84
|
+
created_at=time.time(),
|
|
85
|
+
meta=meta or {},
|
|
86
|
+
path=str(path),
|
|
87
|
+
)
|
|
88
|
+
self._write_sidecar(rec, suffix)
|
|
89
|
+
return rec
|
|
90
|
+
|
|
91
|
+
def _write_sidecar(self, rec: FileRecord, suffix: str) -> None:
|
|
92
|
+
payload = rec.model_dump()
|
|
93
|
+
payload["_suffix"] = suffix
|
|
94
|
+
self._sidecar(rec.file_id).write_text(json.dumps(payload, ensure_ascii=False))
|
|
95
|
+
|
|
96
|
+
# -- read -----------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
def get(self, file_id: FileID) -> FileRecord | None:
|
|
99
|
+
sidecar = self._sidecar(file_id)
|
|
100
|
+
if not sidecar.exists():
|
|
101
|
+
return None
|
|
102
|
+
d = json.loads(sidecar.read_text())
|
|
103
|
+
suffix = d.get("_suffix", "")
|
|
104
|
+
return FileRecord(
|
|
105
|
+
file_id=d["file_id"],
|
|
106
|
+
filename=d["filename"],
|
|
107
|
+
content_type=d["content_type"],
|
|
108
|
+
size=d["size"],
|
|
109
|
+
created_at=d["created_at"],
|
|
110
|
+
meta=d.get("meta", {}),
|
|
111
|
+
path=str(self._blob_path(file_id, suffix)),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def list(self) -> list[FileRecord]:
|
|
115
|
+
recs = []
|
|
116
|
+
for sidecar in self.root.glob("*.json"):
|
|
117
|
+
rec = self.get(sidecar.stem)
|
|
118
|
+
if rec is not None:
|
|
119
|
+
recs.append(rec)
|
|
120
|
+
return sorted(recs, key=lambda r: r.created_at, reverse=True)
|
|
121
|
+
|
|
122
|
+
def delete(self, file_id: FileID) -> bool:
|
|
123
|
+
rec = self.get(file_id)
|
|
124
|
+
if rec is None:
|
|
125
|
+
return False
|
|
126
|
+
Path(rec.path).unlink(missing_ok=True)
|
|
127
|
+
self._sidecar(file_id).unlink(missing_ok=True)
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _generate_file_id() -> FileID:
|
|
132
|
+
return f"file_{uuid.uuid4().hex}"
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _resolve_content_type(filename: str, content_type: str | None) -> str:
|
|
136
|
+
return (
|
|
137
|
+
content_type or mimetypes.guess_type(filename)[0] or "application/octet-stream"
|
|
138
|
+
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from pydantic import Field
|
|
2
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
3
|
+
from pydantic_settings_manager import SettingsManager
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FileSettings(BaseSettings):
|
|
7
|
+
"""Settings for the file store that keeps uploads and generated artifacts."""
|
|
8
|
+
|
|
9
|
+
model_config = SettingsConfigDict(env_prefix="KIAPI_")
|
|
10
|
+
|
|
11
|
+
files_root: str = Field(
|
|
12
|
+
default="/tmp/kiapi/files",
|
|
13
|
+
title="File storage directory",
|
|
14
|
+
description=(
|
|
15
|
+
"Directory where uploaded files and generated artifacts are stored.\n"
|
|
16
|
+
"The default is under /tmp so files may disappear after reboot or tmp cleanup.\n"
|
|
17
|
+
"~ is expanded as the home directory of the running user."
|
|
18
|
+
),
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
settings_manager = SettingsManager(FileSettings)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
from .._schemas.file_data_url_ref import FileDataURLRef
|
|
6
|
+
from .._schemas.file_id_ref import FileIDRef
|
|
7
|
+
from .._schemas.file_url_ref import FileURLRef
|
|
8
|
+
|
|
9
|
+
type FileRef = Annotated[
|
|
10
|
+
FileIDRef | FileURLRef | FileDataURLRef,
|
|
11
|
+
Field(discriminator="type"),
|
|
12
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from .._schemas.file_record import FileRecord
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True)
|
|
7
|
+
class ResolvedFileRef:
|
|
8
|
+
record: FileRecord
|
|
9
|
+
|
|
10
|
+
@property
|
|
11
|
+
def file_id(self) -> str:
|
|
12
|
+
return self.record.file_id
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def path(self) -> str:
|
|
16
|
+
return self.record.path
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Unified job model + in-memory job store. Every generation (sync or async) is
|
|
2
|
+
a Job; sync waits for it, async returns its id. Jobs reset on restart.
|
|
3
|
+
|
|
4
|
+
Public surface (was ``core/jobs.py``): :class:`Job`, :class:`JobStore`,
|
|
5
|
+
:class:`JobStatus`, :class:`ProgressReporter`, :func:`creep_progress`.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from ._enums.job_status import JobStatus
|
|
9
|
+
from ._helpers.creep_progress import creep_progress
|
|
10
|
+
from ._models.job import Job
|
|
11
|
+
from ._services.job_store import JobStore
|
|
12
|
+
from ._services.progress_reporter import ProgressReporter
|
|
13
|
+
from ._types.job_id import JobID
|
|
14
|
+
from ._types.job_result import JobResult
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"Job",
|
|
18
|
+
"JobID",
|
|
19
|
+
"JobResult",
|
|
20
|
+
"JobStatus",
|
|
21
|
+
"JobStore",
|
|
22
|
+
"ProgressReporter",
|
|
23
|
+
"creep_progress",
|
|
24
|
+
]
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""Synthetic 'liveness' progress for jobs whose backend exposes no per-step hook.
|
|
2
|
+
|
|
3
|
+
Some generation backends (notably ltx2's ``mlx_video.generate_video``, which only
|
|
4
|
+
drives its own internal rich progress bar) give us no way to observe denoising
|
|
5
|
+
steps — unlike mflux, which offers a callback registry (see capabilities'
|
|
6
|
+
``attach_mflux_progress``). A polling client, typically an LLM agent, reads a job
|
|
7
|
+
whose ``progress`` never moves as possibly hung and may stop waiting.
|
|
8
|
+
|
|
9
|
+
For those backends we creep the reported fraction forward on a piecewise-linear
|
|
10
|
+
schedule (see :func:`_scheduled_fraction`) — constant, easy-to-read rates rather
|
|
11
|
+
than a smooth curve. This is a deliberate fiction: it signals 'alive and working',
|
|
12
|
+
not real completion; the genuine terminal 1.0 still comes only from
|
|
13
|
+
:meth:`Job.mark_succeeded`.
|
|
14
|
+
|
|
15
|
+
The job's worker thread is blocked inside the backend call while this runs, so a
|
|
16
|
+
daemon thread does the ticking. ContextVars don't cross threads, so it captures
|
|
17
|
+
the worker thread's bound :class:`ProgressReporter` up front and pushes updates
|
|
18
|
+
through it — the same throttling and job wiring apply.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import threading
|
|
24
|
+
import time
|
|
25
|
+
from collections.abc import Iterator
|
|
26
|
+
from contextlib import contextmanager
|
|
27
|
+
|
|
28
|
+
from .._services.progress_reporter import ProgressReporter
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _scheduled_fraction(
|
|
32
|
+
elapsed: float,
|
|
33
|
+
*,
|
|
34
|
+
eta_s: float,
|
|
35
|
+
cap: float,
|
|
36
|
+
ramp_frac: float,
|
|
37
|
+
ramp_rate: float,
|
|
38
|
+
mid_frac: float,
|
|
39
|
+
mid_fallback_rate: float,
|
|
40
|
+
tail_rate: float,
|
|
41
|
+
) -> float:
|
|
42
|
+
"""The reported fraction at ``elapsed`` seconds — three constant-rate phases:
|
|
43
|
+
|
|
44
|
+
1. **ramp** — linear from 0 to ``ramp_frac`` at a *fixed* ``ramp_rate`` per
|
|
45
|
+
second (e.g. 0 → 0.20 at 0.5%/s, reaching 0.20 after 40s). The rate is
|
|
46
|
+
always honoured, even for a short ``eta_s``, so the start is always a gentle
|
|
47
|
+
crawl — easier to wait on than an immediate jump.
|
|
48
|
+
2. **mid** — linear from ``ramp_frac`` to ``mid_frac``. Normally paced to hit
|
|
49
|
+
``mid_frac`` exactly at ``eta_s``; but if the ramp has already consumed
|
|
50
|
+
``eta_s`` (``eta_s <= ramp duration``), there is no room left to pace to, so
|
|
51
|
+
it falls back to the fixed ``mid_fallback_rate`` per second (e.g. 1%/s).
|
|
52
|
+
3. **tail** — linear from ``mid_frac`` upward at the slow ``tail_rate`` per
|
|
53
|
+
second, clamped at ``cap`` — the overrun crawl.
|
|
54
|
+
"""
|
|
55
|
+
knee_t = ramp_frac / ramp_rate # fixed: guarantees the slow start
|
|
56
|
+
if elapsed <= knee_t:
|
|
57
|
+
return min(cap, ramp_rate * elapsed)
|
|
58
|
+
|
|
59
|
+
if eta_s > knee_t:
|
|
60
|
+
mid_end_t = eta_s # room to pace: land on mid_frac exactly at eta
|
|
61
|
+
else:
|
|
62
|
+
mid_end_t = knee_t + (mid_frac - ramp_frac) / mid_fallback_rate
|
|
63
|
+
|
|
64
|
+
if elapsed <= mid_end_t:
|
|
65
|
+
fraction = ramp_frac + (mid_frac - ramp_frac) * (elapsed - knee_t) / (
|
|
66
|
+
mid_end_t - knee_t
|
|
67
|
+
)
|
|
68
|
+
else:
|
|
69
|
+
fraction = mid_frac + tail_rate * (elapsed - mid_end_t)
|
|
70
|
+
return min(cap, max(0.0, fraction))
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@contextmanager
|
|
74
|
+
def creep_progress(
|
|
75
|
+
*,
|
|
76
|
+
eta_s: float,
|
|
77
|
+
cap: float = 0.999,
|
|
78
|
+
label: str = "generating",
|
|
79
|
+
interval_s: float = 1.0,
|
|
80
|
+
ramp_frac: float = 0.20,
|
|
81
|
+
ramp_rate: float = 0.005,
|
|
82
|
+
mid_frac: float = 0.80,
|
|
83
|
+
mid_fallback_rate: float = 0.01,
|
|
84
|
+
tail_rate: float = 0.0005,
|
|
85
|
+
) -> Iterator[None]:
|
|
86
|
+
"""Tick synthetic progress on a piecewise-linear schedule while the block runs.
|
|
87
|
+
|
|
88
|
+
The fraction follows :func:`_scheduled_fraction`: a fixed-rate ramp to
|
|
89
|
+
``ramp_frac`` (always a gentle start), an even climb to ``mid_frac`` timed to
|
|
90
|
+
land at ``eta_s`` (or a fixed ``mid_fallback_rate`` if ``eta_s`` is already
|
|
91
|
+
spent), then a slow ``tail_rate`` crawl up to ``cap``. ``eta_s`` only sets the
|
|
92
|
+
pace, never the ceiling, so a job that overruns keeps inching forward in the
|
|
93
|
+
tail phase rather than stalling. ``eta_s <= 0`` disables creeping entirely.
|
|
94
|
+
"""
|
|
95
|
+
reporter = ProgressReporter.current()
|
|
96
|
+
if eta_s <= 0:
|
|
97
|
+
yield
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
stop = threading.Event()
|
|
101
|
+
t0 = time.monotonic()
|
|
102
|
+
|
|
103
|
+
def _tick() -> None:
|
|
104
|
+
while not stop.wait(interval_s):
|
|
105
|
+
fraction = _scheduled_fraction(
|
|
106
|
+
time.monotonic() - t0,
|
|
107
|
+
eta_s=eta_s,
|
|
108
|
+
cap=cap,
|
|
109
|
+
ramp_frac=ramp_frac,
|
|
110
|
+
ramp_rate=ramp_rate,
|
|
111
|
+
mid_frac=mid_frac,
|
|
112
|
+
mid_fallback_rate=mid_fallback_rate,
|
|
113
|
+
tail_rate=tail_rate,
|
|
114
|
+
)
|
|
115
|
+
reporter.update(fraction, label)
|
|
116
|
+
|
|
117
|
+
thread = threading.Thread(target=_tick, name="kiapi-progress-creep", daemon=True)
|
|
118
|
+
thread.start()
|
|
119
|
+
try:
|
|
120
|
+
yield
|
|
121
|
+
finally:
|
|
122
|
+
stop.set()
|
|
123
|
+
thread.join(timeout=2.0)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Unified job model.
|
|
2
|
+
|
|
3
|
+
Every generation in kiapi — sync or async, chat or video — is a Job. Sync
|
|
4
|
+
requests create a job, wait for it, and return its result; async requests create
|
|
5
|
+
a job and return its id immediately. This gives one consistent place to inspect
|
|
6
|
+
work in flight and one consistent result shape.
|
|
7
|
+
|
|
8
|
+
``result`` is a free-form dict whose shape depends on ``type`` (the caller
|
|
9
|
+
dispatches on ``type``); ``artifacts`` lists file_ids produced (see core/file).
|
|
10
|
+
Jobs are in-memory only and reset on restart; artifacts are Files API entries
|
|
11
|
+
that can outlive jobs while their files remain on disk.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import time
|
|
15
|
+
import uuid
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from pydantic import BaseModel, Field
|
|
19
|
+
|
|
20
|
+
from kiapi.core.file import FileID
|
|
21
|
+
|
|
22
|
+
from .._enums.job_status import JobStatus
|
|
23
|
+
from .._types.job_id import JobID
|
|
24
|
+
from .._types.job_result import JobResult
|
|
25
|
+
from .._types.job_type import JobType
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Job(BaseModel):
|
|
29
|
+
type: JobType = Field(
|
|
30
|
+
...,
|
|
31
|
+
description="Job type. Use this to interpret the capability-specific result payload.",
|
|
32
|
+
examples=["zimage"],
|
|
33
|
+
)
|
|
34
|
+
params: dict[str, Any] = Field(
|
|
35
|
+
default_factory=dict,
|
|
36
|
+
description="Request parameters captured for inspection and reproducibility. Secret or large media payloads may be omitted or redacted by endpoints.",
|
|
37
|
+
)
|
|
38
|
+
id: JobID = Field(
|
|
39
|
+
default_factory=lambda: f"job_{uuid.uuid4().hex}",
|
|
40
|
+
description="In-memory job id. Jobs are cleared when the kiapi process restarts.",
|
|
41
|
+
examples=["job_0123456789abcdef"],
|
|
42
|
+
)
|
|
43
|
+
status: JobStatus = Field(
|
|
44
|
+
default=JobStatus.QUEUED,
|
|
45
|
+
description="Job lifecycle state: queued, running, succeeded, failed, or canceled.",
|
|
46
|
+
examples=["succeeded"],
|
|
47
|
+
)
|
|
48
|
+
result: JobResult | None = Field(
|
|
49
|
+
default=None,
|
|
50
|
+
description="Capability-specific result metadata. Completed generation jobs usually also expose produced file ids in artifacts.",
|
|
51
|
+
examples=[{"file_id": "file_0123456789abcdef", "image_bytes": 123456}],
|
|
52
|
+
)
|
|
53
|
+
artifacts: list[FileID] = Field(
|
|
54
|
+
default_factory=list,
|
|
55
|
+
description="File ids produced by the job. Use GET /v1/files/{file_id} for metadata or /download for bytes.",
|
|
56
|
+
examples=[["file_0123456789abcdef"]],
|
|
57
|
+
)
|
|
58
|
+
error: str | None = Field(
|
|
59
|
+
default=None,
|
|
60
|
+
description="Error message when status is failed; otherwise null.",
|
|
61
|
+
examples=["model 'turbo' is not activated"],
|
|
62
|
+
)
|
|
63
|
+
created_at: float = Field(
|
|
64
|
+
default_factory=lambda: time.time(),
|
|
65
|
+
description="Unix timestamp when the job was created.",
|
|
66
|
+
examples=[1766200000.0],
|
|
67
|
+
)
|
|
68
|
+
started_at: float | None = Field(
|
|
69
|
+
default=None,
|
|
70
|
+
description="Unix timestamp when the worker started the job, or null while queued.",
|
|
71
|
+
examples=[1766200001.0],
|
|
72
|
+
)
|
|
73
|
+
finished_at: float | None = Field(
|
|
74
|
+
default=None,
|
|
75
|
+
description="Unix timestamp when the job reached a terminal state, or null while queued/running.",
|
|
76
|
+
examples=[1766200030.0],
|
|
77
|
+
)
|
|
78
|
+
progress: float | None = Field(
|
|
79
|
+
default=None,
|
|
80
|
+
ge=0.0,
|
|
81
|
+
le=1.0,
|
|
82
|
+
description="Best-effort completion fraction in [0.0, 1.0]. Null means the job has not reported progress.",
|
|
83
|
+
examples=[0.42],
|
|
84
|
+
)
|
|
85
|
+
progress_label: str = Field(
|
|
86
|
+
default="queued",
|
|
87
|
+
description="Short human-readable phase label such as queued, running, denoising, saving, or done.",
|
|
88
|
+
examples=["denoising"],
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def to_dict(self) -> dict[str, Any]:
|
|
92
|
+
return self.model_dump()
|
|
93
|
+
|
|
94
|
+
def mark_running(self) -> None:
|
|
95
|
+
self.status = JobStatus.RUNNING
|
|
96
|
+
self.started_at = time.time()
|
|
97
|
+
self.progress = 0.0
|
|
98
|
+
self.progress_label = "running"
|
|
99
|
+
|
|
100
|
+
def update_progress(self, fraction: float, label: str | None = None) -> None:
|
|
101
|
+
self.progress = min(1.0, max(0.0, fraction))
|
|
102
|
+
|
|
103
|
+
if label is not None:
|
|
104
|
+
self.progress_label = label
|
|
105
|
+
|
|
106
|
+
def mark_succeeded(self, result: JobResult, artifacts: list[FileID]) -> None:
|
|
107
|
+
self.status = JobStatus.SUCCEEDED
|
|
108
|
+
self.result = result
|
|
109
|
+
self.artifacts = artifacts
|
|
110
|
+
self.progress = 1.0
|
|
111
|
+
self.progress_label = "done"
|
|
112
|
+
self.finished_at = time.time()
|
|
113
|
+
|
|
114
|
+
def mark_failed(self, error: str) -> None:
|
|
115
|
+
self.status = JobStatus.FAILED
|
|
116
|
+
self.error = error
|
|
117
|
+
self.finished_at = time.time()
|
|
118
|
+
|
|
119
|
+
def mark_canceled(self) -> None:
|
|
120
|
+
self.status = JobStatus.CANCELED
|
|
121
|
+
self.finished_at = time.time()
|