John6666 commited on
Commit
46d591b
·
verified ·
1 Parent(s): f4ce5f5

Upload 31 files

Browse files
Files changed (15) hide show
  1. README.md +1 -1
  2. app.py +3 -2
  3. constants.py +1 -1
  4. dc.py +33 -0
  5. env.py +4 -0
  6. formatter.py +71 -43
  7. llmdolphin.py +0 -0
  8. llmenv.py +53 -2
  9. modutils.py +0 -0
  10. packages.txt +2 -1
  11. requirements.txt +6 -7
  12. tagger/fl2sd3longcap.py +22 -37
  13. tagger/florence2_compat.py +925 -0
  14. tagger/tagger.py +3 -2
  15. utils.py +35 -20
README.md CHANGED
@@ -5,13 +5,13 @@ colorFrom: purple
5
  colorTo: red
6
  sdk: gradio
7
  sdk_version: 5.45.0
 
8
  app_file: app.py
9
  license: mit
10
  short_description: Text-to-Image
11
  pinned: true
12
  preload_from_hub:
13
  - madebyollin/sdxl-vae-fp16-fix config.json,diffusion_pytorch_model.safetensors
14
- hf_oauth: true
15
  ---
16
 
17
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
5
  colorTo: red
6
  sdk: gradio
7
  sdk_version: 5.45.0
8
+ python_version: "3.10"
9
  app_file: app.py
10
  license: mit
11
  short_description: Text-to-Image
12
  pinned: true
13
  preload_from_hub:
14
  - madebyollin/sdxl-vae-fp16-fix config.json,diffusion_pytorch_model.safetensors
 
15
  ---
16
 
