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,47 @@
|
|
|
1
|
+
DESCRIPTION = """Text and multimodal embeddings for retrieval and similarity search.
|
|
2
|
+
|
|
3
|
+
POST one item to `/v1/embedding` with one field per modality (`text` and/or
|
|
4
|
+
`image`). Returns a single L2-normalized, last-token-pooled vector. This is **not**
|
|
5
|
+
OpenAI's array `input` shape — one item per request. Sync only (no async mode).
|
|
6
|
+
|
|
7
|
+
## Upstream docs
|
|
8
|
+
- [mlx-embeddings](https://github.com/Blaizzy/mlx-embeddings) — the MLX embedding engine kiapi runs
|
|
9
|
+
- [mlx-community/Qwen3-Embedding-8B-mxfp8](https://huggingface.co/mlx-community/Qwen3-Embedding-8B-mxfp8) — `qwen3-embedding-8b` weights
|
|
10
|
+
- [mlx-community/Qwen3-VL-Embedding-2B-mxfp8](https://huggingface.co/mlx-community/Qwen3-VL-Embedding-2B-mxfp8) — `qwen3-vl-embedding-2b` weights
|
|
11
|
+
|
|
12
|
+
## Choosing A Model
|
|
13
|
+
`model` selects a registered embedding model (full catalog and aliases:
|
|
14
|
+
`GET /v1/embedding/models`). The served models differ by input modality, so
|
|
15
|
+
choose by what you send:
|
|
16
|
+
- **qwen3-embedding-8b** (default) — text only. Larger, stronger text retrieval.
|
|
17
|
+
- **qwen3-vl-embedding-2b** — text + image. Use it for image embeddings or a
|
|
18
|
+
shared text/image space; sending `image` to the text-only model returns 400.
|
|
19
|
+
|
|
20
|
+
Vectors from different models live in different spaces and may differ in
|
|
21
|
+
dimensionality — embed both your queries and your corpus with the **same** model.
|
|
22
|
+
|
|
23
|
+
## Notes
|
|
24
|
+
- One input item per request. To embed N items, make N requests.
|
|
25
|
+
- Provide at least one modality input; an empty request returns 400.
|
|
26
|
+
- Vectors are already L2-normalized, so cosine similarity reduces to a dot
|
|
27
|
+
product.
|
|
28
|
+
|
|
29
|
+
## Examples
|
|
30
|
+
|
|
31
|
+
### Text (default model)
|
|
32
|
+
```sh
|
|
33
|
+
curl -sS http://HOST:PORT/v1/embedding \\
|
|
34
|
+
-H 'Content-Type: application/json' \\
|
|
35
|
+
-d '{"text": "今日の天気は晴れです"}'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Image (multimodal model)
|
|
39
|
+
```sh
|
|
40
|
+
curl -sS http://HOST:PORT/v1/embedding \\
|
|
41
|
+
-H 'Content-Type: application/json' \\
|
|
42
|
+
-d '{
|
|
43
|
+
"model": "qwen3-vl-embedding-2b",
|
|
44
|
+
"image": "data:image/png;base64,iVBORw0..."
|
|
45
|
+
}'
|
|
46
|
+
```
|
|
47
|
+
"""
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Register embedding's models + OpenAPI description in the global registries."""
|
|
2
|
+
|
|
3
|
+
from kiapi.core.capability import CapabilitySpec, capability_spec_registry
|
|
4
|
+
from kiapi.core.model import ModelSpec, model_registry
|
|
5
|
+
from kiapi.core.setup import HfSnapshotResource
|
|
6
|
+
|
|
7
|
+
from .._constants.description import DESCRIPTION
|
|
8
|
+
from .._models import qwen3_text, qwen3_vl
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def register() -> None:
|
|
12
|
+
capability_spec_registry.register(
|
|
13
|
+
CapabilitySpec(
|
|
14
|
+
name="embedding",
|
|
15
|
+
domain="embedding",
|
|
16
|
+
title="kiapi Embedding API",
|
|
17
|
+
summary="Create text and multimodal embeddings for retrieval and similarity search.",
|
|
18
|
+
description=DESCRIPTION,
|
|
19
|
+
openapi_path="/v1/embedding/openapi.json",
|
|
20
|
+
docs_path="/v1/embedding/docs",
|
|
21
|
+
redoc_path="/v1/embedding/redoc",
|
|
22
|
+
path_prefixes=("/v1/embedding",),
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
model_registry.register(
|
|
27
|
+
ModelSpec(
|
|
28
|
+
name="qwen3-embedding-8b",
|
|
29
|
+
family="embedding",
|
|
30
|
+
domain="embedding",
|
|
31
|
+
repo="mlx-community/Qwen3-Embedding-8B-mxfp8",
|
|
32
|
+
module=qwen3_text,
|
|
33
|
+
weight_gb=7.3, # measured on device (estimate was 8.0)
|
|
34
|
+
peak_headroom_gb=2.0,
|
|
35
|
+
framework="mlx",
|
|
36
|
+
priority=10,
|
|
37
|
+
aliases=("text", "qwen3-embedding", "qwen3_embedding"),
|
|
38
|
+
default=True,
|
|
39
|
+
setup_resources=(
|
|
40
|
+
HfSnapshotResource(
|
|
41
|
+
repo="mlx-community/Qwen3-Embedding-8B-mxfp8",
|
|
42
|
+
disk_gb=7.82,
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
model_registry.register(
|
|
48
|
+
ModelSpec(
|
|
49
|
+
name="qwen3-vl-embedding-2b",
|
|
50
|
+
family="embedding",
|
|
51
|
+
domain="embedding",
|
|
52
|
+
repo="mlx-community/Qwen3-VL-Embedding-2B-mxfp8",
|
|
53
|
+
module=qwen3_vl,
|
|
54
|
+
weight_gb=2.4, # measured on device (estimate was 2.0)
|
|
55
|
+
peak_headroom_gb=2.0, # image inputs add some headroom
|
|
56
|
+
framework="mlx",
|
|
57
|
+
priority=20, # tiny + frequently used → keep resident
|
|
58
|
+
aliases=("vl", "qwen3-vl-embedding", "qwen3_vl_embedding"),
|
|
59
|
+
setup_resources=(
|
|
60
|
+
HfSnapshotResource(
|
|
61
|
+
repo="mlx-community/Qwen3-VL-Embedding-2B-mxfp8",
|
|
62
|
+
disk_gb=2.59,
|
|
63
|
+
),
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Handler for Qwen3-Embedding-8B (text embeddings; ``model_type: qwen3``).
|
|
2
|
+
|
|
3
|
+
A text-only embedding model. Owns its own load + embed flow; shares the
|
|
4
|
+
input-parsing and response-shaping operations with the VL handler.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import tempfile
|
|
8
|
+
import time
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from types import SimpleNamespace
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from kiapi.core.model import ModelSpec
|
|
14
|
+
|
|
15
|
+
from .._operations.format_response import format_response
|
|
16
|
+
from .._operations.parse_inputs import parse_inputs
|
|
17
|
+
from .._utils.to_vector import to_vector
|
|
18
|
+
from .._views.embed_params import EmbedParams
|
|
19
|
+
|
|
20
|
+
FEATURES = {"text"}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def load(spec: ModelSpec) -> SimpleNamespace:
|
|
24
|
+
from mlx_embeddings import load as _load # type: ignore
|
|
25
|
+
|
|
26
|
+
model, processor = _load(spec.repo)
|
|
27
|
+
return SimpleNamespace(model=model, processor=processor)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def warmup(payload: SimpleNamespace) -> None:
|
|
31
|
+
run(
|
|
32
|
+
payload,
|
|
33
|
+
EmbedParams(model="qwen3-embedding-8b", inputs={"text": "hi"}, max_length=512),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def run(payload: SimpleNamespace, params: EmbedParams) -> dict[str, Any]:
|
|
38
|
+
from mlx_embeddings import generate
|
|
39
|
+
|
|
40
|
+
model, processor = payload.model, payload.processor
|
|
41
|
+
# text-only: parse_inputs validates capabilities; no media files are written,
|
|
42
|
+
# so the tmp_dir is never actually touched.
|
|
43
|
+
parsed = parse_inputs(params.inputs, Path(tempfile.gettempdir()), allow=FEATURES)
|
|
44
|
+
|
|
45
|
+
t0 = time.time()
|
|
46
|
+
output = generate(model, processor, parsed["text"], max_length=params.max_length)
|
|
47
|
+
elapsed = time.time() - t0
|
|
48
|
+
|
|
49
|
+
return format_response(
|
|
50
|
+
model_name=params.model,
|
|
51
|
+
embedding=to_vector(output),
|
|
52
|
+
elapsed=elapsed,
|
|
53
|
+
)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Handler for Qwen3-VL-Embedding-2B (text + image embeddings; ``model_type: qwen3_vl``).
|
|
2
|
+
|
|
3
|
+
Owns the full embed flow for the multimodal embedding model. The MLX-converted
|
|
4
|
+
processor may be missing the ``chat_template`` and token-id attributes that
|
|
5
|
+
Transformers normally sets up, so :func:`load` patches them in after loading (a
|
|
6
|
+
known Qwen3-VL quirk, carried over from the original single-purpose server).
|
|
7
|
+
|
|
8
|
+
Embedding is done via the model's high-level ``process([item], processor=...)``
|
|
9
|
+
API, where ``item`` is ``{"text": ..., "image": <path>}`` (either key optional).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import shutil
|
|
13
|
+
import time
|
|
14
|
+
from types import SimpleNamespace
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from kiapi.core.model import ModelSpec
|
|
18
|
+
from kiapi.core.workdir import create_work_dir
|
|
19
|
+
|
|
20
|
+
from .._operations.format_response import format_response
|
|
21
|
+
from .._operations.parse_inputs import parse_inputs
|
|
22
|
+
from .._utils.to_vector import to_vector
|
|
23
|
+
from .._views.embed_params import EmbedParams
|
|
24
|
+
|
|
25
|
+
FEATURES = {"text", "image"}
|
|
26
|
+
|
|
27
|
+
# Source repo for the chat template, when the converted processor lacks one.
|
|
28
|
+
TEMPLATE_SOURCE_MODEL_ID = "Qwen/Qwen3-VL-Embedding-2B"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def load(spec: ModelSpec) -> SimpleNamespace:
|
|
32
|
+
from mlx_embeddings import load as _load # type: ignore
|
|
33
|
+
|
|
34
|
+
model, processor = _load(spec.repo)
|
|
35
|
+
payload = SimpleNamespace(model=model, processor=processor)
|
|
36
|
+
_patch_processor(payload.processor)
|
|
37
|
+
return payload
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def warmup(payload: SimpleNamespace) -> None:
|
|
41
|
+
run(
|
|
42
|
+
payload,
|
|
43
|
+
EmbedParams(
|
|
44
|
+
model="qwen3-vl-embedding-2b", inputs={"text": "hi"}, max_length=512
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _patch_processor(processor: Any) -> None:
|
|
50
|
+
"""Patch in the chat template / token-id attributes the converted processor
|
|
51
|
+
may be missing (Qwen3-VL quirk). Best-effort and idempotent."""
|
|
52
|
+
inner = getattr(processor, "processor", None)
|
|
53
|
+
tokenizer = getattr(processor, "tokenizer", None)
|
|
54
|
+
|
|
55
|
+
if getattr(processor, "chat_template", None) is None:
|
|
56
|
+
try:
|
|
57
|
+
chat_template = processor._load_chat_template(TEMPLATE_SOURCE_MODEL_ID)
|
|
58
|
+
if inner is not None:
|
|
59
|
+
inner.chat_template = chat_template
|
|
60
|
+
if tokenizer is not None:
|
|
61
|
+
tokenizer.chat_template = chat_template
|
|
62
|
+
except Exception:
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
if inner is not None:
|
|
66
|
+
image_token_id = getattr(processor, "image_token_id", None)
|
|
67
|
+
video_token_id = getattr(processor, "video_token_id", None)
|
|
68
|
+
if image_token_id is not None:
|
|
69
|
+
inner.image_ids = [image_token_id]
|
|
70
|
+
if video_token_id is not None:
|
|
71
|
+
inner.video_ids = [video_token_id]
|
|
72
|
+
inner.audio_ids = []
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def run(payload: SimpleNamespace, params: EmbedParams) -> dict[str, Any]:
|
|
76
|
+
model, processor = payload.model, payload.processor
|
|
77
|
+
tmp_dir = create_work_dir("embedding/qwen3_vl")
|
|
78
|
+
try:
|
|
79
|
+
parsed = parse_inputs(params.inputs, tmp_dir, allow=FEATURES)
|
|
80
|
+
|
|
81
|
+
item: dict[str, Any] = {}
|
|
82
|
+
if "text" in parsed:
|
|
83
|
+
item["text"] = parsed["text"]
|
|
84
|
+
if "image" in parsed:
|
|
85
|
+
item["image"] = parsed["image"] # local file path
|
|
86
|
+
|
|
87
|
+
t0 = time.time()
|
|
88
|
+
embeddings = model.process([item], processor=processor)
|
|
89
|
+
elapsed = time.time() - t0
|
|
90
|
+
|
|
91
|
+
return format_response(
|
|
92
|
+
model_name=params.model,
|
|
93
|
+
embedding=to_vector(embeddings),
|
|
94
|
+
elapsed=elapsed,
|
|
95
|
+
)
|
|
96
|
+
finally:
|
|
97
|
+
shutil.rmtree(tmp_dir, ignore_errors=True)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Shape an embedding vector into the server's ``/v1/embedding`` response dict.
|
|
2
|
+
|
|
3
|
+
Identical across handlers, so it lives here and the per-model ``run()`` calls it
|
|
4
|
+
after computing the vector.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def format_response(
|
|
11
|
+
*,
|
|
12
|
+
model_name: str,
|
|
13
|
+
embedding: list[float],
|
|
14
|
+
elapsed: float,
|
|
15
|
+
prompt_tokens: int = 0,
|
|
16
|
+
) -> dict[str, Any]:
|
|
17
|
+
"""Build the ``/v1/embedding`` response dict for a single embedding."""
|
|
18
|
+
return {
|
|
19
|
+
"model": model_name,
|
|
20
|
+
"embedding": embedding,
|
|
21
|
+
"dimension": len(embedding),
|
|
22
|
+
"usage": {"prompt_tokens": prompt_tokens, "total_tokens": prompt_tokens},
|
|
23
|
+
"timings": {"total_s": round(elapsed, 3)},
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Embedding service entry: resolve → acquire (memory budget) → run.
|
|
2
|
+
|
|
3
|
+
Body of the embedding job's worker-thread thunk. Mirrors mlx-embedding-server's
|
|
4
|
+
Engine.run, but loads through the *global* memory manager so embedding models
|
|
5
|
+
share the budget with every other capability.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from kiapi.core.app import AppContext
|
|
9
|
+
from kiapi.core.model import model_registry
|
|
10
|
+
|
|
11
|
+
from .._settings import settings_manager
|
|
12
|
+
from .._views.embed_request import EmbedRequest
|
|
13
|
+
from .resolve_embed_params import resolve_embed_params
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def handle_embed(ctx: AppContext, req: EmbedRequest) -> tuple[dict, list[str]]:
|
|
17
|
+
"""Run one embedding. Returns (embedding_result, artifact_file_ids)."""
|
|
18
|
+
settings = settings_manager.get_settings()
|
|
19
|
+
spec = model_registry.resolve("embedding", req.model)
|
|
20
|
+
ctx.ensure_model_ready(spec)
|
|
21
|
+
params = resolve_embed_params(settings, req, variant=spec.name)
|
|
22
|
+
payload = ctx.memory_manager.acquire(spec)
|
|
23
|
+
result = spec.module.run(payload, params)
|
|
24
|
+
return result, [] # embeddings produce no file artifacts
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Turn an embedding request's modality inputs into usable values.
|
|
2
|
+
|
|
3
|
+
The request carries one field per modality (``text``, ``image``, ...). Model
|
|
4
|
+
handlers call :func:`parse_inputs` with the set of modalities they support
|
|
5
|
+
(``allow=``); anything outside that set — or an empty request — is rejected with
|
|
6
|
+
:class:`~kiapi.capabilities.CapabilityError` (mapped to HTTP 400 by the API layer).
|
|
7
|
+
|
|
8
|
+
- ``text`` is passed through as-is.
|
|
9
|
+
- ``image`` accepts a data URL, an http(s) URL, or bare base64, and is
|
|
10
|
+
materialized to a file under ``tmp_dir`` (the loader reads paths).
|
|
11
|
+
|
|
12
|
+
Returns a dict keyed by modality, e.g. ``{"text": "...", "image": "/tmp/..png"}``.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import base64
|
|
16
|
+
import binascii
|
|
17
|
+
import re
|
|
18
|
+
import urllib.request
|
|
19
|
+
import uuid
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
from kiapi.capabilities import CapabilityError
|
|
24
|
+
from kiapi.core.net import UnsafeURLError, verify_public_url
|
|
25
|
+
|
|
26
|
+
_DATA_URL = re.compile(r"^data:(?P<mime>[\w./+-]+)?;base64,(?P<data>.*)$", re.DOTALL)
|
|
27
|
+
|
|
28
|
+
_EXT_BY_MIME = {
|
|
29
|
+
"image/png": ".png",
|
|
30
|
+
"image/jpeg": ".jpg",
|
|
31
|
+
"image/jpg": ".jpg",
|
|
32
|
+
"image/webp": ".webp",
|
|
33
|
+
"image/gif": ".gif",
|
|
34
|
+
}
|
|
35
|
+
_DEFAULT_EXT = {"image": ".png"}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _write_bytes(data: bytes, tmp_dir: Path, kind: str, ext: str) -> str:
|
|
39
|
+
path = tmp_dir / f"{kind}_{uuid.uuid4().hex}{ext}"
|
|
40
|
+
path.write_bytes(data)
|
|
41
|
+
return str(path)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _materialize(source: str, kind: str, tmp_dir: Path) -> str:
|
|
45
|
+
"""Turn a data URL / http(s) URL / bare base64 into a file path."""
|
|
46
|
+
if not isinstance(source, str) or not source:
|
|
47
|
+
raise CapabilityError(f"{kind}: empty or non-string source")
|
|
48
|
+
|
|
49
|
+
m = _DATA_URL.match(source)
|
|
50
|
+
if m:
|
|
51
|
+
mime = m.group("mime") or ""
|
|
52
|
+
ext = _EXT_BY_MIME.get(mime, _DEFAULT_EXT[kind])
|
|
53
|
+
try:
|
|
54
|
+
return _write_bytes(base64.b64decode(m.group("data")), tmp_dir, kind, ext)
|
|
55
|
+
except (binascii.Error, ValueError) as exc:
|
|
56
|
+
raise CapabilityError(f"{kind}: invalid base64 data URL ({exc})") # noqa: B904
|
|
57
|
+
|
|
58
|
+
if source.startswith(("http://", "https://")):
|
|
59
|
+
try:
|
|
60
|
+
verify_public_url(source, kind=kind)
|
|
61
|
+
except UnsafeURLError as exc:
|
|
62
|
+
raise CapabilityError(f"{kind}: {exc}") # noqa: B904
|
|
63
|
+
try:
|
|
64
|
+
with urllib.request.urlopen(source, timeout=30) as resp:
|
|
65
|
+
data = resp.read()
|
|
66
|
+
except Exception as exc:
|
|
67
|
+
raise CapabilityError(f"{kind}: failed to fetch {source!r} ({exc})") # noqa: B904
|
|
68
|
+
ext = Path(source.split("?")[0]).suffix or _DEFAULT_EXT[kind]
|
|
69
|
+
return _write_bytes(data, tmp_dir, kind, ext)
|
|
70
|
+
|
|
71
|
+
# Bare base64 (no data: prefix) — the common case for {"image": "<b64>"}.
|
|
72
|
+
try:
|
|
73
|
+
data = base64.b64decode(source, validate=True)
|
|
74
|
+
except (binascii.Error, ValueError):
|
|
75
|
+
raise CapabilityError(f"{kind}: source is not a data URL, URL, or base64") # noqa: B904
|
|
76
|
+
return _write_bytes(data, tmp_dir, kind, _DEFAULT_EXT[kind])
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def parse_inputs(
|
|
80
|
+
inputs: dict[str, Any],
|
|
81
|
+
tmp_dir: Path,
|
|
82
|
+
*,
|
|
83
|
+
allow: set[str] | None = None,
|
|
84
|
+
) -> dict[str, Any]:
|
|
85
|
+
"""Validate + materialize request inputs into a per-modality dict.
|
|
86
|
+
|
|
87
|
+
``allow`` is the set of modalities the selected model supports (e.g.
|
|
88
|
+
``{"text", "image"}``). An input outside it raises :class:`CapabilityError`.
|
|
89
|
+
``None`` allows everything (used by tests).
|
|
90
|
+
"""
|
|
91
|
+
if not inputs:
|
|
92
|
+
raise CapabilityError(
|
|
93
|
+
"request has no input (provide at least one of: text, image)"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
out: dict[str, Any] = {}
|
|
97
|
+
for kind, source in inputs.items():
|
|
98
|
+
if allow is not None and kind not in allow:
|
|
99
|
+
raise CapabilityError(
|
|
100
|
+
f"{kind} input is not supported by the selected model"
|
|
101
|
+
)
|
|
102
|
+
if kind == "text":
|
|
103
|
+
out["text"] = source or ""
|
|
104
|
+
elif kind == "image":
|
|
105
|
+
out["image"] = _materialize(source, "image", tmp_dir)
|
|
106
|
+
else: # pragma: no cover — schema only exposes known modalities
|
|
107
|
+
raise CapabilityError(f"unsupported input modality: {kind!r}")
|
|
108
|
+
return out
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Merge an embedding request with settings defaults into the complete EmbedParams."""
|
|
2
|
+
|
|
3
|
+
from .._settings import EmbeddingSettings
|
|
4
|
+
from .._views.embed_params import EmbedParams
|
|
5
|
+
from .._views.embed_request import EmbedRequest
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def resolve_embed_params(
|
|
9
|
+
settings: EmbeddingSettings,
|
|
10
|
+
req: EmbedRequest,
|
|
11
|
+
*,
|
|
12
|
+
variant: str,
|
|
13
|
+
) -> EmbedParams:
|
|
14
|
+
return EmbedParams(
|
|
15
|
+
model=variant,
|
|
16
|
+
inputs=req.inputs(),
|
|
17
|
+
max_length=settings.max_length,
|
|
18
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Embedding-capability defaults, read from the environment (``KIAPI_EMBEDDING_``)."""
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
5
|
+
from pydantic_settings_manager import SettingsManager
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EmbeddingSettings(BaseSettings):
|
|
9
|
+
"""Settings for Embedding input length limits."""
|
|
10
|
+
|
|
11
|
+
model_config = SettingsConfigDict(
|
|
12
|
+
env_prefix="KIAPI_EMBEDDING_",
|
|
13
|
+
extra="ignore",
|
|
14
|
+
protected_namespaces=(),
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
max_length: int = Field(
|
|
18
|
+
default=512,
|
|
19
|
+
title="Maximum input token length",
|
|
20
|
+
description=(
|
|
21
|
+
"Maximum token length passed to the embedding model tokenizer.\n"
|
|
22
|
+
"Inputs longer than this are truncated before model processing."
|
|
23
|
+
),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
settings_manager = SettingsManager(EmbeddingSettings)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Extract a single embedding vector from an mlx-embeddings model output."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def to_vector(output: Any) -> list[float]:
|
|
7
|
+
"""Extract a single embedding vector from an mlx-embeddings model output.
|
|
8
|
+
|
|
9
|
+
mlx-embeddings returns a model-output object with ``text_embeds`` (normalized,
|
|
10
|
+
pooled, shape ``(batch, dim)``). We embed one item per request, so take row 0.
|
|
11
|
+
Falls back to treating the output itself as the array if there's no attribute.
|
|
12
|
+
"""
|
|
13
|
+
import mlx.core as mx
|
|
14
|
+
|
|
15
|
+
embeds = getattr(output, "text_embeds", None)
|
|
16
|
+
if embeds is None:
|
|
17
|
+
embeds = getattr(output, "pooler_output", output)
|
|
18
|
+
mx.eval(embeds)
|
|
19
|
+
rows = embeds.tolist()
|
|
20
|
+
return rows[0] if rows and isinstance(rows[0], list) else rows
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""The complete contract for one embedding run.
|
|
2
|
+
|
|
3
|
+
Built from settings + request by ``resolve_embed_params``; a model's ``run`` needs
|
|
4
|
+
nothing else (no ``req`` / ``settings``) to produce the embedding and its response.
|
|
5
|
+
``model`` is the canonical model name to echo back; ``inputs`` are the request's
|
|
6
|
+
raw modality sources (materialized per-model via ``parse_inputs``).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EmbedParams(BaseModel):
|
|
15
|
+
model: str
|
|
16
|
+
inputs: dict[str, Any]
|
|
17
|
+
max_length: int
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Embedding request model — one item, one field per modality."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EmbedRequest(BaseModel):
|
|
9
|
+
model_config = ConfigDict(extra="allow")
|
|
10
|
+
|
|
11
|
+
model: str | None = Field(
|
|
12
|
+
default=None,
|
|
13
|
+
description=(
|
|
14
|
+
"Registry name, alias, or repo of the embedding model (see "
|
|
15
|
+
"`GET /v1/embedding/models`). Omit to use the default model."
|
|
16
|
+
),
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
text: str | None = Field(
|
|
20
|
+
default=None,
|
|
21
|
+
description="Text to embed. Supported by every embedding model.",
|
|
22
|
+
)
|
|
23
|
+
image: str | None = Field(
|
|
24
|
+
default=None,
|
|
25
|
+
description=(
|
|
26
|
+
"Image to embed, as a base64 string, data URL, http(s) URL, or local "
|
|
27
|
+
"path. Only multimodal models accept it; sending it to a text-only "
|
|
28
|
+
"model returns HTTP 400."
|
|
29
|
+
),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def inputs(self) -> dict[str, Any]:
|
|
33
|
+
"""Present modality inputs, keyed by modality name (skips unset ones)."""
|
|
34
|
+
out: dict[str, Any] = {}
|
|
35
|
+
if self.text is not None:
|
|
36
|
+
out["text"] = self.text
|
|
37
|
+
if self.image is not None:
|
|
38
|
+
out["image"] = self.image
|
|
39
|
+
return out
|