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.
Files changed (465) hide show
  1. kiapi/__init__.py +3 -0
  2. kiapi/__main__.py +4 -0
  3. kiapi/api/__init__.py +26 -0
  4. kiapi/api/_constants/require_auth.py +14 -0
  5. kiapi/api/_helpers/build_job_responses.py +73 -0
  6. kiapi/api/_helpers/build_openapi.py +55 -0
  7. kiapi/api/_helpers/build_train_responses.py +21 -0
  8. kiapi/api/_helpers/get_accept.py +20 -0
  9. kiapi/api/_helpers/get_ctx.py +7 -0
  10. kiapi/api/_helpers/get_worker.py +7 -0
  11. kiapi/api/_helpers/register_capability_endpoints.py +70 -0
  12. kiapi/api/_helpers/submit_and_maybe_wait.py +113 -0
  13. kiapi/api/_settings.py +50 -0
  14. kiapi/api/_views/async_job_response.py +21 -0
  15. kiapi/api/_views/capability_model_spec.py +49 -0
  16. kiapi/api/app.py +150 -0
  17. kiapi/api/audio/acestep/_views/extract_response.py +57 -0
  18. kiapi/api/audio/acestep/_views/track_response.py +57 -0
  19. kiapi/api/audio/acestep/router.py +190 -0
  20. kiapi/api/audio/audiogen/_views/audio_response.py +46 -0
  21. kiapi/api/audio/audiogen/router.py +77 -0
  22. kiapi/api/chat/_views/chat_completion_response.py +71 -0
  23. kiapi/api/chat/router.py +253 -0
  24. kiapi/api/embedding/_views/embedding_response.py +35 -0
  25. kiapi/api/embedding/router.py +82 -0
  26. kiapi/api/files/_operations/get_file.py +14 -0
  27. kiapi/api/files/_operations/get_file_id.py +18 -0
  28. kiapi/api/files/_views/file_delete_response.py +14 -0
  29. kiapi/api/files/_views/file_list_response.py +15 -0
  30. kiapi/api/files/router.py +105 -0
  31. kiapi/api/health/_views/health_response.py +26 -0
  32. kiapi/api/health/router.py +28 -0
  33. kiapi/api/image/depthpro/_views/estimate_response.py +70 -0
  34. kiapi/api/image/depthpro/router.py +81 -0
  35. kiapi/api/image/ernie/_views/image_response.py +50 -0
  36. kiapi/api/image/ernie/router.py +181 -0
  37. kiapi/api/image/flux2/_views/image_response.py +53 -0
  38. kiapi/api/image/flux2/router.py +197 -0
  39. kiapi/api/image/ideogram4/_views/image_response.py +55 -0
  40. kiapi/api/image/ideogram4/router.py +87 -0
  41. kiapi/api/image/qwen/_views/image_response.py +47 -0
  42. kiapi/api/image/qwen/router.py +141 -0
  43. kiapi/api/image/seedvr2/_views/image_response.py +50 -0
  44. kiapi/api/image/seedvr2/router.py +83 -0
  45. kiapi/api/image/zimage/_views/image_response.py +49 -0
  46. kiapi/api/image/zimage/router.py +131 -0
  47. kiapi/api/jobs/_operations/get_job_id.py +18 -0
  48. kiapi/api/jobs/_views/job_delete_response.py +19 -0
  49. kiapi/api/jobs/_views/job_list_response.py +15 -0
  50. kiapi/api/jobs/router.py +62 -0
  51. kiapi/api/models/_schemas/openai_model_spec.py +26 -0
  52. kiapi/api/models/_views/model_list_response.py +15 -0
  53. kiapi/api/models/router.py +29 -0
  54. kiapi/api/video/ltx2/_views/__init__.py +1 -0
  55. kiapi/api/video/ltx2/_views/video_response.py +59 -0
  56. kiapi/api/video/ltx2/router.py +89 -0
  57. kiapi/api/web/_views/__init__.py +7 -0
  58. kiapi/api/web/_views/fetch_api_request.py +26 -0
  59. kiapi/api/web/_views/fetch_error_response.py +25 -0
  60. kiapi/api/web/router.py +302 -0
  61. kiapi/capabilities/__init__.py +24 -0
  62. kiapi/capabilities/_exceptions/CapabilityError.py +2 -0
  63. kiapi/capabilities/_exceptions/ValidationError.py +2 -0
  64. kiapi/capabilities/_helpers/attach_mflux_progress.py +50 -0
  65. kiapi/capabilities/_helpers/get_file_path.py +20 -0
  66. kiapi/capabilities/_helpers/resolve_file_ref.py +16 -0
  67. kiapi/capabilities/acestep/README.ja.md +174 -0
  68. kiapi/capabilities/acestep/README.md +167 -0
  69. kiapi/capabilities/acestep/__init__.py +37 -0
  70. kiapi/capabilities/acestep/_constants/description.py +122 -0
  71. kiapi/capabilities/acestep/_exceptions/ace_step_engine_error.py +2 -0
  72. kiapi/capabilities/acestep/_helpers/register.py +96 -0
  73. kiapi/capabilities/acestep/_models/acestep.py +105 -0
  74. kiapi/capabilities/acestep/_operations/handle_cover.py +23 -0
  75. kiapi/capabilities/acestep/_operations/handle_extract.py +38 -0
  76. kiapi/capabilities/acestep/_operations/handle_generate.py +18 -0
  77. kiapi/capabilities/acestep/_operations/handle_repaint.py +25 -0
  78. kiapi/capabilities/acestep/_operations/resolve_ace_step_paths.py +30 -0
  79. kiapi/capabilities/acestep/_operations/resolve_cover_params.py +21 -0
  80. kiapi/capabilities/acestep/_operations/resolve_engine.py +10 -0
  81. kiapi/capabilities/acestep/_operations/resolve_extract_params.py +24 -0
  82. kiapi/capabilities/acestep/_operations/resolve_generate_params.py +18 -0
  83. kiapi/capabilities/acestep/_operations/resolve_repaint_params.py +22 -0
  84. kiapi/capabilities/acestep/_schemas/ace_step_paths.py +29 -0
  85. kiapi/capabilities/acestep/_services/ace_step_engine.py +143 -0
  86. kiapi/capabilities/acestep/_settings.py +82 -0
  87. kiapi/capabilities/acestep/_views/ace_step_base.py +61 -0
  88. kiapi/capabilities/acestep/_views/cover_params.py +26 -0
  89. kiapi/capabilities/acestep/_views/cover_request.py +49 -0
  90. kiapi/capabilities/acestep/_views/extract_params.py +24 -0
  91. kiapi/capabilities/acestep/_views/extract_request.py +33 -0
  92. kiapi/capabilities/acestep/_views/generate_params.py +25 -0
  93. kiapi/capabilities/acestep/_views/generate_request.py +54 -0
  94. kiapi/capabilities/acestep/_views/repaint_params.py +27 -0
  95. kiapi/capabilities/acestep/_views/repaint_request.py +51 -0
  96. kiapi/capabilities/acestep/_views/track_params.py +7 -0
  97. kiapi/capabilities/acestep/worker_subprocess.py +247 -0
  98. kiapi/capabilities/audiogen/README.ja.md +49 -0
  99. kiapi/capabilities/audiogen/README.md +48 -0
  100. kiapi/capabilities/audiogen/__init__.py +25 -0
  101. kiapi/capabilities/audiogen/_constants/description.py +60 -0
  102. kiapi/capabilities/audiogen/_helpers/attach_audiogen_progress.py +27 -0
  103. kiapi/capabilities/audiogen/_helpers/register.py +42 -0
  104. kiapi/capabilities/audiogen/_helpers/validate_generate.py +14 -0
  105. kiapi/capabilities/audiogen/_models/audiogen.py +98 -0
  106. kiapi/capabilities/audiogen/_operations/handle_generate.py +27 -0
  107. kiapi/capabilities/audiogen/_operations/resolve_generate_params.py +25 -0
  108. kiapi/capabilities/audiogen/_settings.py +34 -0
  109. kiapi/capabilities/audiogen/_views/generate_params.py +14 -0
  110. kiapi/capabilities/audiogen/_views/generate_request.py +96 -0
  111. kiapi/capabilities/chat/README.ja.md +822 -0
  112. kiapi/capabilities/chat/README.md +806 -0
  113. kiapi/capabilities/chat/__init__.py +25 -0
  114. kiapi/capabilities/chat/_constants/description.py +163 -0
  115. kiapi/capabilities/chat/_helpers/register.py +67 -0
  116. kiapi/capabilities/chat/_models/qwen3_5.py +183 -0
  117. kiapi/capabilities/chat/_models/qwen3_omni.py +242 -0
  118. kiapi/capabilities/chat/_operations/apply_template.py +74 -0
  119. kiapi/capabilities/chat/_operations/completed_hermes_tool_call_text.py +9 -0
  120. kiapi/capabilities/chat/_operations/emit_streaming_response.py +297 -0
  121. kiapi/capabilities/chat/_operations/ensure_streaming_detokenizer_compat.py +49 -0
  122. kiapi/capabilities/chat/_operations/format_response.py +72 -0
  123. kiapi/capabilities/chat/_operations/handle_chat.py +25 -0
  124. kiapi/capabilities/chat/_operations/parse_hermes_tool_calls.py +63 -0
  125. kiapi/capabilities/chat/_operations/parse_json_tool_calls.py +66 -0
  126. kiapi/capabilities/chat/_operations/parse_messages.py +326 -0
  127. kiapi/capabilities/chat/_operations/resolve_chat_params.py +50 -0
  128. kiapi/capabilities/chat/_operations/stream_text_from_tokens.py +32 -0
  129. kiapi/capabilities/chat/_operations/strip_tool_calls.py +15 -0
  130. kiapi/capabilities/chat/_settings.py +74 -0
  131. kiapi/capabilities/chat/_utils/apply_parallel_tool_call_policy.py +17 -0
  132. kiapi/capabilities/chat/_utils/apply_seed.py +8 -0
  133. kiapi/capabilities/chat/_utils/load_audio_mono.py +31 -0
  134. kiapi/capabilities/chat/_utils/load_mlx_vlm.py +12 -0
  135. kiapi/capabilities/chat/_utils/warmup_params.py +26 -0
  136. kiapi/capabilities/chat/_views/chat_params.py +34 -0
  137. kiapi/capabilities/chat/_views/chat_request.py +153 -0
  138. kiapi/capabilities/depthpro/README.ja.md +131 -0
  139. kiapi/capabilities/depthpro/README.md +124 -0
  140. kiapi/capabilities/depthpro/__init__.py +23 -0
  141. kiapi/capabilities/depthpro/_constants/description.py +39 -0
  142. kiapi/capabilities/depthpro/_helpers/register.py +46 -0
  143. kiapi/capabilities/depthpro/_helpers/validate_estimate.py +32 -0
  144. kiapi/capabilities/depthpro/_models/depthpro.py +144 -0
  145. kiapi/capabilities/depthpro/_operations/handle_estimate.py +50 -0
  146. kiapi/capabilities/depthpro/_operations/is_quantize_override.py +12 -0
  147. kiapi/capabilities/depthpro/_operations/resolve_estimate_params.py +24 -0
  148. kiapi/capabilities/depthpro/_settings.py +46 -0
  149. kiapi/capabilities/depthpro/_views/estimate_params.py +21 -0
  150. kiapi/capabilities/depthpro/_views/estimate_request.py +63 -0
  151. kiapi/capabilities/embedding/README.ja.md +137 -0
  152. kiapi/capabilities/embedding/README.md +128 -0
  153. kiapi/capabilities/embedding/__init__.py +24 -0
  154. kiapi/capabilities/embedding/_constants/description.py +47 -0
  155. kiapi/capabilities/embedding/_helpers/register.py +66 -0
  156. kiapi/capabilities/embedding/_models/qwen3_text.py +53 -0
  157. kiapi/capabilities/embedding/_models/qwen3_vl.py +97 -0
  158. kiapi/capabilities/embedding/_operations/format_response.py +24 -0
  159. kiapi/capabilities/embedding/_operations/handle_embed.py +24 -0
  160. kiapi/capabilities/embedding/_operations/parse_inputs.py +108 -0
  161. kiapi/capabilities/embedding/_operations/resolve_embed_params.py +18 -0
  162. kiapi/capabilities/embedding/_settings.py +27 -0
  163. kiapi/capabilities/embedding/_utils/to_vector.py +20 -0
  164. kiapi/capabilities/embedding/_views/embed_params.py +17 -0
  165. kiapi/capabilities/embedding/_views/embed_request.py +39 -0
  166. kiapi/capabilities/ernie/README.ja.md +242 -0
  167. kiapi/capabilities/ernie/README.md +223 -0
  168. kiapi/capabilities/ernie/__init__.py +34 -0
  169. kiapi/capabilities/ernie/_constants/description.py +71 -0
  170. kiapi/capabilities/ernie/_helpers/register.py +60 -0
  171. kiapi/capabilities/ernie/_helpers/validate_edit.py +19 -0
  172. kiapi/capabilities/ernie/_helpers/validate_generate.py +8 -0
  173. kiapi/capabilities/ernie/_models/ernie.py +356 -0
  174. kiapi/capabilities/ernie/_operations/handle_edit.py +39 -0
  175. kiapi/capabilities/ernie/_operations/handle_generate.py +38 -0
  176. kiapi/capabilities/ernie/_operations/handle_train.py +77 -0
  177. kiapi/capabilities/ernie/_operations/is_quantize_override.py +16 -0
  178. kiapi/capabilities/ernie/_operations/resolve_edit_params.py +44 -0
  179. kiapi/capabilities/ernie/_operations/resolve_generate_params.py +40 -0
  180. kiapi/capabilities/ernie/_operations/resolve_lora_params.py +23 -0
  181. kiapi/capabilities/ernie/_operations/resolve_train_params.py +43 -0
  182. kiapi/capabilities/ernie/_operations/validate_common.py +33 -0
  183. kiapi/capabilities/ernie/_schemas/lora_ref.py +27 -0
  184. kiapi/capabilities/ernie/_settings.py +151 -0
  185. kiapi/capabilities/ernie/_views/edit_params.py +36 -0
  186. kiapi/capabilities/ernie/_views/edit_request.py +142 -0
  187. kiapi/capabilities/ernie/_views/generate_params.py +32 -0
  188. kiapi/capabilities/ernie/_views/generate_request.py +127 -0
  189. kiapi/capabilities/ernie/_views/train_params.py +28 -0
  190. kiapi/capabilities/ernie/_views/train_request.py +96 -0
  191. kiapi/capabilities/flux2/README.ja.md +260 -0
  192. kiapi/capabilities/flux2/README.md +241 -0
  193. kiapi/capabilities/flux2/__init__.py +36 -0
  194. kiapi/capabilities/flux2/_constants/description.py +81 -0
  195. kiapi/capabilities/flux2/_helpers/register.py +98 -0
  196. kiapi/capabilities/flux2/_helpers/validate_edit.py +12 -0
  197. kiapi/capabilities/flux2/_helpers/validate_generate.py +8 -0
  198. kiapi/capabilities/flux2/_models/flux2.py +396 -0
  199. kiapi/capabilities/flux2/_operations/handle_edit.py +44 -0
  200. kiapi/capabilities/flux2/_operations/handle_generate.py +46 -0
  201. kiapi/capabilities/flux2/_operations/handle_train.py +105 -0
  202. kiapi/capabilities/flux2/_operations/is_quantize_override.py +16 -0
  203. kiapi/capabilities/flux2/_operations/resolve_edit_params.py +43 -0
  204. kiapi/capabilities/flux2/_operations/resolve_generate_params.py +44 -0
  205. kiapi/capabilities/flux2/_operations/resolve_lora_params.py +23 -0
  206. kiapi/capabilities/flux2/_operations/resolve_train_params.py +42 -0
  207. kiapi/capabilities/flux2/_operations/role_spec.py +13 -0
  208. kiapi/capabilities/flux2/_operations/split_repo.py +11 -0
  209. kiapi/capabilities/flux2/_operations/validate_common.py +35 -0
  210. kiapi/capabilities/flux2/_schemas/lora_ref.py +27 -0
  211. kiapi/capabilities/flux2/_settings.py +181 -0
  212. kiapi/capabilities/flux2/_views/edit_params.py +36 -0
  213. kiapi/capabilities/flux2/_views/edit_request.py +145 -0
  214. kiapi/capabilities/flux2/_views/generate_params.py +36 -0
  215. kiapi/capabilities/flux2/_views/generate_request.py +150 -0
  216. kiapi/capabilities/flux2/_views/train_params.py +32 -0
  217. kiapi/capabilities/flux2/_views/train_request.py +102 -0
  218. kiapi/capabilities/ideogram4/README.ja.md +230 -0
  219. kiapi/capabilities/ideogram4/README.md +219 -0
  220. kiapi/capabilities/ideogram4/__init__.py +24 -0
  221. kiapi/capabilities/ideogram4/_constants/description.py +65 -0
  222. kiapi/capabilities/ideogram4/_helpers/register.py +44 -0
  223. kiapi/capabilities/ideogram4/_helpers/validate_generate.py +31 -0
  224. kiapi/capabilities/ideogram4/_models/ideogram4.py +120 -0
  225. kiapi/capabilities/ideogram4/_operations/handle_generate.py +30 -0
  226. kiapi/capabilities/ideogram4/_operations/is_quantize_override.py +12 -0
  227. kiapi/capabilities/ideogram4/_operations/resolve_generate_params.py +30 -0
  228. kiapi/capabilities/ideogram4/_settings.py +89 -0
  229. kiapi/capabilities/ideogram4/_views/generate_params.py +27 -0
  230. kiapi/capabilities/ideogram4/_views/generate_request.py +118 -0
  231. kiapi/capabilities/ltx2/README.ja.md +287 -0
  232. kiapi/capabilities/ltx2/README.md +274 -0
  233. kiapi/capabilities/ltx2/__init__.py +26 -0
  234. kiapi/capabilities/ltx2/_constants/description.py +81 -0
  235. kiapi/capabilities/ltx2/_helpers/register.py +58 -0
  236. kiapi/capabilities/ltx2/_helpers/validate_generate.py +42 -0
  237. kiapi/capabilities/ltx2/_models/ltx2.py +106 -0
  238. kiapi/capabilities/ltx2/_operations/detect_mode.py +19 -0
  239. kiapi/capabilities/ltx2/_operations/handle_generate.py +52 -0
  240. kiapi/capabilities/ltx2/_operations/resolve_generate_params.py +31 -0
  241. kiapi/capabilities/ltx2/_settings.py +93 -0
  242. kiapi/capabilities/ltx2/_views/generate_params.py +34 -0
  243. kiapi/capabilities/ltx2/_views/generate_request.py +151 -0
  244. kiapi/capabilities/qwen/README.ja.md +251 -0
  245. kiapi/capabilities/qwen/README.md +235 -0
  246. kiapi/capabilities/qwen/__init__.py +30 -0
  247. kiapi/capabilities/qwen/_constants/description.py +53 -0
  248. kiapi/capabilities/qwen/_helpers/register.py +60 -0
  249. kiapi/capabilities/qwen/_helpers/validate_edit.py +12 -0
  250. kiapi/capabilities/qwen/_helpers/validate_generate.py +12 -0
  251. kiapi/capabilities/qwen/_models/qwen.py +224 -0
  252. kiapi/capabilities/qwen/_operations/handle_edit.py +44 -0
  253. kiapi/capabilities/qwen/_operations/handle_generate.py +46 -0
  254. kiapi/capabilities/qwen/_operations/is_quantize_override.py +15 -0
  255. kiapi/capabilities/qwen/_operations/resolve_edit_params.py +35 -0
  256. kiapi/capabilities/qwen/_operations/resolve_generate_params.py +39 -0
  257. kiapi/capabilities/qwen/_operations/resolve_lora_params.py +23 -0
  258. kiapi/capabilities/qwen/_operations/validate_common.py +32 -0
  259. kiapi/capabilities/qwen/_schemas/lora_ref.py +27 -0
  260. kiapi/capabilities/qwen/_settings.py +107 -0
  261. kiapi/capabilities/qwen/_views/edit_params.py +36 -0
  262. kiapi/capabilities/qwen/_views/edit_request.py +138 -0
  263. kiapi/capabilities/qwen/_views/generate_params.py +37 -0
  264. kiapi/capabilities/qwen/_views/generate_request.py +149 -0
  265. kiapi/capabilities/seedvr2/README.ja.md +234 -0
  266. kiapi/capabilities/seedvr2/README.md +219 -0
  267. kiapi/capabilities/seedvr2/__init__.py +24 -0
  268. kiapi/capabilities/seedvr2/_constants/description.py +46 -0
  269. kiapi/capabilities/seedvr2/_helpers/register.py +60 -0
  270. kiapi/capabilities/seedvr2/_helpers/validate_upscale.py +52 -0
  271. kiapi/capabilities/seedvr2/_models/seedvr2.py +140 -0
  272. kiapi/capabilities/seedvr2/_operations/handle_upscale.py +42 -0
  273. kiapi/capabilities/seedvr2/_operations/is_quantize_override.py +12 -0
  274. kiapi/capabilities/seedvr2/_operations/resolve_upscale_params.py +30 -0
  275. kiapi/capabilities/seedvr2/_settings.py +74 -0
  276. kiapi/capabilities/seedvr2/_views/upscale_params.py +27 -0
  277. kiapi/capabilities/seedvr2/_views/upscale_request.py +94 -0
  278. kiapi/capabilities/web/README.ja.md +187 -0
  279. kiapi/capabilities/web/README.md +171 -0
  280. kiapi/capabilities/web/__init__.py +30 -0
  281. kiapi/capabilities/web/_constants/description.py +38 -0
  282. kiapi/capabilities/web/_exceptions/FetchError.py +24 -0
  283. kiapi/capabilities/web/_exceptions/SearchBackendError.py +11 -0
  284. kiapi/capabilities/web/_helpers/register.py +59 -0
  285. kiapi/capabilities/web/_models/crawl4ai.py +32 -0
  286. kiapi/capabilities/web/_models/searxng.py +44 -0
  287. kiapi/capabilities/web/_operations/handle_fetch.py +145 -0
  288. kiapi/capabilities/web/_operations/handle_search.py +56 -0
  289. kiapi/capabilities/web/_operations/resolve_fetch_params.py +19 -0
  290. kiapi/capabilities/web/_operations/resolve_search_params.py +34 -0
  291. kiapi/capabilities/web/_operations/resolve_searxng_config_dir.py +21 -0
  292. kiapi/capabilities/web/_services/__init__.py +1 -0
  293. kiapi/capabilities/web/_services/web_backend_process.py +144 -0
  294. kiapi/capabilities/web/_settings.py +138 -0
  295. kiapi/capabilities/web/_views/fetch_params.py +27 -0
  296. kiapi/capabilities/web/_views/fetch_request.py +29 -0
  297. kiapi/capabilities/web/_views/fetch_result.py +20 -0
  298. kiapi/capabilities/web/_views/search_params.py +43 -0
  299. kiapi/capabilities/web/_views/search_request.py +93 -0
  300. kiapi/capabilities/web/_views/search_response.py +49 -0
  301. kiapi/capabilities/web/resources/__init__.py +0 -0
  302. kiapi/capabilities/web/resources/searxng/__init__.py +0 -0
  303. kiapi/capabilities/web/resources/searxng/settings.yml +11 -0
  304. kiapi/capabilities/zimage/README.ja.md +249 -0
  305. kiapi/capabilities/zimage/README.md +231 -0
  306. kiapi/capabilities/zimage/__init__.py +34 -0
  307. kiapi/capabilities/zimage/_constants/description.py +73 -0
  308. kiapi/capabilities/zimage/_helpers/register.py +62 -0
  309. kiapi/capabilities/zimage/_helpers/validate_generate.py +37 -0
  310. kiapi/capabilities/zimage/_models/zimage.py +278 -0
  311. kiapi/capabilities/zimage/_operations/handle_generate.py +38 -0
  312. kiapi/capabilities/zimage/_operations/handle_train.py +78 -0
  313. kiapi/capabilities/zimage/_operations/is_quantize_override.py +11 -0
  314. kiapi/capabilities/zimage/_operations/resolve_generate_params.py +39 -0
  315. kiapi/capabilities/zimage/_operations/resolve_lora_params.py +23 -0
  316. kiapi/capabilities/zimage/_operations/resolve_train_params.py +38 -0
  317. kiapi/capabilities/zimage/_schemas/lora_ref.py +27 -0
  318. kiapi/capabilities/zimage/_settings.py +166 -0
  319. kiapi/capabilities/zimage/_views/generate_params.py +23 -0
  320. kiapi/capabilities/zimage/_views/generate_request.py +127 -0
  321. kiapi/capabilities/zimage/_views/train_params.py +22 -0
  322. kiapi/capabilities/zimage/_views/train_request.py +101 -0
  323. kiapi/cli/__init__.py +11 -0
  324. kiapi/cli/_helpers/dedupe_resources.py +12 -0
  325. kiapi/cli/_helpers/register_all_capabilities.py +36 -0
  326. kiapi/cli/_helpers/run_resources.py +24 -0
  327. kiapi/cli/_helpers/select_specs.py +67 -0
  328. kiapi/cli/activate/_operations/preflight_hf_access.py +129 -0
  329. kiapi/cli/activate/cli.py +37 -0
  330. kiapi/cli/check/_helpers/check_audio_acestep_turbo.py +22 -0
  331. kiapi/cli/check/_helpers/check_audio_acestep_xl_base.py +22 -0
  332. kiapi/cli/check/_helpers/check_audio_audiogen_medium.py +24 -0
  333. kiapi/cli/check/_helpers/check_chat_chat_qwen3_6_27b.py +19 -0
  334. kiapi/cli/check/_helpers/check_chat_chat_qwen3_omni.py +19 -0
  335. kiapi/cli/check/_helpers/check_embedding_embedding_qwen3_embedding_8b.py +14 -0
  336. kiapi/cli/check/_helpers/check_embedding_embedding_qwen3_vl_embedding_2b.py +19 -0
  337. kiapi/cli/check/_helpers/check_image_depthpro_base.py +21 -0
  338. kiapi/cli/check/_helpers/check_image_ernie_base.py +23 -0
  339. kiapi/cli/check/_helpers/check_image_ernie_turbo.py +23 -0
  340. kiapi/cli/check/_helpers/check_image_flux2_klein_9b.py +23 -0
  341. kiapi/cli/check/_helpers/check_image_flux2_klein_base_4b.py +23 -0
  342. kiapi/cli/check/_helpers/check_image_flux2_klein_base_9b.py +23 -0
  343. kiapi/cli/check/_helpers/check_image_ideogram4_fp8.py +23 -0
  344. kiapi/cli/check/_helpers/check_image_qwen_edit_2509.py +27 -0
  345. kiapi/cli/check/_helpers/check_image_qwen_image.py +23 -0
  346. kiapi/cli/check/_helpers/check_image_seedvr2_3b.py +23 -0
  347. kiapi/cli/check/_helpers/check_image_seedvr2_7b.py +23 -0
  348. kiapi/cli/check/_helpers/check_image_zimage_base.py +23 -0
  349. kiapi/cli/check/_helpers/check_image_zimage_turbo.py +23 -0
  350. kiapi/cli/check/_helpers/check_video_ltx2_distilled.py +26 -0
  351. kiapi/cli/check/_helpers/check_web_web_fetch.py +14 -0
  352. kiapi/cli/check/_helpers/check_web_web_search.py +14 -0
  353. kiapi/cli/check/_operations/build_check_result.py +20 -0
  354. kiapi/cli/check/_operations/check_context.py +10 -0
  355. kiapi/cli/check/_operations/create_sample_image.py +46 -0
  356. kiapi/cli/check/_schemas/check_result.py +12 -0
  357. kiapi/cli/check/cli.py +89 -0
  358. kiapi/cli/cli.py +29 -0
  359. kiapi/cli/config/cli.py +17 -0
  360. kiapi/cli/config/edit/cli.py +32 -0
  361. kiapi/cli/config/init/cli.py +33 -0
  362. kiapi/cli/config/show/cli.py +13 -0
  363. kiapi/cli/config/template/cli.py +36 -0
  364. kiapi/cli/deactivate/cli.py +49 -0
  365. kiapi/cli/run/asgi.py +16 -0
  366. kiapi/cli/run/cli.py +59 -0
  367. kiapi/cli/service/__init__.py +5 -0
  368. kiapi/cli/service/_services/service_manager.py +107 -0
  369. kiapi/cli/service/cli.py +19 -0
  370. kiapi/cli/service/install/cli.py +17 -0
  371. kiapi/cli/service/start/cli.py +22 -0
  372. kiapi/cli/service/status/cli.py +81 -0
  373. kiapi/cli/service/stop/cli.py +19 -0
  374. kiapi/cli/service/uninstall/cli.py +16 -0
  375. kiapi/cli/status/cli.py +226 -0
  376. kiapi/core/app/__init__.py +16 -0
  377. kiapi/core/app/_schemas/app_context.py +28 -0
  378. kiapi/core/app/_services/user_directory.py +59 -0
  379. kiapi/core/app/_settings.py +46 -0
  380. kiapi/core/capability/__init__.py +11 -0
  381. kiapi/core/capability/_schemas/capability_spec.py +17 -0
  382. kiapi/core/capability/_services/capability_spec_registry.py +21 -0
  383. kiapi/core/config/__init__.py +11 -0
  384. kiapi/core/config/_exceptions/user_config_error.py +2 -0
  385. kiapi/core/config/_helpers/get_user_settings_path.py +7 -0
  386. kiapi/core/config/_helpers/load_user_settings.py +54 -0
  387. kiapi/core/file/__init__.py +37 -0
  388. kiapi/core/file/_helpers/create_file_store.py +7 -0
  389. kiapi/core/file/_helpers/resolve_file_ref.py +90 -0
  390. kiapi/core/file/_schemas/file_data_url_ref.py +19 -0
  391. kiapi/core/file/_schemas/file_id_ref.py +16 -0
  392. kiapi/core/file/_schemas/file_record.py +46 -0
  393. kiapi/core/file/_schemas/file_url_ref.py +16 -0
  394. kiapi/core/file/_services/file_store.py +138 -0
  395. kiapi/core/file/_settings.py +22 -0
  396. kiapi/core/file/_types/file_id.py +2 -0
  397. kiapi/core/file/_types/file_ref.py +12 -0
  398. kiapi/core/file/_views/resolved_file_ref.py +16 -0
  399. kiapi/core/job/__init__.py +24 -0
  400. kiapi/core/job/_enums/job_status.py +9 -0
  401. kiapi/core/job/_helpers/creep_progress.py +123 -0
  402. kiapi/core/job/_models/job.py +121 -0
  403. kiapi/core/job/_services/job_store.py +33 -0
  404. kiapi/core/job/_services/progress_reporter.py +79 -0
  405. kiapi/core/job/_types/job_id.py +3 -0
  406. kiapi/core/job/_types/job_result.py +5 -0
  407. kiapi/core/job/_types/job_type.py +4 -0
  408. kiapi/core/logging/__init__.py +14 -0
  409. kiapi/core/logging/_helpers/get_log_level.py +8 -0
  410. kiapi/core/logging/_helpers/setup_logger.py +14 -0
  411. kiapi/core/logging/_settings.py +34 -0
  412. kiapi/core/logging/_types/log_level.py +3 -0
  413. kiapi/core/memory/__init__.py +21 -0
  414. kiapi/core/memory/_exceptions/memory_budget_error.py +2 -0
  415. kiapi/core/memory/_helpers/create_memory_manager.py +17 -0
  416. kiapi/core/memory/_operations/log_memory_limit.py +29 -0
  417. kiapi/core/memory/_operations/resolve_effective_memory_limit_gb.py +13 -0
  418. kiapi/core/memory/_operations/resolve_framework_strategy.py +42 -0
  419. kiapi/core/memory/_schemas/framework_strategy.py +14 -0
  420. kiapi/core/memory/_schemas/memory_stats.py +24 -0
  421. kiapi/core/memory/_schemas/resident_model.py +14 -0
  422. kiapi/core/memory/_schemas/resident_model_stats.py +49 -0
  423. kiapi/core/memory/_services/memory_manager.py +257 -0
  424. kiapi/core/memory/_settings.py +36 -0
  425. kiapi/core/memory/_types/framework_name.py +4 -0
  426. kiapi/core/memory/_types/resident_key.py +4 -0
  427. kiapi/core/model/__init__.py +36 -0
  428. kiapi/core/model/_exceptions/unknown_model_error.py +2 -0
  429. kiapi/core/model/_schemas/model_spec.py +145 -0
  430. kiapi/core/model/_services/model_registry.py +85 -0
  431. kiapi/core/model/_types/model_alias.py +2 -0
  432. kiapi/core/model/_types/model_domain.py +6 -0
  433. kiapi/core/model/_types/model_family.py +5 -0
  434. kiapi/core/model/_types/model_key.py +8 -0
  435. kiapi/core/model/_types/model_name.py +4 -0
  436. kiapi/core/model/_types/model_repo.py +3 -0
  437. kiapi/core/net/__init__.py +16 -0
  438. kiapi/core/net/_exceptions/unsafe_url_error.py +7 -0
  439. kiapi/core/net/_helpers/verify_public_url.py +68 -0
  440. kiapi/core/net/_settings.py +28 -0
  441. kiapi/core/setup/__init__.py +25 -0
  442. kiapi/core/setup/_exceptions/setup_required_error.py +2 -0
  443. kiapi/core/setup/_schemas/docker_image_resource.py +37 -0
  444. kiapi/core/setup/_schemas/hf_snapshot_resource.py +50 -0
  445. kiapi/core/setup/_schemas/local_path_resource.py +43 -0
  446. kiapi/core/setup/_schemas/python_package_resource.py +58 -0
  447. kiapi/core/setup/_schemas/python_venv_resource.py +63 -0
  448. kiapi/core/setup/_schemas/setup_status.py +7 -0
  449. kiapi/core/setup/_schemas/url_file_resource.py +43 -0
  450. kiapi/core/setup/_services/setup_manager.py +331 -0
  451. kiapi/core/setup/_types/setup_resource.py +21 -0
  452. kiapi/core/setup/_types/setup_target.py +11 -0
  453. kiapi/core/workdir/__init__.py +8 -0
  454. kiapi/core/workdir/_helpers/create_work_dir.py +11 -0
  455. kiapi/core/workdir/_settings.py +21 -0
  456. kiapi/core/worker/__init__.py +23 -0
  457. kiapi/core/worker/_helpers/create_worker.py +9 -0
  458. kiapi/core/worker/_services/worker.py +165 -0
  459. kiapi/core/worker/_settings.py +36 -0
  460. kiapi/core/worker/_types/job_thunk.py +6 -0
  461. kiapi-0.1.0.dist-info/METADATA +337 -0
  462. kiapi-0.1.0.dist-info/RECORD +465 -0
  463. kiapi-0.1.0.dist-info/WHEEL +4 -0
  464. kiapi-0.1.0.dist-info/entry_points.txt +2 -0
  465. kiapi-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,62 @@
