xinference 1.10.0__py3-none-any.whl → 1.11.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.
Potentially problematic release.
This version of xinference might be problematic. Click here for more details.
- xinference/_version.py +3 -3
- xinference/api/restful_api.py +473 -31
- xinference/client/restful/async_restful_client.py +178 -8
- xinference/client/restful/restful_client.py +151 -3
- xinference/core/supervisor.py +99 -53
- xinference/core/worker.py +10 -0
- xinference/deploy/cmdline.py +15 -0
- xinference/model/audio/core.py +21 -6
- xinference/model/audio/indextts2.py +166 -0
- xinference/model/audio/model_spec.json +58 -21
- xinference/model/image/model_spec.json +159 -90
- xinference/model/image/stable_diffusion/core.py +13 -4
- xinference/model/llm/__init__.py +6 -2
- xinference/model/llm/llm_family.json +1299 -174
- xinference/model/llm/mlx/distributed_models/core.py +41 -0
- xinference/model/llm/mlx/distributed_models/qwen2.py +1 -2
- xinference/model/llm/sglang/core.py +44 -11
- xinference/model/llm/tool_parsers/deepseek_r1_tool_parser.py +94 -32
- xinference/model/llm/tool_parsers/qwen_tool_parser.py +29 -4
- xinference/model/llm/transformers/chatglm.py +3 -0
- xinference/model/llm/transformers/core.py +129 -36
- xinference/model/llm/transformers/multimodal/minicpmv45.py +340 -0
- xinference/model/llm/transformers/multimodal/qwen2_vl.py +34 -8
- xinference/model/llm/transformers/utils.py +23 -0
- xinference/model/llm/utils.py +48 -32
- xinference/model/llm/vllm/core.py +207 -72
- xinference/model/utils.py +74 -31
- xinference/thirdparty/audiotools/__init__.py +10 -0
- xinference/thirdparty/audiotools/core/__init__.py +4 -0
- xinference/thirdparty/audiotools/core/audio_signal.py +1682 -0
- xinference/thirdparty/audiotools/core/display.py +194 -0
- xinference/thirdparty/audiotools/core/dsp.py +390 -0
- xinference/thirdparty/audiotools/core/effects.py +647 -0
- xinference/thirdparty/audiotools/core/ffmpeg.py +211 -0
- xinference/thirdparty/audiotools/core/loudness.py +320 -0
- xinference/thirdparty/audiotools/core/playback.py +252 -0
- xinference/thirdparty/audiotools/core/templates/__init__.py +0 -0
- xinference/thirdparty/audiotools/core/templates/headers.html +322 -0
- xinference/thirdparty/audiotools/core/templates/pandoc.css +407 -0
- xinference/thirdparty/audiotools/core/templates/widget.html +52 -0
- xinference/thirdparty/audiotools/core/util.py +671 -0
- xinference/thirdparty/audiotools/core/whisper.py +97 -0
- xinference/thirdparty/audiotools/data/__init__.py +3 -0
- xinference/thirdparty/audiotools/data/datasets.py +517 -0
- xinference/thirdparty/audiotools/data/preprocess.py +81 -0
- xinference/thirdparty/audiotools/data/transforms.py +1592 -0
- xinference/thirdparty/audiotools/metrics/__init__.py +6 -0
- xinference/thirdparty/audiotools/metrics/distance.py +131 -0
- xinference/thirdparty/audiotools/metrics/quality.py +159 -0
- xinference/thirdparty/audiotools/metrics/spectral.py +247 -0
- xinference/thirdparty/audiotools/ml/__init__.py +5 -0
- xinference/thirdparty/audiotools/ml/accelerator.py +184 -0
- xinference/thirdparty/audiotools/ml/decorators.py +440 -0
- xinference/thirdparty/audiotools/ml/experiment.py +90 -0
- xinference/thirdparty/audiotools/ml/layers/__init__.py +2 -0
- xinference/thirdparty/audiotools/ml/layers/base.py +328 -0
- xinference/thirdparty/audiotools/ml/layers/spectral_gate.py +127 -0
- xinference/thirdparty/audiotools/post.py +140 -0
- xinference/thirdparty/audiotools/preference.py +600 -0
- xinference/thirdparty/fish_speech/fish_speech/text/chn_text_norm/text.py +1 -1
- xinference/thirdparty/indextts/BigVGAN/ECAPA_TDNN.py +656 -0
- xinference/thirdparty/indextts/BigVGAN/__init__.py +0 -0
- xinference/thirdparty/indextts/BigVGAN/activations.py +122 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_activation/__init__.py +0 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_activation/cuda/.gitignore +1 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_activation/cuda/__init__.py +0 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_activation/cuda/activation1d.py +76 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_activation/cuda/anti_alias_activation.cpp +23 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_activation/cuda/anti_alias_activation_cuda.cu +256 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_activation/cuda/compat.h +29 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_activation/cuda/load.py +121 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_activation/cuda/type_shim.h +92 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_activation/torch/__init__.py +6 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_activation/torch/act.py +31 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_activation/torch/filter.py +102 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_activation/torch/resample.py +58 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_torch/__init__.py +6 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_torch/act.py +29 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_torch/filter.py +96 -0
- xinference/thirdparty/indextts/BigVGAN/alias_free_torch/resample.py +49 -0
- xinference/thirdparty/indextts/BigVGAN/bigvgan.py +534 -0
- xinference/thirdparty/indextts/BigVGAN/models.py +451 -0
- xinference/thirdparty/indextts/BigVGAN/nnet/CNN.py +546 -0
- xinference/thirdparty/indextts/BigVGAN/nnet/__init__.py +0 -0
- xinference/thirdparty/indextts/BigVGAN/nnet/linear.py +89 -0
- xinference/thirdparty/indextts/BigVGAN/nnet/normalization.py +670 -0
- xinference/thirdparty/indextts/BigVGAN/utils.py +101 -0
- xinference/thirdparty/indextts/__init__.py +0 -0
- xinference/thirdparty/indextts/cli.py +65 -0
- xinference/thirdparty/indextts/gpt/__init__.py +0 -0
- xinference/thirdparty/indextts/gpt/conformer/__init__.py +0 -0
- xinference/thirdparty/indextts/gpt/conformer/attention.py +312 -0
- xinference/thirdparty/indextts/gpt/conformer/embedding.py +163 -0
- xinference/thirdparty/indextts/gpt/conformer/subsampling.py +348 -0
- xinference/thirdparty/indextts/gpt/conformer_encoder.py +520 -0
- xinference/thirdparty/indextts/gpt/model.py +713 -0
- xinference/thirdparty/indextts/gpt/model_v2.py +747 -0
- xinference/thirdparty/indextts/gpt/perceiver.py +317 -0
- xinference/thirdparty/indextts/gpt/transformers_beam_search.py +1013 -0
- xinference/thirdparty/indextts/gpt/transformers_generation_utils.py +4747 -0
- xinference/thirdparty/indextts/gpt/transformers_gpt2.py +1878 -0
- xinference/thirdparty/indextts/gpt/transformers_modeling_utils.py +5525 -0
- xinference/thirdparty/indextts/infer.py +690 -0
- xinference/thirdparty/indextts/infer_v2.py +739 -0
- xinference/thirdparty/indextts/s2mel/dac/__init__.py +16 -0
- xinference/thirdparty/indextts/s2mel/dac/__main__.py +36 -0
- xinference/thirdparty/indextts/s2mel/dac/model/__init__.py +4 -0
- xinference/thirdparty/indextts/s2mel/dac/model/base.py +294 -0
- xinference/thirdparty/indextts/s2mel/dac/model/dac.py +400 -0
- xinference/thirdparty/indextts/s2mel/dac/model/discriminator.py +228 -0
- xinference/thirdparty/indextts/s2mel/dac/model/encodec.py +320 -0
- xinference/thirdparty/indextts/s2mel/dac/nn/__init__.py +3 -0
- xinference/thirdparty/indextts/s2mel/dac/nn/layers.py +33 -0
- xinference/thirdparty/indextts/s2mel/dac/nn/loss.py +368 -0
- xinference/thirdparty/indextts/s2mel/dac/nn/quantize.py +339 -0
- xinference/thirdparty/indextts/s2mel/dac/utils/__init__.py +123 -0
- xinference/thirdparty/indextts/s2mel/dac/utils/decode.py +95 -0
- xinference/thirdparty/indextts/s2mel/dac/utils/encode.py +94 -0
- xinference/thirdparty/indextts/s2mel/hf_utils.py +12 -0
- xinference/thirdparty/indextts/s2mel/modules/alias_free_torch/__init__.py +5 -0
- xinference/thirdparty/indextts/s2mel/modules/alias_free_torch/act.py +29 -0
- xinference/thirdparty/indextts/s2mel/modules/alias_free_torch/filter.py +96 -0
- xinference/thirdparty/indextts/s2mel/modules/alias_free_torch/resample.py +57 -0
- xinference/thirdparty/indextts/s2mel/modules/audio.py +82 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/activations.py +120 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/alias_free_activation/cuda/__init__.py +0 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/alias_free_activation/cuda/activation1d.py +77 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/alias_free_activation/cuda/anti_alias_activation.cpp +23 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/alias_free_activation/cuda/anti_alias_activation_cuda.cu +246 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/alias_free_activation/cuda/compat.h +29 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/alias_free_activation/cuda/load.py +86 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/alias_free_activation/cuda/type_shim.h +92 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/alias_free_activation/torch/__init__.py +6 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/alias_free_activation/torch/act.py +30 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/alias_free_activation/torch/filter.py +101 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/alias_free_activation/torch/resample.py +58 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/bigvgan.py +492 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/config.json +63 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/env.py +18 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/meldataset.py +354 -0
- xinference/thirdparty/indextts/s2mel/modules/bigvgan/utils.py +99 -0
- xinference/thirdparty/indextts/s2mel/modules/campplus/DTDNN.py +115 -0
- xinference/thirdparty/indextts/s2mel/modules/campplus/classifier.py +70 -0
- xinference/thirdparty/indextts/s2mel/modules/campplus/layers.py +253 -0
- xinference/thirdparty/indextts/s2mel/modules/commons.py +632 -0
- xinference/thirdparty/indextts/s2mel/modules/diffusion_transformer.py +257 -0
- xinference/thirdparty/indextts/s2mel/modules/encodec.py +292 -0
- xinference/thirdparty/indextts/s2mel/modules/flow_matching.py +171 -0
- xinference/thirdparty/indextts/s2mel/modules/gpt_fast/generate.py +436 -0
- xinference/thirdparty/indextts/s2mel/modules/gpt_fast/model.py +360 -0
- xinference/thirdparty/indextts/s2mel/modules/gpt_fast/quantize.py +622 -0
- xinference/thirdparty/indextts/s2mel/modules/hifigan/f0_predictor.py +55 -0
- xinference/thirdparty/indextts/s2mel/modules/hifigan/generator.py +454 -0
- xinference/thirdparty/indextts/s2mel/modules/layers.py +354 -0
- xinference/thirdparty/indextts/s2mel/modules/length_regulator.py +141 -0
- xinference/thirdparty/indextts/s2mel/modules/openvoice/__init__.py +0 -0
- xinference/thirdparty/indextts/s2mel/modules/openvoice/api.py +186 -0
- xinference/thirdparty/indextts/s2mel/modules/openvoice/attentions.py +465 -0
- xinference/thirdparty/indextts/s2mel/modules/openvoice/checkpoints_v2/converter/config.json +57 -0
- xinference/thirdparty/indextts/s2mel/modules/openvoice/commons.py +160 -0
- xinference/thirdparty/indextts/s2mel/modules/openvoice/mel_processing.py +183 -0
- xinference/thirdparty/indextts/s2mel/modules/openvoice/models.py +499 -0
- xinference/thirdparty/indextts/s2mel/modules/openvoice/modules.py +598 -0
- xinference/thirdparty/indextts/s2mel/modules/openvoice/openvoice_app.py +275 -0
- xinference/thirdparty/indextts/s2mel/modules/openvoice/se_extractor.py +153 -0
- xinference/thirdparty/indextts/s2mel/modules/openvoice/transforms.py +209 -0
- xinference/thirdparty/indextts/s2mel/modules/openvoice/utils.py +194 -0
- xinference/thirdparty/indextts/s2mel/modules/quantize.py +229 -0
- xinference/thirdparty/indextts/s2mel/modules/rmvpe.py +631 -0
- xinference/thirdparty/indextts/s2mel/modules/vocos/__init__.py +4 -0
- xinference/thirdparty/indextts/s2mel/modules/vocos/heads.py +164 -0
- xinference/thirdparty/indextts/s2mel/modules/vocos/helpers.py +71 -0
- xinference/thirdparty/indextts/s2mel/modules/vocos/loss.py +114 -0
- xinference/thirdparty/indextts/s2mel/modules/vocos/models.py +118 -0
- xinference/thirdparty/indextts/s2mel/modules/vocos/modules.py +213 -0
- xinference/thirdparty/indextts/s2mel/modules/vocos/pretrained.py +51 -0
- xinference/thirdparty/indextts/s2mel/modules/vocos/spectral_ops.py +192 -0
- xinference/thirdparty/indextts/s2mel/modules/wavenet.py +174 -0
- xinference/thirdparty/indextts/s2mel/optimizers.py +96 -0
- xinference/thirdparty/indextts/s2mel/wav2vecbert_extract.py +148 -0
- xinference/thirdparty/indextts/utils/__init__.py +0 -0
- xinference/thirdparty/indextts/utils/arch_util.py +120 -0
- xinference/thirdparty/indextts/utils/checkpoint.py +34 -0
- xinference/thirdparty/indextts/utils/common.py +121 -0
- xinference/thirdparty/indextts/utils/feature_extractors.py +50 -0
- xinference/thirdparty/indextts/utils/front.py +536 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/__init__.py +0 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/amphion_codec/codec.py +427 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/amphion_codec/quantize/__init__.py +11 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/amphion_codec/quantize/factorized_vector_quantize.py +150 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/amphion_codec/quantize/lookup_free_quantize.py +77 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/amphion_codec/quantize/residual_vq.py +177 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/amphion_codec/quantize/vector_quantize.py +401 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/amphion_codec/vocos.py +881 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/codec_dataset.py +264 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/codec_inference.py +515 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/codec_sampler.py +126 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/codec_trainer.py +166 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/__init__.py +0 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/alias_free_torch/__init__.py +5 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/alias_free_torch/act.py +29 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/alias_free_torch/filter.py +96 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/alias_free_torch/resample.py +57 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/facodec_dataset.py +98 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/facodec_inference.py +137 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/facodec_trainer.py +776 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/modules/JDC/__init__.py +1 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/modules/JDC/bst.t7 +0 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/modules/JDC/model.py +219 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/modules/attentions.py +437 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/modules/commons.py +331 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/modules/gradient_reversal.py +35 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/modules/layers.py +460 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/modules/quantize.py +741 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/modules/style_encoder.py +110 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/modules/wavenet.py +224 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/facodec/optimizer.py +104 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/kmeans/repcodec_model.py +210 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/kmeans/vocos.py +850 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/melvqgan/melspec.py +108 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/ns3_codec/README.md +216 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/ns3_codec/__init__.py +6 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/ns3_codec/alias_free_torch/__init__.py +5 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/ns3_codec/alias_free_torch/act.py +29 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/ns3_codec/alias_free_torch/filter.py +96 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/ns3_codec/alias_free_torch/resample.py +57 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/ns3_codec/facodec.py +1222 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/ns3_codec/gradient_reversal.py +35 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/ns3_codec/melspec.py +102 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/ns3_codec/quantize/__init__.py +7 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/ns3_codec/quantize/fvq.py +116 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/ns3_codec/quantize/rvq.py +87 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/ns3_codec/transformer.py +234 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/speechtokenizer/model.py +184 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/speechtokenizer/modules/__init__.py +27 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/speechtokenizer/modules/conv.py +346 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/speechtokenizer/modules/lstm.py +46 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/speechtokenizer/modules/norm.py +37 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/speechtokenizer/modules/quantization/__init__.py +14 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/speechtokenizer/modules/quantization/ac.py +317 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/speechtokenizer/modules/quantization/core_vq.py +388 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/speechtokenizer/modules/quantization/distrib.py +135 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/speechtokenizer/modules/quantization/vq.py +125 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/speechtokenizer/modules/seanet.py +414 -0
- xinference/thirdparty/indextts/utils/maskgct/models/codec/vevo/vevo_repcodec.py +592 -0
- xinference/thirdparty/indextts/utils/maskgct/models/tts/maskgct/ckpt/wav2vec2bert_stats.pt +0 -0
- xinference/thirdparty/indextts/utils/maskgct/models/tts/maskgct/llama_nar.py +650 -0
- xinference/thirdparty/indextts/utils/maskgct/models/tts/maskgct/maskgct_s2a.py +503 -0
- xinference/thirdparty/indextts/utils/maskgct_utils.py +259 -0
- xinference/thirdparty/indextts/utils/text_utils.py +41 -0
- xinference/thirdparty/indextts/utils/typical_sampling.py +30 -0
- xinference/thirdparty/indextts/utils/utils.py +93 -0
- xinference/thirdparty/indextts/utils/webui_utils.py +42 -0
- xinference/thirdparty/indextts/utils/xtransformers.py +1247 -0
- xinference/thirdparty/indextts/vqvae/__init__.py +0 -0
- xinference/thirdparty/indextts/vqvae/xtts_dvae.py +395 -0
- xinference/thirdparty/melo/text/chinese_mix.py +2 -2
- xinference/types.py +9 -0
- xinference/ui/gradio/media_interface.py +66 -8
- xinference/ui/web/ui/build/asset-manifest.json +6 -6
- xinference/ui/web/ui/build/index.html +1 -1
- xinference/ui/web/ui/build/static/css/main.5ea97072.css +2 -0
- xinference/ui/web/ui/build/static/css/main.5ea97072.css.map +1 -0
- xinference/ui/web/ui/build/static/js/main.45e78536.js +3 -0
- xinference/ui/web/ui/build/static/js/{main.1086c759.js.LICENSE.txt → main.45e78536.js.LICENSE.txt} +0 -7
- xinference/ui/web/ui/build/static/js/main.45e78536.js.map +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/089c38df5f52348d212ed868dda5c518a42e0c2762caed4175487c0405830c35.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/2b6e3a5b6eb2c5c5f2d007e68cd46c372721cd52bf63508adcdb21ecf79241d8.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/2d887825fd07a56f872eda4420da25fba0b5b62a23bdcc6c6da1a5281887f618.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/4001f9c3e64e73a4f2158826650c174a59d5e3f89ddecddf17cbb6bb688cc4ca.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/4a7018a69e6b7f90fc313248c2aa86f2a8f1eb1db120df586047a8023549b44b.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/64b12aaa1c1d1bf53820ada8a63769067c0ccc5aab46b32348eb1917ae7f2a11.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/7275b67c78ec76ce38a686bb8a576d8c9cecf54e1573614c84859d538efb9be5.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/a68b6ee3b31eadc051fb95ce8f8ccb9c2e8b52c60f290dbab545a1917e065282.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/ae8771cc37693feb160fa8727231312a0c54ef2d1d1ca893be568cd70016ca7e.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/bb4e8722d2d41d87f1fce3661bc8937bffe9448e231fc5f0462630849e851592.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/be6aada1ee4adc2bbf65dbe56d17db32bb3b5478be05d6b527805a8ba6cfb2b9.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/de91c352653c233cf0cb6674e6e04049a44fd0e1156560de65d5c4620521391e.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/e85f7002fc325c83b9c9cd8a1619e5b3ebc701d30e811afc284b88e6ae710cb5.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/e8b603c78944bf3d213639078bfe155ff5c0dfa4048a93cbb967cad6a4eb4ff3.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/ea2a26361204e70cf1018d6990fb6354bed82b3ac69690391e0f100385e7abb7.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/f05535160a508b2a312de546a6de234776c613db276479ea4253c0b1bdeeb7d6.json +1 -0
- xinference/ui/web/ui/node_modules/.cache/babel-loader/f09ba9e11106bd59a0de10cc85c55084097729dcab575f43dfcf07375961ed87.json +1 -0
- xinference/ui/web/ui/node_modules/.package-lock.json +0 -33
- xinference/ui/web/ui/package-lock.json +0 -34
- xinference/ui/web/ui/package.json +0 -1
- xinference/ui/web/ui/src/locales/en.json +9 -3
- xinference/ui/web/ui/src/locales/ja.json +9 -3
- xinference/ui/web/ui/src/locales/ko.json +9 -3
- xinference/ui/web/ui/src/locales/zh.json +9 -3
- {xinference-1.10.0.dist-info → xinference-1.11.0.dist-info}/METADATA +24 -6
- {xinference-1.10.0.dist-info → xinference-1.11.0.dist-info}/RECORD +296 -77
- xinference/ui/web/ui/build/static/css/main.013f296b.css +0 -2
- xinference/ui/web/ui/build/static/css/main.013f296b.css.map +0 -1
- xinference/ui/web/ui/build/static/js/main.1086c759.js +0 -3
- xinference/ui/web/ui/build/static/js/main.1086c759.js.map +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/0b0f77000cc1b482ca091cfbcae511dfe02f08916971645fad21d0b1234d04a2.json +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/1c5f8ff423a7c9202bea60b15680f04b1e9964b445b0da3f86c6ff70cf24e797.json +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/44ce7993e344980e3ed4f13e8f69237d4a5dfc60e37ca6b54f51f8ee1357bd67.json +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/4aec1cc414ac3ebb3481d3d915e4db597d9127de813291346eacb8554ab170d4.json +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/644cfec52f3c57a6e222ce60f112237a1efefe9835efd9aad857a685f53d8eed.json +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/663436f72af53fe0d72394f56d003fa4e0bba489e5bb4e483fd34b00f84637f7.json +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/69db82ca9bfe27fe417cc6cf2b1716b09be9c6f0cd198530f12bfc60e801bbcf.json +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/85087e27618d740c236bf159f30e0219db443ab55f0997388eed5fde6f9e90cc.json +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/88b07838348864aa86c672be3bbca1e9f58f6f3a2881b32070ec27f4e7b449d1.json +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/8b8cd408ccfbe115acef27ccfa5b233da8597131a2a5712add13e1e4d5d4504b.json +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/a23824fe746b9c6ca5eee9159b5764d1ff1653c1d856288c0f75c742bbb0023b.json +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/a3eb18af328280b139693c9092dff2a0ef8c9a967e6c8956ceee0996611f1984.json +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/bc1aacc65a102db325ca61bcd2f681e1ae22c36a1f1d98a6ff5e4ad49dc7544f.json +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/c682fd521747c19dae437d83ce3235a306ce6b68e24a117bc57c27ebb8d1f1ca.json +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/d5c224be7081f18cba1678b7874a9782eba895df004874ff8f243f94ba79942a.json +0 -1
- xinference/ui/web/ui/node_modules/.cache/babel-loader/f7f18bfb539b036a6a342176dd98a85df5057a884a8da978d679f2a0264883d0.json +0 -1
- xinference/ui/web/ui/node_modules/clipboard/.babelrc.json +0 -11
- xinference/ui/web/ui/node_modules/clipboard/.eslintrc.json +0 -24
- xinference/ui/web/ui/node_modules/clipboard/.prettierrc.json +0 -9
- xinference/ui/web/ui/node_modules/clipboard/bower.json +0 -18
- xinference/ui/web/ui/node_modules/clipboard/composer.json +0 -25
- xinference/ui/web/ui/node_modules/clipboard/package.json +0 -63
- xinference/ui/web/ui/node_modules/delegate/package.json +0 -31
- xinference/ui/web/ui/node_modules/good-listener/bower.json +0 -11
- xinference/ui/web/ui/node_modules/good-listener/package.json +0 -35
- xinference/ui/web/ui/node_modules/select/bower.json +0 -13
- xinference/ui/web/ui/node_modules/select/package.json +0 -29
- xinference/ui/web/ui/node_modules/tiny-emitter/package.json +0 -53
- {xinference-1.10.0.dist-info → xinference-1.11.0.dist-info}/WHEEL +0 -0
- {xinference-1.10.0.dist-info → xinference-1.11.0.dist-info}/entry_points.txt +0 -0
- {xinference-1.10.0.dist-info → xinference-1.11.0.dist-info}/licenses/LICENSE +0 -0
- {xinference-1.10.0.dist-info → xinference-1.11.0.dist-info}/top_level.txt +0 -0
xinference/_version.py
CHANGED
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-
|
|
11
|
+
"date": "2025-10-19T20:53:12+0800",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "1.
|
|
14
|
+
"full-revisionid": "baaa40b463e4948762b078f5995d67775df53704",
|
|
15
|
+
"version": "1.11.0"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
xinference/api/restful_api.py
CHANGED
|
@@ -739,6 +739,18 @@ class RESTfulAPI(CancelMixin):
|
|
|
739
739
|
else None
|
|
740
740
|
),
|
|
741
741
|
)
|
|
742
|
+
self._router.add_api_route(
|
|
743
|
+
"/v1/images/edits",
|
|
744
|
+
self.create_image_edits,
|
|
745
|
+
methods=["POST"],
|
|
746
|
+
response_model=ImageList,
|
|
747
|
+
dependencies=(
|
|
748
|
+
[Security(self._auth_service, scopes=["models:read"])]
|
|
749
|
+
if self.is_authenticated()
|
|
750
|
+
else None
|
|
751
|
+
),
|
|
752
|
+
)
|
|
753
|
+
|
|
742
754
|
# SD WebUI API
|
|
743
755
|
self._router.add_api_route(
|
|
744
756
|
"/sdapi/v1/options",
|
|
@@ -2136,7 +2148,7 @@ class RESTfulAPI(CancelMixin):
|
|
|
2136
2148
|
async def create_variations(
|
|
2137
2149
|
self,
|
|
2138
2150
|
model: str = Form(...),
|
|
2139
|
-
image: UploadFile = File(media_type="application/octet-stream"),
|
|
2151
|
+
image: List[UploadFile] = File(media_type="application/octet-stream"),
|
|
2140
2152
|
prompt: Optional[Union[str, List[str]]] = Form(None),
|
|
2141
2153
|
negative_prompt: Optional[Union[str, List[str]]] = Form(None),
|
|
2142
2154
|
n: Optional[int] = Form(1),
|
|
@@ -2164,8 +2176,17 @@ class RESTfulAPI(CancelMixin):
|
|
|
2164
2176
|
parsed_kwargs = {}
|
|
2165
2177
|
request_id = parsed_kwargs.get("request_id")
|
|
2166
2178
|
self._add_running_task(request_id)
|
|
2179
|
+
|
|
2180
|
+
# Handle single image or multiple images
|
|
2181
|
+
if len(image) == 1:
|
|
2182
|
+
# Single image
|
|
2183
|
+
image_data = Image.open(image[0].file)
|
|
2184
|
+
else:
|
|
2185
|
+
# Multiple images - convert to list of PIL Images
|
|
2186
|
+
image_data = [Image.open(img.file) for img in image]
|
|
2187
|
+
|
|
2167
2188
|
image_list = await model_ref.image_to_image(
|
|
2168
|
-
image=
|
|
2189
|
+
image=image_data,
|
|
2169
2190
|
prompt=prompt,
|
|
2170
2191
|
negative_prompt=negative_prompt,
|
|
2171
2192
|
n=n,
|
|
@@ -2290,6 +2311,453 @@ class RESTfulAPI(CancelMixin):
|
|
|
2290
2311
|
self.handle_request_limit_error(e)
|
|
2291
2312
|
raise HTTPException(status_code=500, detail=str(e))
|
|
2292
2313
|
|
|
2314
|
+
async def create_image_edits(
|
|
2315
|
+
self,
|
|
2316
|
+
request: Request,
|
|
2317
|
+
prompt: str = Form(...),
|
|
2318
|
+
mask: Optional[UploadFile] = File(None, media_type="application/octet-stream"),
|
|
2319
|
+
model: Optional[str] = Form(None),
|
|
2320
|
+
n: Optional[int] = Form(1),
|
|
2321
|
+
size: Optional[str] = Form("original"),
|
|
2322
|
+
response_format: Optional[str] = Form("url"),
|
|
2323
|
+
stream: Optional[bool] = Form(False),
|
|
2324
|
+
) -> Response:
|
|
2325
|
+
"""OpenAI-compatible image edit endpoint."""
|
|
2326
|
+
import io
|
|
2327
|
+
|
|
2328
|
+
# Parse multipart form data to handle files
|
|
2329
|
+
content_type = request.headers.get("content-type", "")
|
|
2330
|
+
|
|
2331
|
+
if "multipart/form-data" in content_type:
|
|
2332
|
+
# Try manual multipart parsing for better duplicate field handling
|
|
2333
|
+
try:
|
|
2334
|
+
image_files, manual_mask = await self._parse_multipart_manual(request)
|
|
2335
|
+
# Use manually parsed mask if available, otherwise keep the original
|
|
2336
|
+
if manual_mask is not None:
|
|
2337
|
+
mask = manual_mask
|
|
2338
|
+
except Exception as e:
|
|
2339
|
+
logger.error(f"Manual parsing failed, falling back to FastAPI: {e}")
|
|
2340
|
+
# Fallback to FastAPI form parsing
|
|
2341
|
+
form = await request.form()
|
|
2342
|
+
multipart_files: dict[str, list] = {}
|
|
2343
|
+
for key, value in form.items():
|
|
2344
|
+
if hasattr(value, "filename") and value.filename:
|
|
2345
|
+
if key not in multipart_files:
|
|
2346
|
+
multipart_files[key] = []
|
|
2347
|
+
multipart_files[key].append(value)
|
|
2348
|
+
|
|
2349
|
+
image_files = multipart_files.get("image", [])
|
|
2350
|
+
if not image_files:
|
|
2351
|
+
image_files = multipart_files.get("image[]", [])
|
|
2352
|
+
if not image_files:
|
|
2353
|
+
image_files = multipart_files.get("images", [])
|
|
2354
|
+
|
|
2355
|
+
else:
|
|
2356
|
+
# Fallback to FastAPI form parsing
|
|
2357
|
+
form = await request.form()
|
|
2358
|
+
fallback_files: dict[str, list] = {}
|
|
2359
|
+
for key, value in form.items():
|
|
2360
|
+
if hasattr(value, "filename") and value.filename:
|
|
2361
|
+
if key not in fallback_files:
|
|
2362
|
+
fallback_files[key] = []
|
|
2363
|
+
fallback_files[key].append(value)
|
|
2364
|
+
|
|
2365
|
+
image_files = fallback_files.get("image", [])
|
|
2366
|
+
if not image_files:
|
|
2367
|
+
image_files = fallback_files.get("image[]", [])
|
|
2368
|
+
if not image_files:
|
|
2369
|
+
image_files = fallback_files.get("images", [])
|
|
2370
|
+
|
|
2371
|
+
all_file_keys = []
|
|
2372
|
+
if "multipart/form-data" in content_type:
|
|
2373
|
+
all_file_keys = [f"image[] (x{len(image_files)})"] if image_files else []
|
|
2374
|
+
else:
|
|
2375
|
+
# Fallback to FastAPI form parsing
|
|
2376
|
+
form = await request.form()
|
|
2377
|
+
debug_files: dict[str, list] = {}
|
|
2378
|
+
for key, value in form.items():
|
|
2379
|
+
if hasattr(value, "filename") and value.filename:
|
|
2380
|
+
if key not in debug_files:
|
|
2381
|
+
debug_files[key] = []
|
|
2382
|
+
debug_files[key].append(value)
|
|
2383
|
+
|
|
2384
|
+
# Get image files
|
|
2385
|
+
image_files = debug_files.get("image", [])
|
|
2386
|
+
if not image_files:
|
|
2387
|
+
image_files = debug_files.get("image[]", [])
|
|
2388
|
+
if not image_files:
|
|
2389
|
+
image_files = debug_files.get("images", [])
|
|
2390
|
+
|
|
2391
|
+
logger.info(f"Total image files found: {len(image_files)}")
|
|
2392
|
+
|
|
2393
|
+
if not image_files:
|
|
2394
|
+
# Debug: log all received file fields
|
|
2395
|
+
logger.warning(
|
|
2396
|
+
f"No image files found. Available file fields: {all_file_keys}"
|
|
2397
|
+
)
|
|
2398
|
+
raise HTTPException(
|
|
2399
|
+
status_code=400, detail="At least one image file is required"
|
|
2400
|
+
)
|
|
2401
|
+
|
|
2402
|
+
# Validate response format
|
|
2403
|
+
if response_format not in ["url", "b64_json"]:
|
|
2404
|
+
raise HTTPException(
|
|
2405
|
+
status_code=400, detail="response_format must be 'url' or 'b64_json'"
|
|
2406
|
+
)
|
|
2407
|
+
|
|
2408
|
+
# Get default model if not specified
|
|
2409
|
+
if not model:
|
|
2410
|
+
try:
|
|
2411
|
+
models = await (await self._get_supervisor_ref()).list_models()
|
|
2412
|
+
image_models = [
|
|
2413
|
+
name
|
|
2414
|
+
for name, info in models.items()
|
|
2415
|
+
if info["model_type"] == "image"
|
|
2416
|
+
and info.get("model_ability", [])
|
|
2417
|
+
and (
|
|
2418
|
+
"image2image" in info["model_ability"]
|
|
2419
|
+
or "inpainting" in info["model_ability"]
|
|
2420
|
+
)
|
|
2421
|
+
]
|
|
2422
|
+
if not image_models:
|
|
2423
|
+
raise HTTPException(
|
|
2424
|
+
status_code=400, detail="No available image models found"
|
|
2425
|
+
)
|
|
2426
|
+
model = image_models[0]
|
|
2427
|
+
except Exception as e:
|
|
2428
|
+
logger.error(f"Failed to get available models: {e}", exc_info=True)
|
|
2429
|
+
raise HTTPException(
|
|
2430
|
+
status_code=500, detail="Failed to get available models"
|
|
2431
|
+
)
|
|
2432
|
+
|
|
2433
|
+
model_uid = model
|
|
2434
|
+
try:
|
|
2435
|
+
model_ref = await (await self._get_supervisor_ref()).get_model(model_uid)
|
|
2436
|
+
except ValueError as ve:
|
|
2437
|
+
logger.error(str(ve), exc_info=True)
|
|
2438
|
+
await self._report_error_event(model_uid, str(ve))
|
|
2439
|
+
raise HTTPException(status_code=400, detail=str(ve))
|
|
2440
|
+
except Exception as e:
|
|
2441
|
+
logger.error(e, exc_info=True)
|
|
2442
|
+
await self._report_error_event(model_uid, str(e))
|
|
2443
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
2444
|
+
|
|
2445
|
+
request_id = None
|
|
2446
|
+
try:
|
|
2447
|
+
self._add_running_task(request_id)
|
|
2448
|
+
|
|
2449
|
+
# Read and process all images (needed for both streaming and non-streaming)
|
|
2450
|
+
images = []
|
|
2451
|
+
for i, img in enumerate(image_files):
|
|
2452
|
+
image_content = await img.read()
|
|
2453
|
+
image_file = io.BytesIO(image_content)
|
|
2454
|
+
pil_image = Image.open(image_file)
|
|
2455
|
+
|
|
2456
|
+
# Debug: save the received image for inspection
|
|
2457
|
+
debug_filename = f"/tmp/received_image_{i}_{pil_image.mode}_{pil_image.size[0]}x{pil_image.size[1]}.png"
|
|
2458
|
+
pil_image.save(debug_filename)
|
|
2459
|
+
logger.info(f"Saved received image {i} to {debug_filename}")
|
|
2460
|
+
|
|
2461
|
+
# Convert to RGB format to avoid channel mismatch errors
|
|
2462
|
+
if pil_image.mode == "RGBA":
|
|
2463
|
+
logger.info(f"Converting RGBA image {i} to RGB")
|
|
2464
|
+
# Create white background for RGBA images
|
|
2465
|
+
background = Image.new("RGB", pil_image.size, (255, 255, 255))
|
|
2466
|
+
background.paste(pil_image, mask=pil_image.split()[3])
|
|
2467
|
+
pil_image = background
|
|
2468
|
+
elif pil_image.mode != "RGB":
|
|
2469
|
+
logger.info(f"Converting {pil_image.mode} image {i} to RGB")
|
|
2470
|
+
pil_image = pil_image.convert("RGB")
|
|
2471
|
+
|
|
2472
|
+
# Debug: save the converted image
|
|
2473
|
+
converted_filename = f"/tmp/converted_image_{i}_RGB_{pil_image.size[0]}x{pil_image.size[1]}.png"
|
|
2474
|
+
pil_image.save(converted_filename)
|
|
2475
|
+
logger.info(f"Saved converted image {i} to {converted_filename}")
|
|
2476
|
+
|
|
2477
|
+
images.append(pil_image)
|
|
2478
|
+
|
|
2479
|
+
# Debug: log image summary
|
|
2480
|
+
logger.info(f"Processing {len(images)} images:")
|
|
2481
|
+
for i, img in enumerate(images):
|
|
2482
|
+
logger.info(
|
|
2483
|
+
f" Image {i}: mode={img.mode}, size={img.size}, filename={image_files[i].filename if hasattr(image_files[i], 'filename') else 'unknown'}"
|
|
2484
|
+
)
|
|
2485
|
+
|
|
2486
|
+
# Handle streaming if requested
|
|
2487
|
+
if stream:
|
|
2488
|
+
return EventSourceResponse(
|
|
2489
|
+
self._stream_image_edit(
|
|
2490
|
+
model_ref,
|
|
2491
|
+
images, # Pass processed images instead of raw files
|
|
2492
|
+
mask,
|
|
2493
|
+
prompt,
|
|
2494
|
+
(
|
|
2495
|
+
size.replace("x", "*") if size else ""
|
|
2496
|
+
), # Convert size format for streaming
|
|
2497
|
+
response_format,
|
|
2498
|
+
n,
|
|
2499
|
+
)
|
|
2500
|
+
)
|
|
2501
|
+
|
|
2502
|
+
# Use the first image as primary, others as reference
|
|
2503
|
+
primary_image = images[0]
|
|
2504
|
+
reference_images = images[1:] if len(images) > 1 else []
|
|
2505
|
+
|
|
2506
|
+
# Prepare model parameters
|
|
2507
|
+
# If size is "original", use empty string to let model determine original dimensions
|
|
2508
|
+
if size == "original":
|
|
2509
|
+
model_size = ""
|
|
2510
|
+
else:
|
|
2511
|
+
model_size = size.replace("x", "*") if size else ""
|
|
2512
|
+
|
|
2513
|
+
model_params = {
|
|
2514
|
+
"prompt": prompt,
|
|
2515
|
+
"n": n or 1,
|
|
2516
|
+
"size": model_size,
|
|
2517
|
+
"response_format": response_format,
|
|
2518
|
+
"denoising_strength": 0.75, # Default strength for image editing
|
|
2519
|
+
"reference_images": reference_images, # Pass reference images
|
|
2520
|
+
"negative_prompt": " ", # Space instead of empty string to prevent filtering
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
# Generate the image
|
|
2524
|
+
if mask:
|
|
2525
|
+
# Use inpainting for masked edits
|
|
2526
|
+
mask_content = await mask.read()
|
|
2527
|
+
mask_image = Image.open(io.BytesIO(mask_content))
|
|
2528
|
+
result = await model_ref.inpainting(
|
|
2529
|
+
image=primary_image,
|
|
2530
|
+
mask_image=mask_image,
|
|
2531
|
+
**model_params,
|
|
2532
|
+
)
|
|
2533
|
+
else:
|
|
2534
|
+
# Use image-to-image for general edits
|
|
2535
|
+
result = await model_ref.image_to_image(
|
|
2536
|
+
image=primary_image, **model_params
|
|
2537
|
+
)
|
|
2538
|
+
|
|
2539
|
+
# Return the result directly (should be ImageList format)
|
|
2540
|
+
return Response(content=result, media_type="application/json")
|
|
2541
|
+
|
|
2542
|
+
except asyncio.CancelledError:
|
|
2543
|
+
err_str = f"The request has been cancelled: {request_id or 'unknown'}"
|
|
2544
|
+
logger.error(err_str)
|
|
2545
|
+
await self._report_error_event(model_uid, err_str)
|
|
2546
|
+
raise HTTPException(status_code=409, detail=err_str)
|
|
2547
|
+
except Exception as e:
|
|
2548
|
+
e = await self._get_model_last_error(model_ref.uid, e)
|
|
2549
|
+
logger.error(e, exc_info=True)
|
|
2550
|
+
await self._report_error_event(model_uid, str(e))
|
|
2551
|
+
self.handle_request_limit_error(e)
|
|
2552
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
2553
|
+
|
|
2554
|
+
async def _parse_multipart_manual(self, request: Request):
|
|
2555
|
+
"""Manually parse multipart form data to handle duplicate field names"""
|
|
2556
|
+
import io
|
|
2557
|
+
|
|
2558
|
+
class FileWrapper:
|
|
2559
|
+
"""Wrapper for BytesIO to add filename and content_type attributes"""
|
|
2560
|
+
|
|
2561
|
+
def __init__(self, data, filename, content_type="application/octet-stream"):
|
|
2562
|
+
self._file = io.BytesIO(data)
|
|
2563
|
+
self.filename = filename
|
|
2564
|
+
self.content_type = content_type
|
|
2565
|
+
|
|
2566
|
+
def read(self, *args, **kwargs):
|
|
2567
|
+
return self._file.read(*args, **kwargs)
|
|
2568
|
+
|
|
2569
|
+
def seek(self, *args, **kwargs):
|
|
2570
|
+
return self._file.seek(*args, **kwargs)
|
|
2571
|
+
|
|
2572
|
+
def tell(self, *args, **kwargs):
|
|
2573
|
+
return self._file.tell(*args, **kwargs)
|
|
2574
|
+
|
|
2575
|
+
from multipart.multipart import parse_options_header
|
|
2576
|
+
|
|
2577
|
+
content_type = request.headers.get("content-type", "")
|
|
2578
|
+
if not content_type:
|
|
2579
|
+
return [], None
|
|
2580
|
+
|
|
2581
|
+
# Parse content type and boundary
|
|
2582
|
+
content_type, options = parse_options_header(content_type.encode("utf-8"))
|
|
2583
|
+
if content_type != b"multipart/form-data":
|
|
2584
|
+
return [], None
|
|
2585
|
+
|
|
2586
|
+
boundary = options.get(b"boundary")
|
|
2587
|
+
if not boundary:
|
|
2588
|
+
return [], None
|
|
2589
|
+
|
|
2590
|
+
# Get the raw body
|
|
2591
|
+
body = await request.body()
|
|
2592
|
+
|
|
2593
|
+
# Parse multipart data manually
|
|
2594
|
+
image_files = []
|
|
2595
|
+
mask_file = None
|
|
2596
|
+
try:
|
|
2597
|
+
# Import multipart parser
|
|
2598
|
+
from multipart.multipart import MultipartParser
|
|
2599
|
+
|
|
2600
|
+
# Parse the multipart data
|
|
2601
|
+
parser = MultipartParser(
|
|
2602
|
+
io.BytesIO(body),
|
|
2603
|
+
boundary.decode("utf-8") if isinstance(boundary, bytes) else boundary,
|
|
2604
|
+
)
|
|
2605
|
+
|
|
2606
|
+
for part in parser:
|
|
2607
|
+
# Check if this part is an image file
|
|
2608
|
+
field_name = part.name
|
|
2609
|
+
filename = part.filename or ""
|
|
2610
|
+
|
|
2611
|
+
# Look for image fields with different naming conventions
|
|
2612
|
+
if field_name in ["image", "image[]", "images"] and filename:
|
|
2613
|
+
# Create a file-like object from the part data
|
|
2614
|
+
file_obj = FileWrapper(
|
|
2615
|
+
part.data,
|
|
2616
|
+
filename,
|
|
2617
|
+
part.content_type or "application/octet-stream",
|
|
2618
|
+
)
|
|
2619
|
+
image_files.append(file_obj)
|
|
2620
|
+
elif field_name == "mask" and filename:
|
|
2621
|
+
# Handle mask file
|
|
2622
|
+
mask_file = FileWrapper(
|
|
2623
|
+
part.data,
|
|
2624
|
+
filename,
|
|
2625
|
+
part.content_type or "application/octet-stream",
|
|
2626
|
+
)
|
|
2627
|
+
logger.info(f"Manual multipart parsing found mask file: {filename}")
|
|
2628
|
+
|
|
2629
|
+
logger.info(
|
|
2630
|
+
f"Manual multipart parsing found {len(image_files)} image files and mask: {mask_file is not None}"
|
|
2631
|
+
)
|
|
2632
|
+
|
|
2633
|
+
except Exception as e:
|
|
2634
|
+
logger.error(f"Manual multipart parsing failed: {e}")
|
|
2635
|
+
# Return empty list to trigger fallback
|
|
2636
|
+
return [], None
|
|
2637
|
+
|
|
2638
|
+
return image_files, mask_file
|
|
2639
|
+
|
|
2640
|
+
async def _stream_image_edit(
|
|
2641
|
+
self, model_ref, images, mask, prompt, size, response_format, n
|
|
2642
|
+
):
|
|
2643
|
+
"""Stream image editing progress and results"""
|
|
2644
|
+
import io
|
|
2645
|
+
import json
|
|
2646
|
+
from datetime import datetime
|
|
2647
|
+
|
|
2648
|
+
try:
|
|
2649
|
+
# Send start event
|
|
2650
|
+
yield {
|
|
2651
|
+
"event": "start",
|
|
2652
|
+
"data": json.dumps(
|
|
2653
|
+
{
|
|
2654
|
+
"type": "image_edit_started",
|
|
2655
|
+
"timestamp": datetime.now().isoformat(),
|
|
2656
|
+
"prompt": prompt,
|
|
2657
|
+
"image_count": len(images),
|
|
2658
|
+
}
|
|
2659
|
+
),
|
|
2660
|
+
}
|
|
2661
|
+
|
|
2662
|
+
# Images are already processed in the main method, just use them directly
|
|
2663
|
+
image_objects = images
|
|
2664
|
+
logger.info(f"Streaming: Using {len(image_objects)} pre-processed images")
|
|
2665
|
+
|
|
2666
|
+
# Debug: log streaming image summary
|
|
2667
|
+
logger.info(f"Streaming: Processing {len(image_objects)} images:")
|
|
2668
|
+
for i, img in enumerate(image_objects):
|
|
2669
|
+
logger.info(f" Streaming Image {i}: mode={img.mode}, size={img.size}")
|
|
2670
|
+
|
|
2671
|
+
# Use the first image as primary, others as reference
|
|
2672
|
+
primary_image = image_objects[0]
|
|
2673
|
+
reference_images = image_objects[1:] if len(image_objects) > 1 else []
|
|
2674
|
+
|
|
2675
|
+
# Send processing event
|
|
2676
|
+
yield {
|
|
2677
|
+
"event": "processing",
|
|
2678
|
+
"data": json.dumps(
|
|
2679
|
+
{
|
|
2680
|
+
"type": "images_loaded",
|
|
2681
|
+
"timestamp": datetime.now().isoformat(),
|
|
2682
|
+
"primary_image_size": primary_image.size,
|
|
2683
|
+
"reference_images_count": len(reference_images),
|
|
2684
|
+
}
|
|
2685
|
+
),
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
# Prepare model parameters
|
|
2689
|
+
# If size is "original", use empty string to let model determine original dimensions
|
|
2690
|
+
if size == "original":
|
|
2691
|
+
model_size = ""
|
|
2692
|
+
else:
|
|
2693
|
+
model_size = size
|
|
2694
|
+
|
|
2695
|
+
model_params = {
|
|
2696
|
+
"prompt": prompt,
|
|
2697
|
+
"n": n or 1,
|
|
2698
|
+
"size": model_size,
|
|
2699
|
+
"response_format": response_format,
|
|
2700
|
+
"denoising_strength": 0.75,
|
|
2701
|
+
"reference_images": reference_images,
|
|
2702
|
+
"negative_prompt": " ", # Space instead of empty string to prevent filtering
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
# Generate the image
|
|
2706
|
+
if mask:
|
|
2707
|
+
mask_content = await mask.read()
|
|
2708
|
+
mask_image = Image.open(io.BytesIO(mask_content))
|
|
2709
|
+
yield {
|
|
2710
|
+
"event": "processing",
|
|
2711
|
+
"data": json.dumps(
|
|
2712
|
+
{
|
|
2713
|
+
"type": "mask_loaded",
|
|
2714
|
+
"timestamp": datetime.now().isoformat(),
|
|
2715
|
+
"mask_size": mask_image.size,
|
|
2716
|
+
}
|
|
2717
|
+
),
|
|
2718
|
+
}
|
|
2719
|
+
result = await model_ref.inpainting(
|
|
2720
|
+
image=primary_image,
|
|
2721
|
+
mask_image=mask_image,
|
|
2722
|
+
**model_params,
|
|
2723
|
+
)
|
|
2724
|
+
else:
|
|
2725
|
+
yield {
|
|
2726
|
+
"event": "processing",
|
|
2727
|
+
"data": json.dumps(
|
|
2728
|
+
{
|
|
2729
|
+
"type": "starting_generation",
|
|
2730
|
+
"timestamp": datetime.now().isoformat(),
|
|
2731
|
+
}
|
|
2732
|
+
),
|
|
2733
|
+
}
|
|
2734
|
+
result = await model_ref.image_to_image(
|
|
2735
|
+
image=primary_image, **model_params
|
|
2736
|
+
)
|
|
2737
|
+
|
|
2738
|
+
# Parse the result and send final event in OpenAI format
|
|
2739
|
+
result_data = json.loads(result)
|
|
2740
|
+
|
|
2741
|
+
# Send completion event with OpenAI-compatible format
|
|
2742
|
+
yield {
|
|
2743
|
+
"event": "complete",
|
|
2744
|
+
"data": json.dumps(
|
|
2745
|
+
result_data
|
|
2746
|
+
), # Direct send the result in OpenAI format
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2749
|
+
except Exception as e:
|
|
2750
|
+
yield {
|
|
2751
|
+
"event": "error",
|
|
2752
|
+
"data": json.dumps(
|
|
2753
|
+
{
|
|
2754
|
+
"type": "image_edit_error",
|
|
2755
|
+
"timestamp": datetime.now().isoformat(),
|
|
2756
|
+
"error": str(e),
|
|
2757
|
+
}
|
|
2758
|
+
),
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2293
2761
|
async def create_flexible_infer(self, request: Request) -> Response:
|
|
2294
2762
|
payload = await request.json()
|
|
2295
2763
|
|
|
@@ -2346,7 +2814,7 @@ class RESTfulAPI(CancelMixin):
|
|
|
2346
2814
|
)
|
|
2347
2815
|
return Response(content=video_list, media_type="application/json")
|
|
2348
2816
|
except asyncio.CancelledError:
|
|
2349
|
-
err_str = f"The request has been cancelled: {request_id}"
|
|
2817
|
+
err_str = f"The request has been cancelled: {request_id or 'unknown'}"
|
|
2350
2818
|
logger.error(err_str)
|
|
2351
2819
|
await self._report_error_event(model_uid, err_str)
|
|
2352
2820
|
raise HTTPException(status_code=409, detail=err_str)
|
|
@@ -2395,7 +2863,7 @@ class RESTfulAPI(CancelMixin):
|
|
|
2395
2863
|
)
|
|
2396
2864
|
return Response(content=video_list, media_type="application/json")
|
|
2397
2865
|
except asyncio.CancelledError:
|
|
2398
|
-
err_str = f"The request has been cancelled: {request_id}"
|
|
2866
|
+
err_str = f"The request has been cancelled: {request_id or 'unknown'}"
|
|
2399
2867
|
logger.error(err_str)
|
|
2400
2868
|
await self._report_error_event(model_uid, err_str)
|
|
2401
2869
|
raise HTTPException(status_code=409, detail=err_str)
|
|
@@ -2446,7 +2914,7 @@ class RESTfulAPI(CancelMixin):
|
|
|
2446
2914
|
)
|
|
2447
2915
|
return Response(content=video_list, media_type="application/json")
|
|
2448
2916
|
except asyncio.CancelledError:
|
|
2449
|
-
err_str = f"The request has been cancelled: {request_id}"
|
|
2917
|
+
err_str = f"The request has been cancelled: {request_id or 'unknown'}"
|
|
2450
2918
|
logger.error(err_str)
|
|
2451
2919
|
await self._report_error_event(model_uid, err_str)
|
|
2452
2920
|
raise HTTPException(status_code=409, detail=err_str)
|
|
@@ -2858,19 +3326,6 @@ class RESTfulAPI(CancelMixin):
|
|
|
2858
3326
|
def extract_guided_params(raw_body: dict) -> dict:
|
|
2859
3327
|
kwargs = {}
|
|
2860
3328
|
raw_extra_body: dict = raw_body.get("extra_body") # type: ignore
|
|
2861
|
-
# Convert OpenAI response_format to vLLM guided decoding
|
|
2862
|
-
response_format = raw_body.get("response_format")
|
|
2863
|
-
if response_format is not None:
|
|
2864
|
-
if isinstance(response_format, dict):
|
|
2865
|
-
format_type = response_format.get("type")
|
|
2866
|
-
if format_type == "json_schema":
|
|
2867
|
-
json_schema = response_format.get("json_schema")
|
|
2868
|
-
if isinstance(json_schema, dict):
|
|
2869
|
-
schema = json_schema.get("schema")
|
|
2870
|
-
if schema is not None:
|
|
2871
|
-
kwargs["guided_json"] = schema
|
|
2872
|
-
elif format_type == "json_object":
|
|
2873
|
-
kwargs["guided_json_object"] = True
|
|
2874
3329
|
if raw_body.get("guided_json"):
|
|
2875
3330
|
kwargs["guided_json"] = raw_body.get("guided_json")
|
|
2876
3331
|
if raw_body.get("guided_regex") is not None:
|
|
@@ -2889,19 +3344,6 @@ class RESTfulAPI(CancelMixin):
|
|
|
2889
3344
|
)
|
|
2890
3345
|
# Parse OpenAI extra_body
|
|
2891
3346
|
if raw_extra_body is not None:
|
|
2892
|
-
# Convert OpenAI response_format to vLLM guided decoding
|
|
2893
|
-
extra_response_format = raw_extra_body.get("response_format")
|
|
2894
|
-
if extra_response_format is not None:
|
|
2895
|
-
if isinstance(extra_response_format, dict):
|
|
2896
|
-
format_type = extra_response_format.get("type")
|
|
2897
|
-
if format_type == "json_schema":
|
|
2898
|
-
json_schema = extra_response_format.get("json_schema")
|
|
2899
|
-
if isinstance(json_schema, dict):
|
|
2900
|
-
schema = json_schema.get("schema")
|
|
2901
|
-
if schema is not None:
|
|
2902
|
-
kwargs["guided_json"] = schema
|
|
2903
|
-
elif format_type == "json_object":
|
|
2904
|
-
kwargs["guided_json_object"] = True
|
|
2905
3347
|
if raw_extra_body.get("guided_json"):
|
|
2906
3348
|
kwargs["guided_json"] = raw_extra_body.get("guided_json")
|
|
2907
3349
|
if raw_extra_body.get("guided_regex") is not None:
|