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,171 @@
|
|
|
1
|
+
# web
|
|
2
|
+
|
|
3
|
+
**English** | [日本語](README.ja.md)
|
|
4
|
+
|
|
5
|
+
**web capability** for using SearXNG and Crawl4AI from kiapi.
|
|
6
|
+
|
|
7
|
+
- **search**: Search the web with SearXNG and return JSON search results.
|
|
8
|
+
- **fetch**: Render a single URL with Crawl4AI and return Markdown or PDF.
|
|
9
|
+
|
|
10
|
+
The web treats SearXNG / Crawl4AI as a **resident subprocess model**.
|
|
11
|
+
Start `docker run --rm` as foreground subprocess on the first request,
|
|
12
|
+
Allocate a free localhost port and reuse it as a resident backend from now on.
|
|
13
|
+
search / fetch are also submitted to single-flight workers as Jobs.
|
|
14
|
+
|
|
15
|
+
## API
|
|
16
|
+
|
|
17
|
+
| Endpoint | Name | Description |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `POST /v1/web/search` | Web search | Call SearXNG's `/search?format=json` and return the search results. |
|
|
20
|
+
| `GET /v1/web/fetch?url=...` | Get page | Render an HTML page with Crawl4AI and return Markdown or PDF. |
|
|
21
|
+
| `GET /v1/web/openapi.json` | OpenAPI | Returns detailed input/output specifications, usage, and TIPS. |
|
|
22
|
+
|
|
23
|
+
## API Docs
|
|
24
|
+
|
|
25
|
+
- [OpenAPI JSON](https://kiarina.github.io/kiapi/v1/web/openapi.json)
|
|
26
|
+
- [Swagger UI](https://kiarina.github.io/kiapi/v1/web/docs.html)
|
|
27
|
+
- [ReDoc](https://kiarina.github.io/kiapi/v1/web/redoc.html)
|
|
28
|
+
|
|
29
|
+
## Dependencies
|
|
30
|
+
|
|
31
|
+
| Backend | License | Description |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| [SearXNG](https://github.com/searxng/searxng) | AGPL-3.0 | Meta search engine. kiapi starts as `docker run --rm` subprocess. |
|
|
34
|
+
| [Crawl4AI](https://github.com/unclecode/crawl4ai) | Apache-2.0 | Browser rendering + Markdown/PDF conversion. kiapi starts as `docker run --rm` subprocess. |
|
|
35
|
+
|
|
36
|
+
## Notes
|
|
37
|
+
|
|
38
|
+
- **resident subprocess model**:
|
|
39
|
+
SearXNG and Crawl4AI are registered as `ModelSpec(family="web")`. `load()` is
|
|
40
|
+
`docker run --rm` starts the subprocess in the foreground, and `release()` stops the subprocess.
|
|
41
|
+
- **Job/single-flight**:
|
|
42
|
+
Both search/fetch are executed as a single-flight worker as a Job.
|
|
43
|
+
- **search query**:
|
|
44
|
+
`query` is passed directly to SearXNG. `site:`, `!wp`, `!images`, `:ja` etc.
|
|
45
|
+
You can use SearXNG inline operators as is.
|
|
46
|
+
- **search result count**:
|
|
47
|
+
There is no `max_results` in SearXNG itself, so after kiapi receives one page of results.
|
|
48
|
+
Truncate client-side. Default is `KIAPI_WEB_DEFAULT_MAX_RESULTS=10`.
|
|
49
|
+
- **fetch target**:
|
|
50
|
+
`fetch` is only for HTML pages. Images, audio, videos, PDFs, and other binary URLs are
|
|
51
|
+
Do not return it as a byte string and reject it with `not_html`.
|
|
52
|
+
- **fetch empty content protection**:
|
|
53
|
+
Even if you can reach it as HTML, there is no body that can be extracted after rendering.
|
|
54
|
+
Returns `empty_content`.
|
|
55
|
+
- **fetch artifacts**:
|
|
56
|
+
The raw body of fetch is stored in the Files API, with `X-Kiapi-File-Id` in the response header.
|
|
57
|
+
It will have `X-Kiapi-Job-Id`.
|
|
58
|
+
- **Main error**:
|
|
59
|
+
validation is 422, `unsupported_accept` for `fetch` is 406, backend unreachable or
|
|
60
|
+
HTTP error is 502, timeout is 504.
|
|
61
|
+
- **Non-determinism**:
|
|
62
|
+
`search` relies on live search engine results, so the results will vary for the same query.
|
|
63
|
+
|
|
64
|
+
##Settings
|
|
65
|
+
|
|
66
|
+
| Variable | Default value | Meaning |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| `KIAPI_WEB_SEARCH_IMAGE` | `searxng/searxng:latest` | Docker image of SearXNG backend. |
|
|
69
|
+
| `KIAPI_WEB_FETCH_IMAGE` | `unclecode/crawl4ai:latest` | Docker image of Crawl4AI backend. |
|
|
70
|
+
| `KIAPI_WEB_BACKEND_READY_TIMEOUT_S` | `60.0` | Timeout waiting for backend subprocess to start. |
|
|
71
|
+
| `KIAPI_WEB_BACKEND_LOG_DIR` | `/tmp/kiapi/logs/web` | stdout/stderr log output destination of backend subprocess. |
|
|
72
|
+
| `KIAPI_WEB_TIMEOUT_S` | `10.0` | HTTP timeout to SearXNG. |
|
|
73
|
+
| `KIAPI_WEB_DEFAULT_CATEGORIES` | `null` | `categories` Default if omitted. `null` is left to SearXNG. |
|
|
74
|
+
| `KIAPI_WEB_DEFAULT_ENGINES` | `null` | `engines` Default if omitted. `null` is left to SearXNG. |
|
|
75
|
+
| `KIAPI_WEB_DEFAULT_LANGUAGE` | `null` | `language` Default if omitted. |
|
|
76
|
+
| `KIAPI_WEB_DEFAULT_SAFESEARCH` | `null` | `safesearch` Default when omitted. |
|
|
77
|
+
| `KIAPI_WEB_DEFAULT_MAX_RESULTS` | `10` | `max_results` Default client-side truncation number. |
|
|
78
|
+
| `KIAPI_WEB_FETCH_TIMEOUT_S` | `30.0` | HTTP timeout to Crawl4AI. |
|
|
79
|
+
| `KIAPI_WEB_FETCH_MIN_CONTENT_CHARS` | `1` | Markdown less than this is considered `empty_content`. |
|
|
80
|
+
| `KIAPI_WEB_FETCH_FILTER` | `fit` | Crawl4AI `/md` filter. `fit` is readability-pruned, `raw` is the entire DOM. |
|
|
81
|
+
| `KIAPI_WEB_FETCH_CACHE` | `0` | Cache specification for Crawl4AI `/md`. |
|
|
82
|
+
|
|
83
|
+
## Quickstart
|
|
84
|
+
Start kiapi in a state where you can use `docker run`, such as Docker Desktop. SearXNG and
|
|
85
|
+
Crawl4AI will be automatically started on the first request.
|
|
86
|
+
|
|
87
|
+
### search — Web search
|
|
88
|
+
```bash
|
|
89
|
+
jq -n \
|
|
90
|
+
'{
|
|
91
|
+
query: "apple",
|
|
92
|
+
max_results: 5
|
|
93
|
+
}' |
|
|
94
|
+
curl -sS -X POST http://localhost:${PORT:-8000}/v1/web/search \
|
|
95
|
+
-H 'Content-Type: application/json' \
|
|
96
|
+
--data-binary @- |
|
|
97
|
+
jq .
|
|
98
|
+
```
|
|
99
|
+
Filter by category, engine, language, newness:
|
|
100
|
+
```bash
|
|
101
|
+
jq -n \
|
|
102
|
+
'{
|
|
103
|
+
query: "mlx quantization",
|
|
104
|
+
categories: ["it"],
|
|
105
|
+
engines: ["google", "duckduckgo"],
|
|
106
|
+
language: "en",
|
|
107
|
+
time_range: "month",
|
|
108
|
+
max_results: 10
|
|
109
|
+
}' |
|
|
110
|
+
curl -sS -X POST http://localhost:${PORT:-8000}/v1/web/search \
|
|
111
|
+
-H 'Content-Type: application/json' \
|
|
112
|
+
--data-binary @- |
|
|
113
|
+
jq .
|
|
114
|
+
```
|
|
115
|
+
Use SearXNG's inline operators:
|
|
116
|
+
```bash
|
|
117
|
+
jq -n \
|
|
118
|
+
'{
|
|
119
|
+
query: "site:github.com kiapi"
|
|
120
|
+
}' |
|
|
121
|
+
curl -sS -X POST http://localhost:${PORT:-8000}/v1/web/search \
|
|
122
|
+
-H 'Content-Type: application/json' \
|
|
123
|
+
--data-binary @- |
|
|
124
|
+
jq '.results[] | {title, url}'
|
|
125
|
+
|
|
126
|
+
jq -n \
|
|
127
|
+
'{
|
|
128
|
+
query: "!wp :ja Python"
|
|
129
|
+
}' |
|
|
130
|
+
curl -sS -X POST http://localhost:${PORT:-8000}/v1/web/search \
|
|
131
|
+
-H 'Content-Type: application/json' \
|
|
132
|
+
--data-binary @- |
|
|
133
|
+
jq '.results[] | {title, url}'
|
|
134
|
+
```
|
|
135
|
+
The response includes `query`, `results`, `answers`, `infoboxes`, `suggestions`,
|
|
136
|
+
Contains `unresponsive_engines`.
|
|
137
|
+
|
|
138
|
+
### fetch — Markdown fetch
|
|
139
|
+
```bash
|
|
140
|
+
curl -sS -o page.md \
|
|
141
|
+
"http://localhost:${PORT:-8000}/v1/web/fetch?url=https://github.com/kiarina"
|
|
142
|
+
```
|
|
143
|
+
`Accept: text/markdown` is the default and can be omitted. upstream detected by kiapi
|
|
144
|
+
The content-type can be found in the `X-Kiapi-Content-Type` header.
|
|
145
|
+
```bash
|
|
146
|
+
curl -sS -D - -o page.md \
|
|
147
|
+
"http://localhost:${PORT:-8000}/v1/web/fetch?url=https://github.com/kiarina" \
|
|
148
|
+
| grep -i x-kiapi
|
|
149
|
+
```
|
|
150
|
+
### fetch — Get PDF
|
|
151
|
+
```bash
|
|
152
|
+
curl -sS -H 'Accept: application/pdf' -o page.pdf \
|
|
153
|
+
"http://localhost:${PORT:-8000}/v1/web/fetch?url=https://github.com/kiarina"
|
|
154
|
+
```
|
|
155
|
+
### fetch — Reject non-HTML URLs
|
|
156
|
+
```bash
|
|
157
|
+
curl -sS \
|
|
158
|
+
"http://localhost:${PORT:-8000}/v1/web/fetch?url=https://www-media.blazeworks.jp/content/icon/miineko.png" \
|
|
159
|
+
| jq .
|
|
160
|
+
```
|
|
161
|
+
Example response:
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"detail": {
|
|
165
|
+
"error": "not_html",
|
|
166
|
+
"message": "Resource is not an HTML page (content-type: image/png).",
|
|
167
|
+
"url": "https://www-media.blazeworks.jp/content/icon/miineko.png",
|
|
168
|
+
"content_type": "image/png"
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""web family — SearXNG search + Crawl4AI fetch as resident subprocess models.
|
|
2
|
+
|
|
3
|
+
Endpoints: ``POST /v1/web/search`` (metasearch) and ``GET /v1/web/fetch``
|
|
4
|
+
(render one page to markdown/PDF). Each backend is loaded as a foreground
|
|
5
|
+
``docker run --rm`` subprocess and held resident by the global model cache.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from ._exceptions.FetchError import FetchError
|
|
9
|
+
from ._exceptions.SearchBackendError import SearchBackendError
|
|
10
|
+
from ._helpers.register import register
|
|
11
|
+
from ._operations.handle_fetch import handle_fetch
|
|
12
|
+
from ._operations.handle_search import handle_search
|
|
13
|
+
from ._settings import settings_manager
|
|
14
|
+
from ._views.fetch_request import FetchRequest
|
|
15
|
+
from ._views.fetch_result import FetchResult
|
|
16
|
+
from ._views.search_request import SearchRequest
|
|
17
|
+
from ._views.search_response import SearchResponse
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"FetchError", # ._exceptions
|
|
21
|
+
"FetchRequest", # ._views
|
|
22
|
+
"FetchResult", # ._views
|
|
23
|
+
"SearchBackendError", # ._exceptions
|
|
24
|
+
"SearchRequest", # ._views
|
|
25
|
+
"SearchResponse", # ._views
|
|
26
|
+
"handle_fetch", # ._operations
|
|
27
|
+
"handle_search", # ._operations
|
|
28
|
+
"register", # ._helpers
|
|
29
|
+
"settings_manager", # ._settings
|
|
30
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
DESCRIPTION = """Web search + single-page fetch for LLM agents.
|
|
2
|
+
|
|
3
|
+
Use this family when an agent needs current web information or needs to read the
|
|
4
|
+
contents of a specific HTML page. Search returns structured JSON results; fetch
|
|
5
|
+
renders one page and returns Markdown by default, or PDF when requested.
|
|
6
|
+
|
|
7
|
+
## When To Use
|
|
8
|
+
- Use search when you need discovery: recent facts, unknown URLs, multiple
|
|
9
|
+
candidate sources, or comparison across sources.
|
|
10
|
+
- Use fetch when you already have a URL and need the page body in agent-friendly
|
|
11
|
+
Markdown or as a rendered PDF artifact.
|
|
12
|
+
- Search and fetch are separate steps. Search result snippets are useful for
|
|
13
|
+
triage, but fetch is better when the exact page content matters.
|
|
14
|
+
|
|
15
|
+
## Search Tips
|
|
16
|
+
- Search results are live and may change between calls.
|
|
17
|
+
- SearXNG inline operators are useful for agents: `site:`, `!wp`, `!images`,
|
|
18
|
+
`:ja`, and engine/category bangs can be placed directly in `query`.
|
|
19
|
+
- Use `categories`, `engines`, `language`, `time_range`, and `safesearch` to
|
|
20
|
+
narrow the search when the task has a clear domain.
|
|
21
|
+
- Use `max_results` to keep responses small. It truncates one returned page of
|
|
22
|
+
SearXNG results; it does not aggregate multiple pages.
|
|
23
|
+
|
|
24
|
+
## Fetch Tips
|
|
25
|
+
- Fetch is for HTML pages only. It intentionally rejects binary resources such
|
|
26
|
+
as images, videos, audio files, archives, and PDFs instead of returning their
|
|
27
|
+
raw bytes.
|
|
28
|
+
- Markdown output is best for reading and summarization. PDF output is useful
|
|
29
|
+
when layout, pagination, or visual preservation matters.
|
|
30
|
+
- Successful fetches are also stored in the Files API. Use the returned
|
|
31
|
+
`X-Kiapi-File-Id` when the artifact needs to be referenced later.
|
|
32
|
+
|
|
33
|
+
## Common Failures
|
|
34
|
+
- `not_html`: the URL points to a non-HTML resource.
|
|
35
|
+
- `empty_content`: the page loaded but produced no extractable content.
|
|
36
|
+
- `unsupported_accept`: fetch was called with an Accept header that allows
|
|
37
|
+
neither Markdown nor PDF.
|
|
38
|
+
"""
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
class FetchError(RuntimeError):
|
|
2
|
+
"""A fetch could not produce usable page content.
|
|
3
|
+
|
|
4
|
+
Carries a machine-readable ``code`` (``fetch_failed`` / ``not_html`` /
|
|
5
|
+
``empty_content``), the HTTP ``status_code`` the API layer should surface,
|
|
6
|
+
and — when the preflight probe revealed it — the upstream resource's
|
|
7
|
+
``content_type``, so the caller learns *why* (e.g. it was a ``image/png``).
|
|
8
|
+
|
|
9
|
+
Status mapping: 502/504 = backend unreachable/timed out (``fetch_failed``);
|
|
10
|
+
422 = reachable but not a usable HTML page (``not_html`` / ``empty_content``).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
code: str,
|
|
16
|
+
message: str,
|
|
17
|
+
*,
|
|
18
|
+
status_code: int,
|
|
19
|
+
content_type: str | None = None,
|
|
20
|
+
) -> None:
|
|
21
|
+
super().__init__(message)
|
|
22
|
+
self.code = code
|
|
23
|
+
self.status_code = status_code
|
|
24
|
+
self.content_type = content_type
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class SearchBackendError(RuntimeError):
|
|
2
|
+
"""The SearXNG backend was unreachable, timed out, or returned an error.
|
|
3
|
+
|
|
4
|
+
Carries the HTTP ``status_code`` the API layer should surface (502 for an
|
|
5
|
+
unreachable/erroring backend, 504 for a timeout). This is a backend fault,
|
|
6
|
+
not a client mistake — distinct from request validation (422).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, message: str, *, status_code: int) -> None:
|
|
10
|
+
super().__init__(message)
|
|
11
|
+
self.status_code = status_code
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from kiapi.core.capability import CapabilitySpec, capability_spec_registry
|
|
2
|
+
from kiapi.core.model import ModelSpec, model_registry
|
|
3
|
+
from kiapi.core.setup import DockerImageResource
|
|
4
|
+
|
|
5
|
+
from .._constants.description import DESCRIPTION
|
|
6
|
+
from .._models import crawl4ai, searxng
|
|
7
|
+
from .._settings import settings_manager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def register() -> None:
|
|
11
|
+
capability_spec_registry.register(
|
|
12
|
+
CapabilitySpec(
|
|
13
|
+
name="web",
|
|
14
|
+
domain="web",
|
|
15
|
+
title="kiapi Web API",
|
|
16
|
+
summary="Search the web and fetch pages as Markdown or PDF artifacts.",
|
|
17
|
+
description=DESCRIPTION,
|
|
18
|
+
openapi_path="/v1/web/openapi.json",
|
|
19
|
+
docs_path="/v1/web/docs",
|
|
20
|
+
redoc_path="/v1/web/redoc",
|
|
21
|
+
path_prefixes=("/v1/web",),
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
settings = settings_manager.get_settings()
|
|
26
|
+
|
|
27
|
+
model_registry.register(
|
|
28
|
+
ModelSpec(
|
|
29
|
+
name="search",
|
|
30
|
+
family="web",
|
|
31
|
+
domain="web",
|
|
32
|
+
repo=settings.search_image,
|
|
33
|
+
module=searxng,
|
|
34
|
+
weight_gb=0.5,
|
|
35
|
+
peak_headroom_gb=0.2,
|
|
36
|
+
framework="rss",
|
|
37
|
+
priority=0,
|
|
38
|
+
default=True,
|
|
39
|
+
setup_resources=(
|
|
40
|
+
DockerImageResource(image=settings.search_image, disk_gb=0.4),
|
|
41
|
+
),
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
model_registry.register(
|
|
45
|
+
ModelSpec(
|
|
46
|
+
name="fetch",
|
|
47
|
+
family="web",
|
|
48
|
+
domain="web",
|
|
49
|
+
repo=settings.fetch_image,
|
|
50
|
+
module=crawl4ai,
|
|
51
|
+
weight_gb=1.0,
|
|
52
|
+
peak_headroom_gb=1.0,
|
|
53
|
+
framework="rss",
|
|
54
|
+
priority=0,
|
|
55
|
+
setup_resources=(
|
|
56
|
+
DockerImageResource(image=settings.fetch_image, disk_gb=9.6),
|
|
57
|
+
),
|
|
58
|
+
)
|
|
59
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Resident Crawl4AI backend, launched as a foreground Docker subprocess."""
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
from kiapi.core.model import ModelSpec
|
|
6
|
+
|
|
7
|
+
from .._services.web_backend_process import WebBackendProcess
|
|
8
|
+
from .._settings import settings_manager
|
|
9
|
+
|
|
10
|
+
FEATURES = {"fetch"}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def load(spec: ModelSpec) -> WebBackendProcess:
|
|
14
|
+
settings = settings_manager.get_settings()
|
|
15
|
+
return WebBackendProcess.start(
|
|
16
|
+
name="crawl4ai",
|
|
17
|
+
image=spec.repo,
|
|
18
|
+
container_port=11235,
|
|
19
|
+
log_dir=settings.backend_log_dir,
|
|
20
|
+
ready_timeout_s=settings.backend_ready_timeout_s,
|
|
21
|
+
healthcheck=_healthcheck,
|
|
22
|
+
extra_args=["--shm-size", "1g"],
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def release(payload: WebBackendProcess) -> None:
|
|
27
|
+
payload.release()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _healthcheck(base_url: str) -> None:
|
|
31
|
+
resp = httpx.get(f"{base_url.rstrip('/')}/health", timeout=5.0)
|
|
32
|
+
resp.raise_for_status()
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Resident SearXNG backend, launched as a foreground Docker subprocess."""
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
from kiapi.core.model import ModelSpec
|
|
6
|
+
|
|
7
|
+
from .._operations.resolve_searxng_config_dir import resolve_searxng_config_dir
|
|
8
|
+
from .._services.web_backend_process import WebBackendProcess
|
|
9
|
+
from .._settings import settings_manager
|
|
10
|
+
|
|
11
|
+
FEATURES = {"search"}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def load(spec: ModelSpec) -> WebBackendProcess:
|
|
15
|
+
settings = settings_manager.get_settings()
|
|
16
|
+
searxng_dir = resolve_searxng_config_dir()
|
|
17
|
+
return WebBackendProcess.start(
|
|
18
|
+
name="searxng",
|
|
19
|
+
image=spec.repo,
|
|
20
|
+
container_port=8080,
|
|
21
|
+
log_dir=settings.backend_log_dir,
|
|
22
|
+
ready_timeout_s=settings.backend_ready_timeout_s,
|
|
23
|
+
healthcheck=_healthcheck,
|
|
24
|
+
extra_args=[
|
|
25
|
+
"-v",
|
|
26
|
+
f"{searxng_dir}:/etc/searxng:rw",
|
|
27
|
+
"-e",
|
|
28
|
+
"SEARXNG_BASE_URL=http://localhost/",
|
|
29
|
+
],
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def release(payload: WebBackendProcess) -> None:
|
|
34
|
+
payload.release()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _healthcheck(base_url: str) -> None:
|
|
38
|
+
resp = httpx.get(
|
|
39
|
+
f"{base_url.rstrip('/')}/search",
|
|
40
|
+
params={"q": "kiapi", "format": "json"},
|
|
41
|
+
timeout=5.0,
|
|
42
|
+
)
|
|
43
|
+
resp.raise_for_status()
|
|
44
|
+
resp.json()
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""web fetch service: render one page to readable markdown (or PDF) via Crawl4AI.
|
|
2
|
+
|
|
3
|
+
Flow:
|
|
4
|
+
|
|
5
|
+
1. **Preflight probe** (cheap ``HEAD``) → reachability + the upstream MIME. A
|
|
6
|
+
connection failure here is ``fetch_failed``; a non-HTML MIME is rejected as
|
|
7
|
+
``not_html`` *before* paying for a browser render.
|
|
8
|
+
2. **Render** through Crawl4AI ``/md`` (``f=fit`` -> readability-pruned
|
|
9
|
+
fit_markdown) or ``/pdf``.
|
|
10
|
+
3. **Empty guard** → a render that produced no content is ``empty_content``.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import base64
|
|
14
|
+
|
|
15
|
+
import httpx
|
|
16
|
+
|
|
17
|
+
from kiapi.core.app import AppContext
|
|
18
|
+
from kiapi.core.file import FileID
|
|
19
|
+
from kiapi.core.job import JobResult
|
|
20
|
+
from kiapi.core.model import model_registry
|
|
21
|
+
|
|
22
|
+
from .._exceptions.FetchError import FetchError
|
|
23
|
+
from .._settings import settings_manager
|
|
24
|
+
from .._views.fetch_params import FetchParams
|
|
25
|
+
from .._views.fetch_request import FetchRequest
|
|
26
|
+
from .._views.fetch_result import FetchResult
|
|
27
|
+
from .resolve_fetch_params import resolve_fetch_params
|
|
28
|
+
|
|
29
|
+
# Content types fetch treats as a renderable page. Anything else (image/audio/
|
|
30
|
+
# video/pdf/octet-stream/...) carries no extractable text and is rejected.
|
|
31
|
+
_HTML_TYPES = frozenset({"text/html", "application/xhtml+xml"})
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _probe_content_type(params: FetchParams) -> str | None:
|
|
35
|
+
"""HEAD the target for reachability + MIME; None if the server omits it."""
|
|
36
|
+
try:
|
|
37
|
+
with httpx.Client(timeout=params.timeout_s, follow_redirects=True) as client:
|
|
38
|
+
resp = client.head(params.url)
|
|
39
|
+
except httpx.HTTPError as exc:
|
|
40
|
+
raise FetchError(
|
|
41
|
+
"fetch_failed",
|
|
42
|
+
f"Could not reach {params.url}: {exc}",
|
|
43
|
+
status_code=502,
|
|
44
|
+
) from exc
|
|
45
|
+
|
|
46
|
+
ct = resp.headers.get("content-type")
|
|
47
|
+
return ct.split(";")[0].strip().lower() if ct else None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _post_crawl4ai(params: FetchParams, path: str, payload: dict) -> dict:
|
|
51
|
+
url = f"{params.base_url.rstrip('/')}{path}"
|
|
52
|
+
try:
|
|
53
|
+
with httpx.Client(timeout=params.timeout_s) as client:
|
|
54
|
+
resp = client.post(url, json=payload)
|
|
55
|
+
resp.raise_for_status()
|
|
56
|
+
data: dict = resp.json()
|
|
57
|
+
return data
|
|
58
|
+
except httpx.TimeoutException as exc:
|
|
59
|
+
raise FetchError(
|
|
60
|
+
"fetch_failed",
|
|
61
|
+
f"Crawl4AI timed out after {params.timeout_s}s",
|
|
62
|
+
status_code=504,
|
|
63
|
+
) from exc
|
|
64
|
+
except httpx.HTTPStatusError as exc:
|
|
65
|
+
raise FetchError(
|
|
66
|
+
"fetch_failed",
|
|
67
|
+
f"Crawl4AI returned HTTP {exc.response.status_code}",
|
|
68
|
+
status_code=502,
|
|
69
|
+
) from exc
|
|
70
|
+
except httpx.HTTPError as exc:
|
|
71
|
+
raise FetchError(
|
|
72
|
+
"fetch_failed",
|
|
73
|
+
f"Crawl4AI unreachable at {params.base_url}: {exc}",
|
|
74
|
+
status_code=502,
|
|
75
|
+
) from exc
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _render(params: FetchParams) -> tuple[bytes, str, bool]:
|
|
79
|
+
"""Return (body, media_type, is_empty) for the requested format."""
|
|
80
|
+
if params.format == "pdf":
|
|
81
|
+
data = _post_crawl4ai(params, "/pdf", {"url": params.url})
|
|
82
|
+
b64 = data.get("pdf") or ""
|
|
83
|
+
body = base64.b64decode(b64) if b64 else b""
|
|
84
|
+
return body, "application/pdf", len(body) == 0
|
|
85
|
+
|
|
86
|
+
payload = {"url": params.url, "f": params.filter, "c": params.cache}
|
|
87
|
+
data = _post_crawl4ai(params, "/md", payload)
|
|
88
|
+
markdown = data.get("markdown") or ""
|
|
89
|
+
is_empty = len(markdown.strip()) < params.min_content_chars
|
|
90
|
+
return markdown.encode("utf-8"), "text/markdown; charset=utf-8", is_empty
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def handle_fetch(ctx: AppContext, req: FetchRequest) -> tuple[JobResult, list[FileID]]:
|
|
94
|
+
settings = settings_manager.get_settings()
|
|
95
|
+
spec = model_registry.resolve("web", "fetch")
|
|
96
|
+
ctx.ensure_model_ready(spec)
|
|
97
|
+
backend = ctx.memory_manager.acquire(spec)
|
|
98
|
+
params = resolve_fetch_params(
|
|
99
|
+
settings,
|
|
100
|
+
req,
|
|
101
|
+
base_url=backend.base_url, # type: ignore[attr-defined]
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
content_type = _probe_content_type(params)
|
|
105
|
+
if content_type is not None and content_type not in _HTML_TYPES:
|
|
106
|
+
raise FetchError(
|
|
107
|
+
"not_html",
|
|
108
|
+
f"Resource is not an HTML page (content-type: {content_type}).",
|
|
109
|
+
status_code=422,
|
|
110
|
+
content_type=content_type,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
body, media_type, is_empty = _render(params)
|
|
114
|
+
|
|
115
|
+
if is_empty:
|
|
116
|
+
raise FetchError(
|
|
117
|
+
"empty_content",
|
|
118
|
+
"The page was fetched but produced no extractable content.",
|
|
119
|
+
status_code=422,
|
|
120
|
+
content_type=content_type,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
result = FetchResult(
|
|
124
|
+
content=body,
|
|
125
|
+
media_type=media_type,
|
|
126
|
+
url=params.url,
|
|
127
|
+
content_type=content_type,
|
|
128
|
+
)
|
|
129
|
+
suffix = ".pdf" if req.format == "pdf" else ".md"
|
|
130
|
+
rec = ctx.file_store.put_bytes(
|
|
131
|
+
result.content,
|
|
132
|
+
filename=f"fetch{suffix}",
|
|
133
|
+
content_type=result.media_type,
|
|
134
|
+
meta={"url": result.url, "content_type": result.content_type},
|
|
135
|
+
)
|
|
136
|
+
return (
|
|
137
|
+
{
|
|
138
|
+
"file_id": rec.file_id,
|
|
139
|
+
"url": result.url,
|
|
140
|
+
"content_type": result.content_type,
|
|
141
|
+
"media_type": result.media_type,
|
|
142
|
+
"bytes": rec.size,
|
|
143
|
+
},
|
|
144
|
+
[rec.file_id],
|
|
145
|
+
)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""web search service entry, run on the single worker thread."""
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
from kiapi.core.app import AppContext
|
|
6
|
+
from kiapi.core.file import FileID
|
|
7
|
+
from kiapi.core.job import JobResult
|
|
8
|
+
from kiapi.core.model import model_registry
|
|
9
|
+
|
|
10
|
+
from .._exceptions.SearchBackendError import SearchBackendError
|
|
11
|
+
from .._settings import settings_manager
|
|
12
|
+
from .._views.search_request import SearchRequest
|
|
13
|
+
from .._views.search_response import SearchResponse
|
|
14
|
+
from .resolve_search_params import resolve_search_params
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def handle_search(
|
|
18
|
+
ctx: AppContext, req: SearchRequest
|
|
19
|
+
) -> tuple[JobResult, list[FileID]]:
|
|
20
|
+
settings = settings_manager.get_settings()
|
|
21
|
+
spec = model_registry.resolve("web", "search")
|
|
22
|
+
ctx.ensure_model_ready(spec)
|
|
23
|
+
backend = ctx.memory_manager.acquire(spec)
|
|
24
|
+
params = resolve_search_params(settings, req)
|
|
25
|
+
|
|
26
|
+
url = f"{backend.base_url.rstrip('/')}/search" # type: ignore[attr-defined]
|
|
27
|
+
try:
|
|
28
|
+
with httpx.Client(timeout=settings.timeout_s) as client:
|
|
29
|
+
resp = client.get(url, params=params.query_params())
|
|
30
|
+
resp.raise_for_status()
|
|
31
|
+
data = resp.json()
|
|
32
|
+
except httpx.TimeoutException as exc:
|
|
33
|
+
raise SearchBackendError(
|
|
34
|
+
f"SearXNG timed out after {settings.timeout_s}s", status_code=504
|
|
35
|
+
) from exc
|
|
36
|
+
except httpx.HTTPStatusError as exc:
|
|
37
|
+
raise SearchBackendError(
|
|
38
|
+
f"SearXNG returned HTTP {exc.response.status_code}", status_code=502
|
|
39
|
+
) from exc
|
|
40
|
+
except httpx.HTTPError as exc:
|
|
41
|
+
raise SearchBackendError(
|
|
42
|
+
f"SearXNG unreachable at {url}: {exc}", status_code=502
|
|
43
|
+
) from exc
|
|
44
|
+
|
|
45
|
+
results = data.get("results", [])
|
|
46
|
+
if params.max_results is not None:
|
|
47
|
+
results = results[: params.max_results]
|
|
48
|
+
|
|
49
|
+
return SearchResponse(
|
|
50
|
+
query=data.get("query", req.query),
|
|
51
|
+
results=results,
|
|
52
|
+
answers=data.get("answers", []),
|
|
53
|
+
infoboxes=data.get("infoboxes", []),
|
|
54
|
+
suggestions=data.get("suggestions", []),
|
|
55
|
+
unresponsive_engines=data.get("unresponsive_engines", []),
|
|
56
|
+
).model_dump(), []
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Merge a fetch request with settings defaults into the complete FetchParams."""
|
|
2
|
+
|
|
3
|
+
from .._settings import WebSettings
|
|
4
|
+
from .._views.fetch_params import FetchParams
|
|
5
|
+
from .._views.fetch_request import FetchRequest
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def resolve_fetch_params(
|
|
9
|
+
settings: WebSettings, req: FetchRequest, *, base_url: str
|
|
10
|
+
) -> FetchParams:
|
|
11
|
+
return FetchParams(
|
|
12
|
+
url=req.url,
|
|
13
|
+
format=req.format,
|
|
14
|
+
base_url=base_url,
|
|
15
|
+
timeout_s=settings.fetch_timeout_s,
|
|
16
|
+
filter=settings.fetch_filter,
|
|
17
|
+
cache=settings.fetch_cache,
|
|
18
|
+
min_content_chars=settings.fetch_min_content_chars,
|
|
19
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Merge a search request with settings defaults into the complete SearchParams."""
|
|
2
|
+
|
|
3
|
+
from .._settings import WebSettings
|
|
4
|
+
from .._views.search_params import SearchParams
|
|
5
|
+
from .._views.search_request import SearchRequest
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _join(values: list[str] | None) -> str | None:
|
|
9
|
+
"""SearXNG takes categories/engines as a comma-separated string."""
|
|
10
|
+
return ",".join(values) if values else None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def resolve_search_params(settings: WebSettings, req: SearchRequest) -> SearchParams:
|
|
14
|
+
categories = (
|
|
15
|
+
req.categories if req.categories is not None else settings.default_categories
|
|
16
|
+
)
|
|
17
|
+
engines = req.engines if req.engines is not None else settings.default_engines
|
|
18
|
+
|
|
19
|
+
return SearchParams(
|
|
20
|
+
query=req.query,
|
|
21
|
+
categories=_join(categories),
|
|
22
|
+
engines=_join(engines),
|
|
23
|
+
language=req.language
|
|
24
|
+
if req.language is not None
|
|
25
|
+
else settings.default_language,
|
|
26
|
+
time_range=req.time_range,
|
|
27
|
+
safesearch=req.safesearch
|
|
28
|
+
if req.safesearch is not None
|
|
29
|
+
else settings.default_safesearch,
|
|
30
|
+
pageno=req.page,
|
|
31
|
+
max_results=req.max_results
|
|
32
|
+
if req.max_results is not None
|
|
33
|
+
else settings.default_max_results,
|
|
34
|
+
)
|