1
+ """Jobs API: list / get / delete. Every generation (sync or async) is a job."""
2
+
3
+ from fastapi import APIRouter, Depends, HTTPException
4
+
5
+ from kiapi.api import REQUIRE_AUTH, get_ctx
6
+ from kiapi.core.app import AppContext
7
+ from kiapi.core.job import Job, JobID, JobStatus
8
+
9
+ from ._operations.get_job_id import get_job_id
10
+ from ._views.job_delete_response import JobDeleteResponse
11
+ from ._views.job_list_response import JobListResponse
12
+
13
+ router = APIRouter(prefix="/v1/jobs", dependencies=REQUIRE_AUTH)
14
+
15
+
16
+ @router.get("", response_model=JobListResponse)
17
+ async def list_jobs(ctx: AppContext = Depends(get_ctx)) -> dict:
18
+ """List in-memory jobs.
19
+
20
+ Every generation request creates a job, including sync requests. Jobs are
21
+ useful for progress polling, result inspection, and discovering artifact
22
+ file_ids. They are not persisted across process restarts.
23
+ """
24
+ return {"object": "list", "data": [j.to_dict() for j in ctx.job_store.list_all()]}
25
+
26
+
27
+ @router.get("/{job_id}", response_model=Job)
28
+ async def get_job(
29
+ job_id: JobID = Depends(get_job_id),
30
+ ctx: AppContext = Depends(get_ctx),
31
+ ) -> dict:
32
+ """Get a job's status, progress, result, and artifacts.
33
+
34
+ Poll this endpoint after an async response or after a sync request times
35
+ out. When status is succeeded, artifact file ids can be downloaded through
36
+ the Files API.
37
+ """
38
+ job = ctx.job_store.get(job_id)
39
+ if job is None:
40
+ raise HTTPException(status_code=404, detail=f"unknown job {job_id!r}")
41
+ return job.to_dict()
42
+
43
+
44
+ @router.delete("/{job_id}", response_model=JobDeleteResponse)
45
+ async def delete_job(
46
+ job_id: JobID = Depends(get_job_id),
47
+ ctx: AppContext = Depends(get_ctx),
48
+ ) -> dict:
49
+ """Remove a job record from the in-memory store.
50
+
51
+ Queued jobs are forgotten and effectively canceled. A running job cannot be
52
+ interrupted; deleting it only removes the record, and generated files are
53
+ managed separately by the Files API.
54
+ """
55
+ job = ctx.job_store.get(job_id)
56
+ if job is None:
57
+ raise HTTPException(status_code=404, detail=f"unknown job {job_id!r}")
58
+ # A running job cannot be interrupted (MLX/generation is not preemptible);
59
+ # deleting it just forgets it. Queued jobs are effectively canceled.
60
+ running = job.status == JobStatus.RUNNING
61
+ ctx.job_store.delete(job_id)
62
+ return {"deleted": True, "job_id": job_id, "was_running": running}
@@ -0,0 +1,26 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+
4
+ class OpenAIModelSpec(BaseModel):
5
+ """An OpenAI-compatible model spec (one entry in a model list)."""
6
+
7
+ id: str = Field(
8
+ ...,
9
+ description="Model name to pass in the chat request model field.",
10
+ examples=["qwen3-omni"],
11
+ )
12
+ object: str = Field(
13
+ default="model",
14
+ description="OpenAI-compatible object marker.",
15
+ examples=["model"],
16
+ )
17
+ created: int = Field(
18
+ ...,
19
+ description="Unix timestamp (seconds) for when the model became available.",
20
+ examples=[1735689600],
21
+ )
22
+ owned_by: str = Field(
23
+ default="kiapi",
24
+ description="Owner marker for OpenAI-compatible model lists.",
25
+ examples=["kiapi"],
26
+ )
@@ -0,0 +1,15 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+ from .._schemas.openai_model_spec import OpenAIModelSpec
4
+
5
+
6
+ class ModelListResponse(BaseModel):
7
+ object: str = Field(
8
+ default="list",
9
+ description="OpenAI-style list envelope marker.",
10
+ examples=["list"],
11
+ )
12
+ data: list[OpenAIModelSpec] = Field(
13
+ default_factory=list,
14
+ description="Chat models available via the OpenAI-compatible /v1/models endpoint.",
15
+ )
@@ -0,0 +1,29 @@
1
+ import time
2
+
3
+ from fastapi import APIRouter
4
+
5
+ from kiapi.core.model import model_registry
6
+
7
+ from ._schemas.openai_model_spec import OpenAIModelSpec
8
+ from ._views.model_list_response import ModelListResponse
9
+
10
+ router = APIRouter()
11
+
12
+ # Stable per-process timestamp used for the OpenAI-compatible ``created`` field;
13
+ # kiapi models don't carry a real creation date, so we report server start.
14
+ _CREATED = int(time.time())
15
+
16
+
17
+ @router.get("/v1/models", response_model=ModelListResponse)
18
+ async def list_openai_compatible_chat_models() -> ModelListResponse:
19
+ """List OpenAI-compatible chat models.
20
+
21
+ Returns the OpenAI /v1/models shape so OpenAI chat clients work unchanged.
22
+ Other capabilities expose richer, family-specific model lists at
23
+ /v1/{domain}/{family}/models.
24
+ """
25
+ data = [
26
+ OpenAIModelSpec(id=spec.name, created=_CREATED)
27
+ for spec in model_registry.list_specs("chat")
28
+ ]
29
+ return ModelListResponse(data=data)
@@ -0,0 +1 @@
1
+ """API response views for the LTX-2 video endpoints."""
@@ -0,0 +1,59 @@
1
+ """``/v1/video/ltx2/generate`` result payload (the Job ``result`` shape).
2
+
3
+ Mirrors the dict returned by ``capabilities/ltx2/_models/ltx2.run_generate`` so
4
+ the MP4 result is self-describing in OpenAPI. It is an output-side projection of
5
+ the model result, so it lives under ``api/video/ltx2/_views``.
6
+ """
7
+
8
+ from typing import Any, Literal
9
+
10
+ from pydantic import BaseModel, Field
11
+
12
+
13
+ class _Timings(BaseModel):
14
+ total_s: float = Field(
15
+ description="Wall-clock generation time in seconds (model run only)."
16
+ )
17
+
18
+
19
+ class VideoResponse(BaseModel):
20
+ """Capability-specific ``result`` for a succeeded LTX-2 generation job."""
21
+
22
+ file_id: str = Field(
23
+ description=(
24
+ "Files-API id of the produced MP4. Fetch metadata at GET /v1/files/{id} "
25
+ "or bytes at /download. This is also the artifact returned as raw "
26
+ "bytes by a single-artifact sync call."
27
+ )
28
+ )
29
+ video_bytes: int = Field(description="Size of the produced MP4 in bytes.")
30
+
31
+ mode: Literal[
32
+ "T2V",
33
+ "I2V",
34
+ "I2V(first+last)",
35
+ "I2V(last)",
36
+ "A2V",
37
+ "A2V+I2V",
38
+ "T2V+Audio",
39
+ ] = Field(
40
+ description=(
41
+ "Detected generation mode, inferred from image/end_image/audio inputs "
42
+ "and `generate_audio`."
43
+ )
44
+ )
45
+ prompt: str = Field(description="Prompt used for the generation.")
46
+ params: dict[str, Any] = Field(
47
+ description=(
48
+ "Resolved parameters actually used for the run (prompt, dimensions, "
49
+ "frame count, fps, seed, conditioning strengths, and generate_audio), "
50
+ "so the result is reproducible."
51
+ )
52
+ )
53
+ has_audio: bool = Field(
54
+ description=(
55
+ "Whether an auxiliary audio track was produced or supplied and muxed "
56
+ "into the MP4."
57
+ )
58
+ )
59
+ timings: _Timings = Field(description="kiapi extension: server-side timing.")
@@ -0,0 +1,89 @@
1
+ """Video API (ltx2): ``POST /v1/video/ltx2/generate`` (JSON; sync or async)."""
2
+
3
+ from fastapi import APIRouter, Depends, HTTPException
4
+ from fastapi.responses import Response
5
+
6
+ from kiapi.api import (
7
+ REQUIRE_AUTH,
8
+ build_job_responses,
9
+ get_accept,
10
+ get_ctx,
11
+ get_worker,
12
+ register_capability_endpoints,
13
+ submit_and_maybe_wait,
14
+ )
15
+ from kiapi.capabilities.ltx2 import (
16
+ GenerateRequest,
17
+ ValidationError,
18
+ detect_mode,
19
+ handle_generate,
20
+ validate_generate,
21
+ )
22
+ from kiapi.core.app import AppContext
23
+ from kiapi.core.model import UnknownModelError, model_registry
24
+ from kiapi.core.worker import Worker
25
+
26
+ from ._views.video_response import VideoResponse
27
+
28
+ router = APIRouter(dependencies=REQUIRE_AUTH)
29
+
30
+
31
+ @router.post(
32
+ "/v1/video/ltx2/generate",
33
+ responses=build_job_responses("video/mp4", result_model=VideoResponse),
34
+ )
35
+ async def generate(
36
+ req: GenerateRequest,
37
+ accept: str | None = Depends(get_accept),
38
+ ctx: AppContext = Depends(get_ctx),
39
+ worker: Worker = Depends(get_worker),
40
+ ) -> Response:
41
+ """Generate a short MP4 with LTX-2 distilled (text/image/audio to video).
42
+
43
+ The mode is inferred from supplied inputs: prompt only is T2V; `image` adds
44
+ first-frame I2V; `end_image` adds last-frame conditioning; `audio` drives A2V
45
+ timing/motion; and `generate_audio=true` asks LTX-2 to synthesize audio. The
46
+ same endpoint serves both `sync` and `async` via `mode`.
47
+
48
+ Sync content negotiation: the job produces one MP4 artifact, so unless the
49
+ client asks for JSON the raw video bytes are returned with
50
+ `X-Kiapi-File-Id` / `X-Kiapi-Job-Id` headers. With
51
+ `Accept: application/json` (or async), the Job JSON is returned, whose
52
+ `result` follows VideoResponse.
53
+
54
+ LTX-2 is a transient model: each call reserves the configured memory budget,
55
+ loads the pipeline, generates, and frees it instead of keeping a resident
56
+ model in `/health`. Async returns 202 immediately; poll
57
+ GET /v1/jobs/{job_id} and fetch the artifact via GET /v1/files/{file_id}.
58
+ """
59
+ try:
60
+ spec = model_registry.resolve("ltx2", req.model)
61
+ except UnknownModelError as exc:
62
+ raise HTTPException(status_code=400, detail=str(exc)) # noqa: B904
63
+ try:
64
+ validate_generate(req, variant=spec.name, has_audio=req.audio is not None)
65
+ except ValidationError as exc:
66
+ raise HTTPException(status_code=422, detail=str(exc)) # noqa: B904
67
+
68
+ mode = detect_mode(
69
+ has_image=req.image is not None,
70
+ has_end_image=req.end_image is not None,
71
+ has_audio=req.audio is not None,
72
+ generate_audio=req.generate_audio,
73
+ )
74
+
75
+ def thunk(): # type: ignore
76
+ return handle_generate(ctx, req, mode)
77
+
78
+ return await submit_and_maybe_wait(
79
+ ctx,
80
+ worker,
81
+ type="ltx2",
82
+ params=req.gen_params() | {"model": spec.name, "mode_detected": mode},
83
+ thunk=thunk,
84
+ mode=req.mode,
85
+ accept=accept,
86
+ )
87
+
88
+
89
+ register_capability_endpoints(router, name="ltx2", base_path="/v1/video/ltx2")
@@ -0,0 +1,7 @@
1
+ from .fetch_api_request import FetchAPIRequest
2
+ from .fetch_error_response import FetchErrorResponse
3
+
4
+ __all__ = [
5
+ "FetchAPIRequest",
6
+ "FetchErrorResponse",
7
+ ]
@@ -0,0 +1,26 @@
1
+ from pydantic import BaseModel, ConfigDict, Field
2
+
3
+
4
+ class FetchAPIRequest(BaseModel):
5
+ model_config = ConfigDict(extra="forbid")
6
+
7
+ url: str = Field(
8
+ ...,
9
+ min_length=1,
10
+ description=(
11
+ "Absolute `http://` or `https://` URL of an HTML page to render. "
12
+ "Binary resources such as images, audio, video, archives, and PDFs "
13
+ "are rejected with `not_html`; use the Files API for stored artifacts."
14
+ ),
15
+ examples=["https://example.com/"],
16
+ )
17
+ accept: str | None = Field(
18
+ default=None,
19
+ description=(
20
+ "Requested output media type. `application/pdf` returns a PDF. "
21
+ "`text/markdown`, `text/plain`, `text/*`, `*/*`, browser default "
22
+ "headers, or an omitted header return Markdown. Other concrete media "
23
+ "types such as `application/json` are rejected with HTTP 406."
24
+ ),
25
+ examples=["text/markdown", "application/pdf"],
26
+ )
@@ -0,0 +1,25 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+
4
+ class _FetchErrorDetail(BaseModel):
5
+ error: str = Field(
6
+ description=(
7
+ "Stable error code such as `unsupported_accept`, `invalid_request`, "
8
+ "`not_html`, `empty_content`, or `fetch_failed`."
9
+ )
10
+ )
11
+ message: str = Field(description="Human-readable error detail.")
12
+ url: str = Field(description="URL submitted to `/v1/web/fetch`.")
13
+ content_type: str | None = Field(
14
+ default=None,
15
+ description=(
16
+ "Detected upstream Content-Type when known. Present for non-HTML "
17
+ "resources and some fetch failures."
18
+ ),
19
+ )
20
+
21
+
22
+ class FetchErrorResponse(BaseModel):
23
+ detail: _FetchErrorDetail = Field(
24
+ description="Structured fetch error payload returned by FastAPI."
25
+ )
@@ -0,0 +1,302 @@
1
+ """Web API (web): ``POST /v1/web/search`` + ``GET /v1/web/fetch``.
2
+
3
+ ``fetch`` selects its output format from the ``Accept`` header (``text/markdown``
4
+ default, ``application/pdf``) and returns the rendered page as a raw body, in the
5
+ generation families' raw-bytes-by-default style; errors come back as JSON.
6
+ """
7
+
8
+ import asyncio
9
+ from typing import Annotated
10
+
11
+ from fastapi import APIRouter, Depends, Header, HTTPException, Query, Response
12
+ from fastapi.responses import FileResponse
13
+ from pydantic import ValidationError as PydanticValidationError
14
+
15
+ from kiapi.api import (
16
+ REQUIRE_AUTH,
17
+ get_ctx,
18
+ get_worker,
19
+ register_capability_endpoints,
20
+ )
21
+ from kiapi.api._settings import settings_manager
22
+ from kiapi.api.web._views import FetchAPIRequest, FetchErrorResponse
23
+ from kiapi.capabilities.web import (
24
+ FetchError,
25
+ FetchRequest,
26
+ SearchBackendError,
27
+ SearchRequest,
28
+ SearchResponse,
29
+ handle_fetch,
30
+ handle_search,
31
+ )
32
+ from kiapi.core.app import AppContext
33
+ from kiapi.core.memory import MemoryBudgetError
34
+ from kiapi.core.model import UnknownModelError
35
+ from kiapi.core.setup import SetupRequiredError
36
+ from kiapi.core.worker import Worker
37
+
38
+ router = APIRouter(dependencies=REQUIRE_AUTH)
39
+
40
+
41
+ def fetch_api_request(
42
+ url: Annotated[
43
+ str,
44
+ Query(
45
+ min_length=1,
46
+ description=(
47
+ "Absolute http:// or https:// URL of an HTML page to fetch. "
48
+ "Non-HTML resources such as images, audio, video, archives, and "
49
+ "PDFs are rejected with `not_html`."
50
+ ),
51
+ examples=["https://example.com/"],
52
+ ),
53
+ ],
54
+ accept: Annotated[
55
+ str | None,
56
+ Header(
57
+ alias="Accept",
58
+ description=(
59
+ "Requested output media type. application/pdf returns PDF; "
60
+ "text/markdown, text/plain, text/*, */*, browser default "
61
+ "headers, or an omitted header return Markdown. application/json "
62
+ "and other incompatible concrete media types return HTTP 406."
63
+ ),
64
+ examples=["text/markdown", "application/pdf"],
65
+ ),
66
+ ] = None,
67
+ ) -> FetchAPIRequest:
68
+ return FetchAPIRequest(url=url, accept=accept)
69
+
70
+
71
+ @router.post(
72
+ "/v1/web/search",
73
+ response_model=SearchResponse,
74
+ responses={
75
+ 400: {"description": "Unknown web model or invalid model selection."},
76
+ 422: {"description": "Request schema or validation error."},
77
+ 502: {
78
+ "description": "SearXNG backend was unreachable or returned an HTTP error."
79
+ },
80
+ 503: {
81
+ "description": "Backend Docker image is not activated or memory budget is exhausted."
82
+ },
83
+ 504: {
84
+ "description": "SearXNG or the sync job exceeded the configured timeout."
85
+ },
86
+ },
87
+ )
88
+ async def search_web(
89
+ req: SearchRequest,
90
+ ctx: AppContext = Depends(get_ctx),
91
+ worker: Worker = Depends(get_worker),
92
+ ) -> dict:
93
+ """Search the live web with SearXNG and return JSON results.
94
+
95
+ The request body maps to SearXNG's search parameters. `query` is passed
96
+ through verbatim, so SearXNG inline operators such as `site:`, `!wp`,
97
+ `!images`, and language operators like `:ja` work normally. `format=json` is
98
+ always added by kiapi.
99
+
100
+ This endpoint is synchronous from the client's perspective: it waits up to
101
+ `KIAPI_SYNC_TIMEOUT_S` and returns `SearchResponse`. Internally it still
102
+ creates a Job and runs through the single-flight worker so web backends share
103
+ the same queue, setup checks, and resident subprocess lifecycle as other
104
+ capabilities.
105
+
106
+ Results are live and non-deterministic. `results` is optionally truncated by
107
+ `max_results`, while `answers`, `infoboxes`, `suggestions`, and
108
+ `unresponsive_engines` are forwarded from SearXNG without truncation.
109
+ """
110
+ settings = settings_manager.get_settings()
111
+
112
+ job = ctx.job_store.create(type="web.search", params=req.model_dump(mode="json"))
113
+ fut = await worker.submit(job, lambda: handle_search(ctx, req))
114
+
115
+ try:
116
+ return await asyncio.wait_for(fut, timeout=settings.sync_timeout_s)
117
+ except TimeoutError:
118
+ raise HTTPException( # noqa: B904
119
+ status_code=504,
120
+ detail=f"web.search job {job.id} exceeded sync timeout "
121
+ f"({settings.sync_timeout_s}s); it keeps running - poll /v1/jobs/{job.id}",
122
+ )
123
+ except SearchBackendError as exc:
124
+ raise HTTPException(status_code=exc.status_code, detail=str(exc)) # noqa: B904
125
+ except UnknownModelError as exc:
126
+ raise HTTPException(status_code=400, detail=str(exc)) # noqa: B904
127
+ except SetupRequiredError as exc:
128
+ raise HTTPException(status_code=503, detail=str(exc)) # noqa: B904
129
+ except MemoryBudgetError as exc:
130
+ raise HTTPException(status_code=503, detail=str(exc)) # noqa: B904
131
+ except Exception as exc:
132
+ raise HTTPException(status_code=500, detail=f"web.search failed: {exc}") # noqa: B904
133
+
134
+
135
+ def _negotiate_format(accept: str | None) -> str | None:
136
+ """Map the Accept header to a fetch format, or None if unacceptable.
137
+
138
+ ``application/pdf`` → ``pdf``; markdown/text/wildcard/absent → ``markdown``
139
+ (the default); a concrete incompatible type (e.g. ``application/json``) → None.
140
+ """
141
+ if accept is None:
142
+ return "markdown"
143
+ a = accept.lower()
144
+ if "application/pdf" in a:
145
+ return "pdf"
146
+ if any(t in a for t in ("text/markdown", "text/plain", "text/*", "*/*")):
147
+ return "markdown"
148
+ return None
149
+
150
+
151
+ @router.get(
152
+ "/v1/web/fetch",
153
+ responses={
154
+ 200: {
155
+ "description": (
156
+ "Rendered page body. Markdown is returned by default; PDF is "
157
+ "returned when the Accept header allows application/pdf."
158
+ ),
159
+ "content": {
160
+ "text/markdown": {"schema": {"type": "string"}},
161
+ "application/pdf": {"schema": {"type": "string", "format": "binary"}},
162
+ },
163
+ "headers": {
164
+ "X-Kiapi-Url": {
165
+ "description": "Fetched URL recorded on the stored artifact.",
166
+ "schema": {"type": "string"},
167
+ },
168
+ "X-Kiapi-File-Id": {
169
+ "description": "Files-API id of the rendered Markdown or PDF artifact.",
170
+ "schema": {"type": "string"},
171
+ },
172
+ "X-Kiapi-Job-Id": {
173
+ "description": "Job id created for the fetch operation.",
174
+ "schema": {"type": "string"},
175
+ },
176
+ "X-Kiapi-Content-Type": {
177
+ "description": "Detected upstream page Content-Type, when known.",
178
+ "schema": {"type": "string"},
179
+ },
180
+ },
181
+ },
182
+ 406: {
183
+ "model": FetchErrorResponse,
184
+ "description": "Accept header does not allow Markdown or PDF.",
185
+ },
186
+ 422: {
187
+ "model": FetchErrorResponse,
188
+ "description": "Invalid URL, non-HTML resource, or empty rendered content.",
189
+ },
190
+ 502: {
191
+ "model": FetchErrorResponse,
192
+ "description": "Target URL or Crawl4AI backend was unreachable or returned an HTTP error.",
193
+ },
194
+ 503: {
195
+ "description": "Backend Docker image is not activated or memory budget is exhausted."
196
+ },
197
+ 504: {
198
+ "description": "Crawl4AI or the sync job exceeded the configured timeout."
199
+ },
200
+ },
201
+ )
202
+ async def fetch_web(
203
+ api_req: FetchAPIRequest = Depends(fetch_api_request),
204
+ ctx: AppContext = Depends(get_ctx),
205
+ worker: Worker = Depends(get_worker),
206
+ ) -> Response:
207
+ """Render one HTML page to Markdown or PDF and stream the raw body.
208
+
209
+ `url` is supplied as a query parameter. The router resolves the `Accept`
210
+ header to the capability request's internal `format`: `application/pdf`
211
+ selects PDF; Markdown-compatible, text-compatible, wildcard, browser default,
212
+ or omitted Accept headers select Markdown. Incompatible concrete media types
213
+ return HTTP 406 with a structured JSON error.
214
+
215
+ Fetch performs a cheap preflight check before rendering. Non-HTML resources
216
+ are rejected with `not_html`; pages that render to no extractable text are
217
+ rejected with `empty_content`. Successful bodies are stored in the Files API
218
+ and streamed back directly with `X-Kiapi-File-Id`, `X-Kiapi-Job-Id`, and
219
+ upstream `X-Kiapi-Content-Type` headers when available.
220
+ """
221
+ settings = settings_manager.get_settings()
222
+ fmt = _negotiate_format(api_req.accept)
223
+ if fmt is None:
224
+ raise HTTPException(
225
+ status_code=406,
226
+ detail={
227
+ "error": "unsupported_accept",
228
+ "message": "Accept must allow text/markdown or application/pdf.",
229
+ "url": api_req.url,
230
+ },
231
+ )
232
+
233
+ try:
234
+ req = FetchRequest(url=api_req.url, format=fmt) # type: ignore[arg-type]
235
+ except PydanticValidationError as exc:
236
+ # Keep the fetch error envelope and stay JSON-serializable: a custom
237
+ # field_validator raises ValueError, whose object pydantic stashes in
238
+ # each error's ``ctx`` — passing exc.errors() straight through would
239
+ # 500 on encoding. Surface the human-readable messages only.
240
+ raise HTTPException( # noqa: B904
241
+ status_code=422,
242
+ detail={
243
+ "error": "invalid_request",
244
+ "message": "; ".join(e["msg"] for e in exc.errors()),
245
+ "url": api_req.url,
246
+ },
247
+ )
248
+
249
+ job = ctx.job_store.create(type="web.fetch", params=req.model_dump(mode="json"))
250
+ fut = await worker.submit(job, lambda: handle_fetch(ctx, req))
251
+
252
+ try:
253
+ result = await asyncio.wait_for(fut, timeout=settings.sync_timeout_s)
254
+ except FetchError as exc:
255
+ raise HTTPException( # noqa: B904
256
+ status_code=exc.status_code,
257
+ detail={
258
+ "error": exc.code,
259
+ "message": str(exc),
260
+ "url": api_req.url,
261
+ "content_type": exc.content_type,
262
+ },
263
+ )
264
+ except TimeoutError:
265
+ raise HTTPException( # noqa: B904
266
+ status_code=504,
267
+ detail=f"web.fetch job {job.id} exceeded sync timeout "
268
+ f"({settings.sync_timeout_s}s); it keeps running - poll /v1/jobs/{job.id}",
269
+ )
270
+ except UnknownModelError as exc:
271
+ raise HTTPException(status_code=400, detail=str(exc)) # noqa: B904
272
+ except SetupRequiredError as exc:
273
+ raise HTTPException(status_code=503, detail=str(exc)) # noqa: B904
274
+ except MemoryBudgetError as exc:
275
+ raise HTTPException(status_code=503, detail=str(exc)) # noqa: B904
276
+ except Exception as exc:
277
+ raise HTTPException(status_code=500, detail=f"web.fetch failed: {exc}") # noqa: B904
278
+
279
+ file_id = result["file_id"]
280
+ rec = ctx.file_store.get(file_id)
281
+ if rec is None:
282
+ raise HTTPException(
283
+ status_code=500,
284
+ detail=f"web.fetch produced missing artifact {file_id!r}",
285
+ )
286
+
287
+ headers = {
288
+ "X-Kiapi-Url": result["url"],
289
+ "X-Kiapi-File-Id": file_id,
290
+ "X-Kiapi-Job-Id": job.id,
291
+ }
292
+ if result.get("content_type"):
293
+ headers["X-Kiapi-Content-Type"] = result["content_type"]
294
+ return FileResponse(
295
+ rec.path,
296
+ media_type=rec.content_type,
297
+ filename=rec.filename,
298
+ headers=headers,
299
+ )
300
+
301
+
302
+ register_capability_endpoints(router, name="web", base_path="/v1/web")
@@ -0,0 +1,24 @@
1
+ """Family packages — one subpackage per family (dir == family).
2
+
3
+ Each family exposes a ``register()`` that populates the global model + help
4
+ registries (see core/model, core/help). Registration is explicit, not an import
5
+ side effect: the CLI bootstrap calls each family's ``register`` before starting
6
+ the API server or running setup/check commands.
7
+
8
+ Shared building blocks reused across families live here too: the common
9
+ request-error types and the Files-API path resolver.
10
+ """
11
+
12
+ from ._exceptions.CapabilityError import CapabilityError
13
+ from ._exceptions.ValidationError import ValidationError
14
+ from ._helpers.attach_mflux_progress import attach_mflux_progress
15
+ from ._helpers.get_file_path import get_file_path
16
+ from ._helpers.resolve_file_ref import resolve_file_ref
17
+
18
+ __all__ = [
19
+ "CapabilityError",
20
+ "ValidationError",
21
+ "attach_mflux_progress",
22
+ "get_file_path",
23
+ "resolve_file_ref",
24
+ ]
@@ -0,0 +1,2 @@
1
+ class CapabilityError(ValueError):
2
+ """Bad request detected at service time."""