InvokeAI 2.2.4.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. backend/__init__.py +0 -0
  2. backend/invoke_ai_web_server.py +1724 -0
  3. backend/modules/__init__.py +0 -0
  4. backend/modules/create_cmd_parser.py +55 -0
  5. backend/modules/get_canvas_generation_mode.py +117 -0
  6. backend/modules/parameters.py +71 -0
  7. backend/modules/parse_seed_weights.py +47 -0
  8. configs/INITIAL_MODELS.yaml +80 -0
  9. configs/models.yaml.example +29 -0
  10. configs/sd-concepts.txt +803 -0
  11. configs/stable-diffusion/v1-finetune.yaml +110 -0
  12. configs/stable-diffusion/v1-finetune_style.yaml +103 -0
  13. configs/stable-diffusion/v1-inference.yaml +79 -0
  14. configs/stable-diffusion/v1-inpainting-inference.yaml +79 -0
  15. configs/stable-diffusion/v1-m1-finetune.yaml +110 -0
  16. docs/assets/invoke-web-server-1.png +0 -0
  17. docs/assets/invoke_ai_banner.png +0 -0
  18. invokeai-2.2.4.5.data/data/assets/caution.png +0 -0
  19. invokeai-2.2.4.5.data/data/configs/INITIAL_MODELS.yaml +80 -0
  20. invokeai-2.2.4.5.data/data/configs/models.yaml.example +29 -0
  21. invokeai-2.2.4.5.data/data/configs/sd-concepts.txt +803 -0
  22. invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-finetune.yaml +110 -0
  23. invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-finetune_style.yaml +103 -0
  24. invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-inference.yaml +79 -0
  25. invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-inpainting-inference.yaml +79 -0
  26. invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-m1-finetune.yaml +110 -0
  27. invokeai-2.2.4.5.data/data/frontend/dist/assets/Inter-Bold.790c108b.ttf +0 -0
  28. invokeai-2.2.4.5.data/data/frontend/dist/assets/Inter.b9a8e5e2.ttf +0 -0
  29. invokeai-2.2.4.5.data/data/frontend/dist/assets/favicon.0d253ced.ico +0 -0
  30. invokeai-2.2.4.5.data/data/frontend/dist/assets/index-legacy-b741077f.js +48 -0
  31. invokeai-2.2.4.5.data/data/frontend/dist/assets/index.0dadf5d0.css +1 -0
  32. invokeai-2.2.4.5.data/data/frontend/dist/assets/index.21ff81eb.js +625 -0
  33. invokeai-2.2.4.5.data/data/frontend/dist/assets/logo.13003d72.png +0 -0
  34. invokeai-2.2.4.5.data/data/frontend/dist/assets/polyfills-legacy-dde3a68a.js +4 -0
  35. invokeai-2.2.4.5.data/data/frontend/dist/assets/polyfills.1ff60148.js +1 -0
  36. invokeai-2.2.4.5.data/data/frontend/dist/index.html +23 -0
  37. invokeai-2.2.4.5.data/data/scripts/configure_invokeai.py +797 -0
  38. invokeai-2.2.4.5.data/data/scripts/create_root.py +43 -0
  39. invokeai-2.2.4.5.data/data/scripts/dream.py +10 -0
  40. invokeai-2.2.4.5.data/data/scripts/images2prompt.py +30 -0
  41. invokeai-2.2.4.5.data/data/scripts/invoke.py +5 -0
  42. invokeai-2.2.4.5.data/data/scripts/legacy_api.py +681 -0
  43. invokeai-2.2.4.5.data/data/scripts/load_models.py +12 -0
  44. invokeai-2.2.4.5.data/data/scripts/merge_embeddings.py +124 -0
  45. invokeai-2.2.4.5.data/data/scripts/preload_models.py +12 -0
  46. invokeai-2.2.4.5.data/data/scripts/sd-metadata.py +22 -0
  47. invokeai-2.2.4.5.dist-info/LICENSE +21 -0
  48. invokeai-2.2.4.5.dist-info/LICENSE-ModelWeights.txt +294 -0
  49. invokeai-2.2.4.5.dist-info/METADATA +288 -0
  50. invokeai-2.2.4.5.dist-info/RECORD +169 -0
  51. invokeai-2.2.4.5.dist-info/WHEEL +5 -0
  52. invokeai-2.2.4.5.dist-info/entry_points.txt +4 -0
  53. invokeai-2.2.4.5.dist-info/top_level.txt +6 -0
  54. invokeai-2.2.4.5.dist-info/zip-safe +1 -0
  55. ldm/__init__.py +0 -0
  56. ldm/data/__init__.py +0 -0
  57. ldm/data/base.py +31 -0
  58. ldm/data/imagenet.py +466 -0
  59. ldm/data/lsun.py +126 -0
  60. ldm/data/personalized.py +202 -0
  61. ldm/data/personalized_style.py +169 -0
  62. ldm/generate.py +1159 -0
  63. ldm/invoke/CLI.py +960 -0
  64. ldm/invoke/__init__.py +3 -0
  65. ldm/invoke/args.py +1257 -0
  66. ldm/invoke/concepts_lib.py +169 -0
  67. ldm/invoke/conditioning.py +289 -0
  68. ldm/invoke/devices.py +27 -0
  69. ldm/invoke/generator/__init__.py +4 -0
  70. ldm/invoke/generator/base.py +335 -0
  71. ldm/invoke/generator/embiggen.py +501 -0
  72. ldm/invoke/generator/img2img.py +97 -0
  73. ldm/invoke/generator/inpaint.py +358 -0
  74. ldm/invoke/generator/omnibus.py +175 -0
  75. ldm/invoke/generator/txt2img.py +88 -0
  76. ldm/invoke/generator/txt2img2img.py +182 -0
  77. ldm/invoke/globals.py +31 -0
  78. ldm/invoke/image_util.py +70 -0
  79. ldm/invoke/log.py +66 -0
  80. ldm/invoke/model_cache.py +449 -0
  81. ldm/invoke/patchmatch.py +44 -0
  82. ldm/invoke/pngwriter.py +113 -0
  83. ldm/invoke/prompt_parser.py +655 -0
  84. ldm/invoke/readline.py +442 -0
  85. ldm/invoke/restoration/__init__.py +4 -0
  86. ldm/invoke/restoration/base.py +38 -0
  87. ldm/invoke/restoration/codeformer.py +103 -0
  88. ldm/invoke/restoration/codeformer_arch.py +276 -0
  89. ldm/invoke/restoration/gfpgan.py +84 -0
  90. ldm/invoke/restoration/outcrop.py +108 -0
  91. ldm/invoke/restoration/outpaint.py +92 -0
  92. ldm/invoke/restoration/realesrgan.py +90 -0
  93. ldm/invoke/restoration/vqgan_arch.py +435 -0
  94. ldm/invoke/seamless.py +30 -0
  95. ldm/invoke/server.py +282 -0
  96. ldm/invoke/server_legacy.py +246 -0
  97. ldm/invoke/txt2mask.py +136 -0
  98. ldm/lr_scheduler.py +143 -0
  99. ldm/models/__init__.py +0 -0
  100. ldm/models/autoencoder.py +596 -0
  101. ldm/models/diffusion/__init__.py +0 -0
  102. ldm/models/diffusion/classifier.py +355 -0
  103. ldm/models/diffusion/cross_attention_control.py +443 -0
  104. ldm/models/diffusion/cross_attention_map_saving.py +95 -0
  105. ldm/models/diffusion/ddim.py +111 -0
  106. ldm/models/diffusion/ddpm.py +2263 -0
  107. ldm/models/diffusion/ksampler.py +312 -0
  108. ldm/models/diffusion/plms.py +146 -0
  109. ldm/models/diffusion/sampler.py +450 -0
  110. ldm/models/diffusion/shared_invokeai_diffusion.py +244 -0
  111. ldm/modules/__init__.py +0 -0
  112. ldm/modules/attention.py +274 -0
  113. ldm/modules/diffusionmodules/__init__.py +0 -0
  114. ldm/modules/diffusionmodules/model.py +894 -0
  115. ldm/modules/diffusionmodules/openaimodel.py +1036 -0
  116. ldm/modules/diffusionmodules/util.py +314 -0
  117. ldm/modules/distributions/__init__.py +0 -0
  118. ldm/modules/distributions/distributions.py +104 -0
  119. ldm/modules/ema.py +88 -0
  120. ldm/modules/embedding_manager.py +413 -0
  121. ldm/modules/encoders/__init__.py +0 -0
  122. ldm/modules/encoders/modules.py +772 -0
  123. ldm/modules/image_degradation/__init__.py +6 -0
  124. ldm/modules/image_degradation/bsrgan.py +846 -0
  125. ldm/modules/image_degradation/bsrgan_light.py +751 -0
  126. ldm/modules/image_degradation/utils_image.py +1024 -0
  127. ldm/modules/losses/__init__.py +1 -0
  128. ldm/modules/losses/contperceptual.py +175 -0
  129. ldm/modules/losses/vqperceptual.py +240 -0
  130. ldm/modules/x_transformer.py +752 -0
  131. ldm/simplet2i.py +13 -0
  132. ldm/util.py +264 -0
  133. scripts/configure_invokeai.py +797 -0
  134. scripts/create_root.py +43 -0
  135. scripts/dream.py +10 -0
  136. scripts/images2prompt.py +30 -0
  137. scripts/invoke.py +5 -0
  138. scripts/legacy_api.py +681 -0
  139. scripts/load_models.py +12 -0
  140. scripts/merge_embeddings.py +124 -0
  141. scripts/orig_scripts/img2img.py +296 -0
  142. scripts/orig_scripts/inpaint.py +98 -0
  143. scripts/orig_scripts/knn2img.py +397 -0
  144. scripts/orig_scripts/sample_diffusion.py +313 -0
  145. scripts/orig_scripts/train_searcher.py +147 -0
  146. scripts/orig_scripts/txt2img.py +324 -0
  147. scripts/preload_models.py +12 -0
  148. scripts/sd-metadata.py +22 -0
  149. vendored/clip/clip/__init__.py +1 -0
  150. vendored/clip/clip/bpe_simple_vocab_16e6.txt.gz +0 -0
  151. vendored/clip/clip/clip.py +237 -0
  152. vendored/clip/clip/model.py +436 -0
  153. vendored/clip/clip/simple_tokenizer.py +132 -0
  154. vendored/clip/hubconf.py +42 -0
  155. vendored/clip/setup.py +21 -0
  156. vendored/clip/tests/test_consistency.py +25 -0
  157. vendored/clipseg/datasets/coco_wrapper.py +99 -0
  158. vendored/clipseg/datasets/pascal_zeroshot.py +60 -0
  159. vendored/clipseg/datasets/pfe_dataset.py +129 -0
  160. vendored/clipseg/datasets/phrasecut.py +380 -0
  161. vendored/clipseg/datasets/utils.py +68 -0
  162. vendored/clipseg/evaluation_utils.py +292 -0
  163. vendored/clipseg/general_utils.py +272 -0
  164. vendored/clipseg/metrics.py +271 -0
  165. vendored/clipseg/models/clipseg.py +538 -0
  166. vendored/clipseg/models/vitseg.py +286 -0
  167. vendored/clipseg/score.py +453 -0
  168. vendored/clipseg/setup.py +30 -0
  169. vendored/clipseg/training.py +266 -0