17
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py CHANGED
@@ -16,7 +16,9 @@ from dc import (infer, _infer, pass_result, get_diffusers_model_list, get_sample
16
  # Translator
17
  from llmdolphin import (dolphin_respond_auto, dolphin_parse_simple,
18
  get_llm_formats, get_dolphin_model_format, get_dolphin_models, get_dolphin_loras, select_dolphin_lora, add_dolphin_loras,
19
- get_dolphin_model_info, select_dolphin_model, select_dolphin_format, get_dolphin_sysprompt)
 
 
20
  # Tagger
21
  from tagger.v2 import v2_upsampling_prompt, V2_ALL_MODELS
22
  from tagger.utils import (gradio_copy_text, gradio_copy_prompt, COPY_ACTION_JS,
@@ -681,7 +683,6 @@ with gr.Blocks(fill_width=True, elem_id="container", css=css) as demo:
681
  with gr.Tab("Preprocessor", render=True):
682
  preprocessor_tab()
683
 
684
- gr.LoginButton()
685
  gr.DuplicateButton(value="Duplicate Space for private use (This demo does not work on CPU. Requires GPU Space)")
686
 
687
  if __name__ == "__main__":
 
16
  # Translator
17
  from llmdolphin import (dolphin_respond_auto, dolphin_parse_simple,
18
  get_llm_formats, get_dolphin_model_format, get_dolphin_models, get_dolphin_loras, select_dolphin_lora, add_dolphin_loras,
19
+ get_dolphin_model_info, select_dolphin_model, select_dolphin_format, get_dolphin_sysprompt, initialize_llmdolphin_runtime)
20
+ initialize_llmdolphin_runtime()
21
+
22
  # Tagger
23
  from tagger.v2 import v2_upsampling_prompt, V2_ALL_MODELS
24
  from tagger.utils import (gradio_copy_text, gradio_copy_prompt, COPY_ACTION_JS,
 
683
  with gr.Tab("Preprocessor", render=True):
684
  preprocessor_tab()
685
 
 
686
  gr.DuplicateButton(value="Duplicate Space for private use (This demo does not work on CPU. Requires GPU Space)")
687
 
688
  if __name__ == "__main__":
constants.py CHANGED
@@ -19,7 +19,7 @@ DOWNLOAD_MODEL = "https://huggingface.co/zuv0/test/resolve/main/milkyWonderland_
19
  DOWNLOAD_VAE = "https://huggingface.co/Anzhc/Anzhcs-VAEs/resolve/main/SDXL%20Anime%20VAE%20Dec-only%20B3.safetensors, https://huggingface.co/fp16-guy/anything_kl-f8-anime2_vae-ft-mse-840000-ema-pruned_blessed_clearvae_fp16_cleaned/resolve/main/vae-ft-mse-840000-ema-pruned_fp16.safetensors?download=true"
20
 
21
  # - **Download LoRAs**
22
- DOWNLOAD_LORA = "https://huggingface.co/Leopain/color/resolve/main/Coloring_book_-_LineArt.safetensors, https://civitai.com/api/download/models/135867, https://huggingface.co/Linaqruf/anime-detailer-xl-lora/resolve/main/anime-detailer-xl.safetensors?download=true, https://huggingface.co/Linaqruf/style-enhancer-xl-lora/resolve/main/style-enhancer-xl.safetensors?download=true, https://huggingface.co/ByteDance/Hyper-SD/resolve/main/Hyper-SD15-8steps-CFG-lora.safetensors?download=true, https://huggingface.co/ByteDance/Hyper-SD/resolve/main/Hyper-SDXL-8steps-CFG-lora.safetensors?download=true"
23
 
24
  LOAD_DIFFUSERS_FORMAT_MODEL = [
25
  'TestOrganizationPleaseIgnore/potato_quality_anime_plzwork_sdxl',
 
19
  DOWNLOAD_VAE = "https://huggingface.co/Anzhc/Anzhcs-VAEs/resolve/main/SDXL%20Anime%20VAE%20Dec-only%20B3.safetensors, https://huggingface.co/fp16-guy/anything_kl-f8-anime2_vae-ft-mse-840000-ema-pruned_blessed_clearvae_fp16_cleaned/resolve/main/vae-ft-mse-840000-ema-pruned_fp16.safetensors?download=true"
20
 
21
  # - **Download LoRAs**
22
+ DOWNLOAD_LORA = "https://huggingface.co/Leopain/color/resolve/main/Coloring_book_-_LineArt.safetensors, https://civitai.com/api/download/models/135867, https://huggingface.co/Linaqruf/anime-detailer-xl-lora/resolve/main/anime-detailer-xl.safetensors?download=true, https://huggingface.co/Linaqruf/style-enhancer-xl-lora/resolve/main/style-enhancer-xl.safetensors?download=true"
23
 
24
  LOAD_DIFFUSERS_FORMAT_MODEL = [
25
  'TestOrganizationPleaseIgnore/potato_quality_anime_plzwork_sdxl',
dc.py CHANGED
@@ -181,6 +181,11 @@ class GuiSD:
181
  # Avoid duplicate downloads
182
  self.active_downloads = set()
183
  self.download_lock = threading.Lock()
 
 
 
 
 
184
  def update_storage_models(self, storage_floor_gb=24, required_inventory_for_purge=3):
185
  while get_used_storage_gb() > storage_floor_gb:
186
  if len(self.inventory) < required_inventory_for_purge:
@@ -205,6 +210,34 @@ class GuiSD:
205
  print(self.inventory)
206
 
207
  def load_new_model(self, model_name, vae_model, task, controlnet_model, progress=gr.Progress(track_tqdm=True)):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  lock_key = model_name
209
 
210
  while True:
 
181
  # Avoid duplicate downloads
182
  self.active_downloads = set()
183
  self.download_lock = threading.Lock()
184
+
185
+ # Anti-abuse: track new model requests.
186
+ self.used_models = []
187
+ self.new_model_history = []
188
+
189
  def update_storage_models(self, storage_floor_gb=24, required_inventory_for_purge=3):
190
  while get_used_storage_gb() > storage_floor_gb:
191
  if len(self.inventory) < required_inventory_for_purge:
 
210
  print(self.inventory)
211
 
212
  def load_new_model(self, model_name, vae_model, task, controlnet_model, progress=gr.Progress(track_tqdm=True)):
213
+
214
+ if model_name != model_list[0]:
215
+ # --- Anti-Abuse Check Start ---
216
+ if model_name in self.used_models:
217
+ # Move to the end to mark as the most recently used.
218
+ self.used_models.remove(model_name)
219
+ self.used_models.append(model_name)
220
+ else:
221
+ current_time = datetime.now()
222
+ # Retain history of new model requests from the last 20 minutes.
223
+ self.new_model_history = [
224
+ t for t in self.new_model_history
225
+ if (current_time - t).total_seconds() < 1200
226
+ ]
227
+
228
+ # Allow a maximum of 5 new model requests per 20 minutes.
229
+ if len(self.new_model_history) >= 5:
230
+ yield "Rate limit exceeded: Too many new models requested."
231
+ raise gr.Error("Too many new models requested. Please reuse your previously loaded models or wait a few minutes before trying new ones.")
232
+
233
+ self.new_model_history.append(current_time)
234
+ self.used_models.append(model_name)
235
+
236
+ # Cap the reuse list to the 5 most recent models.
237
+ if len(self.used_models) > 5:
238
+ self.used_models.pop(0)
239
+ # --- Anti-Abuse Check End ---
240
+
241
  lock_key = model_name
242
 
243
  while True:
env.py CHANGED
@@ -77,6 +77,10 @@ LOAD_DIFFUSERS_FORMAT_MODEL = [
77
  'Raelina/Raehoshi-illust-XL-7',
78
  'Raelina/Raehoshi-illust-XL-7.1',
79
  'Raelina/Raehoshi-illust-XL-8',
 
 
 
 
80
  'camenduru/FLUX.1-dev-diffusers',
81
  'black-forest-labs/FLUX.1-schnell',
82
  'sayakpaul/FLUX.1-merged',
 
77
  'Raelina/Raehoshi-illust-XL-7',
78
  'Raelina/Raehoshi-illust-XL-7.1',
79
  'Raelina/Raehoshi-illust-XL-8',
80
+ 'Raelina/Raehoshi-illust-XL-8.1',
81
+ 'Raelina/Raehoshi-illust-XL-9',
82
+ 'Raelina/Raehoshi-illust-XL-9.1',
83
+ 'Raelina/Raehoshi-illust-vpred',
84
  'camenduru/FLUX.1-dev-diffusers',
85
  'black-forest-labs/FLUX.1-schnell',
86
  'sayakpaul/FLUX.1-merged',
formatter.py CHANGED
@@ -1,43 +1,71 @@
1
- from llama_cpp_agent.messages_formatter import MessagesFormatter, PromptMarkers, Roles
2
-
3
- mistral_v1_markers = {
4
- Roles.system: PromptMarkers(""" [INST]""", """ [/INST] Understood.</s>"""),
5
- Roles.user: PromptMarkers(""" [INST]""", """ [/INST]"""),
6
- Roles.assistant: PromptMarkers(" ", "</s>"),
7
- Roles.tool: PromptMarkers("", ""),
8
- }
9
-
10
- mistral_v1_formatter = MessagesFormatter(
11
- pre_prompt="",
12
- prompt_markers=mistral_v1_markers,
13
- include_sys_prompt_in_first_user_message=False,
14
- default_stop_sequences=["</s>"]
15
- )
16
-
17
- mistral_v2_markers = {
18
- Roles.system: PromptMarkers("""[INST] """, """[/INST] Understood.</s>"""),
19
- Roles.user: PromptMarkers("""[INST] """, """[/INST]"""),
20
- Roles.assistant: PromptMarkers(" ", "</s>"),
21
- Roles.tool: PromptMarkers("", ""),
22
- }
23
-
24
- mistral_v2_formatter = MessagesFormatter(
25
- pre_prompt="",
26
- prompt_markers=mistral_v2_markers,
27
- include_sys_prompt_in_first_user_message=False,
28
- default_stop_sequences=["</s>"]
29
- )
30
-
31
- mistral_v3_tekken_markers = {
32
- Roles.system: PromptMarkers("""[INST]""", """[/INST]Understood.</s>"""),
33
- Roles.user: PromptMarkers("""[INST]""", """[/INST]"""),
34
- Roles.assistant: PromptMarkers("", "</s>"),
35
- Roles.tool: PromptMarkers("", ""),
36
- }
37
-
38
- mistral_v3_tekken_formatter = MessagesFormatter(
39
- pre_prompt="",
40
- prompt_markers=mistral_v3_tekken_markers,
41
- include_sys_prompt_in_first_user_message=False,
42
- default_stop_sequences=["</s>"]
43
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class LazyMessagesFormatterType:
2
+ def __getattr__(self, name):
3
+ return LazyLlamaAgentValue("MessagesFormatterType", name)
4
+
5
+
6
+ class LazyLlamaAgentValue:
7
+ def __init__(self, source, name):
8
+ self.source = source
9
+ self.name = name
10
+
11
+ def resolve(self):
12
+ if self.source == "MessagesFormatterType":
13
+ from llama_cpp_agent import MessagesFormatterType
14
+ return getattr(MessagesFormatterType, self.name)
15
+ if self.source == "custom_formatter":
16
+ return build_mistral_formatter(self.name)
17
+ raise ValueError(f"Unknown lazy llama agent value source: {self.source}")
18
+
19
+ def __repr__(self):
20
+ return f"<LazyLlamaAgentValue {self.source}.{self.name}>"
21
+
22
+ def __eq__(self, other):
23
+ return (
24
+ isinstance(other, LazyLlamaAgentValue)
25
+ and self.source == other.source
26
+ and self.name == other.name
27
+ )
28
+
29
+ def __hash__(self):
30
+ return hash((self.source, self.name))
31
+
32
+
33
+ MessagesFormatterType = LazyMessagesFormatterType()
34
+
35
+
36
+ def build_mistral_formatter(name):
37
+ from llama_cpp_agent.messages_formatter import MessagesFormatter, PromptMarkers, Roles
38
+
39
+ if name == "mistral_v1":
40
+ markers = {
41
+ Roles.system: PromptMarkers(""" [INST]""", """ [/INST]"""),
42
+ Roles.user: PromptMarkers(""" [INST]""", """ [/INST]"""),
43
+ Roles.assistant: PromptMarkers(""" """, """</s>"""),
44
+ Roles.tool: PromptMarkers("", ""),
45
+ }
46
+ return MessagesFormatter("", markers, False, ["</s>"])
47
+
48
+ if name == "mistral_v2":
49
+ markers = {
50
+ Roles.system: PromptMarkers("""[INST] """, """[/INST]"""),
51
+ Roles.user: PromptMarkers("""[INST] """, """[/INST]"""),
52
+ Roles.assistant: PromptMarkers(""" """, """</s>"""),
53
+ Roles.tool: PromptMarkers("", ""),
54
+ }
55
+ return MessagesFormatter("", markers, False, ["</s>"])
56
+
57
+ if name == "mistral_v3_tekken":
58
+ markers = {
59
+ Roles.system: PromptMarkers("""[INST]""", """[/INST]"""),
60
+ Roles.user: PromptMarkers("""[INST]""", """[/INST]"""),
61
+ Roles.assistant: PromptMarkers("""""", """</s>"""),
62
+ Roles.tool: PromptMarkers("", ""),
63
+ }
64
+ return MessagesFormatter("", markers, False, ["</s>"])
65
+
66
+ raise ValueError(f"Unknown Mistral formatter: {name}")
67
+
68
+
69
+ mistral_v1_formatter = LazyLlamaAgentValue("custom_formatter", "mistral_v1")
70
+ mistral_v2_formatter = LazyLlamaAgentValue("custom_formatter", "mistral_v2")
71
+ mistral_v3_tekken_formatter = LazyLlamaAgentValue("custom_formatter", "mistral_v3_tekken")
llmdolphin.py CHANGED
The diff for this file is too large to render. See raw diff
 
llmenv.py CHANGED
@@ -1,8 +1,11 @@
1
- from llama_cpp_agent import MessagesFormatterType
2
- from formatter import mistral_v1_formatter, mistral_v2_formatter, mistral_v3_tekken_formatter
3
  from pathlib import Path
4
 
5
 
 
 
 
 
6
  llm_models = {
7
  #"": ["", MessagesFormatterType.LLAMA_3],
8
  #"": ["", MessagesFormatterType.MISTRAL],
@@ -161,8 +164,30 @@ llm_models = {
161
  "claude-3.7-sonnet-reasoning-gemma3-12B.Q4_K_M.gguf": ["mradermacher/claude-3.7-sonnet-reasoning-gemma3-12B-GGUF", MessagesFormatterType.ALPACA],
162
  "allura-org_MN-Lyrebird-12B-Q4_K_M.gguf": ["bartowski/allura-org_MN-Lyrebird-12B-GGUF", MessagesFormatterType.MISTRAL],
163
  "ape-fiction-2-mistral-nemo.Q4_K_M.gguf": ["mradermacher/ape-fiction-2-mistral-nemo-GGUF", MessagesFormatterType.MISTRAL],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  "Irixxed_Homunculus-12B-Q3T-v.0.3-Reasoner.Q4_K_M.gguf": ["mradermacher/Irixxed_Homunculus-12B-Q3T-v.0.3-Reasoner-GGUF", MessagesFormatterType.MISTRAL],
165
  "Gemma-2-Llama-Swallow-9b-pt-v0.1.Q4_K_M.gguf": ["mradermacher/Gemma-2-Llama-Swallow-9b-pt-v0.1-GGUF", MessagesFormatterType.ALPACA],
 
 
166
  "Qwen2.5-7B-base-french-bespoke-stratos-full-sft.i1-Q5_K_S.gguf": ["mradermacher/Qwen2.5-7B-base-french-bespoke-stratos-full-sft-i1-GGUF", MessagesFormatterType.OPEN_CHAT],
167
  "Protestant-Christian-Bible-Expert-v2.0-12B.Q4_K_M.gguf": ["mradermacher/Protestant-Christian-Bible-Expert-v2.0-12B-GGUF", MessagesFormatterType.MISTRAL],
168
  "openbuddy-qwen2.5llamaify-14b-v23.1-200k.i1-Q4_K_M.gguf": ["mradermacher/openbuddy-qwen2.5llamaify-14b-v23.1-200k-i1-GGUF", MessagesFormatterType.OPEN_CHAT],
@@ -178,6 +203,29 @@ llm_models = {
178
  #"": ["", MessagesFormatterType.OPEN_CHAT],
179
  #"": ["", MessagesFormatterType.CHATML],
180
  #"": ["", MessagesFormatterType.PHI_3],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  "SauerHuatuoSkyworkDeepWatt-o1-Llama-3.1-8B.Q5_K_M.gguf": ["mradermacher/SauerHuatuoSkyworkDeepWatt-o1-Llama-3.1-8B-GGUF", MessagesFormatterType.LLAMA_3],
182
  "care-japanese-llama3.1-8b.Q5_K_M.gguf": ["mradermacher/care-japanese-llama3.1-8b-GGUF", MessagesFormatterType.LLAMA_3],
183
  "UltraPatriMerge-12B.Q4_K_M.gguf": ["mradermacher/UltraPatriMerge-12B-GGUF", MessagesFormatterType.MISTRAL],
@@ -3838,8 +3886,11 @@ llm_loras = {str(Path(u).name): u for u in llm_loras_urls}
3838
  llm_models_dir = "./llm_models"
3839
  llm_loras_dir = "./llm_loras"
3840
 
 
 
3841
 
3842
  llm_formats = {
 
3843
  "MISTRAL": MessagesFormatterType.MISTRAL,
3844
  "CHATML": MessagesFormatterType.CHATML,
3845
  "VICUNA": MessagesFormatterType.VICUNA,
 
1
+ from formatter import MessagesFormatterType, mistral_v1_formatter, mistral_v2_formatter, mistral_v3_tekken_formatter
 
2
  from pathlib import Path
3
 
4
 
5
+ GRADIO_DEBUG_ENV_NAME = "GRADIO_DEBUG"
6
+ LLMDOLPHIN_STATE_NAMESPACE = "__llmdolphin__"
7
+
8
+
9
  llm_models = {
10
  #"": ["", MessagesFormatterType.LLAMA_3],
11
  #"": ["", MessagesFormatterType.MISTRAL],
 
164
  "claude-3.7-sonnet-reasoning-gemma3-12B.Q4_K_M.gguf": ["mradermacher/claude-3.7-sonnet-reasoning-gemma3-12B-GGUF", MessagesFormatterType.ALPACA],
165
  "allura-org_MN-Lyrebird-12B-Q4_K_M.gguf": ["bartowski/allura-org_MN-Lyrebird-12B-GGUF", MessagesFormatterType.MISTRAL],
166
  "ape-fiction-2-mistral-nemo.Q4_K_M.gguf": ["mradermacher/ape-fiction-2-mistral-nemo-GGUF", MessagesFormatterType.MISTRAL],
167
+ "Shadow-Crystal-12B.Q4_K_M.gguf": ["mradermacher/Shadow-Crystal-12B-GGUF", MessagesFormatterType.MISTRAL],
168
+ "Starry-Shadow-12B.Q4_K_M.gguf": ["mradermacher/Starry-Shadow-12B-GGUF", MessagesFormatterType.MISTRAL],
169
+ "Radiant-Shadow-12B.Q4_K_M.gguf": ["mradermacher/Radiant-Shadow-12B-GGUF", MessagesFormatterType.MISTRAL],
170
+ "Arsenic-Shahrazad-12B-v3.i1-Q4_K_M.gguf": ["mradermacher/Arsenic-Shahrazad-12B-v3-i1-GGUF", MessagesFormatterType.MISTRAL],
171
+ "Mistral-Nemo-2407-Instruct-12B-Deep-Thinking-Claude-Gemini-GPT5.2.i1-Q4_K_M.gguf": ["mradermacher/Mistral-Nemo-2407-Instruct-12B-Deep-Thinking-Claude-Gemini-GPT5.2-i1-GGUF", MessagesFormatterType.MISTRAL],
172
+ "Lunar-Abyss-12B.Q4_K_S.gguf": ["mradermacher/Lunar-Abyss-12B-GGUF", MessagesFormatterType.CHATML],
173
+ "prototype-x-12b-q6_k.gguf": ["Vortex5/Prototype-X-12b-Q6_K-GGUF", MessagesFormatterType.MISTRAL],
174
+ "gemma-3n-E4B-it-PaperWitch-heresy.i1-Q4_K_M.gguf": ["mradermacher/gemma-3n-E4B-it-PaperWitch-heresy-i1-GGUF", MessagesFormatterType.ALPACA],
175
+ "Violet-Mist-12B.i1-Q4_K_M.gguf": ["mradermacher/Violet-Mist-12B-i1-GGUF", MessagesFormatterType.MISTRAL],
176
+ "TheStoryteller-12B.i1-Q4_K_M.gguf": ["mradermacher/TheStoryteller-12B-i1-GGUF", MessagesFormatterType.CHATML],
177
+ "Dreamstar-12B.Q4_K_M.gguf": ["mradermacher/Dreamstar-12B-GGUF", MessagesFormatterType.MISTRAL],
178
+ "Mistral-Nemo-Instruct-2407-heretic-noslop-MPOA.Q4_K_M.gguf": ["mradermacher/Mistral-Nemo-Instruct-2407-heretic-noslop-MPOA-GGUF", MessagesFormatterType.MISTRAL],
179
+ "Equatorium-v3-12B.Q4_K_M.gguf": ["mradermacher/Equatorium-v3-12B-GGUF", MessagesFormatterType.MISTRAL],
180
+ "RPBizkit-v6-12B.Q4_K_M.gguf": ["mradermacher/RPBizkit-v6-12B-GGUF", MessagesFormatterType.MISTRAL],
181
+ "MN-VelvetCafe-RP-12B.i1-Q4_K_M.gguf": ["mradermacher/MN-VelvetCafe-RP-12B-i1-GGUF", MessagesFormatterType.CHATML],
182
+ "Aurora-Mirage-12B.Q4_K_M.gguf": ["mradermacher/Aurora-Mirage-12B-GGUF", MessagesFormatterType.CHATML],
183
+ "littlemonster-reasoning-12B-QKVO-heretic-HF.Q4_K_M.gguf": ["mradermacher/littlemonster-reasoning-12B-QKVO-heretic-HF-GGUF", MessagesFormatterType.ALPACA],
184
+ "QuasiStarSynth-12B-absolute-heresy.Q4_K_M.gguf": ["mradermacher/QuasiStarSynth-12B-absolute-heresy-GGUF", MessagesFormatterType.CHATML],
185
+ "ColdBrew-Nemo-12B-Arcane-Fusion-Combined-Thinker.Q4_K_M.gguf": ["mradermacher/ColdBrew-Nemo-12B-Arcane-Fusion-Combined-Thinker-GGUF", MessagesFormatterType.MISTRAL],
186
+ "AuroSlayerEtherealKrixUnslopMellRPMaxDARKNESS-12B.i1-Q4_K_M.gguf": ["mradermacher/AuroSlayerEtherealKrixUnslopMellRPMaxDARKNESS-12B-i1-GGUF", MessagesFormatterType.MISTRAL],
187
  "Irixxed_Homunculus-12B-Q3T-v.0.3-Reasoner.Q4_K_M.gguf": ["mradermacher/Irixxed_Homunculus-12B-Q3T-v.0.3-Reasoner-GGUF", MessagesFormatterType.MISTRAL],
188
  "Gemma-2-Llama-Swallow-9b-pt-v0.1.Q4_K_M.gguf": ["mradermacher/Gemma-2-Llama-Swallow-9b-pt-v0.1-GGUF", MessagesFormatterType.ALPACA],
189
+ "Tlacuilo-12B.Q4_K_M.gguf": ["mradermacher/Tlacuilo-12B-GGUF", MessagesFormatterType.MISTRAL],
190
+ "Nemo-Instruct-2407-MPOA-v4-12B.i1-Q4_K_M.gguf": ["mradermacher/Nemo-Instruct-2407-MPOA-v4-12B-i1-GGUF", MessagesFormatterType.MISTRAL],
191
  "Qwen2.5-7B-base-french-bespoke-stratos-full-sft.i1-Q5_K_S.gguf": ["mradermacher/Qwen2.5-7B-base-french-bespoke-stratos-full-sft-i1-GGUF", MessagesFormatterType.OPEN_CHAT],
192
  "Protestant-Christian-Bible-Expert-v2.0-12B.Q4_K_M.gguf": ["mradermacher/Protestant-Christian-Bible-Expert-v2.0-12B-GGUF", MessagesFormatterType.MISTRAL],
193
  "openbuddy-qwen2.5llamaify-14b-v23.1-200k.i1-Q4_K_M.gguf": ["mradermacher/openbuddy-qwen2.5llamaify-14b-v23.1-200k-i1-GGUF", MessagesFormatterType.OPEN_CHAT],
 
203
  #"": ["", MessagesFormatterType.OPEN_CHAT],
204
  #"": ["", MessagesFormatterType.CHATML],
205
  #"": ["", MessagesFormatterType.PHI_3],
206
+ "Equatorium-v2-12B.Q4_K_M.gguf": ["mradermacher/Equatorium-v2-12B-GGUF", MessagesFormatterType.MISTRAL],
207
+ "Mangione-12B-Model_Stock.Q4_K_M.gguf": ["mradermacher/Mangione-12B-Model_Stock-GGUF", MessagesFormatterType.MISTRAL],
208
+ "gemma-3-12b-it-biprojected-abliterated.Q4_K_M.gguf": ["mradermacher/gemma-3-12b-it-biprojected-abliterated-GGUF", MessagesFormatterType.ALPACA],
209
+ "mistralai-Mistral-Nemo-Instruct-2407-extensive-BP-abliteration-12B.Q4_K_M.gguf": ["mradermacher/mistralai-Mistral-Nemo-Instruct-2407-extensive-BP-abliteration-12B-GGUF", MessagesFormatterType.MISTRAL],
210
+ "PokeeAI_pokee_research_7b-Q5_K_M.gguf": ["bartowski/PokeeAI_pokee_research_7b-GGUF", MessagesFormatterType.OPEN_CHAT],
211
+ "FARE-8B.Q5_K_M.gguf": ["mradermacher/FARE-8B-GGUF", MessagesFormatterType.OPEN_CHAT],
212
+ "AdaVaR-3B.Q5_K_M.gguf": ["mradermacher/AdaVaR-3B-GGUF", MessagesFormatterType.OPEN_CHAT],
213
+ "Qwen2.5-14B-Valor.Q4_K_M.gguf": ["mradermacher/Qwen2.5-14B-Valor-GGUF", MessagesFormatterType.OPEN_CHAT],
214
+ "ukko-thinking-3.09b-v1.i1-Q5_K_M.gguf": ["mradermacher/ukko-thinking-3.09b-v1-i1-GGUF", MessagesFormatterType.OPEN_CHAT],
215
+ "Logos-3B.Q5_K_M.gguf": ["mradermacher/Logos-3B-GGUF", MessagesFormatterType.OPEN_CHAT],
216
+ "ReSeek-qwen2.5-7b-em-grpo.Q5_K_M.gguf": ["mradermacher/ReSeek-qwen2.5-7b-em-grpo-GGUF", MessagesFormatterType.OPEN_CHAT],
217
+ "VideoChat-R1_5.Q5_K_M.gguf": ["mradermacher/VideoChat-R1_5-GGUF", MessagesFormatterType.OPEN_CHAT],
218
+ "zen-eco-4b-instruct.i1-Q5_K_M.gguf": ["mradermacher/zen-eco-4b-instruct-i1-GGUF", MessagesFormatterType.OPEN_CHAT],
219
+ "nytheria-3b.i1-Q5_K_M.gguf": ["mradermacher/nytheria-3b-i1-GGUF", MessagesFormatterType.OPEN_CHAT],
220
+ "VideoJudgeR-3B.Q5_K_M.gguf": ["mradermacher/VideoJudgeR-3B-GGUF", MessagesFormatterType.OPEN_CHAT],
221
+ "Magrathic-12B.Q4_K_M.gguf": ["mradermacher/Magrathic-12B-GGUF", MessagesFormatterType.MISTRAL],
222
+ "phoenix-core-v1.0.Q4_K_M.gguf": ["mradermacher/phoenix-core-v1.0-GGUF", MessagesFormatterType.MISTRAL],
223
+ "GUI-G1-3B-v1.Q5_K_M.gguf": ["mradermacher/GUI-G1-3B-v1-GGUF", MessagesFormatterType.OPEN_CHAT],
224
+ "Impish-Irix-Kitsune.Q4_K_M.gguf": ["mradermacher/Impish-Irix-Kitsune-GGUF", MessagesFormatterType.MISTRAL],
225
+ "L3.3-Nemoblated-8B-V0.1.i1-Q5_K_M.gguf": ["mradermacher/L3.3-Nemoblated-8B-V0.1-i1-GGUF", MessagesFormatterType.LLAMA_3],
226
+ "Luna-Karcher-12B.Q4_K_M.gguf": ["mradermacher/Luna-Karcher-12B-GGUF", MessagesFormatterType.MISTRAL],
227
+ "Chimera-DeepSeek-NSFW-8B.Q5_K_M.gguf": ["mradermacher/Chimera-DeepSeek-NSFW-8B-GGUF", MessagesFormatterType.LLAMA_3],
228
+ "Toolbox-sft-3B.Q5_K_M.gguf": ["mradermacher/Toolbox-sft-3B-GGUF", MessagesFormatterType.OPEN_CHAT],
229
  "SauerHuatuoSkyworkDeepWatt-o1-Llama-3.1-8B.Q5_K_M.gguf": ["mradermacher/SauerHuatuoSkyworkDeepWatt-o1-Llama-3.1-8B-GGUF", MessagesFormatterType.LLAMA_3],
230
  "care-japanese-llama3.1-8b.Q5_K_M.gguf": ["mradermacher/care-japanese-llama3.1-8b-GGUF", MessagesFormatterType.LLAMA_3],
231
  "UltraPatriMerge-12B.Q4_K_M.gguf": ["mradermacher/UltraPatriMerge-12B-GGUF", MessagesFormatterType.MISTRAL],
 
3886
  llm_models_dir = "./llm_models"
3887
  llm_loras_dir = "./llm_loras"
3888
 
3889
+ LLM_FORMAT_AUTO_GGUF_DEFAULT = "__GGUF_DEFAULT__"
3890
+
3891
 
3892
  llm_formats = {
3893
+ "AUTO (GGUF DEFAULT)": LLM_FORMAT_AUTO_GGUF_DEFAULT,
3894
  "MISTRAL": MessagesFormatterType.MISTRAL,
3895
  "CHATML": MessagesFormatterType.CHATML,
3896
  "VICUNA": MessagesFormatterType.VICUNA,
modutils.py CHANGED
The diff for this file is too large to render. See raw diff
 
packages.txt CHANGED
@@ -1 +1,2 @@
1
- git-lfs aria2 ffmpeg
 
 
1
+ git-lfs
2
+ ffmpeg
requirements.txt CHANGED
@@ -2,19 +2,17 @@ stablepy==0.6.5
2
  diffusers
3
  transformers
4
  accelerate
5
- torch==2.5.1
6
  numpy<2
7
  gdown
8
  opencv-python
9
  huggingface_hub
10
- hf_xet
11
- hf_transfer
12
  scikit-build-core
13
- https://github.com/abetlen/llama-cpp-python/releases/download/v0.3.16-cu124/llama_cpp_python-0.3.16-cp310-cp310-linux_x86_64.whl
14
  git+https://github.com/John6666cat/llama-cpp-agent
15
  pybind11>=2.12
16
  rapidfuzz
17
- torchvision
18
  optimum[onnxruntime]
19
  #dartrs
20
  git+https://github.com/John6666cat/dartrs
@@ -24,5 +22,6 @@ wrapt-timeout-decorator
24
  sentencepiece
25
  unidecode
26
  matplotlib-inline
27
- https://github.com/mjun0812/flash-attention-prebuild-wheels/releases/download/v0.4.11/flash_attn-2.8.3+cu124torch2.5-cp310-cp310-linux_x86_64.whl
28
- pydantic==2.10.6
 
 
2
  diffusers
3
  transformers
4
  accelerate
5
+ torch==2.8.0
6
  numpy<2
7
  gdown
8
  opencv-python
9
  huggingface_hub
10
+ spaces
 
11
  scikit-build-core
12
+ https://github.com/John6666cat/llama-cpp-python/releases/download/v0.3.35-cu128-Basic-linux-20260412/llama_cpp_python-0.3.35+cu128.basic-cp310-cp310-linux_x86_64.whl
13
  git+https://github.com/John6666cat/llama-cpp-agent
14
  pybind11>=2.12
15
  rapidfuzz
 
16
  optimum[onnxruntime]
17
  #dartrs
18
  git+https://github.com/John6666cat/dartrs
 
22
  sentencepiece
23
  unidecode
24
  matplotlib-inline
25
+ mediapipe==0.10.5
26
+ einops
27
+ # pydantic==2.10.6
tagger/fl2sd3longcap.py CHANGED
@@ -1,49 +1,34 @@
1
- import spaces
2
- from transformers import AutoProcessor, AutoModelForCausalLM
3
  import re
4
- from PIL import Image
 
5
  import torch
 
 
 
6
 
7
- from transformers.utils import is_flash_attn_2_available
8
- if not is_flash_attn_2_available():
9
- import subprocess
10
- subprocess.run('pip install flash-attn --no-build-isolation', env={'FLASH_ATTENTION_SKIP_CUDA_BUILD': "TRUE"}, shell=True)
11
 
12
  device = "cuda" if torch.cuda.is_available() else "cpu"
13
 
14
  try:
15
- fl_model = AutoModelForCausalLM.from_pretrained('gokaygokay/Florence-2-SD3-Captioner', trust_remote_code=True).to("cpu").eval()
16
- fl_processor = AutoProcessor.from_pretrained('gokaygokay/Florence-2-SD3-Captioner', trust_remote_code=True)
17
  except Exception as e:
18
  print(e)
19
  fl_model = fl_processor = None
 
 
20
 
21
  def fl_modify_caption(caption: str) -> str:
22
- """
23
- Removes specific prefixes from captions if present, otherwise returns the original caption.
24
- Args:
25
- caption (str): A string containing a caption.
26
- Returns:
27
- str: The caption with the prefix removed if it was present, or the original caption.
28
- """
29
- # Define the prefixes to remove
30
  prefix_substrings = [
31
  ('captured from ', ''),
32
- ('captured at ', '')
33
  ]
34
-
35
- # Create a regex pattern to match any of the prefixes
36
  pattern = '|'.join([re.escape(opening) for opening, _ in prefix_substrings])
37
  replacers = {opening.lower(): replacer for opening, replacer in prefix_substrings}
38
-
39
- # Function to replace matched prefix with its corresponding replacement
40
  def replace_fn(match):
41
  return replacers[match.group(0).lower()]
42
-
43
- # Apply the regex to the caption
44
  modified_caption = re.sub(pattern, replace_fn, caption, count=1, flags=re.IGNORECASE)
45
-
46
- # If the caption was modified, return the modified version; otherwise, return the original
47
  return modified_caption if modified_caption != caption else caption
48
 
49
 
@@ -52,32 +37,32 @@ def fl_run_example(image):
52
  task_prompt = "<DESCRIPTION>"
53
  prompt = task_prompt + "Describe this image in great detail."
54
 
55
- # Ensure the image is in RGB mode
56
  if image.mode != "RGB":
57
  image = image.convert("RGB")
58
 
59
- fl_model.to(device)
60
- inputs = fl_processor(text=prompt, images=image, return_tensors="pt").to(device)
61
- generated_ids = fl_model.generate(
62
  input_ids=inputs["input_ids"],
 
63
  pixel_values=inputs["pixel_values"],
64
- max_new_tokens=1024,
65
- num_beams=3
66
  )
67
- fl_model.to("cpu")
68
  generated_text = fl_processor.batch_decode(generated_ids, skip_special_tokens=False)[0]
69
- parsed_answer = fl_processor.post_process_generation(generated_text, task=task_prompt, image_size=(image.width, image.height))
 
 
 
 
70
  return fl_modify_caption(parsed_answer["<DESCRIPTION>"])
71
 
72
 
73
  def predict_tags_fl2_sd3(image: Image.Image, input_tags: str, algo: list[str]):
74
  def to_list(s):
75
  return [x.strip() for x in s.split(",") if not s == ""]
76
-
77
  def list_uniq(l):
78
  return sorted(set(l), key=l.index)
79
-
80
- if not "Use Florence-2-SD3-Long-Captioner" in algo:
81
  return input_tags
82
  tag_list = list_uniq(to_list(input_tags) + to_list(fl_run_example(image) + ", "))
83
  tag_list.remove("")
 
 
 
1
  import re
2
+
3
+ import spaces
4
  import torch
5
+ from PIL import Image
6
+
7
+ from tagger.florence2_compat import generate_florence2, load_florence2_bundle, prepare_florence2_inputs
8
 
 
 
 
 
9
 
10
  device = "cuda" if torch.cuda.is_available() else "cpu"
11
 
12
  try:
13
+ fl_model, fl_processor, fl_audit = load_florence2_bundle('gokaygokay/Florence-2-SD3-Captioner', device)
 
14
  except Exception as e:
15
  print(e)
16
  fl_model = fl_processor = None
17
+ fl_audit = {"load_error": repr(e)}
18
+
19
 
20
  def fl_modify_caption(caption: str) -> str:
 
 
 
 
 
 
 
 
21
  prefix_substrings = [
22
  ('captured from ', ''),
23
+ ('captured at ', ''),
24
  ]
 
 
25
  pattern = '|'.join([re.escape(opening) for opening, _ in prefix_substrings])
26
  replacers = {opening.lower(): replacer for opening, replacer in prefix_substrings}
27
+
 
28
  def replace_fn(match):
29
  return replacers[match.group(0).lower()]
30
+
 
31
  modified_caption = re.sub(pattern, replace_fn, caption, count=1, flags=re.IGNORECASE)
 
 
32
  return modified_caption if modified_caption != caption else caption
33
 
34
 
 
37
  task_prompt = "<DESCRIPTION>"
38
  prompt = task_prompt + "Describe this image in great detail."
39
 
 
40
  if image.mode != "RGB":
41
  image = image.convert("RGB")
42
 
43
+ inputs = prepare_florence2_inputs(fl_processor, fl_model, prompt, image, device)
44
+ generated_ids = generate_florence2(fl_model,
 
45
  input_ids=inputs["input_ids"],
46
+ attention_mask=inputs.get("attention_mask"),
47
  pixel_values=inputs["pixel_values"],
 
 
48
  )
 
49
  generated_text = fl_processor.batch_decode(generated_ids, skip_special_tokens=False)[0]
50
+ parsed_answer = fl_processor.post_process_generation(
51
+ generated_text,
52
+ task=task_prompt,
53
+ image_size=(image.width, image.height),
54
+ )
55
  return fl_modify_caption(parsed_answer["<DESCRIPTION>"])
56
 
57
 
58
  def predict_tags_fl2_sd3(image: Image.Image, input_tags: str, algo: list[str]):
59
  def to_list(s):
60
  return [x.strip() for x in s.split(",") if not s == ""]
61
+
62
  def list_uniq(l):
63
  return sorted(set(l), key=l.index)
64
+
65
+ if "Use Florence-2-SD3-Long-Captioner" not in algo:
66
  return input_tags
67
  tag_list = list_uniq(to_list(input_tags) + to_list(fl_run_example(image) + ", "))
68
  tag_list.remove("")
tagger/florence2_compat.py ADDED
@@ -0,0 +1,925 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import functools
3
+ import gc
4
+ import json
5
+ import os
6
+ import re
7
+ import types
8
+ from typing import Any, Dict, Iterable, Optional, Tuple
9
+
10
+ import torch
11
+ import transformers
12
+ from transformers import AutoProcessor
13
+ from transformers.tokenization_utils_base import AddedToken
14
+
15
+
16
+ try:
17
+ from huggingface_hub import snapshot_download
18
+ except Exception:
19
+ snapshot_download = None
20
+
21
+ try:
22
+ from safetensors.torch import load_file as load_safetensors_file
23
+ except Exception:
24
+ load_safetensors_file = None
25
+
26
+ try:
27
+ from transformers import AutoModelForCausalLM, Florence2ForConditionalGeneration
28
+ except Exception:
29
+ AutoModelForCausalLM = None
30
+ Florence2ForConditionalGeneration = None
31
+
32
+ try:
33
+ from transformers.generation.utils import GenerationMixin
34
+ except Exception:
35
+ GenerationMixin = None
36
+
37
+ try:
38
+ from transformers.models.auto.auto_factory import add_generation_mixin_to_remote_model
39
+ except Exception:
40
+ add_generation_mixin_to_remote_model = None
41
+
42
+
43
+ _IMAGE_TOKEN = "<image>"
44
+
45
+ # Florence2 compat settings. Keep these as plain module constants for easier HF Space maintenance.
46
+ FLORENCE2_LEGACY_ATTN_IMPLEMENTATION = "eager"
47
+ FLORENCE2_LEGACY_USE_CACHE = True
48
+ FLORENCE2_LEGACY_MAX_NUM_BEAMS = 1
49
+ FLORENCE2_PROCESSOR_USE_FAST = False
50
+ FLORENCE2_GENERATE_DEFAULT_MAX_NEW_TOKENS = 512
51
+ FLORENCE2_GENERATE_DEFAULT_NUM_BEAMS = 3
52
+ FLORENCE2_GENERATE_MAX_NEW_TOKENS_BY_REPO = {
53
+ "MiaoshouAI/Florence-2-large-PromptGen-v2.0": 256,
54
+ "gokaygokay/Florence-2-SD3-Captioner": 384,
55
+ "gokaygokay/Florence-2-Flux": 384,
56
+ "gokaygokay/Florence-2-Flux-Large": 384,
57
+ "thwri/CogFlorence-2.2-Large": 512,
58
+ }
59
+
60
+ # Pin the currently working Florence checkpoints so trust_remote_code fetches stay reproducible.
61
+ FLORENCE2_REVISIONS = {
62
+ "gokaygokay/Florence-2-Flux": "d17454c4e4bccb3bfe52477634089b8515b1bc3c",
63
+ "gokaygokay/Florence-2-Flux-Large": "ed3af3df6d23d9f25d1dd4ce05ba95bb43c37209",
64
+ "gokaygokay/Florence-2-SD3-Captioner": "178fd612533079793c33ede7245b29d23db307b5",
65
+ "thwri/CogFlorence-2.2-Large": "19f2c614fdfd18ba49c81d567d70d3a68313e0bb",
66
+ "MiaoshouAI/Florence-2-large-PromptGen-v2.0": "4aa33eaf50aab040fe8523312ff52eb53322c220",
67
+ }
68
+
69
+ # Cog processor is the only unstable case. Try the model repo first as documented,
70
+ # then fall back to upstream Florence processor sources.
71
+ FLORENCE2_PROCESSOR_CANDIDATES = {
72
+ "thwri/CogFlorence-2.2-Large": [
73
+ {
74
+ "repo_id": "thwri/CogFlorence-2.2-Large",
75
+ "trust_remote_code": True,
76
+ "revision": "19f2c614fdfd18ba49c81d567d70d3a68313e0bb",
77
+ },
78
+ {
79
+ "repo_id": "microsoft/Florence-2-large",
80
+ "trust_remote_code": True,
81
+ "revision": "00d2f1570b00c6dea5df998f5635db96840436bc",
82
+ },
83
+ {
84
+ "repo_id": "microsoft/Florence-2-large",
85
+ "trust_remote_code": False,
86
+ },
87
+ ],
88
+ }
89
+
90
+
91
+ def _major_version() -> int:
92
+ m = re.match(r"(\d+)", getattr(transformers, "__version__", "0"))
93
+ return int(m.group(1)) if m else 0
94
+
95
+
96
+ def _dtype_kwargs(device: str) -> Dict[str, Any]:
97
+ dtype = torch.float16 if device == "cuda" else torch.float32
98
+ if _major_version() >= 5:
99
+ return {"dtype": dtype}
100
+ return {"torch_dtype": dtype}
101
+
102
+
103
+ def _revision_kwargs(repo_id: str) -> Dict[str, Any]:
104
+ revision = FLORENCE2_REVISIONS.get(repo_id)
105
+ if not revision:
106
+ return {}
107
+ return {"revision": revision, "code_revision": revision}
108
+
109
+
110
+ def _processor_candidates(repo_id: str):
111
+ if repo_id in FLORENCE2_PROCESSOR_CANDIDATES:
112
+ return FLORENCE2_PROCESSOR_CANDIDATES[repo_id]
113
+ base = {"repo_id": repo_id, "trust_remote_code": True}
114
+ base.update(_revision_kwargs(repo_id))
115
+ return [base, {"repo_id": repo_id, "trust_remote_code": False}]
116
+
117
+
118
+ def _processor_use_fast(processor_repo_id: str, requested_repo_id: str) -> bool:
119
+ if requested_repo_id == "thwri/CogFlorence-2.2-Large":
120
+ return True
121
+ return FLORENCE2_PROCESSOR_USE_FAST
122
+
123
+
124
+
125
+ @functools.lru_cache(maxsize=4)
126
+ def _ensure_florence2_runtime(device: str) -> Dict[str, Any]:
127
+ status: Dict[str, Any] = {
128
+ "device": device,
129
+ "flash_attn_2_available": False,
130
+ "install_attempted": False,
131
+ "install_succeeded": False,
132
+ "install_returncode": None,
133
+ }
134
+ if device != "cuda":
135
+ print("[Florence2 compat] runtime probe: non-cuda device")
136
+ else:
137
+ print("[Florence2 compat] runtime probe: flash-attn runtime install disabled")
138
+ return status
139
+
140
+ def _native_attn_kwargs(device: str) -> Tuple[Dict[str, Any], Dict[str, Any]]:
141
+ runtime = dict(_ensure_florence2_runtime(device))
142
+ runtime["attn_implementation"] = "default"
143
+ return {}, runtime
144
+
145
+
146
+ def _legacy_attn_kwargs(device: str) -> Tuple[Dict[str, Any], Dict[str, Any]]:
147
+ runtime = dict(_ensure_florence2_runtime(device))
148
+ requested = str(FLORENCE2_LEGACY_ATTN_IMPLEMENTATION).strip().lower() or "eager"
149
+ if requested != "eager":
150
+ print(f"[Florence2 compat] legacy attention request {requested} unsupported here, falling back to eager")
151
+ requested = "eager"
152
+ runtime["attn_implementation"] = requested
153
+ return {"attn_implementation": requested}, runtime
154
+
155
+
156
+ def _native_florence_module(model: Any) -> bool:
157
+ module = getattr(getattr(model, "__class__", None), "__module__", "") or ""
158
+ return module.startswith("transformers.models.florence2")
159
+
160
+
161
+ def _normalize_load_result(result: Any) -> Tuple[Any, Dict[str, Any]]:
162
+ if isinstance(result, tuple) and len(result) == 2 and isinstance(result[1], dict):
163
+ return result
164
+ return result, {}
165
+
166
+
167
+ def _safe_to_device(model: Any, device: str):
168
+ return model.eval().to(device)
169
+
170
+
171
+ def _load_processor(repo_id: str):
172
+ errors = []
173
+ for candidate in _processor_candidates(repo_id):
174
+ processor_repo_id = candidate["repo_id"]
175
+ trust_remote_code = bool(candidate.get("trust_remote_code", True))
176
+ revision = candidate.get("revision", None)
177
+ use_fast = _processor_use_fast(processor_repo_id, repo_id)
178
+ kwargs = {"use_fast": use_fast}
179
+ if revision:
180
+ kwargs["revision"] = revision
181
+ try:
182
+ processor = AutoProcessor.from_pretrained(
183
+ processor_repo_id,
184
+ trust_remote_code=trust_remote_code,
185
+ **kwargs,
186
+ )
187
+ mode = "trust_remote_code=True" if trust_remote_code else "native fallback"
188
+ print(
189
+ f"[Florence2 compat] processor={repo_id} source={processor_repo_id} "
190
+ f"mode={mode} revision={revision or 'main'} use_fast={use_fast}"
191
+ )
192
+ return processor
193
+ except Exception as e:
194
+ errors.append({
195
+ "source": processor_repo_id,
196
+ "trust_remote_code": trust_remote_code,
197
+ "revision": revision,
198
+ "error": repr(e),
199
+ })
200
+ raise RuntimeError(f"Failed to load processor for {repo_id}: {errors}")
201
+
202
+
203
+ def _load_native_model(repo_id: str, device: str):
204
+ if Florence2ForConditionalGeneration is None:
205
+ raise RuntimeError("Florence2ForConditionalGeneration is unavailable in this transformers build")
206
+ attn_kwargs, runtime = _native_attn_kwargs(device)
207
+ load_kwargs = dict(output_loading_info=True, **_dtype_kwargs(device), **attn_kwargs, **_revision_kwargs(repo_id))
208
+ model, loading_info = _normalize_load_result(
209
+ Florence2ForConditionalGeneration.from_pretrained(repo_id, **load_kwargs)
210
+ )
211
+ print(
212
+ f"[Florence2 compat] model={repo_id} loaded as native Florence2ForConditionalGeneration "
213
+ f"revision={FLORENCE2_REVISIONS.get(repo_id) or 'main'} attn={runtime.get('attn_implementation')}"
214
+ )
215
+ return model, loading_info, runtime
216
+
217
+
218
+ def _patch_generation_mixin(module: Any, label: str) -> bool:
219
+ if module is None or GenerationMixin is None:
220
+ return False
221
+ if hasattr(module, "generate") and callable(getattr(module, "generate")):
222
+ return False
223
+
224
+ cls = module.__class__
225
+ if issubclass(cls, GenerationMixin):
226
+ return False
227
+
228
+ if add_generation_mixin_to_remote_model is not None:
229
+ try:
230
+ patched_cls = add_generation_mixin_to_remote_model(cls)
231
+ if patched_cls is not cls:
232
+ module.__class__ = patched_cls
233
+ print(f"[Florence2 compat] patched {label} class with GenerationMixin: {patched_cls.__name__}")
234
+ return True
235
+ except Exception as e:
236
+ print(f"[Florence2 compat] official GenerationMixin patch failed for {label}: {e!r}")
237
+
238
+ patched_cls = type(f"{cls.__name__}WithGenerationMixin", (cls, GenerationMixin), {})
239
+ module.__class__ = patched_cls
240
+ print(f"[Florence2 compat] patched {label} class with GenerationMixin: {patched_cls.__name__}")
241
+ return True
242
+
243
+
244
+ def _first_non_none(*values):
245
+ for value in values:
246
+ if value is not None:
247
+ return value
248
+ return None
249
+
250
+
251
+ def _token_like_int(value: Any) -> Optional[int]:
252
+ if isinstance(value, bool):
253
+ return None
254
+ if isinstance(value, int):
255
+ return int(value)
256
+ return None
257
+
258
+
259
+ def _resolve_generation_ids(model: Any, processor: Any = None) -> Dict[str, Optional[int]]:
260
+ config = getattr(model, "config", None)
261
+ text_config = getattr(config, "text_config", None) if config is not None else None
262
+ tokenizer = getattr(processor, "tokenizer", None) if processor is not None else None
263
+
264
+ bos_token_id = _first_non_none(
265
+ _token_like_int(getattr(config, "bos_token_id", None)),
266
+ _token_like_int(getattr(text_config, "bos_token_id", None)),
267
+ _token_like_int(getattr(tokenizer, "bos_token_id", None)),
268
+ )
269
+ eos_token_id = _first_non_none(
270
+ _token_like_int(getattr(config, "eos_token_id", None)),
271
+ _token_like_int(getattr(text_config, "eos_token_id", None)),
272
+ _token_like_int(getattr(tokenizer, "eos_token_id", None)),
273
+ )
274
+ pad_token_id = _first_non_none(
275
+ _token_like_int(getattr(config, "pad_token_id", None)),
276
+ _token_like_int(getattr(text_config, "pad_token_id", None)),
277
+ _token_like_int(getattr(tokenizer, "pad_token_id", None)),
278
+ )
279
+ decoder_start_token_id = _first_non_none(
280
+ _token_like_int(getattr(config, "decoder_start_token_id", None)),
281
+ _token_like_int(getattr(text_config, "decoder_start_token_id", None)),
282
+ eos_token_id,
283
+ bos_token_id,
284
+ )
285
+
286
+ return {
287
+ "bos_token_id": bos_token_id,
288
+ "eos_token_id": eos_token_id,
289
+ "pad_token_id": pad_token_id,
290
+ "decoder_start_token_id": decoder_start_token_id,
291
+ }
292
+
293
+
294
+ def _apply_generation_ids(target: Any, ids: Dict[str, Optional[int]]) -> None:
295
+ if target is None:
296
+ return
297
+ for key, value in ids.items():
298
+ if value is None:
299
+ continue
300
+ try:
301
+ current = getattr(target, key, None)
302
+ except Exception:
303
+ current = None
304
+ if current is None:
305
+ try:
306
+ setattr(target, key, value)
307
+ except Exception:
308
+ pass
309
+
310
+
311
+ def _patch_prepare_inputs_for_generation(module: Any) -> bool:
312
+ if module is None or getattr(module, "_florence2_prepare_inputs_patched", False):
313
+ return False
314
+ original = getattr(module, "prepare_inputs_for_generation", None)
315
+ if not callable(original):
316
+ return False
317
+
318
+ def _wrapped_prepare_inputs_for_generation(self, decoder_input_ids, *args, **kwargs):
319
+ past_key_values = kwargs.get("past_key_values", None)
320
+ if past_key_values is not None:
321
+ reset_to_none = False
322
+ try:
323
+ first_layer = past_key_values[0]
324
+ first_k = first_layer[0] if first_layer is not None else None
325
+ if first_k is None:
326
+ reset_to_none = True
327
+ except Exception:
328
+ reset_to_none = True
329
+ if reset_to_none:
330
+ kwargs["past_key_values"] = None
331
+ return original(decoder_input_ids, *args, **kwargs)
332
+
333
+ module.prepare_inputs_for_generation = types.MethodType(_wrapped_prepare_inputs_for_generation, module)
334
+ module._florence2_prepare_inputs_patched = True
335
+ print("[Florence2 compat] patched legacy prepare_inputs_for_generation guard")
336
+ return True
337
+
338
+
339
+ def _safe_module_device_dtype(module: Any) -> Tuple[Optional[str], Optional[torch.dtype]]:
340
+ if module is None or not hasattr(module, "parameters"):
341
+ return None, None
342
+ try:
343
+ param = next(module.parameters())
344
+ return str(param.device), param.dtype
345
+ except Exception:
346
+ return None, None
347
+
348
+
349
+
350
+ def _beam_cap(default: int = 1) -> int:
351
+ raw = str(FLORENCE2_LEGACY_MAX_NUM_BEAMS if FLORENCE2_LEGACY_MAX_NUM_BEAMS is not None else default).strip()
352
+ try:
353
+ value = int(raw)
354
+ except Exception:
355
+ value = default
356
+ return max(0, value)
357
+
358
+
359
+ def _copy_generation_config(config: Any) -> Any:
360
+ if config is None:
361
+ return None
362
+ try:
363
+ return copy.deepcopy(config)
364
+ except Exception:
365
+ return config
366
+
367
+
368
+ def _prepare_generation_config_for_call(model: Any, language_model: Any, kwargs: Dict[str, Any], generation_ids: Dict[str, Optional[int]]) -> None:
369
+ active_generation_config = _copy_generation_config(getattr(model, "generation_config", None))
370
+ if active_generation_config is None and language_model is not None:
371
+ active_generation_config = _copy_generation_config(getattr(language_model, "generation_config", None))
372
+ if active_generation_config is None:
373
+ return
374
+
375
+ _apply_generation_ids(active_generation_config, generation_ids)
376
+
377
+ num_beams = kwargs.get("num_beams", getattr(active_generation_config, "num_beams", None))
378
+ try:
379
+ num_beams = int(num_beams) if num_beams is not None else None
380
+ except Exception:
381
+ num_beams = None
382
+
383
+ if num_beams is not None:
384
+ active_generation_config.num_beams = num_beams
385
+
386
+ if num_beams is None or num_beams <= 1:
387
+ try:
388
+ active_generation_config.early_stopping = False
389
+ except Exception:
390
+ pass
391
+ kwargs.pop("early_stopping", None)
392
+
393
+ kwargs.setdefault("generation_config", active_generation_config)
394
+ if language_model is not None:
395
+ try:
396
+ language_model.generation_config = active_generation_config
397
+ except Exception:
398
+ pass
399
+
400
+
401
+ def _patch_legacy_generate_bridge(model: Any, processor: Any = None) -> None:
402
+ language_model = getattr(model, "language_model", None)
403
+ patched_inner = _patch_generation_mixin(language_model, "language_model")
404
+ _patch_prepare_inputs_for_generation(language_model)
405
+
406
+ generation_ids = _resolve_generation_ids(model, processor)
407
+ _apply_generation_ids(getattr(model, "generation_config", None), generation_ids)
408
+
409
+ if language_model is not None:
410
+ try:
411
+ if hasattr(model, "generation_config"):
412
+ language_model.generation_config = model.generation_config
413
+ if hasattr(language_model, "generation_config"):
414
+ _apply_generation_ids(language_model.generation_config, generation_ids)
415
+ print("[Florence2 compat] bridged generation_config -> language_model.generation_config")
416
+ except Exception as e:
417
+ print(f"[Florence2 compat] failed to bridge generation_config: {e!r}")
418
+
419
+ if getattr(model, "_florence2_generate_bridge_patched", False):
420
+ return
421
+
422
+ original_generate = getattr(model, "generate", None)
423
+ if not callable(original_generate):
424
+ return
425
+
426
+ def _wrapped_generate(self, *args, **kwargs):
427
+ lm = getattr(self, "language_model", None)
428
+ if lm is not None and hasattr(self, "generation_config"):
429
+ try:
430
+ lm.generation_config = self.generation_config
431
+ except Exception:
432
+ pass
433
+ generation_ids = _resolve_generation_ids(self, processor)
434
+ _apply_generation_ids(getattr(self, "generation_config", None), generation_ids)
435
+ if lm is not None and hasattr(lm, "generation_config"):
436
+ _apply_generation_ids(lm.generation_config, generation_ids)
437
+
438
+ kwargs.setdefault("use_cache", FLORENCE2_LEGACY_USE_CACHE)
439
+
440
+ beam_cap = _beam_cap(default=1)
441
+ if beam_cap > 0:
442
+ requested_beams = kwargs.get("num_beams", None)
443
+ if requested_beams is None:
444
+ kwargs["num_beams"] = beam_cap
445
+ else:
446
+ try:
447
+ requested_beams_int = int(requested_beams)
448
+ except Exception:
449
+ requested_beams_int = requested_beams
450
+ if isinstance(requested_beams_int, int) and requested_beams_int > beam_cap:
451
+ print(
452
+ f"[Florence2 compat] clamped legacy num_beams from {requested_beams_int} to {beam_cap}"
453
+ )
454
+ kwargs["num_beams"] = beam_cap
455
+
456
+ for key, value in generation_ids.items():
457
+ if value is not None:
458
+ kwargs.setdefault(key, value)
459
+
460
+ _prepare_generation_config_for_call(self, lm, kwargs, generation_ids)
461
+
462
+ _condensed_generate_log(self, kwargs)
463
+ return original_generate(*args, **kwargs)
464
+
465
+ model.generate = types.MethodType(_wrapped_generate, model)
466
+ model._florence2_generate_bridge_patched = True
467
+ print("[Florence2 compat] legacy generate defaults: " + ", ".join(f"{k}={v}" for k, v in generation_ids.items()))
468
+ if patched_inner:
469
+ print("[Florence2 compat] patched legacy Florence generate bridge")
470
+
471
+
472
+ def _load_legacy_model(repo_id: str, device: str, processor: Any = None):
473
+ if AutoModelForCausalLM is None:
474
+ raise RuntimeError("AutoModelForCausalLM is unavailable in this transformers build")
475
+ attn_kwargs, runtime = _legacy_attn_kwargs(device)
476
+ load_kwargs = dict(output_loading_info=True, **_dtype_kwargs(device), **attn_kwargs, **_revision_kwargs(repo_id))
477
+ try:
478
+ model, loading_info = _normalize_load_result(
479
+ AutoModelForCausalLM.from_pretrained(repo_id, trust_remote_code=True, **load_kwargs)
480
+ )
481
+ except TypeError:
482
+ load_kwargs.pop("attn_implementation", None)
483
+ runtime["attn_implementation"] = "legacy_default"
484
+ model, loading_info = _normalize_load_result(
485
+ AutoModelForCausalLM.from_pretrained(repo_id, trust_remote_code=True, **load_kwargs)
486
+ )
487
+ _patch_legacy_generate_bridge(model, processor=processor)
488
+ print(
489
+ f"[Florence2 compat] model={repo_id} loaded via AutoModelForCausalLM + trust_remote_code=True "
490
+ f"revision={FLORENCE2_REVISIONS.get(repo_id) or 'main'} attn={runtime.get('attn_implementation')}"
491
+ )
492
+ return model, loading_info, runtime
493
+
494
+
495
+ def _ensure_image_token(processor: Any, model: Any) -> None:
496
+ tokenizer = getattr(processor, "tokenizer", None)
497
+ config = getattr(model, "config", None)
498
+ if tokenizer is None or config is None:
499
+ return
500
+
501
+ tokenizer.image_token = getattr(tokenizer, "image_token", None) or _IMAGE_TOKEN
502
+
503
+ token_id = tokenizer.convert_tokens_to_ids(tokenizer.image_token)
504
+ needs_add = token_id is None
505
+ if token_id is not None:
506
+ unk_id = getattr(tokenizer, "unk_token_id", None)
507
+ if unk_id is not None and token_id == unk_id:
508
+ pieces = tokenizer.encode(tokenizer.image_token, add_special_tokens=False)
509
+ needs_add = len(pieces) != 1 or pieces[0] == unk_id
510
+
511
+ added = 0
512
+ if needs_add:
513
+ added = tokenizer.add_tokens(
514
+ AddedToken(tokenizer.image_token, special=True, normalized=False),
515
+ special_tokens=True,
516
+ )
517
+ print(f"[Florence2 compat] added special image token to tokenizer: added={added}")
518
+
519
+ pieces = tokenizer.encode(tokenizer.image_token, add_special_tokens=False)
520
+ if len(pieces) == 1:
521
+ image_token_id = int(pieces[0])
522
+ tokenizer.image_token_id = image_token_id
523
+ if getattr(config, "image_token_id", None) != image_token_id:
524
+ config.image_token_id = image_token_id
525
+ print(f"[Florence2 compat] model.config.image_token_id set to {image_token_id}")
526
+
527
+ if added > 0 and hasattr(model, "resize_token_embeddings"):
528
+ try:
529
+ model.resize_token_embeddings(len(tokenizer), pad_to_multiple_of=64)
530
+ except TypeError:
531
+ try:
532
+ model.resize_token_embeddings(len(tokenizer), 64)
533
+ except TypeError:
534
+ model.resize_token_embeddings(len(tokenizer))
535
+ print(f"[Florence2 compat] resize_token_embeddings({len(tokenizer)}) applied")
536
+
537
+
538
+ def audit_loading_info(repo_id: str, loading_info: Dict[str, Any]) -> Dict[str, Any]:
539
+ missing = list(loading_info.get("missing_keys") or [])
540
+ unexpected = list(loading_info.get("unexpected_keys") or [])
541
+
542
+ critical_patterns = (
543
+ "lm_head.weight",
544
+ "model.language_model.decoder.",
545
+ "model.multi_modal_projector.",
546
+ )
547
+ legacy_patterns = (
548
+ "language_model.model.",
549
+ "language_model.lm_head.weight",
550
+ "image_projection",
551
+ "image_proj_norm.",
552
+ "image_pos_embed.",
553
+ "visual_temporal_embed.",
554
+ "vision_tower.",
555
+ )
556
+
557
+ critical_missing = [k for k in missing if any(p in k for p in critical_patterns)]
558
+ legacy_unexpected = [k for k in unexpected if any(p in k for p in legacy_patterns)]
559
+ suspicious = bool(critical_missing and legacy_unexpected)
560
+
561
+ if suspicious:
562
+ print(
563
+ f"[Florence2 compat] suspicious native load mismatch for {repo_id}: "
564
+ f"critical_missing={len(critical_missing)} legacy_unexpected={len(legacy_unexpected)}"
565
+ )
566
+ print("[Florence2 compat] trying legacy->native key conversion fallback")
567
+
568
+ return {
569
+ "missing_keys": missing,
570
+ "unexpected_keys": unexpected,
571
+ "missing_count": len(missing),
572
+ "unexpected_count": len(unexpected),
573
+ "critical_missing_count": len(critical_missing),
574
+ "legacy_unexpected_count": len(legacy_unexpected),
575
+ "suspicious_mismatch": suspicious,
576
+ }
577
+
578
+
579
+ def _collect_weight_files(snapshot_dir: str) -> Iterable[str]:
580
+ candidates = []
581
+ for index_name in ("model.safetensors.index.json", "pytorch_model.bin.index.json"):
582
+ index_path = os.path.join(snapshot_dir, index_name)
583
+ if os.path.exists(index_path):
584
+ with open(index_path, "r", encoding="utf-8") as f:
585
+ index_data = json.load(f)
586
+ weight_map = index_data.get("weight_map") or {}
587
+ seen = set()
588
+ for rel_path in weight_map.values():
589
+ if rel_path not in seen:
590
+ candidates.append(os.path.join(snapshot_dir, rel_path))
591
+ seen.add(rel_path)
592
+ return candidates
593
+
594
+ for file_name in (
595
+ "model.safetensors",
596
+ "pytorch_model.bin",
597
+ "pytorch_model.pt",
598
+ ):
599
+ path = os.path.join(snapshot_dir, file_name)
600
+ if os.path.exists(path):
601
+ return [path]
602
+
603
+ for name in sorted(os.listdir(snapshot_dir)):
604
+ if name.endswith(".safetensors") or name.endswith(".bin"):
605
+ candidates.append(os.path.join(snapshot_dir, name))
606
+ return candidates
607
+
608
+
609
+ def _load_state_dict_from_snapshot(repo_id: str) -> Dict[str, torch.Tensor]:
610
+ if snapshot_download is None:
611
+ raise RuntimeError("huggingface_hub.snapshot_download is unavailable")
612
+
613
+ snapshot_dir = snapshot_download(
614
+ repo_id,
615
+ revision=FLORENCE2_REVISIONS.get(repo_id),
616
+ allow_patterns=[
617
+ "*.safetensors",
618
+ "*.safetensors.index.json",
619
+ "*.bin",
620
+ "*.bin.index.json",
621
+ "config.json",
622
+ "generation_config.json",
623
+ ],
624
+ ignore_patterns=["*.msgpack", "*.h5", "*.ot", "*.onnx", "*.tflite"],
625
+ )
626
+
627
+ state_dict: Dict[str, torch.Tensor] = {}
628
+ weight_files = list(_collect_weight_files(snapshot_dir))
629
+ if not weight_files:
630
+ raise RuntimeError(f"No model weight files found in snapshot for {repo_id}")
631
+
632
+ for path in weight_files:
633
+ if path.endswith(".safetensors"):
634
+ if load_safetensors_file is None:
635
+ raise RuntimeError("safetensors is unavailable but checkpoint uses safetensors")
636
+ shard = load_safetensors_file(path, device="cpu")
637
+ else:
638
+ shard = torch.load(path, map_location="cpu")
639
+ if isinstance(shard, dict) and "state_dict" in shard and isinstance(shard["state_dict"], dict):
640
+ shard = shard["state_dict"]
641
+ state_dict.update(shard)
642
+
643
+ print(f"[Florence2 compat] loaded raw state_dict from snapshot: files={len(weight_files)} tensors={len(state_dict)}")
644
+ return state_dict
645
+
646
+
647
+ def _rename_vision_tower_key(key: str) -> str:
648
+ new_key = f"model.{key}"
649
+ new_key = new_key.replace(".convs.", ".convs.")
650
+ new_key = new_key.replace(".proj.", ".conv.") if ".convs." in new_key else new_key
651
+
652
+ replacements = (
653
+ (".spatial_block.conv1.fn.dw.", ".spatial_block.conv1."),
654
+ (".spatial_block.window_attn.norm.", ".spatial_block.norm1."),
655
+ (".spatial_block.window_attn.fn.", ".spatial_block.window_attn."),
656
+ (".spatial_block.ffn.norm.", ".spatial_block.norm2."),
657
+ (".spatial_block.ffn.fn.net.", ".spatial_block.ffn."),
658
+ (".channel_block.conv1.fn.dw.", ".channel_block.conv1."),
659
+ (".channel_block.channel_attn.norm.", ".channel_block.norm1."),
660
+ (".channel_block.channel_attn.fn.", ".channel_block.channel_attn."),
661
+ (".channel_block.conv2.fn.dw.", ".channel_block.conv2."),
662
+ (".channel_block.ffn.norm.", ".channel_block.norm2."),
663
+ (".channel_block.ffn.fn.net.", ".channel_block.ffn."),
664
+ )
665
+ for src, dst in replacements:
666
+ if src in new_key:
667
+ new_key = new_key.replace(src, dst)
668
+ return new_key
669
+
670
+
671
+ def convert_legacy_state_dict_to_native(state_dict: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]:
672
+ converted: Dict[str, torch.Tensor] = {}
673
+
674
+ direct_map = {
675
+ "image_proj_norm.weight": "model.multi_modal_projector.image_proj_norm.weight",
676
+ "image_proj_norm.bias": "model.multi_modal_projector.image_proj_norm.bias",
677
+ "image_pos_embed.row_embeddings.weight": "model.multi_modal_projector.image_position_embed.row_embeddings.weight",
678
+ "image_pos_embed.column_embeddings.weight": "model.multi_modal_projector.image_position_embed.column_embeddings.weight",
679
+ "visual_temporal_embed.pos_idx_to_embed": "model.multi_modal_projector.visual_temporal_embed.pos_idx_to_embed",
680
+ "language_model.lm_head.weight": "lm_head.weight",
681
+ }
682
+
683
+ for key, value in state_dict.items():
684
+ tensor = value
685
+ new_key = key
686
+
687
+ if key in direct_map:
688
+ new_key = direct_map[key]
689
+ elif key == "image_projection":
690
+ new_key = "model.multi_modal_projector.image_projection.weight"
691
+ tensor = value.transpose(1, 0).contiguous()
692
+ elif key.startswith("language_model.model."):
693
+ new_key = key.replace("language_model.model.", "model.language_model.", 1)
694
+ elif key.startswith("vision_tower."):
695
+ new_key = _rename_vision_tower_key(key)
696
+ elif key.startswith("model.") or key == "lm_head.weight":
697
+ new_key = key
698
+ else:
699
+ # Keep unknown keys as-is; they will appear in audit logs if unused.
700
+ new_key = key
701
+
702
+ converted[new_key] = tensor
703
+
704
+ print(
705
+ "[Florence2 compat] converted legacy state_dict to native-style keys: "
706
+ f"input={len(state_dict)} output={len(converted)}"
707
+ )
708
+ return converted
709
+
710
+
711
+ def _rebuild_native_model_from_converted_state(base_model: Any, processor: Any, repo_id: str):
712
+ state_dict = _load_state_dict_from_snapshot(repo_id)
713
+ converted_state = convert_legacy_state_dict_to_native(state_dict)
714
+
715
+ native_cls = Florence2ForConditionalGeneration or type(base_model)
716
+ converted_model = native_cls(base_model.config)
717
+ incompatible = converted_model.load_state_dict(converted_state, strict=False)
718
+ if hasattr(converted_model, "tie_weights"):
719
+ converted_model.tie_weights()
720
+
721
+ loading_info = {
722
+ "missing_keys": list(getattr(incompatible, "missing_keys", []) or []),
723
+ "unexpected_keys": list(getattr(incompatible, "unexpected_keys", []) or []),
724
+ }
725
+ _ensure_image_token(processor, converted_model)
726
+ audit = audit_loading_info(repo_id, loading_info)
727
+ audit["converted_from_legacy"] = True
728
+ return converted_model, audit
729
+
730
+
731
+ def _boolish(value: Any) -> Optional[bool]:
732
+ if isinstance(value, bool):
733
+ return value
734
+ return None
735
+
736
+
737
+ def _effective_generate_settings(model: Any, kwargs: Dict[str, Any]) -> Dict[str, Any]:
738
+ generation_config = kwargs.get("generation_config", getattr(model, "generation_config", None))
739
+ use_cache = kwargs.get("use_cache", getattr(generation_config, "use_cache", None))
740
+ num_beams = kwargs.get("num_beams", getattr(generation_config, "num_beams", None))
741
+ early_stopping = kwargs.get("early_stopping", getattr(generation_config, "early_stopping", None))
742
+ max_new_tokens = kwargs.get("max_new_tokens", getattr(generation_config, "max_new_tokens", None))
743
+ return {
744
+ "use_cache": use_cache,
745
+ "num_beams": num_beams,
746
+ "early_stopping": early_stopping,
747
+ "max_new_tokens": max_new_tokens,
748
+ }
749
+
750
+
751
+ def _condensed_generate_log(model: Any, kwargs: Dict[str, Any]) -> None:
752
+ settings = _effective_generate_settings(model, kwargs)
753
+ loader = getattr(model, "_florence2_loader", "unknown")
754
+ attn = getattr(model, "_florence2_attn_impl", "unknown")
755
+ vision_device, vision_dtype = _safe_module_device_dtype(getattr(model, "vision_tower", None))
756
+ language_device, language_dtype = _safe_module_device_dtype(getattr(model, "language_model", None))
757
+ print(
758
+ "[Florence2 compat] call "
759
+ f"loader={loader} attn={attn} "
760
+ f"vt={vision_device}/{vision_dtype} lm={language_device}/{language_dtype} "
761
+ f"cache={settings['use_cache']} beams={settings['num_beams']} "
762
+ f"early={settings['early_stopping']} max_new_tokens={settings['max_new_tokens']} "
763
+ f"repo={getattr(model, '_florence2_repo_id', None)}"
764
+ )
765
+
766
+
767
+ def _default_max_new_tokens(model: Any) -> int:
768
+ repo_id = getattr(model, "_florence2_repo_id", None)
769
+ if isinstance(repo_id, str) and repo_id in FLORENCE2_GENERATE_MAX_NEW_TOKENS_BY_REPO:
770
+ return int(FLORENCE2_GENERATE_MAX_NEW_TOKENS_BY_REPO[repo_id])
771
+ return int(FLORENCE2_GENERATE_DEFAULT_MAX_NEW_TOKENS)
772
+
773
+
774
+ def _autocast_context(model: Any):
775
+ device, dtype = _infer_tensor_runtime(model, "cpu")
776
+ if not str(device).startswith("cuda"):
777
+ return torch.autocast(device_type="cpu", enabled=False)
778
+ target_dtype = dtype if dtype in (torch.float16, torch.bfloat16) else torch.float16
779
+ return torch.autocast(device_type="cuda", dtype=target_dtype)
780
+
781
+
782
+ def generate_florence2(model: Any, **kwargs):
783
+ kwargs.setdefault("max_new_tokens", _default_max_new_tokens(model))
784
+ kwargs.setdefault("num_beams", FLORENCE2_GENERATE_DEFAULT_NUM_BEAMS)
785
+ if isinstance(kwargs.get("num_beams"), int) and kwargs["num_beams"] <= 1:
786
+ kwargs.pop("early_stopping", None)
787
+ _condensed_generate_log(model, kwargs)
788
+ with torch.inference_mode():
789
+ with _autocast_context(model):
790
+ return model.generate(**kwargs)
791
+
792
+
793
+ def load_florence2_bundle(repo_id: str, device: str, prefer_native_on_v5: bool = True):
794
+ major = _major_version()
795
+ processor = _load_processor(repo_id)
796
+
797
+ if major < 5 or not prefer_native_on_v5 or Florence2ForConditionalGeneration is None:
798
+ model, loading_info, runtime = _load_legacy_model(repo_id, device=device, processor=processor)
799
+ model = _safe_to_device(model, device)
800
+ audit = audit_loading_info(repo_id, loading_info)
801
+ audit["loader"] = "legacy_remote_code"
802
+ audit["runtime"] = runtime
803
+ model._florence2_loader = audit["loader"]
804
+ model._florence2_attn_impl = runtime.get("attn_implementation")
805
+ model._florence2_repo_id = repo_id
806
+ return model, processor, audit
807
+
808
+ model, loading_info, runtime = _load_native_model(repo_id, device=device)
809
+ audit = audit_loading_info(repo_id, loading_info)
810
+ audit["loader"] = "native"
811
+ audit["runtime"] = runtime
812
+
813
+ if audit.get("suspicious_mismatch"):
814
+ try:
815
+ converted_model, converted_audit = _rebuild_native_model_from_converted_state(model, processor, repo_id)
816
+ del model
817
+ gc.collect()
818
+ model = converted_model
819
+ audit = converted_audit
820
+ audit["loader"] = "converted_native"
821
+ audit["runtime"] = runtime
822
+ except Exception as e:
823
+ audit["conversion_error"] = repr(e)
824
+ print(f"[Florence2 compat] legacy->native conversion fallback failed for {repo_id}: {e!r}")
825
+
826
+ if _native_florence_module(model):
827
+ _ensure_image_token(processor, model)
828
+
829
+ model = _safe_to_device(model, device)
830
+ model._florence2_loader = audit.get("loader", "unknown")
831
+ model._florence2_attn_impl = runtime.get("attn_implementation")
832
+ model._florence2_repo_id = repo_id
833
+ return model, processor, audit
834
+
835
+
836
+ def _candidate_image_seq_length(processor: Any, model: Any) -> Optional[int]:
837
+ candidates = (
838
+ getattr(getattr(model, "config", None), "image_seq_length", None),
839
+ getattr(processor, "num_image_tokens", None),
840
+ getattr(processor, "image_seq_length", None),
841
+ getattr(getattr(processor, "image_processor", None), "image_seq_length", None),
842
+ )
843
+ for value in candidates:
844
+ if isinstance(value, int) and value > 0:
845
+ return value
846
+ return None
847
+
848
+
849
+ def _infer_tensor_runtime(model: Any, fallback_device: str) -> Tuple[str, Optional[torch.dtype]]:
850
+ modules = [
851
+ getattr(model, "vision_tower", None),
852
+ getattr(getattr(model, "model", None), "vision_tower", None),
853
+ model,
854
+ ]
855
+ for module in modules:
856
+ if module is None or not hasattr(module, "parameters"):
857
+ continue
858
+ try:
859
+ param = next(module.parameters())
860
+ return str(param.device), param.dtype
861
+ except StopIteration:
862
+ continue
863
+ except Exception:
864
+ continue
865
+ return fallback_device, getattr(model, "dtype", None)
866
+
867
+
868
+
869
+ def prepare_florence2_inputs(processor: Any, model: Any, prompt: str, image: Any, device: str):
870
+ inputs = processor(text=prompt, images=image, return_tensors="pt")
871
+
872
+ if _native_florence_module(model):
873
+ input_ids = inputs.get("input_ids")
874
+ pixel_values = inputs.get("pixel_values")
875
+ image_token_id = getattr(getattr(model, "config", None), "image_token_id", None)
876
+ image_seq_length = _candidate_image_seq_length(processor, model)
877
+
878
+ if input_ids is not None and pixel_values is not None and image_token_id is not None and image_seq_length:
879
+ n_image_tokens = int((input_ids == image_token_id).sum().item())
880
+ if n_image_tokens == 0:
881
+ batch_size = input_ids.shape[0]
882
+ prefix = torch.full(
883
+ (batch_size, int(image_seq_length)),
884
+ int(image_token_id),
885
+ dtype=input_ids.dtype,
886
+ device=input_ids.device,
887
+ )
888
+ inputs["input_ids"] = torch.cat([prefix, input_ids], dim=1)
889
+
890
+ attention_mask = inputs.get("attention_mask")
891
+ if attention_mask is not None:
892
+ prefix_mask = torch.ones(
893
+ (batch_size, int(image_seq_length)),
894
+ dtype=attention_mask.dtype,
895
+ device=attention_mask.device,
896
+ )
897
+ inputs["attention_mask"] = torch.cat([prefix_mask, attention_mask], dim=1)
898
+
899
+ print(
900
+ f"[Florence2 compat] injected {int(image_seq_length)} image placeholder tokens for native Florence-2"
901
+ )
902
+
903
+ target_device, float_dtype = _infer_tensor_runtime(model, device)
904
+ prepared = {}
905
+ for k, v in inputs.items():
906
+ if not hasattr(v, "to"):
907
+ prepared[k] = v
908
+ continue
909
+ if isinstance(v, torch.Tensor) and torch.is_floating_point(v):
910
+ prepared[k] = v.to(device=target_device, dtype=float_dtype or v.dtype)
911
+ else:
912
+ prepared[k] = v.to(device=target_device)
913
+
914
+ pv = prepared.get("pixel_values")
915
+ if isinstance(pv, torch.Tensor):
916
+ print(
917
+ "[Florence2 compat] prepared pixel_values "
918
+ f"device={pv.device} dtype={pv.dtype} target_float_dtype={float_dtype} native={_native_florence_module(model)}"
919
+ )
920
+
921
+ return prepared
922
+
923
+
924
+ def safe_load_florence2_bundle(repo_id: str, device: str, prefer_native_on_v5: bool = True):
925
+ return load_florence2_bundle(repo_id=repo_id, device=device, prefer_native_on_v5=prefer_native_on_v5)
tagger/tagger.py CHANGED
@@ -8,13 +8,14 @@ from pathlib import Path
8
 
9
  WD_MODEL_NAMES = ["p1atdev/wd-swinv2-tagger-v3-hf"]
10
  WD_MODEL_NAME = WD_MODEL_NAMES[0]
 
11
 
12
  device = "cuda" if torch.cuda.is_available() else "cpu"
13
  default_device = device
14
 
15
  try:
16
- wd_model = AutoModelForImageClassification.from_pretrained(WD_MODEL_NAME, trust_remote_code=True).to(default_device).eval()
17
- wd_processor = AutoImageProcessor.from_pretrained(WD_MODEL_NAME, trust_remote_code=True)
18
  except Exception as e:
19
  print(e)
20
  wd_model = wd_processor = None
 
8
 
9
  WD_MODEL_NAMES = ["p1atdev/wd-swinv2-tagger-v3-hf"]
10
  WD_MODEL_NAME = WD_MODEL_NAMES[0]
11
+ WD_MODEL_REVISION = "b09fdef"
12
 
13
  device = "cuda" if torch.cuda.is_available() else "cpu"
14
  default_device = device
15
 
16
  try:
17
+ wd_model = AutoModelForImageClassification.from_pretrained(WD_MODEL_NAME, trust_remote_code=True, revision=WD_MODEL_REVISION, code_revision=WD_MODEL_REVISION).to(default_device).eval()
18
+ wd_processor = AutoImageProcessor.from_pretrained(WD_MODEL_NAME, trust_remote_code=True, revision=WD_MODEL_REVISION, code_revision=WD_MODEL_REVISION)
19
  except Exception as e:
20
  print(e)
21
  wd_model = wd_processor = None
utils.py CHANGED
@@ -275,11 +275,11 @@ def civ_redirect_down(url, dir_, civitai_api_key, romanize, alternative_name):
275
  elif os.path.exists(os.path.join(dir_, filename_base)):
276
  return os.path.join(dir_, filename_base), filename_base
277
 
278
- aria2_command = (
279
- f'aria2c --console-log-level=error --summary-interval=10 -c -x 16 '
280
- f'-k 1M -s 16 -d "{dir_}" -o "{filename_base}" "{redirect_url}"'
281
  )
282
- r_code = os.system(aria2_command) # noqa
283
 
284
  # if r_code != 0:
285
  # raise RuntimeError(f"Failed to download file: {filename_base}. Error code: {r_code}")
@@ -293,27 +293,32 @@ def civ_redirect_down(url, dir_, civitai_api_key, romanize, alternative_name):
293
 
294
  def civ_api_down(url, dir_, civitai_api_key, civ_filename):
295
  """
296
- This method is susceptible to being blocked because it generates a lot of temp redirect links with aria2c.
297
- If an API key limit is reached, generating a new API key and using it can fix the issue.
298
  """
299
  output_path = None
300
-
301
  url_dl = url + f"?token={civitai_api_key}"
 
302
  if not civ_filename:
303
- aria2_command = f'aria2c -c -x 1 -s 1 -d "{dir_}" "{url_dl}"'
304
- os.system(aria2_command)
 
 
 
 
305
  else:
306
  output_path = os.path.join(dir_, civ_filename)
 
307
  if not os.path.exists(output_path):
308
- aria2_command = (
309
- f'aria2c --console-log-level=error --summary-interval=10 -c -x 16 '
310
- f'-k 1M -s 16 -d "{dir_}" -o "{civ_filename}" "{url_dl}"'
311
  )
312
- os.system(aria2_command)
313
-
314
  return output_path
315
 
316
-
317
  def drive_down(url, dir_):
318
  import gdown
319
 
@@ -354,10 +359,16 @@ def hf_down(url, dir_, hf_token, romanize):
354
  url = url.replace("/blob/", "/resolve/")
355
 
356
  if hf_token:
357
- user_header = f'"Authorization: Bearer {hf_token}"'
358
- os.system(f"aria2c --console-log-level=error --summary-interval=10 --header={user_header} -c -x 16 -k 1M -s 16 {url} -d {dir_} -o {filename}")
 
 
 
359
  else:
360
- os.system(f"aria2c --optimize-concurrent-downloads --console-log-level=error --summary-interval=10 -c -x 16 -k 1M -s 16 {url} -d {dir_} -o {filename}")
 
 
 
361
 
362
  return output_path
363
 
@@ -370,7 +381,8 @@ def download_things(directory, url, hf_token="", civitai_api_key="", romanize=Fa
370
  downloaded_file_path = drive_down(url, directory)
371
  elif "huggingface.co" in url:
372
  downloaded_file_path = hf_down(url, directory, hf_token, romanize)
373
- elif "civitai.com" in url:
 
374
  if not civitai_api_key:
375
  msg = "You need an API key to download Civitai models."
376
  print(f"\033[91m{msg}\033[0m")
@@ -393,7 +405,10 @@ def download_things(directory, url, hf_token="", civitai_api_key="", romanize=Fa
393
  gr.Warning(msg)
394
  downloaded_file_path = civ_api_down(url, directory, civitai_api_key, civ_filename)
395
  else:
396
- os.system(f"aria2c --console-log-level=error --summary-interval=10 -c -x 16 -k 1M -s 16 -d {directory} {url}")
 
 
 
397
 
398
  return downloaded_file_path
399
 
 
275
  elif os.path.exists(os.path.join(dir_, filename_base)):
276
  return os.path.join(dir_, filename_base), filename_base
277
 
278
+ wget_command = (
279
+ f'wget -c -nv '
280
+ f'-O "{os.path.join(dir_, filename_base)}" "{redirect_url}"'
281
  )
282
+ r_code = os.system(wget_command) # noqa
283
 
284
  # if r_code != 0:
285
  # raise RuntimeError(f"Failed to download file: {filename_base}. Error code: {r_code}")
 
293
 
294
  def civ_api_down(url, dir_, civitai_api_key, civ_filename):
295
  """
296
+ This method is susceptible to being blocked because it generates a lot of temp redirect links with wget.
297
+ If an API key limit is reached, generating a new API key and using it can fix the issue. no
298
  """
299
  output_path = None
300
+
301
  url_dl = url + f"?token={civitai_api_key}"
302
+
303
  if not civ_filename:
304
+ wget_command = (
305
+ f'wget -c -nv '
306
+ f'-P "{dir_}" "{url_dl}"'
307
+ )
308
+ os.system(wget_command)
309
+
310
  else:
311
  output_path = os.path.join(dir_, civ_filename)
312
+
313
  if not os.path.exists(output_path):
314
+ wget_command = (
315
+ f'wget -c -nv '
316
+ f'-O "{output_path}" "{url_dl}"'
317
  )
318
+ os.system(wget_command)
319
+
320
  return output_path
321
 
 
322
  def drive_down(url, dir_):
323
  import gdown
324
 
 
359
  url = url.replace("/blob/", "/resolve/")
360
 
361
  if hf_token:
362
+ os.system(
363
+ f'wget -c -nv '
364
+ f'--header="Authorization: Bearer {hf_token}" '
365
+ f'-O "{os.path.join(dir_, filename)}" "{url}"'
366
+ )
367
  else:
368
+ os.system(
369
+ f'wget -c -nv '
370
+ f'-O "{os.path.join(dir_, filename)}" "{url}"'
371
+ )
372
 
373
  return output_path
374
 
 
381
  downloaded_file_path = drive_down(url, directory)
382
  elif "huggingface.co" in url:
383
  downloaded_file_path = hf_down(url, directory, hf_token, romanize)
384
+ elif "civitai." in url:
385
+ url = url.replace("civitai.red", "civitai.com")
386
  if not civitai_api_key:
387
  msg = "You need an API key to download Civitai models."
388
  print(f"\033[91m{msg}\033[0m")
 
405
  gr.Warning(msg)
406
  downloaded_file_path = civ_api_down(url, directory, civitai_api_key, civ_filename)
407
  else:
408
+ os.system(
409
+ f'wget -c -nv '
410
+ f'-P "{directory}" "{url}"'
411
+ )
412
 
413
  return downloaded_file_path
414