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
kiapi/api/chat/router.py
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"""Chat API: OpenAI-compatible ``POST /v1/chat/completions``.
|
|
2
|
+
|
|
3
|
+
Internally still a job (so it shows up in /v1/jobs and shares the single-flight
|
|
4
|
+
worker). Non-streaming callers wait for the completion dict. Streaming callers
|
|
5
|
+
receive OpenAI-style SSE chunks while the worker thread generates; the final job
|
|
6
|
+
result remains inspectable through /v1/jobs.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import base64
|
|
11
|
+
import binascii
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
import re
|
|
15
|
+
from collections.abc import AsyncIterator
|
|
16
|
+
from copy import deepcopy
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from fastapi import APIRouter, Depends, HTTPException
|
|
20
|
+
from fastapi.responses import StreamingResponse
|
|
21
|
+
|
|
22
|
+
from kiapi.api import (
|
|
23
|
+
REQUIRE_AUTH,
|
|
24
|
+
get_ctx,
|
|
25
|
+
get_worker,
|
|
26
|
+
register_capability_endpoints,
|
|
27
|
+
)
|
|
28
|
+
from kiapi.api._settings import settings_manager
|
|
29
|
+
from kiapi.capabilities.chat import ChatRequest, handle_chat
|
|
30
|
+
from kiapi.core.app import AppContext
|
|
31
|
+
from kiapi.core.memory import MemoryBudgetError
|
|
32
|
+
from kiapi.core.model import UnknownModelError, model_registry
|
|
33
|
+
from kiapi.core.setup import SetupRequiredError
|
|
34
|
+
from kiapi.core.worker import Worker
|
|
35
|
+
|
|
36
|
+
from ._views.chat_completion_response import ChatCompletionResponse
|
|
37
|
+
|
|
38
|
+
logger = logging.getLogger(__name__)
|
|
39
|
+
|
|
40
|
+
router = APIRouter(dependencies=REQUIRE_AUTH)
|
|
41
|
+
|
|
42
|
+
_BASE64_PLACEHOLDER = "<base64_encoded_data>"
|
|
43
|
+
|
|
44
|
+
_DATA_URL_BASE64 = re.compile(
|
|
45
|
+
r"^(?P<prefix>data:[^,]*;base64,)(?P<data>.*)$",
|
|
46
|
+
re.DOTALL,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@router.post(
|
|
51
|
+
"/v1/chat/completions",
|
|
52
|
+
response_model=None,
|
|
53
|
+
responses={
|
|
54
|
+
200: {
|
|
55
|
+
"model": ChatCompletionResponse,
|
|
56
|
+
"description": (
|
|
57
|
+
"Non-streaming: the full `chat.completion` object. "
|
|
58
|
+
"Streaming (`stream: true`): an OpenAI `text/event-stream` of "
|
|
59
|
+
"`chat.completion.chunk` events, each `data: {...}`, terminated "
|
|
60
|
+
"by `data: [DONE]`. Tool calls arrive as `delta.tool_calls`; the "
|
|
61
|
+
"final chunk carries `finish_reason`."
|
|
62
|
+
),
|
|
63
|
+
"content": {
|
|
64
|
+
"text/event-stream": {"schema": {"type": "string", "format": "binary"}},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
400: {"description": "Invalid request, unknown model, or bad input/modality."},
|
|
68
|
+
503: {"description": "Model not set up, or memory budget exceeded."},
|
|
69
|
+
504: {"description": "Sync timeout exceeded; the job keeps running."},
|
|
70
|
+
},
|
|
71
|
+
)
|
|
72
|
+
async def chat_completions(
|
|
73
|
+
req: ChatRequest,
|
|
74
|
+
ctx: AppContext = Depends(get_ctx),
|
|
75
|
+
worker: Worker = Depends(get_worker),
|
|
76
|
+
) -> dict | StreamingResponse:
|
|
77
|
+
"""Generate a chat completion (OpenAI-compatible).
|
|
78
|
+
|
|
79
|
+
Accepts the OpenAI `chat.completions` request shape with multimodal
|
|
80
|
+
`messages` (text / image / audio / video), function `tools`, and
|
|
81
|
+
`tool_choice`. The resolved `model` (see GET /v1/models) determines which
|
|
82
|
+
input modalities are accepted.
|
|
83
|
+
|
|
84
|
+
Every request runs as a single-flight job, so it appears in /v1/jobs and is
|
|
85
|
+
serialized with all other generation. Non-streaming callers wait up to the
|
|
86
|
+
sync timeout and receive the full `chat.completion`; on timeout the job
|
|
87
|
+
keeps running and can be polled at /v1/jobs/{id}. Set `stream: true` to
|
|
88
|
+
receive incremental `chat.completion.chunk` SSE events instead.
|
|
89
|
+
"""
|
|
90
|
+
settings = settings_manager.get_settings()
|
|
91
|
+
|
|
92
|
+
logger.debug(
|
|
93
|
+
"chat/completions request validated: %s",
|
|
94
|
+
_redacted_chat_request_dump(req),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if req.stream:
|
|
98
|
+
try:
|
|
99
|
+
model_registry.resolve("chat", req.model)
|
|
100
|
+
except UnknownModelError as exc:
|
|
101
|
+
raise HTTPException(status_code=400, detail=str(exc)) # noqa: B904
|
|
102
|
+
|
|
103
|
+
loop = asyncio.get_running_loop()
|
|
104
|
+
queue: asyncio.Queue[dict | None] = asyncio.Queue()
|
|
105
|
+
|
|
106
|
+
def emit(chunk: dict) -> None:
|
|
107
|
+
loop.call_soon_threadsafe(queue.put_nowait, chunk)
|
|
108
|
+
|
|
109
|
+
def finish_stream() -> None:
|
|
110
|
+
loop.call_soon_threadsafe(queue.put_nowait, None)
|
|
111
|
+
|
|
112
|
+
def thunk(): # type: ignore
|
|
113
|
+
try:
|
|
114
|
+
return handle_chat(ctx, req, emit=emit)
|
|
115
|
+
finally:
|
|
116
|
+
finish_stream()
|
|
117
|
+
|
|
118
|
+
job = ctx.job_store.create(
|
|
119
|
+
type="chat", params={"model": req.model, "stream": True}
|
|
120
|
+
)
|
|
121
|
+
fut = await worker.submit(job, thunk)
|
|
122
|
+
|
|
123
|
+
async def events() -> AsyncIterator[str]:
|
|
124
|
+
try:
|
|
125
|
+
while True:
|
|
126
|
+
item = await queue.get()
|
|
127
|
+
if item is None:
|
|
128
|
+
break
|
|
129
|
+
yield _sse(item)
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
await asyncio.shield(fut)
|
|
133
|
+
except Exception as exc:
|
|
134
|
+
yield _sse(_stream_error(exc))
|
|
135
|
+
yield _sse("[DONE]")
|
|
136
|
+
except asyncio.CancelledError:
|
|
137
|
+
# The running worker job is not preemptible; let it finish and
|
|
138
|
+
# keep its final state in the job store.
|
|
139
|
+
return
|
|
140
|
+
|
|
141
|
+
return StreamingResponse(
|
|
142
|
+
events(),
|
|
143
|
+
media_type="text/event-stream",
|
|
144
|
+
headers={
|
|
145
|
+
"Cache-Control": "no-cache",
|
|
146
|
+
"X-Accel-Buffering": "no",
|
|
147
|
+
},
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
job = ctx.job_store.create(type="chat", params={"model": req.model})
|
|
151
|
+
fut = await worker.submit(job, lambda: handle_chat(ctx, req))
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
return await asyncio.wait_for(fut, timeout=settings.sync_timeout_s)
|
|
155
|
+
except TimeoutError:
|
|
156
|
+
raise HTTPException( # noqa: B904
|
|
157
|
+
status_code=504,
|
|
158
|
+
detail=f"chat job {job.id} exceeded sync timeout "
|
|
159
|
+
f"({settings.sync_timeout_s}s); it keeps running — poll /v1/jobs/{job.id}",
|
|
160
|
+
)
|
|
161
|
+
except UnknownModelError as exc:
|
|
162
|
+
raise HTTPException(status_code=400, detail=str(exc)) # noqa: B904
|
|
163
|
+
except SetupRequiredError as exc:
|
|
164
|
+
raise HTTPException(status_code=503, detail=str(exc)) # noqa: B904
|
|
165
|
+
except MemoryBudgetError as exc:
|
|
166
|
+
raise HTTPException(status_code=503, detail=str(exc)) # noqa: B904
|
|
167
|
+
except Exception as exc:
|
|
168
|
+
# MediaError/CapabilityError (bad input / unsupported modality) → 400.
|
|
169
|
+
if exc.__class__.__name__ in ("MediaError", "CapabilityError"):
|
|
170
|
+
raise HTTPException(status_code=400, detail=str(exc)) # noqa: B904
|
|
171
|
+
raise HTTPException(status_code=500, detail=f"generation failed: {exc}") # noqa: B904
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
register_capability_endpoints(router, name="chat", base_path="/v1/chat")
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _redact_data_url(value: str) -> str:
|
|
178
|
+
m = _DATA_URL_BASE64.match(value)
|
|
179
|
+
if not m:
|
|
180
|
+
return value
|
|
181
|
+
return f"{m.group('prefix')}{_BASE64_PLACEHOLDER}"
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _looks_like_base64(value: str) -> bool:
|
|
185
|
+
"""Heuristic for bare media payloads; avoids redacting ordinary short text."""
|
|
186
|
+
s = value.strip()
|
|
187
|
+
if len(s) < 128:
|
|
188
|
+
return False
|
|
189
|
+
try:
|
|
190
|
+
base64.b64decode(s, validate=True)
|
|
191
|
+
except (binascii.Error, ValueError):
|
|
192
|
+
return False
|
|
193
|
+
return True
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _redact_media_source(value: Any, *, bare_base64: bool = False) -> Any:
|
|
197
|
+
if not isinstance(value, str):
|
|
198
|
+
return value
|
|
199
|
+
redacted = _redact_data_url(value)
|
|
200
|
+
if redacted != value:
|
|
201
|
+
return redacted
|
|
202
|
+
if bare_base64 and _looks_like_base64(value):
|
|
203
|
+
return _BASE64_PLACEHOLDER
|
|
204
|
+
return value
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _redact_content_part(part: Any) -> Any:
|
|
208
|
+
if not isinstance(part, dict):
|
|
209
|
+
return part
|
|
210
|
+
|
|
211
|
+
out = deepcopy(part)
|
|
212
|
+
for key in ("image_url", "audio_url", "video_url"):
|
|
213
|
+
container = out.get(key)
|
|
214
|
+
if isinstance(container, dict) and "url" in container:
|
|
215
|
+
container["url"] = _redact_media_source(container["url"])
|
|
216
|
+
elif isinstance(container, str):
|
|
217
|
+
out[key] = _redact_media_source(container)
|
|
218
|
+
|
|
219
|
+
for key in ("input_audio", "input_video"):
|
|
220
|
+
container = out.get(key)
|
|
221
|
+
if isinstance(container, dict) and "data" in container:
|
|
222
|
+
container["data"] = _BASE64_PLACEHOLDER
|
|
223
|
+
elif isinstance(container, str):
|
|
224
|
+
out[key] = _redact_media_source(container, bare_base64=True)
|
|
225
|
+
|
|
226
|
+
for key in ("image", "audio", "video"):
|
|
227
|
+
if key in out:
|
|
228
|
+
out[key] = _redact_media_source(out[key], bare_base64=True)
|
|
229
|
+
|
|
230
|
+
return out
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _redacted_chat_request_dump(req: ChatRequest) -> str:
|
|
234
|
+
data = req.model_dump(mode="json")
|
|
235
|
+
messages = data.get("messages")
|
|
236
|
+
if isinstance(messages, list):
|
|
237
|
+
for msg in messages:
|
|
238
|
+
if not isinstance(msg, dict):
|
|
239
|
+
continue
|
|
240
|
+
content = msg.get("content")
|
|
241
|
+
if isinstance(content, list):
|
|
242
|
+
msg["content"] = [_redact_content_part(part) for part in content]
|
|
243
|
+
return json.dumps(data, ensure_ascii=False, indent=2)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _sse(payload) -> str: # type: ignore
|
|
247
|
+
if payload == "[DONE]":
|
|
248
|
+
return "data: [DONE]\n\n"
|
|
249
|
+
return f"data: {json.dumps(payload, ensure_ascii=False)}\n\n"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _stream_error(exc: BaseException) -> dict:
|
|
253
|
+
return {"error": {"message": str(exc), "type": exc.__class__.__name__}}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""``/v1/embedding`` response model.
|
|
2
|
+
|
|
3
|
+
Mirrors the dict shaped by ``capabilities/embedding/_operations/format_response``
|
|
4
|
+
so the response is self-describing in OpenAPI. It is an output-side projection of
|
|
5
|
+
the model result (not a model-coupled view), so it lives under
|
|
6
|
+
``api/embedding/_views``.
|
|
7
|
+
|
|
8
|
+
``EmbeddingResponse`` is the sole public element; the nested pieces are private
|
|
9
|
+
since nothing references them on their own.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, Field
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class _Usage(BaseModel):
|
|
16
|
+
prompt_tokens: int = Field(
|
|
17
|
+
description="Tokens in the embedded input (0 when the model does not report it)."
|
|
18
|
+
)
|
|
19
|
+
total_tokens: int = Field(
|
|
20
|
+
description="Total tokens accounted for; equals `prompt_tokens` (no generation)."
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class _Timings(BaseModel):
|
|
25
|
+
total_s: float = Field(description="Wall-clock embedding time in seconds.")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class EmbeddingResponse(BaseModel):
|
|
29
|
+
model: str = Field(description="Resolved model name that produced the vector.")
|
|
30
|
+
embedding: list[float] = Field(
|
|
31
|
+
description="L2-normalized, last-token-pooled embedding vector for the item."
|
|
32
|
+
)
|
|
33
|
+
dimension: int = Field(description="Length of `embedding` (vector dimensionality).")
|
|
34
|
+
usage: _Usage = Field(description="Token accounting for the request.")
|
|
35
|
+
timings: _Timings = Field(description="kiapi extension: server-side timing.")
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Embedding API: ``POST /v1/embedding`` (sync only).
|
|
2
|
+
|
|
3
|
+
Like chat, still a job internally (shows up in /v1/jobs, shares the single-flight
|
|
4
|
+
worker), but no async mode: the caller always gets the vector back. A sync wait
|
|
5
|
+
that exceeds ``sync_timeout_s`` is an error (504).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
|
|
10
|
+
from fastapi import APIRouter, Depends, HTTPException
|
|
11
|
+
|
|
12
|
+
from kiapi.api import (
|
|
13
|
+
REQUIRE_AUTH,
|
|
14
|
+
get_ctx,
|
|
15
|
+
get_worker,
|
|
16
|
+
register_capability_endpoints,
|
|
17
|
+
)
|
|
18
|
+
from kiapi.api._settings import settings_manager
|
|
19
|
+
from kiapi.capabilities.embedding import EmbedRequest, handle_embed
|
|
20
|
+
from kiapi.core.app import AppContext
|
|
21
|
+
from kiapi.core.memory import MemoryBudgetError
|
|
22
|
+
from kiapi.core.model import UnknownModelError
|
|
23
|
+
from kiapi.core.setup import SetupRequiredError
|
|
24
|
+
from kiapi.core.worker import Worker
|
|
25
|
+
|
|
26
|
+
from ._views.embedding_response import EmbeddingResponse
|
|
27
|
+
|
|
28
|
+
router = APIRouter(dependencies=REQUIRE_AUTH)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@router.post(
|
|
32
|
+
"/v1/embedding",
|
|
33
|
+
response_model=EmbeddingResponse,
|
|
34
|
+
responses={
|
|
35
|
+
400: {"description": "Unknown model, empty input, or unsupported modality."},
|
|
36
|
+
503: {"description": "Model not set up, or memory budget exceeded."},
|
|
37
|
+
504: {"description": "Sync timeout exceeded; the job keeps running."},
|
|
38
|
+
},
|
|
39
|
+
)
|
|
40
|
+
async def embedding(
|
|
41
|
+
req: EmbedRequest,
|
|
42
|
+
ctx: AppContext = Depends(get_ctx),
|
|
43
|
+
worker: Worker = Depends(get_worker),
|
|
44
|
+
) -> dict:
|
|
45
|
+
"""Embed a single item (one field per modality).
|
|
46
|
+
|
|
47
|
+
Send `text` and/or `image` in one request (one item, not OpenAI's array
|
|
48
|
+
`input`). The resolved `model` (see GET /v1/embedding/models) determines which
|
|
49
|
+
modalities are accepted; provide at least one input it supports. Returns one
|
|
50
|
+
L2-normalized, last-token-pooled vector with its `dimension`.
|
|
51
|
+
|
|
52
|
+
Embedding runs as a single-flight job, so it appears in /v1/jobs and is
|
|
53
|
+
serialized with all other generation, but there is no async mode: the caller
|
|
54
|
+
always waits for the vector. A wait that exceeds the sync timeout returns 504
|
|
55
|
+
while the job keeps running and can be polled at /v1/jobs/{id}.
|
|
56
|
+
"""
|
|
57
|
+
settings = settings_manager.get_settings()
|
|
58
|
+
|
|
59
|
+
job = ctx.job_store.create(type="embedding", params={"model": req.model})
|
|
60
|
+
fut = await worker.submit(job, lambda: handle_embed(ctx, req))
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
return await asyncio.wait_for(fut, timeout=settings.sync_timeout_s)
|
|
64
|
+
except TimeoutError:
|
|
65
|
+
raise HTTPException( # noqa: B904
|
|
66
|
+
status_code=504,
|
|
67
|
+
detail=f"embedding job {job.id} exceeded sync timeout "
|
|
68
|
+
f"({settings.sync_timeout_s}s); poll /v1/jobs/{job.id}",
|
|
69
|
+
)
|
|
70
|
+
except UnknownModelError as exc:
|
|
71
|
+
raise HTTPException(status_code=400, detail=str(exc)) # noqa: B904
|
|
72
|
+
except SetupRequiredError as exc:
|
|
73
|
+
raise HTTPException(status_code=503, detail=str(exc)) # noqa: B904
|
|
74
|
+
except MemoryBudgetError as exc:
|
|
75
|
+
raise HTTPException(status_code=503, detail=str(exc)) # noqa: B904
|
|
76
|
+
except Exception as exc:
|
|
77
|
+
if exc.__class__.__name__ in ("MediaError", "CapabilityError"):
|
|
78
|
+
raise HTTPException(status_code=400, detail=str(exc)) # noqa: B904
|
|
79
|
+
raise HTTPException(status_code=500, detail=f"embedding failed: {exc}") # noqa: B904
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
register_capability_endpoints(router, name="embedding", base_path="/v1/embedding")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
from fastapi import File, UploadFile
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _get_file(
|
|
7
|
+
file: Annotated[
|
|
8
|
+
UploadFile,
|
|
9
|
+
File(
|
|
10
|
+
description="File bytes to store and reference from inference requests.",
|
|
11
|
+
),
|
|
12
|
+
],
|
|
13
|
+
) -> UploadFile:
|
|
14
|
+
return file
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
from fastapi import Path
|
|
4
|
+
|
|
5
|
+
from kiapi.core.file import FileID
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _get_file_id(
|
|
9
|
+
file_id: Annotated[
|
|
10
|
+
str,
|
|
11
|
+
Path(
|
|
12
|
+
pattern=r"^file_[0-9a-f]+$",
|
|
13
|
+
description="Persistent file id returned by the Files API.",
|
|
14
|
+
examples=["file_0123456789abcdef"],
|
|
15
|
+
),
|
|
16
|
+
],
|
|
17
|
+
) -> FileID:
|
|
18
|
+
return file_id
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class FileDeleteResponse(BaseModel):
|
|
5
|
+
deleted: bool = Field(
|
|
6
|
+
...,
|
|
7
|
+
description="True when the file record and stored bytes were deleted.",
|
|
8
|
+
examples=[True],
|
|
9
|
+
)
|
|
10
|
+
file_id: str = Field(
|
|
11
|
+
...,
|
|
12
|
+
description="Deleted file id.",
|
|
13
|
+
examples=["file_0123456789abcdef"],
|
|
14
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
|
|
3
|
+
from kiapi.core.file import FileRecord
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FileListResponse(BaseModel):
|
|
7
|
+
object: str = Field(
|
|
8
|
+
default="list",
|
|
9
|
+
description="OpenAI-style list envelope marker.",
|
|
10
|
+
examples=["list"],
|
|
11
|
+
)
|
|
12
|
+
data: list[FileRecord] = Field(
|
|
13
|
+
default_factory=list,
|
|
14
|
+
description="Stored files, including uploaded inputs and generated artifacts.",
|
|
15
|
+
)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Files API: upload / list / get / download / delete. Unified across capabilities."""
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Depends, HTTPException, UploadFile
|
|
4
|
+
from fastapi.responses import FileResponse
|
|
5
|
+
|
|
6
|
+
from kiapi.api import REQUIRE_AUTH, get_ctx
|
|
7
|
+
from kiapi.core.app import AppContext
|
|
8
|
+
from kiapi.core.file import FileID, FileRecord
|
|
9
|
+
|
|
10
|
+
from ._operations.get_file import _get_file
|
|
11
|
+
from ._operations.get_file_id import _get_file_id
|
|
12
|
+
from ._views.file_delete_response import FileDeleteResponse
|
|
13
|
+
from ._views.file_list_response import FileListResponse
|
|
14
|
+
|
|
15
|
+
router = APIRouter(prefix="/v1/files", dependencies=REQUIRE_AUTH)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@router.post("", response_model=FileRecord)
|
|
19
|
+
async def upload(
|
|
20
|
+
file: UploadFile = Depends(_get_file),
|
|
21
|
+
ctx: AppContext = Depends(get_ctx),
|
|
22
|
+
) -> FileRecord:
|
|
23
|
+
"""Store a file and return a reusable file_id.
|
|
24
|
+
|
|
25
|
+
Uploaded files become persistent Files API records. Use the returned
|
|
26
|
+
file_id as a FileRef input for generation/editing endpoints, or download
|
|
27
|
+
the bytes later with GET /v1/files/{file_id}/download.
|
|
28
|
+
"""
|
|
29
|
+
data = await file.read()
|
|
30
|
+
return ctx.file_store.put_bytes(
|
|
31
|
+
data,
|
|
32
|
+
filename=file.filename or "upload",
|
|
33
|
+
content_type=file.content_type,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@router.get("", response_model=FileListResponse)
|
|
38
|
+
async def list_files(ctx: AppContext = Depends(get_ctx)) -> FileListResponse:
|
|
39
|
+
"""List stored files.
|
|
40
|
+
|
|
41
|
+
Files include explicit uploads and generated artifacts. File records are
|
|
42
|
+
disk-backed and can outlive in-memory jobs across process restarts.
|
|
43
|
+
"""
|
|
44
|
+
return FileListResponse(data=ctx.file_store.list())
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@router.get("/{file_id}", response_model=FileRecord)
|
|
48
|
+
async def get_file(
|
|
49
|
+
file_id: FileID = Depends(_get_file_id),
|
|
50
|
+
ctx: AppContext = Depends(get_ctx),
|
|
51
|
+
) -> FileRecord:
|
|
52
|
+
"""Get metadata for a stored file.
|
|
53
|
+
|
|
54
|
+
This returns metadata only, not file bytes. Use the download endpoint when
|
|
55
|
+
the client needs the artifact body.
|
|
56
|
+
"""
|
|
57
|
+
rec = ctx.file_store.get(file_id)
|
|
58
|
+
if rec is None:
|
|
59
|
+
raise HTTPException(status_code=404, detail=f"unknown file {file_id!r}")
|
|
60
|
+
return rec
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@router.get(
|
|
64
|
+
"/{file_id}/download",
|
|
65
|
+
responses={
|
|
66
|
+
200: {
|
|
67
|
+
"description": "Stored file bytes.",
|
|
68
|
+
"content": {
|
|
69
|
+
"application/octet-stream": {
|
|
70
|
+
"schema": {"type": "string", "format": "binary"}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
404: {"description": "Unknown file_id."},
|
|
75
|
+
},
|
|
76
|
+
)
|
|
77
|
+
async def download_file(
|
|
78
|
+
file_id: FileID = Depends(_get_file_id),
|
|
79
|
+
ctx: AppContext = Depends(get_ctx),
|
|
80
|
+
) -> FileResponse:
|
|
81
|
+
"""Download stored file bytes.
|
|
82
|
+
|
|
83
|
+
The response media type is the stored file's content_type, such as
|
|
84
|
+
image/png, audio/wav, video/mp4, text/markdown, or application/pdf.
|
|
85
|
+
"""
|
|
86
|
+
rec = ctx.file_store.get(file_id)
|
|
87
|
+
if rec is None:
|
|
88
|
+
raise HTTPException(status_code=404, detail=f"unknown file {file_id!r}")
|
|
89
|
+
return FileResponse(rec.path, media_type=rec.content_type, filename=rec.filename)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@router.delete("/{file_id}", response_model=FileDeleteResponse)
|
|
93
|
+
async def delete_file(
|
|
94
|
+
file_id: FileID = Depends(_get_file_id),
|
|
95
|
+
ctx: AppContext = Depends(get_ctx),
|
|
96
|
+
) -> dict:
|
|
97
|
+
"""Delete a stored file.
|
|
98
|
+
|
|
99
|
+
Deleting a file removes the stored bytes and metadata. Existing job records
|
|
100
|
+
may still mention the deleted file_id in artifacts, but the file can no
|
|
101
|
+
longer be downloaded.
|
|
102
|
+
"""
|
|
103
|
+
if not ctx.file_store.delete(file_id):
|
|
104
|
+
raise HTTPException(status_code=404, detail=f"unknown file {file_id!r}")
|
|
105
|
+
return {"deleted": True, "file_id": file_id}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
|
|
3
|
+
from kiapi.core.memory import MemoryStats
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class HealthResponse(BaseModel):
|
|
7
|
+
status: str = Field(
|
|
8
|
+
...,
|
|
9
|
+
description="Service health marker. The value is ok when the API process is accepting requests.",
|
|
10
|
+
examples=["ok"],
|
|
11
|
+
)
|
|
12
|
+
warm: bool = Field(
|
|
13
|
+
...,
|
|
14
|
+
description="Whether startup warmup has finished. Warmup may skip models that are not activated.",
|
|
15
|
+
examples=[True],
|
|
16
|
+
)
|
|
17
|
+
queue_len: int = Field(
|
|
18
|
+
...,
|
|
19
|
+
ge=0,
|
|
20
|
+
description="Number of jobs waiting in the single-flight worker queue.",
|
|
21
|
+
examples=[0],
|
|
22
|
+
)
|
|
23
|
+
memory: MemoryStats = Field(
|
|
24
|
+
...,
|
|
25
|
+
description="Resident model memory budget and loaded model snapshot.",
|
|
26
|
+
)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from fastapi import APIRouter, Depends
|
|
2
|
+
|
|
3
|
+
from kiapi.api import get_ctx, get_worker
|
|
4
|
+
from kiapi.core.app import AppContext
|
|
5
|
+
from kiapi.core.worker import Worker
|
|
6
|
+
|
|
7
|
+
from ._views.health_response import HealthResponse
|
|
8
|
+
|
|
9
|
+
router = APIRouter()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@router.get("/health", response_model=HealthResponse)
|
|
13
|
+
async def health(
|
|
14
|
+
ctx: AppContext = Depends(get_ctx),
|
|
15
|
+
worker: Worker = Depends(get_worker),
|
|
16
|
+
) -> dict:
|
|
17
|
+
"""Inspect server readiness, queue depth, and resident model memory.
|
|
18
|
+
|
|
19
|
+
Use this endpoint to see whether warmup has finished, how many jobs are
|
|
20
|
+
queued behind the single-flight worker, and which resident models currently
|
|
21
|
+
count against the global memory budget.
|
|
22
|
+
"""
|
|
23
|
+
return {
|
|
24
|
+
"status": "ok",
|
|
25
|
+
"warm": worker.warm,
|
|
26
|
+
"queue_len": worker.queue.qsize(),
|
|
27
|
+
"memory": ctx.memory_manager.stats(),
|
|
28
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""``/v1/image/depthpro/estimate`` result payload (the Job ``result`` shape).
|
|
2
|
+
|
|
3
|
+
Mirrors the dict returned by ``capabilities/depthpro/_models/depthpro._estimate``
|
|
4
|
+
so the depth-map result is self-describing in OpenAPI. It is an output-side
|
|
5
|
+
projection of the model result (not a model-coupled view), so it lives under
|
|
6
|
+
``api/image/depthpro/_views``.
|
|
7
|
+
|
|
8
|
+
``EstimateResponse`` is the sole public element; nested pieces stay private.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class _Timings(BaseModel):
|
|
17
|
+
total_s: float = Field(
|
|
18
|
+
description="Wall-clock depth-estimation time in seconds (model run only)."
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class EstimateResponse(BaseModel):
|
|
23
|
+
"""Capability-specific ``result`` for a succeeded depthpro estimate job."""
|
|
24
|
+
|
|
25
|
+
model: str = Field(
|
|
26
|
+
description="Resolved model variant that produced the depth map."
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
depth_image_file_id: str = Field(
|
|
30
|
+
description=(
|
|
31
|
+
"Files-API id of the grayscale depth PNG (near = bright, far = dark). "
|
|
32
|
+
"Fetch metadata at GET /v1/files/{id} or bytes at /download. This is "
|
|
33
|
+
"also the artifact returned as raw bytes by a single-artifact sync call."
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
depth_image_bytes: int = Field(description="Size of the depth PNG in bytes.")
|
|
37
|
+
depth_data_file_id: str | None = Field(
|
|
38
|
+
description=(
|
|
39
|
+
"Files-API id of the compressed NPZ holding the raw float depth array "
|
|
40
|
+
"plus min/max depth, or null when `include_depth_data` was false."
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
depth_data_bytes: int | None = Field(
|
|
44
|
+
description="Size of the NPZ in bytes, or null when no depth data was stored."
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
input_width: int = Field(description="Width in pixels of the submitted image.")
|
|
48
|
+
input_height: int = Field(description="Height in pixels of the submitted image.")
|
|
49
|
+
input_mode: str = Field(
|
|
50
|
+
description="PIL mode of the submitted image (e.g. RGB, L, RGBA)."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
width: int = Field(description="Width in pixels of the produced depth map.")
|
|
54
|
+
height: int = Field(description="Height in pixels of the produced depth map.")
|
|
55
|
+
mode: str = Field(description="PIL mode of the produced depth PNG (typically L).")
|
|
56
|
+
|
|
57
|
+
array_shape: list[int] = Field(
|
|
58
|
+
description="Shape of the raw depth array stored in the NPZ (rows, cols)."
|
|
59
|
+
)
|
|
60
|
+
min_depth: float = Field(
|
|
61
|
+
description="Minimum depth value in the raw array, in meters."
|
|
62
|
+
)
|
|
63
|
+
max_depth: float = Field(
|
|
64
|
+
description="Maximum depth value in the raw array, in meters."
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
params: dict[str, Any] = Field(
|
|
68
|
+
description="Resolved parameters used for the run (model, quantize, etc.)."
|
|
69
|
+
)
|
|
70
|
+
timings: _Timings = Field(description="kiapi extension: server-side timing.")
|