@@ -0,0 +1,169 @@
1
+ """
2
+ Query and install embeddings from the HuggingFace SD Concepts Library
3
+ at https://huggingface.co/sd-concepts-library.
4
+
5
+ The interface is through the Concepts() object.
6
+ """
7
+ import os
8
+ import re
9
+ import traceback
10
+ from typing import Callable
11
+ from urllib import request, error as ul_error
12
+ from huggingface_hub import HfFolder, hf_hub_url, ModelSearchArguments, ModelFilter, HfApi
13
+ from ldm.invoke.globals import Globals
14
+
15
+ class Concepts(object):
16
+ def __init__(self, root=None):
17
+ '''
18
+ Initialize the Concepts object. May optionally pass a root directory.
19
+ '''
20
+ self.root = root or Globals.root
21
+ self.hf_api = HfApi()
22
+ self.concept_list = None
23
+ self.concepts_loaded = dict()
24
+ self.triggers = dict() # concept name to trigger phrase
25
+ self.concept_names = dict() # trigger phrase to concept name
26
+ self.match_trigger = re.compile('(<[\w\- >]+>)') # trigger is slightly less restrictive than HF concept name
27
+ self.match_concept = re.compile('<([\w\-]+)>') # HF concept name can only contain A-Za-z0-9_-
28
+
29
+ def list_concepts(self)->list:
30
+ '''
31
+ Return a list of all the concepts by name, without the 'sd-concepts-library' part.
32
+ '''
33
+ if self.concept_list is not None:
34
+ return self.concept_list
35
+ try:
36
+ models = self.hf_api.list_models(filter=ModelFilter(model_name='sd-concepts-library/'))
37
+ self.concept_list = [a.id.split('/')[1] for a in models]
38
+ except Exception as e:
39
+ print(f' ** WARNING: Hugging Face textual inversion concepts libraries could not be loaded. The error was {str(e)}.')
40
+ print(' ** You may load .bin and .pt file(s) manually using the --embedding_directory argument.')
41
+ return self.concept_list
42
+
43
+ def get_concept_model_path(self, concept_name:str)->str:
44
+ '''
45
+ Returns the path to the 'learned_embeds.bin' file in
46
+ the named concept. Returns None if invalid or cannot
47
+ be downloaded.
48
+ '''
49
+ if not concept_name in self.list_concepts():
50
+ print(f'This concept is not known to the Hugging Face library. Generation will continue without the concept.')
51
+ return None
52
+ return self.get_concept_file(concept_name.lower(),'learned_embeds.bin')
53
+
54
+ def concept_to_trigger(self, concept_name:str)->str:
55
+ '''
56
+ Given a concept name returns its trigger by looking in the
57
+ "token_identifier.txt" file.
58
+ '''
59
+ if concept_name in self.triggers:
60
+ return self.triggers[concept_name]
61
+ file = self.get_concept_file(concept_name, 'token_identifier.txt', local_only=True)
62
+ if not file:
63
+ return None
64
+ with open(file,'r') as f:
65
+ trigger = f.readline()
66
+ trigger = trigger.strip()
67
+ self.triggers[concept_name] = trigger
68
+ self.concept_names[trigger] = concept_name
69
+ return trigger
70
+
71
+ def trigger_to_concept(self, trigger:str)->str:
72
+ '''
73
+ Given a trigger phrase, maps it to the concept library name.
74
+ Only works if concept_to_trigger() has previously been called
75
+ on this library. There needs to be a persistent database for
76
+ this.
77
+ '''
78
+ concept = self.concept_names.get(trigger,None)
79
+ return f'<{concept}>' if concept else f'{trigger}'
80
+
81
+ def replace_triggers_with_concepts(self, prompt:str)->str:
82
+ '''
83
+ Given a prompt string that contains <trigger> tags, replace these
84
+ tags with the concept name. The reason for this is so that the
85
+ concept names get stored in the prompt metadata. There is no
86
+ controlling of colliding triggers in the SD library, so it is
87
+ better to store the concept name (unique) than the concept trigger
88
+ (not necessarily unique!)
89
+ '''
90
+ if not prompt:
91
+ return prompt
92
+ triggers = self.match_trigger.findall(prompt)
93
+ if not triggers:
94
+ return prompt
95
+
96
+ def do_replace(match)->str:
97
+ return self.trigger_to_concept(match.group(1)) or f'<{match.group(1)}>'
98
+ return self.match_trigger.sub(do_replace, prompt)
99
+
100
+ def replace_concepts_with_triggers(self, prompt:str, load_concepts_callback: Callable[[list], any])->str:
101
+ '''
102
+ Given a prompt string that contains `<concept_name>` tags, replace
103
+ these tags with the appropriate trigger.
104
+
105
+ If any `<concept_name>` tags are found, `load_concepts_callback()` is called with a list
106
+ of `concepts_name` strings.
107
+ '''
108
+ concepts = self.match_concept.findall(prompt)
109
+ if not concepts:
110
+ return prompt
111
+ load_concepts_callback(concepts)
112
+
113
+ def do_replace(match)->str:
114
+ return self.concept_to_trigger(match.group(1)) or f'<{match.group(1)}>'
115
+ return self.match_concept.sub(do_replace, prompt)
116
+
117
+ def get_concept_file(self, concept_name:str, file_name:str='learned_embeds.bin' , local_only:bool=False)->str:
118
+ if not self.concept_is_downloaded(concept_name) and not local_only:
119
+ self.download_concept(concept_name)
120
+ path = os.path.join(self._concept_path(concept_name), file_name)
121
+ return path if os.path.exists(path) else None
122
+
123
+ def concept_is_downloaded(self, concept_name)->bool:
124
+ concept_directory = self._concept_path(concept_name)
125
+ return os.path.exists(concept_directory)
126
+
127
+ def download_concept(self,concept_name)->bool:
128
+ repo_id = self._concept_id(concept_name)
129
+ dest = self._concept_path(concept_name)
130
+
131
+ access_token = HfFolder.get_token()
132
+ header = [("Authorization", f'Bearer {access_token}')] if access_token else []
133
+ opener = request.build_opener()
134
+ opener.addheaders = header
135
+ request.install_opener(opener)
136
+
137
+ os.makedirs(dest, exist_ok=True)
138
+ succeeded = True
139
+
140
+ bytes = 0
141
+ def tally_download_size(chunk, size, total):
142
+ nonlocal bytes
143
+ if chunk==0:
144
+ bytes += total
145
+
146
+ print(f'>> Downloading {repo_id}...',end='')
147
+ try:
148
+ for file in ('README.md','learned_embeds.bin','token_identifier.txt','type_of_concept.txt'):
149
+ url = hf_hub_url(repo_id, file)
150
+ request.urlretrieve(url, os.path.join(dest,file),reporthook=tally_download_size)
151
+ except ul_error.HTTPError as e:
152
+ if e.code==404:
153
+ print(f'This concept is not known to the Hugging Face library. Generation will continue without the concept.')
154
+ else:
155
+ print(f'Failed to download {concept_name}/{file} ({str(e)}. Generation will continue without the concept.)')
156
+ os.rmdir(dest)
157
+ return False
158
+ except ul_error.URLError as e:
159
+ print(f'ERROR: {str(e)}. This may reflect a network issue. Generation will continue without the concept.')
160
+ os.rmdir(dest)
161
+ return False
162
+ print('...{:.2f}Kb'.format(bytes/1024))
163
+ return succeeded
164
+
165
+ def _concept_id(self, concept_name:str)->str:
166
+ return f'sd-concepts-library/{concept_name}'
167
+
168
+ def _concept_path(self, concept_name:str)->str:
169
+ return os.path.join(self.root,'models','sd-concepts-library',concept_name)
@@ -0,0 +1,289 @@
1
+ '''
2
+ This module handles the generation of the conditioning tensors.
3
+
4
+ Useful function exports:
5
+
6
+ get_uc_and_c_and_ec() get the conditioned and unconditioned latent, and edited conditioning if we're doing cross-attention control
7
+
8
+ '''
9
+ import re
10
+ from typing import Union
11
+
12
+ import torch
13
+
14
+ from .prompt_parser import PromptParser, Blend, FlattenedPrompt, \
15
+ CrossAttentionControlledFragment, CrossAttentionControlSubstitute, Fragment
16
+ from ..models.diffusion import cross_attention_control
17
+ from ..models.diffusion.shared_invokeai_diffusion import InvokeAIDiffuserComponent
18
+ from ..modules.encoders.modules import WeightedFrozenCLIPEmbedder
19
+
20
+
21
+ def get_uc_and_c_and_ec(prompt_string, model, log_tokens=False, skip_normalize_legacy_blend=False):
22
+ prompt, negative_prompt = get_prompt_structure(prompt_string,
23
+ skip_normalize_legacy_blend=skip_normalize_legacy_blend)
24
+ conditioning = _get_conditioning_for_prompt(prompt, negative_prompt, model, log_tokens)
25
+
26
+ return conditioning
27
+
28
+
29
+ def get_prompt_structure(prompt_string, skip_normalize_legacy_blend: bool = False) -> (
30
+ Union[FlattenedPrompt, Blend], FlattenedPrompt):
31
+ """
32
+ parse the passed-in prompt string and return tuple (positive_prompt, negative_prompt)
33
+ """
34
+ prompt, negative_prompt = _parse_prompt_string(prompt_string,
35
+ skip_normalize_legacy_blend=skip_normalize_legacy_blend)
36
+ return prompt, negative_prompt
37
+
38
+
39
+ def get_tokens_for_prompt(model, parsed_prompt: FlattenedPrompt, truncate_if_too_long=True) -> [str]:
40
+ text_fragments = [x.text if type(x) is Fragment else
41
+ (" ".join([f.text for f in x.original]) if type(x) is CrossAttentionControlSubstitute else
42
+ str(x))
43
+ for x in parsed_prompt.children]
44
+ text = " ".join(text_fragments)
45
+ tokens = model.cond_stage_model.tokenizer.tokenize(text)
46
+ if truncate_if_too_long:
47
+ max_tokens_length = model.cond_stage_model.max_length - 2 # typically 75
48
+ tokens = tokens[0:max_tokens_length]
49
+ return tokens
50
+
51
+
52
+ def _parse_prompt_string(prompt_string_uncleaned, skip_normalize_legacy_blend=False) -> Union[FlattenedPrompt, Blend]:
53
+ # Extract Unconditioned Words From Prompt
54
+ unconditioned_words = ''
55
+ unconditional_regex = r'\[(.*?)\]'
56
+ unconditionals = re.findall(unconditional_regex, prompt_string_uncleaned)
57
+
58
+ if len(unconditionals) > 0:
59
+ unconditioned_words = ' '.join(unconditionals)
60
+
61
+ # Remove Unconditioned Words From Prompt
62
+ unconditional_regex_compile = re.compile(unconditional_regex)
63
+ clean_prompt = unconditional_regex_compile.sub(' ', prompt_string_uncleaned)
64
+ prompt_string_cleaned = re.sub(' +', ' ', clean_prompt)
65
+ else:
66
+ prompt_string_cleaned = prompt_string_uncleaned
67
+
68
+ pp = PromptParser()
69
+
70
+ parsed_prompt: Union[FlattenedPrompt, Blend] = None
71
+ legacy_blend: Blend = pp.parse_legacy_blend(prompt_string_cleaned, skip_normalize_legacy_blend)
72
+ if legacy_blend is not None:
73
+ parsed_prompt = legacy_blend
74
+ else:
75
+ # we don't support conjunctions for now
76
+ parsed_prompt = pp.parse_conjunction(prompt_string_cleaned).prompts[0]
77
+
78
+ parsed_negative_prompt: FlattenedPrompt = pp.parse_conjunction(unconditioned_words).prompts[0]
79
+ return parsed_prompt, parsed_negative_prompt
80
+
81
+
82
+ def _get_conditioning_for_prompt(parsed_prompt: Union[Blend, FlattenedPrompt], parsed_negative_prompt: FlattenedPrompt,
83
+ model, log_tokens=False) \
84
+ -> tuple[torch.Tensor, torch.Tensor, InvokeAIDiffuserComponent.ExtraConditioningInfo]:
85
+ """
86
+ Process prompt structure and tokens, and return (conditioning, unconditioning, extra_conditioning_info)
87
+ """
88
+
89
+ if log_tokens:
90
+ print(f">> Parsed prompt to {parsed_prompt}")
91
+ print(f">> Parsed negative prompt to {parsed_negative_prompt}")
92
+
93
+ conditioning = None
94
+ cac_args: cross_attention_control.Arguments = None
95
+
96
+ if type(parsed_prompt) is Blend:
97
+ conditioning = _get_conditioning_for_blend(model, parsed_prompt, log_tokens)
98
+ elif type(parsed_prompt) is FlattenedPrompt:
99
+ if parsed_prompt.wants_cross_attention_control:
100
+ conditioning, cac_args = _get_conditioning_for_cross_attention_control(model, parsed_prompt, log_tokens)
101
+
102
+ else:
103
+ conditioning, _ = _get_embeddings_and_tokens_for_prompt(model,
104
+ parsed_prompt,
105
+ log_tokens=log_tokens,
106
+ log_display_label="(prompt)")
107
+ else:
108
+ raise ValueError(f"parsed_prompt is '{type(parsed_prompt)}' which is not a supported prompt type")
109
+
110
+ unconditioning, _ = _get_embeddings_and_tokens_for_prompt(model,
111
+ parsed_negative_prompt,
112
+ log_tokens=log_tokens,
113
+ log_display_label="(unconditioning)")
114
+ if isinstance(conditioning, dict):
115
+ # hybrid conditioning is in play
116
+ unconditioning, conditioning = _flatten_hybrid_conditioning(unconditioning, conditioning)
117
+ if cac_args is not None:
118
+ print(
119
+ ">> Hybrid conditioning cannot currently be combined with cross attention control. Cross attention control will be ignored.")
120
+ cac_args = None
121
+
122
+ if type(parsed_prompt) is Blend:
123
+ blend: Blend = parsed_prompt
124
+ all_token_sequences = [get_tokens_for_prompt(model, p) for p in blend.prompts]
125
+ longest_token_sequence = max(all_token_sequences, key=lambda t: len(t))
126
+ eos_token_index = len(longest_token_sequence)+1
127
+ else:
128
+ tokens = get_tokens_for_prompt(model, parsed_prompt)
129
+ eos_token_index = len(tokens)+1
130
+ return (
131
+ unconditioning, conditioning, InvokeAIDiffuserComponent.ExtraConditioningInfo(
132
+ tokens_count_including_eos_bos=eos_token_index + 1,
133
+ cross_attention_control_args=cac_args
134
+ )
135
+ )
136
+
137
+
138
+ def _get_conditioning_for_cross_attention_control(model, prompt: FlattenedPrompt, log_tokens: bool = True):
139
+ original_prompt = FlattenedPrompt()
140
+ edited_prompt = FlattenedPrompt()
141
+ # for name, a0, a1, b0, b1 in edit_opcodes: only name == 'equal' is currently parsed
142
+ original_token_count = 0
143
+ edited_token_count = 0
144
+ edit_options = []
145
+ edit_opcodes = []
146
+ # beginning of sequence
147
+ edit_opcodes.append(
148
+ ('equal', original_token_count, original_token_count + 1, edited_token_count, edited_token_count + 1))
149
+ edit_options.append(None)
150
+ original_token_count += 1
151
+ edited_token_count += 1
152
+ for fragment in prompt.children:
153
+ if type(fragment) is CrossAttentionControlSubstitute:
154
+ original_prompt.append(fragment.original)
155
+ edited_prompt.append(fragment.edited)
156
+
157
+ to_replace_token_count = _get_tokens_length(model, fragment.original)
158
+ replacement_token_count = _get_tokens_length(model, fragment.edited)
159
+ edit_opcodes.append(('replace',
160
+ original_token_count, original_token_count + to_replace_token_count,
161
+ edited_token_count, edited_token_count + replacement_token_count
162
+ ))
163
+ original_token_count += to_replace_token_count
164
+ edited_token_count += replacement_token_count
165
+ edit_options.append(fragment.options)
166
+ # elif type(fragment) is CrossAttentionControlAppend:
167
+ # edited_prompt.append(fragment.fragment)
168
+ else:
169
+ # regular fragment
170
+ original_prompt.append(fragment)
171
+ edited_prompt.append(fragment)
172
+
173
+ count = _get_tokens_length(model, [fragment])
174
+ edit_opcodes.append(('equal', original_token_count, original_token_count + count, edited_token_count,
175
+ edited_token_count + count))
176
+ edit_options.append(None)
177
+ original_token_count += count
178
+ edited_token_count += count
179
+ # end of sequence
180
+ edit_opcodes.append(
181
+ ('equal', original_token_count, original_token_count + 1, edited_token_count, edited_token_count + 1))
182
+ edit_options.append(None)
183
+ original_token_count += 1
184
+ edited_token_count += 1
185
+ original_embeddings, original_tokens = _get_embeddings_and_tokens_for_prompt(model,
186
+ original_prompt,
187
+ log_tokens=log_tokens,
188
+ log_display_label="(.swap originals)")
189
+ # naïvely building a single edited_embeddings like this disregards the effects of changing the absolute location of
190
+ # subsequent tokens when there is >1 edit and earlier edits change the total token count.
191
+ # eg "a cat.swap(smiling dog, s_start=0.5) eating a hotdog.swap(pizza)" - when the 'pizza' edit is active but the
192
+ # 'cat' edit is not, the 'pizza' feature vector will nevertheless be affected by the introduction of the extra
193
+ # token 'smiling' in the inactive 'cat' edit.
194
+ # todo: build multiple edited_embeddings, one for each edit, and pass just the edited fragments through to the CrossAttentionControl functions
195
+ edited_embeddings, edited_tokens = _get_embeddings_and_tokens_for_prompt(model,
196
+ edited_prompt,
197
+ log_tokens=log_tokens,
198
+ log_display_label="(.swap replacements)")
199
+ conditioning = original_embeddings
200
+ edited_conditioning = edited_embeddings
201
+ # print('>> got edit_opcodes', edit_opcodes, 'options', edit_options)
202
+ cac_args = cross_attention_control.Arguments(
203
+ edited_conditioning=edited_conditioning,
204
+ edit_opcodes=edit_opcodes,
205
+ edit_options=edit_options
206
+ )
207
+ return conditioning, cac_args
208
+
209
+
210
+ def _get_conditioning_for_blend(model, blend: Blend, log_tokens: bool = False):
211
+ embeddings_to_blend = None
212
+ for i, flattened_prompt in enumerate(blend.prompts):
213
+ this_embedding, _ = _get_embeddings_and_tokens_for_prompt(model,
214
+ flattened_prompt,
215
+ log_tokens=log_tokens,
216
+ log_display_label=f"(blend part {i + 1}, weight={blend.weights[i]})")
217
+ embeddings_to_blend = this_embedding if embeddings_to_blend is None else torch.cat(
218
+ (embeddings_to_blend, this_embedding))
219
+ conditioning = WeightedFrozenCLIPEmbedder.apply_embedding_weights(embeddings_to_blend.unsqueeze(0),
220
+ blend.weights,
221
+ normalize=blend.normalize_weights)
222
+ return conditioning
223
+
224
+
225
+ def _get_embeddings_and_tokens_for_prompt(model, flattened_prompt: FlattenedPrompt, log_tokens: bool = False,
226
+ log_display_label: str = None):
227
+ if type(flattened_prompt) is not FlattenedPrompt:
228
+ raise Exception(f"embeddings can only be made from FlattenedPrompts, got {type(flattened_prompt)} instead")
229
+ fragments = [x.text for x in flattened_prompt.children]
230
+ weights = [x.weight for x in flattened_prompt.children]
231
+ embeddings, tokens = model.get_learned_conditioning([fragments], return_tokens=True, fragment_weights=[weights])
232
+ if log_tokens:
233
+ text = " ".join(fragments)
234
+ log_tokenization(text, model, display_label=log_display_label)
235
+
236
+ return embeddings, tokens
237
+
238
+
239
+ def _get_tokens_length(model, fragments: list[Fragment]):
240
+ fragment_texts = [x.text for x in fragments]
241
+ tokens = model.cond_stage_model.get_tokens(fragment_texts, include_start_and_end_markers=False)
242
+ return sum([len(x) for x in tokens])
243
+
244
+
245
+ def _flatten_hybrid_conditioning(uncond, cond):
246
+ '''
247
+ This handles the choice between a conditional conditioning
248
+ that is a tensor (used by cross attention) vs one that has additional
249
+ dimensions as well, as used by 'hybrid'
250
+ '''
251
+ assert isinstance(uncond, dict)
252
+ assert isinstance(cond, dict)
253
+ cond_flattened = dict()
254
+ for k in cond:
255
+ if isinstance(cond[k], list):
256
+ cond_flattened[k] = [
257
+ torch.cat([uncond[k][i], cond[k][i]])
258
+ for i in range(len(cond[k]))
259
+ ]
260
+ else:
261
+ cond_flattened[k] = torch.cat([uncond[k], cond[k]])
262
+ return uncond, cond_flattened
263
+
264
+
265
+ def log_tokenization(text, model, display_label=None):
266
+ """ shows how the prompt is tokenized
267
+ # usually tokens have '</w>' to indicate end-of-word,
268
+ # but for readability it has been replaced with ' '
269
+ """
270
+
271
+ tokens = model.cond_stage_model.tokenizer.tokenize(text)
272
+ tokenized = ""
273
+ discarded = ""
274
+ usedTokens = 0
275
+ totalTokens = len(tokens)
276
+ for i in range(0, totalTokens):
277
+ token = tokens[i].replace('</w>', ' ')
278
+ # alternate color
279
+ s = (usedTokens % 6) + 1
280
+ if i < model.cond_stage_model.max_length:
281
+ tokenized = tokenized + f"\x1b[0;3{s};40m{token}"
282
+ usedTokens += 1
283
+ else: # over max token length
284
+ discarded = discarded + f"\x1b[0;3{s};40m{token}"
285
+ print(f"\n>> Tokens {display_label or ''} ({usedTokens}):\n{tokenized}\x1b[0m")
286
+ if discarded != "":
287
+ print(
288
+ f">> Tokens Discarded ({totalTokens - usedTokens}):\n{discarded}\x1b[0m"
289
+ )
ldm/invoke/devices.py ADDED
@@ -0,0 +1,27 @@
1
+ import torch
2
+ from torch import autocast
3
+ from contextlib import nullcontext
4
+
5
+ def choose_torch_device() -> str:
6
+ '''Convenience routine for guessing which GPU device to run model on'''
7
+ if torch.cuda.is_available():
8
+ return 'cuda'
9
+ if hasattr(torch.backends, 'mps') and torch.backends.mps.is_available():
10
+ return 'mps'
11
+ return 'cpu'
12
+
13
+ def choose_precision(device) -> str:
14
+ '''Returns an appropriate precision for the given torch device'''
15
+ if device.type == 'cuda':
16
+ device_name = torch.cuda.get_device_name(device)
17
+ if not ('GeForce GTX 1660' in device_name or 'GeForce GTX 1650' in device_name):
18
+ return 'float16'
19
+ return 'float32'
20
+
21
+ def choose_autocast(precision):
22
+ '''Returns an autocast context or nullcontext for the given precision string'''
23
+ # float16 currently requires autocast to avoid errors like:
24
+ # 'expected scalar type Half but found Float'
25
+ if precision == 'autocast' or precision == 'float16':
26
+ return autocast
27
+ return nullcontext
@@ -0,0 +1,4 @@
1
+ '''
2
+ Initialization file for the ldm.invoke.generator package
3
+ '''
4
+ from .base import Generator