drakosfire commited on
Commit
738437e
1 Parent(s): 533717d

fixing build

Browse files
.gitattributes CHANGED
@@ -1 +1,2 @@
1
  *.png filter=lfs diff=lfs merge=lfs -text
 
 
1
  *.png filter=lfs diff=lfs merge=lfs -text
2
+ *.jpg filter=lfs diff=lfs merge=lfs -text
.gitignore CHANGED
@@ -1,11 +1,13 @@
1
- #Directories used for testing
2
  models/
3
  homebrewery/
4
  exllamav2/
5
  output/
6
 
7
- #Test and backup Docker files
8
  Dockerfile - node
9
  Dockerfile copy
10
  Dockerfile Master copy
11
 
 
 
 
1
+ # Directories used for testing
2
  models/
3
  homebrewery/
4
  exllamav2/
5
  output/
6
 
7
+ # Test and backup Docker files
8
  Dockerfile - node
9
  Dockerfile copy
10
  Dockerfile Master copy
11
 
12
+ # File type to ignore
13
+ *.glb
Dockerfile DELETED
@@ -1,88 +0,0 @@
1
- # Stage 1: Node and NPM setup
2
- FROM ubuntu:22.04 as node-setup
3
-
4
- ENV NVM_DIR /usr/local/nvm
5
- ENV NODE_VERSION 20.10.0
6
-
7
- # Install dependencies and Node.js
8
- RUN mkdir -p /usr/local/nvm \
9
- && apt-get update \
10
- && apt-get remove -y nodejs \
11
- && apt-get autoremove -y \
12
- && rm -rf /var/lib/apt/lists/*
13
- # Bundle app source and build application
14
- RUN apt-get update \
15
- && apt-get install git -y\
16
- && apt-get install npm -y \
17
- && apt-get install -y python3 python3-pip \
18
- && apt install -y git \
19
- # && pip install --upgrade pip \
20
- && apt-get install wget --assume-yes \
21
- && wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash \
22
- && . $NVM_DIR/nvm.sh \
23
- && nvm install $NODE_VERSION \
24
- && nvm use $NODE_VERSION \
25
- && nvm alias default $NODE_VERSION \
26
- && node --version \
27
- && nvm --version
28
-
29
- FROM ubuntu:22.04 as python-env
30
-
31
- # Install Python and dependencies
32
- COPY requirements.txt /tmp/
33
- RUN apt-get update && \
34
- apt install -y git && \
35
- apt-get install -y python3 python3-pip && \
36
- pip3 install -r /tmp/requirements.txt && \
37
- pip install auto-gptq && \
38
- rm /tmp/requirements.txt
39
-
40
-
41
- # Stage 3: Final image based on NVIDIA CUDA base
42
- FROM nvidia/cuda:12.1.0-devel-ubuntu22.04
43
-
44
- RUN useradd -m -u 1000 user
45
- # Copy over the node_modules from the previous stage
46
-
47
- COPY --from=node-setup /usr/local/nvm /usr/local/nvm
48
-
49
- # Copy Python environment from python-env stage
50
- COPY --from=python-env /usr/local /usr/local
51
-
52
- # Set up environment variables for Node and Python
53
- ENV NVM_DIR /usr/local/nvm
54
- ENV NODE_VERSION 20.10.0
55
- ENV NODE_PATH $NVM_DIR/versions/node/v$NODE_VERSION/lib/node_modules
56
- ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH
57
-
58
- #Copy Homebrewery css dependencies
59
- COPY dependencies /home/user/app
60
-
61
-
62
- # Set working directory and user
63
- WORKDIR /home/user/app
64
-
65
- # Additional setup, must make .cache to bypass an exllama build bug, make sure user can access, then mk dir for app, and steps to install flash-attn which must be done AFTER everything else is built and in this stage to access CUDA
66
- RUN mkdir -p /home/user/.cache && \
67
- chmod 777 /home/user/.cache && \
68
- apt-get update && \
69
- apt install -y git && \
70
- apt-get install -y python3-pip && \
71
- pip install flash-attn && \
72
- git clone -b experimentalCommandLineBrewProcess https://github.com/G-Ambatte/homebrewery.git && \
73
- cd homebrewery && \
74
- npm install && \
75
- chown -R user:user /home/user/app/ && \
76
- chown -R user:user /home/user/app/homebrewery
77
-
78
-
79
-
80
-
81
- USER user
82
-
83
-
84
- # Set the entrypoint
85
- EXPOSE 8000
86
- ENTRYPOINT ["/usr/bin/python3", "main.py"]
87
-
88
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Drakosfires Dungeons and Dragons Statblock Generator
3
+ emoji: 🃏
4
+ colorFrom: yellow
5
+ colorTo: purple
6
+ sdk: gradio
7
+ sdk_version: 4.26.0
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
11
+
12
+ # Drakosfire's Dungeons and Dragons Statblock Generator
13
+
14
+ Welcome to the Drakosfire's Dungeons and Dragons Statblock Generator! This innovative tool harnesses the power of AI to generate unique statblocks as html pages that are ready to print! Including images for paper tokens and 3D models for your D&D adventures.
15
+
16
+ ## Overview
17
+
18
+ This generator leverages an API call to Open AI ChatGPT 4o, [Replicate](https://replicate.com/) combined with a custom fine-tuned version of the Stable Diffusion SDXL model to generate paper token images. You can find more about the specific model this project was based on at [Civitai](https://civitai.com/models/129681/sdxl-faetastic).
19
+ Leveraging the incredible [Tripo3d](https://www.tripo3d.ai/) to convert 2d images into 3d models with textures.
20
+
21
+ ## Key Features
22
+
23
+ Coming Soon
24
+
25
+
26
+
27
+ I hope you enjoy enhancing your Dungeons and Dragons experience with this unique tool. Happy adventuring!
__pycache__/description_helper.cpython-310.pyc DELETED
Binary file (2.58 kB)
 
__pycache__/process_html.cpython-310.pyc DELETED
Binary file (3.67 kB)
 
__pycache__/sd_generator.cpython-310.pyc DELETED
Binary file (1.83 kB)
 
__pycache__/statblock_helper.cpython-310.pyc DELETED
Binary file (3.31 kB)
 
__pycache__/utilities.cpython-310.pyc DELETED
Binary file (4.79 kB)
 
app.py ADDED
@@ -0,0 +1,460 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # this imports the code from files and modules
2
+ import description_helper as dsh
3
+ import gradio as gr
4
+ import sd_generator as sd
5
+ import utilities as u
6
+ import process_html
7
+ import process_text
8
+ import os
9
+ import ctypes
10
+ import tripo3d as tripo3d
11
+
12
+ # This is a fix for the way that python doesn't release system memory back to the OS and it was leading to locking up the system
13
+ libc = ctypes.cdll.LoadLibrary("libc.so.6")
14
+ M_MMAP_THRESHOLD = -3
15
+
16
+ # Set malloc mmap threshold.
17
+ libc.mallopt(M_MMAP_THRESHOLD, 2**20)
18
+
19
+ style_css = custom_css = """
20
+ <link href='file=/media/drakosfire/Shared/Docker/StatblockGenerator/dependencies/all.css' rel='stylesheet' />
21
+ <link href='file=/media/drakosfire/Shared/Docker/StatblockGenerator/dependencies/css.css?family=Open+Sans:400,300,600,700' rel='stylesheet' type='text/css' />
22
+ <link href='file=/media/drakosfire/Shared/Docker/StatblockGenerator/dependencies/bundle.css' rel='stylesheet' />
23
+ <link href='file=/media/drakosfire/Shared/Docker/StatblockGenerator/dependencies/style.css' rel='stylesheet' />
24
+ <link href='file=/media/drakosfire/Shared/Docker/StatblockGenerator/dependencies/5ePHBstyle.css' rel='stylesheet' />
25
+ """
26
+
27
+ # Build gradio app
28
+
29
+ with gr.Blocks(css = "style.css") as demo:
30
+ # Functions and State Variables
31
+ mon_name = gr.State()
32
+ mon_size = gr.State()
33
+ mon_type = gr.State()
34
+ mon_subtype = gr.State()
35
+ mon_alignment = gr.State()
36
+ mon_armor_class = gr.State()
37
+ mon_hp = gr.State()
38
+ mon_hit_dice = gr.State()
39
+ mon_speed = gr.State()
40
+ mon_abilities = gr.State()
41
+ mon_saving_throws = gr.State()
42
+ mon_skills = gr.State()
43
+ mon_damage_resistance = gr.State()
44
+ mon_senses = gr.State()
45
+ mon_languages = gr.State()
46
+ mon_challenge_rating = gr.State()
47
+ mon_xp = gr.State()
48
+ mon_actions = gr.State()
49
+ mon_cantrips = gr.State()
50
+ mon_spells = gr.State()
51
+ mon_spell_slots = gr.State()
52
+ mon_legendary_actions = gr.State()
53
+ mon_description = gr.State()
54
+ mon_sd_prompt = gr.State()
55
+ # Image Variables
56
+ generated_image_list = gr.State([])
57
+ selected_generated_image = gr.State()
58
+ selected_seed_image = gr.State()
59
+ selected_token_image = gr.State()
60
+
61
+
62
+
63
+ # Take input from gradio user and call the llm in description_helper
64
+ def gen_mon_desc(user_monster_text, spellcaster, legendary_actions):
65
+
66
+ # declare cantrips, spells, and spell slots as empty string unless the variable is changed.
67
+ mon_cantrips = ""
68
+ mon_spells = ""
69
+ mon_spell_slots = ""
70
+ mon_legendary_actions = ""
71
+
72
+ llm_output = dsh.call_llm_and_cleanup(user_monster_text, spellcaster, legendary_actions)
73
+ user_monster = dsh.convert_to_dict(llm_output)
74
+ user_monster = llm_output
75
+ keys_list = list(user_monster)
76
+ mon_name = user_monster['name']
77
+ mon_size = user_monster['size']
78
+ mon_type = user_monster['type']
79
+ mon_subtype = user_monster['subtype']
80
+ mon_alignment = user_monster['alignment']
81
+ mon_armor_class = user_monster['armor_class']
82
+ mon_hp = user_monster['hit_points']
83
+ mon_hit_dice = user_monster['hit_dice']
84
+ mon_speed = user_monster['speed']
85
+ if type(mon_speed) == dict:
86
+ mon_speed = process_text.format_mon_qualities(mon_speed)
87
+
88
+ mon_abilities = process_text.format_abilities_for_editing(user_monster['abilities'])
89
+ mon_saving_throws = user_monster['saving_throws']
90
+ if type(mon_saving_throws) == dict:
91
+ mon_saving_throws = process_text.format_mon_qualities(mon_saving_throws)
92
+
93
+ mon_skills = user_monster['skills']
94
+ if type(mon_skills) == dict:
95
+ mon_skills = process_text.format_mon_qualities(mon_skills)
96
+ mon_damage_resistance = user_monster['damage_resistance']
97
+ if type(mon_damage_resistance) == dict:
98
+ mon_damage_resistance = process_text.format_mon_qualities(mon_damage_resistance)
99
+ mon_senses = user_monster['senses']
100
+ if type(mon_senses) == dict:
101
+ mon_senses = process_text.format_mon_qualities(mon_senses)
102
+ mon_languages = user_monster['languages']
103
+ mon_challenge_rating = user_monster['challenge_rating']
104
+ mon_xp = user_monster['xp']
105
+ if 'actions' in keys_list:
106
+ mon_actions = process_text.format_actions_for_editing(user_monster['actions'])
107
+
108
+
109
+ if "spells" in keys_list :
110
+ print(user_monster['spells'])
111
+ print(f"Length of spells : {len(user_monster['spells'])}")
112
+ if len(user_monster['spells']) >= 1 and user_monster['spells'] != "{}":
113
+ mon_spells = user_monster['spells']
114
+ mon_cantrips,mon_spells,mon_spell_slots = process_text.format_spells_for_editing(mon_spells)
115
+ print(mon_cantrips,mon_spells, mon_spell_slots)
116
+
117
+ if 'legendary_actions' in keys_list and len(user_monster['legendary_actions']) >= 1 and user_monster['legendary_actions'] != "{}":
118
+ mon_legendary_actions = user_monster['legendary_actions']
119
+ if type(mon_legendary_actions) == dict:
120
+ mon_legendary_actions = process_text.format_legendaries_for_editing(mon_legendary_actions)
121
+
122
+ mon_description = user_monster['description']
123
+ mon_sd_prompt = user_monster['sd_prompt']
124
+
125
+ #Return each State variable twice, once to the variable and once to the textbox
126
+ return [mon_name,mon_name,
127
+ mon_size,mon_size,
128
+ mon_type,mon_type,
129
+ mon_subtype,mon_subtype,
130
+ mon_alignment, mon_alignment,
131
+ mon_armor_class, mon_armor_class,
132
+ mon_hp, mon_hp,
133
+ mon_hit_dice, mon_hit_dice,
134
+ mon_speed, mon_speed,
135
+ mon_abilities,mon_abilities,
136
+ mon_saving_throws, mon_saving_throws,
137
+ mon_skills, mon_skills,
138
+ mon_damage_resistance, mon_damage_resistance,
139
+ mon_senses, mon_senses,
140
+ mon_languages, mon_languages,
141
+ mon_challenge_rating, mon_challenge_rating,
142
+ mon_xp, mon_xp,
143
+ mon_actions, mon_actions,
144
+ mon_cantrips, mon_cantrips, mon_spells, mon_spells,mon_spell_slots,mon_spell_slots,
145
+ mon_legendary_actions, mon_legendary_actions,
146
+ mon_description, mon_description,
147
+ mon_sd_prompt,mon_sd_prompt
148
+ ]
149
+
150
+
151
+ # Called on user selecting an image from the gallery, outputs the path of the image
152
+ def assign_img_path(evt: gr.SelectData):
153
+ img_dict = evt.value
154
+ print(img_dict)
155
+ selected_image_path = img_dict['image']['url']
156
+ print(selected_image_path)
157
+ return selected_image_path
158
+
159
+ # Make a list of files in image_temp and delete them
160
+ def delete_temp_images():
161
+ image_list = u.directory_contents('./image_temp')
162
+ u.delete_files(image_list)
163
+ #img2img.image_list.clear()
164
+
165
+ # Called when pressing button to generate image, updates gallery by returning the list of image URLs
166
+ def generate_image_update_gallery(image_prompt,image_name, token = False):
167
+ delete_temp_images()
168
+ print(f"sd_prompt is a {type(image_prompt)}")
169
+ image_list = []
170
+
171
+ num_img = 4
172
+ for x in range(num_img):
173
+ preview = sd.preview_and_generate_image(image_name, image_prompt,token)
174
+ image_list.append(preview)
175
+ yield image_list
176
+ del preview
177
+
178
+ return image_list
179
+
180
+ def generate_token_update_gallery(image_prompt,image_name,token = True):
181
+ delete_temp_images()
182
+ print(f"sd_prompt is a {type(image_prompt)}")
183
+ image_list = []
184
+
185
+ num_img = 4
186
+ for x in range(num_img):
187
+ preview = sd.preview_and_generate_image(image_name, image_prompt,token)
188
+ image_list.append(preview)
189
+ yield image_list
190
+ del preview
191
+
192
+ return image_list
193
+
194
+
195
+ # Generate a 3D model by passing the chosen token image to Tripo3D.ai API
196
+ def generate_model_update_gallery(image):
197
+ generated_model = tripo3d.generate_model(image)
198
+ return generated_model
199
+
200
+ # Build html text by processing the generated dictionaries.
201
+ def build_html_file(mon_name_output,
202
+ mon_size_output,
203
+ mon_type_output,
204
+ mon_subtype_output,
205
+ mon_alignment_output,
206
+ mon_armor_class_output,
207
+ mon_hp_output,
208
+ mon_hit_dice_output,
209
+ mon_speed_output,
210
+ mon_abilities,
211
+ mon_saving_throws_output,
212
+ mon_skills_output,
213
+ mon_damage_resistance_output,
214
+ mon_senses_output,
215
+ mon_languages_output,
216
+ mon_challenge_rating_output,
217
+ mon_xp_output,
218
+ mon_actions_output,
219
+ selected_generated_image,
220
+ mon_description_output,
221
+ mon_cantrips_output,
222
+ mon_spells_output,
223
+ mon_spell_slot_output,
224
+ mon_legendary_actions_output
225
+ ):
226
+ mon_file_path = process_html.build_html_base(
227
+ mon_name_output,
228
+ mon_size_output,
229
+ mon_type_output,
230
+ mon_subtype_output,
231
+ mon_alignment_output,
232
+ mon_armor_class_output,
233
+ mon_hp_output,
234
+ mon_hit_dice_output,
235
+ mon_speed_output,
236
+ mon_abilities,
237
+ mon_saving_throws_output,
238
+ mon_skills_output,
239
+ mon_damage_resistance_output,
240
+ mon_senses_output,
241
+ mon_languages_output,
242
+ mon_challenge_rating_output,
243
+ mon_xp_output,
244
+ mon_actions_output,
245
+ selected_generated_image,
246
+ mon_description_output,
247
+ mon_cantrips_output,
248
+ mon_spells_output,
249
+ mon_spell_slot_output,
250
+ mon_legendary_actions_output,
251
+
252
+
253
+ )
254
+ mon_file_path = u.file_name_list[0]+'/' + u.file_name_list[1] +'.html'
255
+ if not os.path.exists(mon_file_path):
256
+ print(f"{mon_file_path} not found")
257
+ else: print(f"{mon_file_path} found")
258
+ iframe = iframe = f"""<iframe src="file={mon_file_path}" width="100%" height="500px"></iframe>"""
259
+
260
+ return iframe
261
+
262
+ # Build the html and file path to pass to gradio to output html file in gr.html
263
+ def gen_link():
264
+ mon_file_path = u.file_name_list[0]+'/' + u.file_name_list[1] +'.html'
265
+ if not os.path.exists(mon_file_path):
266
+ print(f"{mon_file_path} not found")
267
+ else: print(f"{mon_file_path} found")
268
+ iframe = iframe = f"""<iframe src="file={mon_file_path}" width="100%" height="500px"></iframe>"""
269
+ link = f'<a href="file={mon_file_path}" target="_blank">{u.file_name_list[1] +".html"}</a>'
270
+ return iframe
271
+
272
+ gr.HTML(""" <div id="inner"> <header>
273
+ <h1>Monster Statblock Generator</h1>
274
+ <p>
275
+ With this AI driven tool you will build a collectible style card of a fantasy flavored item with details.
276
+ </p>
277
+ </div>""")
278
+
279
+ markdown_instructions = """## How It Works
280
+ This tool is a fun way to quickly generate Dungeons and Dragons monster manual style statblocks with art and a token for a Virtual Table Top.
281
+ 1. Your intitial text along with the prompt is sent to Llama 3 70b to generate all the values for a Dungeons and Dragons creature.
282
+ a. Include as much or as little information as you'd like.
283
+ b. Just a name : The Flavor Lich
284
+ c. A bit of detail : A friendly skeletal lich who is a master of flavor, called The Flavor Lich
285
+ d. Lots of detail : A friendly skeletal lich who is a master of flavor, called The Flavor Lich, the Lich is Challenge Rating 8 and is a 4th level spell caster whose spells are all about food.
286
+ 2. The results will populate below in editable fields that are saved on edit.
287
+ 3. Review the results, make any changes you'd like.
288
+ ## The first image generation take about 2 minutes for model to 'cold boot' after that it's ~10s per image.
289
+ **Image and Text Generation**: Now you can generate 4 images for the statblock page without text and pick your favorite.
290
+ 4. Click 'Generate Statblock Art' and wait for the images to generate, then select the one you'd like to use.
291
+ 5. Click 'Generate HTML' to generate a webpage that can be saved or printed as PDF.
292
+ 6. Last, you can generate a Virtual Tabletop token or figure of your creature.
293
+ """
294
+
295
+ gr.Markdown(markdown_instructions)
296
+ with gr.Tab("Generator"):
297
+
298
+ with gr.Row():
299
+ with gr.Column():
300
+ user_mon_description = gr.Textbox(label = "Step 1 : Write a description and give a name or type to your creation!",
301
+ lines = 1, placeholder=f"Ex : A friendly skeletal lich who is a master of flavor, called The Flavor Lich",
302
+
303
+ elem_id= "custom-textbox")
304
+ with gr.Column():
305
+ spells_checkbox = gr.Checkbox(label= "Spellcaster?")
306
+ legendary_action_checkbox = gr.Checkbox(label= "Legendary Actions?")
307
+
308
+ desc_gen = gr.Button(value = "Step 2 : Generate Description")
309
+
310
+ mon_description_output = gr.Textbox(label = 'Description', lines = 2, interactive=True)
311
+
312
+
313
+ with gr.Row():
314
+ with gr.Column(scale = 1):
315
+ mon_name_output = gr.Textbox(label = 'Name', lines = 1, interactive=True)
316
+ mon_size_output = gr.Textbox(label = 'Size', lines = 1, interactive=True)
317
+ mon_alignment_output = gr.Textbox(label = 'Alignment', lines = 1, interactive=True)
318
+ mon_armor_class_output = gr.Textbox(label = 'Armor Class', lines = 1, interactive=True)
319
+ mon_hit_dice_output = gr.Textbox(label = 'Hit Dice', lines = 1, interactive=True)
320
+ mon_senses_output = gr.Textbox(label = 'Senses', lines = 1, interactive=True)
321
+ mon_actions_output = gr.Textbox(label = 'Actions', lines = 16, interactive=True)
322
+
323
+ with gr.Column(scale = 1):
324
+ mon_type_output = gr.Textbox(label = 'Type', lines = 1, interactive=True)
325
+ mon_speed_output = gr.Textbox(label = 'Speed', lines = 1, interactive=True)
326
+ mon_abilities_output = gr.Textbox(label ='Ability Scores', lines = 5, interactive=True)
327
+ mon_damage_resistance_output = gr.Textbox(label = 'Damage Resistance', lines = 1, interactive=True)
328
+ mon_challenge_rating_output = gr.Textbox(label = 'Challenge Rating', lines = 1, interactive=True)
329
+ mon_cantrips_output = gr.Textbox(label = 'Cantrips', lines = 16, interactive=True)
330
+ mon_spells_output = gr.Textbox(label = 'Spells', lines = 16, interactive=True)
331
+ mon_spell_slot_output = gr.Textbox(label = 'Spell Slots', lines = 8, interactive=True)
332
+
333
+ with gr.Column(scale = 1):
334
+ mon_subtype_output = gr.Textbox(label = 'Subtype', lines = 1, interactive=True)
335
+ mon_saving_throws_output = gr.Textbox(label = 'Saving Throws', lines = 1, interactive=True)
336
+ mon_skills_output = gr.Textbox(label = 'Skills', lines = 1, interactive=True)
337
+ mon_hp_output = gr.Textbox(label = 'Health Points', lines = 1, interactive=True)
338
+ mon_languages_output = gr.Textbox(label = 'Languages', lines = 1, interactive=True)
339
+ mon_xp_output = gr.Textbox(label = 'XP', lines = 1, interactive=True)
340
+ mon_legendary_actions_output = gr.Textbox(label = 'Legendary Actions', lines = 16, interactive=True)
341
+
342
+ mon_sd_prompt_output = gr.Textbox(label = 'Image Generation Prompt', lines = 1, interactive=True)
343
+
344
+ with gr.Row():
345
+ with gr.Column():
346
+ image_gen = gr.Button(value = "Generate Art for the statblock" )
347
+ mon_image_gallery = gr.Gallery(label = "Generated Images",
348
+ show_label = True,
349
+ elem_id = "gallery",
350
+ columns =[4], rows =[1],
351
+ object_fit = "contain", height ="auto")
352
+
353
+ with gr.Column():
354
+ token_gen = gr.Button(value = "Generate Token Image" )
355
+ mon_token_gallery = gr.Gallery(label = "Generated Tokens",
356
+ show_label = True,
357
+ elem_id = "token_gallery",
358
+ columns =[4], rows =[1],
359
+ object_fit = "contain", height ="auto")
360
+ model_gen = gr.Button(value= "Generate a 3D model")
361
+ mon_model_gallery = gr.Model3D(label = "3D Model Display",
362
+ elem_id = "model_gallery",
363
+ )
364
+
365
+
366
+
367
+
368
+
369
+
370
+ image_gen.click(generate_image_update_gallery, inputs = [mon_sd_prompt_output,mon_name], outputs = mon_image_gallery)
371
+ token_gen.click(generate_token_update_gallery, inputs = [mon_sd_prompt_output,mon_name], outputs = mon_token_gallery)
372
+ model_gen.click(generate_model_update_gallery, inputs = selected_generated_image, outputs = mon_model_gallery)
373
+
374
+ mon_image_gallery.select(fn = assign_img_path, outputs=selected_generated_image)
375
+ mon_token_gallery.select(fn = assign_img_path, outputs=selected_token_image)
376
+
377
+ desc_gen.click(fn = gen_mon_desc, inputs = [user_mon_description,spells_checkbox, legendary_action_checkbox],
378
+ outputs= [mon_name, mon_name_output,
379
+ mon_size,mon_size_output,
380
+ mon_type,mon_type_output,
381
+ mon_subtype,mon_subtype_output,
382
+ mon_alignment, mon_alignment_output,
383
+ mon_armor_class, mon_armor_class_output,
384
+ mon_hp, mon_hp_output,
385
+ mon_hit_dice, mon_hit_dice_output,
386
+ mon_speed, mon_speed_output,
387
+ mon_abilities, mon_abilities_output,
388
+ mon_saving_throws, mon_saving_throws_output,
389
+ mon_skills, mon_skills_output,
390
+ mon_damage_resistance, mon_damage_resistance_output,
391
+ mon_senses, mon_senses_output,
392
+ mon_languages, mon_languages_output,
393
+ mon_challenge_rating, mon_challenge_rating_output,
394
+ mon_xp, mon_xp_output,
395
+ mon_actions, mon_actions_output,
396
+ mon_cantrips,mon_cantrips_output,
397
+ mon_spells, mon_spells_output,
398
+ mon_spell_slots, mon_spell_slot_output,
399
+ mon_legendary_actions, mon_legendary_actions_output,
400
+ mon_description, mon_description_output,
401
+ mon_sd_prompt,mon_sd_prompt_output
402
+
403
+ ])
404
+
405
+
406
+ # Build buttons to modify to html and show html
407
+ gen_html = gr.Button(value = "Step 8 : Generate html, click once then go to Step 9")
408
+ html = gr.HTML(label="HTML preview", show_label=True)
409
+ gen_html.click(build_html_file,inputs =[
410
+ mon_name_output,
411
+ mon_size_output,
412
+ mon_type_output,
413
+ mon_subtype_output,
414
+ mon_alignment_output,
415
+ mon_armor_class_output,
416
+ mon_hp_output,
417
+ mon_hit_dice_output,
418
+ mon_speed_output,
419
+ mon_abilities_output,
420
+ mon_saving_throws_output,
421
+ mon_skills_output,
422
+ mon_damage_resistance_output,
423
+ mon_senses_output,
424
+ mon_languages_output,
425
+ mon_challenge_rating_output,
426
+ mon_xp_output,
427
+ mon_actions_output,
428
+ mon_description_output,
429
+ selected_generated_image,
430
+ mon_cantrips_output,
431
+ mon_spells_output,
432
+ mon_spell_slot_output,
433
+ mon_legendary_actions_output,
434
+
435
+ ],
436
+ outputs= html
437
+ )
438
+
439
+
440
+
441
+
442
+
443
+
444
+ def main() -> None:
445
+ # run web server, expose port 8000, share to create web link, give app access folder path, gradio was updated for security and can no longer serve any directory not specified.
446
+ if __name__ == '__main__':
447
+ demo.launch(server_name = "0.0.0.0", server_port = 8000, share = False, allowed_paths = ["/media/drakosfire/Shared/Docker/StatblockGenerator/output","/media/drakosfire/Shared/Docker/StatblockGenerator/dependencies"])
448
+
449
+ main()
450
+
451
+
452
+
453
+
454
+
455
+
456
+
457
+
458
+
459
+
460
+
dependencies/bundle.css CHANGED
The diff for this file is too large to render. See raw diff
 
dependencies/style.css CHANGED
@@ -17,4 +17,579 @@
17
  @font-face{font-family:Overpass;src:url('./themes/fonts/5e/Overpass Medium.woff2');font-weight:500;font-style:normal}@font-face{font-family:Davek;src:url('./themes/fonts/5e/Davek.woff2');font-weight:500;font-style:normal}
18
  @font-face{font-family:Iokharic;src:url('./themes/fonts/5e/Iokharic.woff2');font-weight:500;font-style:normal}
19
  @font-face{font-family:Rellanic;src:url('./themes/fonts/5e/Rellanic.woff2');font-weight:500;font-style:normal}:root{--HB_Color_Background:#FFFFFF;--HB_Color_WatercolorStain:#000000}
20
- @page{margin:0}body{counter-reset:phb-page-numbers}*{-webkit-print-color-adjust:exact}.page .block{break-inside:avoid;display:inline-block;width:100%}.page .block img{z-index:0}.page .inline-block{display:inline-block;text-indent:initial}.columnWrapper{max-height:100%;column-span:all;columns:inherit;column-gap:inherit;column-fill:inherit}.page{column-fill:auto;column-count:2;height:279.4mm;width:215.9mm;padding:1.4cm 1.9cm 1.7cm;counter-increment:phb-page-numbers;background-color:var(--HB_Color_Background);position:relative;z-index:15;box-sizing:border-box;overflow:hidden;text-rendering:optimizeLegibility;page-break-before:always;page-break-after:always;contain:size}.page p{overflow-wrap:break-word;display:block}.page strong{font-weight:bold}.page em{font-style:italic}.page sup{vertical-align:super;font-size:smaller;line-height:0}.page sub{vertical-align:sub;font-size:smaller;line-height:0}.page ul{list-style-position:outside;list-style-type:disc;padding-left:1.4em}.page ol{list-style-position:outside;list-style-type:decimal;padding-left:1.4em}.page img{z-index:-1}.page h1,.page h2,.page h3,.page h4,.page h5,.page h6{font-weight:bold;line-height:1.2em}.page h1{font-size:2em}.page h2{font-size:1.5em}.page h3{font-size:1.17em}.page h4{font-size:1em}.page h5{font-size:.83em}.page table{width:100%}.page table thead{display:table-row-group;font-weight:bold}.page div:not(.columnWrapper)>table+table{margin-top:0}.page code{font-family:"Courier New",Courier,monospace;white-space:pre-wrap;overflow-wrap:break-word}.page pre code{width:100%;display:inline-block}.page .columnSplit{visibility:hidden;-webkit-column-break-after:always;break-after:always;-moz-column-break-after:always;margin-top:0}.page .columnSplit+*{margin-top:0}.page blockquote,.page table{z-index:15;-webkit-column-break-inside:avoid;page-break-inside:avoid;break-inside:avoid}.page ul ul,.page ol ol,.page ul ol,.page ol ul{margin-bottom:0px;margin-left:1.5em}.page li{-webkit-column-break-inside:avoid;page-break-inside:avoid;break-inside:avoid}.page .watermark{display:grid !important;place-items:center;justify-content:center;position:absolute;margin:0;top:0;left:0;width:100%;height:100%;font-size:120px;text-transform:uppercase;mix-blend-mode:overlay;opacity:30%;transform:rotate(-45deg);z-index:500}.page .watermark p{margin-bottom:none}.page [class*="watercolor"]{position:absolute;width:2000px;height:2000px;-webkit-mask-image:var(--wc);-webkit-mask-size:contain;-webkit-mask-repeat:no-repeat;mask-image:var(--wc);mask-size:contain;mask-repeat:no-repeat;background-size:cover;background-color:var(--HB_Color_WatercolorStain);--wc:url('/assets/watercolor/watercolor1.png');z-index:-2}.page .watercolor1{--wc:url('/assets/watercolor/watercolor1.png')}.page .watercolor2{--wc:url('/assets/watercolor/watercolor2.png')}.page .watercolor3{--wc:url('/assets/watercolor/watercolor3.png')}.page .watercolor4{--wc:url('/assets/watercolor/watercolor4.png')}.page .watercolor5{--wc:url('/assets/watercolor/watercolor5.png')}.page .watercolor6{--wc:url('/assets/watercolor/watercolor6.png')}.page .watercolor7{--wc:url('/assets/watercolor/watercolor7.png')}.page .watercolor8{--wc:url('/assets/watercolor/watercolor8.png')}.page .watercolor9{--wc:url('/assets/watercolor/watercolor9.png')}.page .watercolor10{--wc:url('/assets/watercolor/watercolor10.png')}.page .watercolor11{--wc:url('/assets/watercolor/watercolor11.png')}.page .watercolor12{--wc:url('/assets/watercolor/watercolor12.png')}.page [class*="imageMask"]{position:absolute;height:200%;width:200%;left:50%;bottom:50%;--rotation:0;--revealer:none;--checkerboard:none;--scaleX:1;--scaleY:1;-webkit-mask-image:var(--wc),var(--revealer);-webkit-mask-repeat:repeat-x;-webkit-mask-size:50%;-webkit-mask-position:50% calc(50% - var(--offset));mask-image:var(--wc);mask-repeat:repeat-x;mask-size:50%;mask-position:50% calc(50% - var(--offset));background-image:var(--checkerboard);background-size:20px;z-index:-1;transform:translateY(50%) translateX(-50%) rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY))}.page [class*="imageMask"]>p:has(img){position:absolute;width:50%;height:50%;bottom:50%;left:50%;transform:translateX(-50%) translateY(50%) rotate(calc(-1deg * var(--rotation))) scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY)))}.page [class*="imageMask"] img{position:absolute;display:block;bottom:0}.page [class*="imageMask"].bottom{--rotation:0}.page [class*="imageMask"].bottom img{bottom:0}.page [class*="imageMask"].top{--rotation:180}.page [class*="imageMask"].top img{top:0}.page [class*="imageMask"].left{--rotation:90}.page [class*="imageMask"].left img{left:0}.page [class*="imageMask"].right{--rotation:-90}.page [class*="imageMask"].right img{right:0}.page [class*="imageMask"].revealImage{--revealer:linear-gradient(0deg, rgba(0,0,0,0.2) 0%, rgba(0,0,0,0.2));--checkerboard:url(/assets/waterColorMasks/missingImage.png)}.page .imageMaskEdge1{--wc:url(/assets/waterColorMasks/edge/0001.webp)}.page .imageMaskEdge2{--wc:url(/assets/waterColorMasks/edge/0002.webp)}.page .imageMaskEdge3{--wc:url(/assets/waterColorMasks/edge/0003.webp)}.page .imageMaskEdge4{--wc:url(/assets/waterColorMasks/edge/0004.webp)}.page .imageMaskEdge5{--wc:url(/assets/waterColorMasks/edge/0005.webp)}.page .imageMaskEdge6{--wc:url(/assets/waterColorMasks/edge/0006.webp)}.page .imageMaskEdge7{--wc:url(/assets/waterColorMasks/edge/0007.webp)}.page .imageMaskEdge8{--wc:url(/assets/waterColorMasks/edge/0008.webp)}.page [class*="imageMaskCenter"]{width:100%;height:100%;left:calc(var(--offsetX));bottom:calc(var(--offsetY));-webkit-mask-image:var(--wc),var(--revealer);-webkit-mask-repeat:no-repeat;-webkit-mask-size:100% 100%;-webkit-mask-position:0% 0%;mask-image:var(--wc),var(--revealer);mask-repeat:no-repeat;mask-size:100% 100%;mask-position:50% 50%;transform:rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY))}.page [class*="imageMaskCenter"]>p:has(img){position:absolute;width:100%;height:100%;bottom:0;left:0;transform:unset;transform:scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY))) rotate(calc(-1deg * var(--rotation))) translateX(calc(-1 * var(--offsetX))) translateY(calc(1 * var(--offsetY)))}.page .imageMaskCenter1{--wc:url(/assets/waterColorMasks/center/0001.webp)}.page .imageMaskCenter2{--wc:url(/assets/waterColorMasks/center/0002.webp)}.page .imageMaskCenter3{--wc:url(/assets/waterColorMasks/center/0003.webp)}.page .imageMaskCenter4{--wc:url(/assets/waterColorMasks/center/0004.webp)}.page .imageMaskCenter5{--wc:url(/assets/waterColorMasks/center/0005.webp)}.page .imageMaskCenter6{--wc:url(/assets/waterColorMasks/center/0006.webp)}.page .imageMaskCenter7{--wc:url(/assets/waterColorMasks/center/0007.webp)}.page .imageMaskCenter8{--wc:url(/assets/waterColorMasks/center/0008.webp)}.page .imageMaskCenter9{--wc:url(/assets/waterColorMasks/center/0009.webp)}.page .imageMaskCenter10{--wc:url(/assets/waterColorMasks/center/0010.webp)}.page .imageMaskCenter11{--wc:url(/assets/waterColorMasks/center/0011.webp)}.page .imageMaskCenter12{--wc:url(/assets/waterColorMasks/center/0012.webp)}.page .imageMaskCenter13{--wc:url(/assets/waterColorMasks/center/0013.webp)}.page .imageMaskCenter14{--wc:url(/assets/waterColorMasks/center/0014.webp)}.page .imageMaskCenter15{--wc:url(/assets/waterColorMasks/center/0015.webp)}.page .imageMaskCenter16{--wc:url(/assets/waterColorMasks/center/0016.webp)}.page .imageMaskCenterspecial{--wc:url(/assets/waterColorMasks/center/special.webp)}.page [class*="imageMaskCorner"]{height:200%;width:200%;left:calc(-50% + var(--offsetX));bottom:calc(-50% + var(--offsetY));-webkit-mask-image:var(--wc),var(--revealer);-webkit-mask-repeat:no-repeat;-webkit-mask-size:100% 100%;-webkit-mask-position:50% 50%;mask-image:var(--wc),var(--revealer);mask-repeat:no-repeat;mask-size:100% 100%;mask-position:50% 50%;transform:rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY))}.page [class*="imageMaskCorner"]>p:has(img){width:50%;height:50%;left:25%;bottom:25%;transform:scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY))) rotate(calc(-1deg * var(--rotation))) translateX(calc(-1 * var(--offsetX))) translateY(calc(1 * var(--offsetY)))}.page .imageMaskCorner1{--wc:url(/assets/waterColorMasks/corner/0001.webp)}.page .imageMaskCorner2{--wc:url(/assets/waterColorMasks/corner/0002.webp)}.page .imageMaskCorner3{--wc:url(/assets/waterColorMasks/corner/0003.webp)}.page .imageMaskCorner4{--wc:url(/assets/waterColorMasks/corner/0004.webp)}.page .imageMaskCorner5{--wc:url(/assets/waterColorMasks/corner/0005.webp)}.page .imageMaskCorner6{--wc:url(/assets/waterColorMasks/corner/0006.webp)}.page .imageMaskCorner7{--wc:url(/assets/waterColorMasks/corner/0007.webp)}.page .imageMaskCorner8{--wc:url(/assets/waterColorMasks/corner/0008.webp)}.page .imageMaskCorner9{--wc:url(/assets/waterColorMasks/corner/0009.webp)}.page .imageMaskCorner10{--wc:url(/assets/waterColorMasks/corner/0010.webp)}.page .imageMaskCorner11{--wc:url(/assets/waterColorMasks/corner/0011.webp)}.page .imageMaskCorner12{--wc:url(/assets/waterColorMasks/corner/0012.webp)}.page .imageMaskCorner13{--wc:url(/assets/waterColorMasks/corner/0013.webp)}.page .imageMaskCorner14{--wc:url(/assets/waterColorMasks/corner/0014.webp)}.page .imageMaskCorner15{--wc:url(/assets/waterColorMasks/corner/0015.webp)}.page .imageMaskCorner16{--wc:url(/assets/waterColorMasks/corner/0016.webp)}.page .imageMaskCorner17{--wc:url(/assets/waterColorMasks/corner/0017.webp)}.page .imageMaskCorner18{--wc:url(/assets/waterColorMasks/corner/0018.webp)}.page .imageMaskCorner19{--wc:url(/assets/waterColorMasks/corner/0019.webp)}.page .imageMaskCorner20{--wc:url(/assets/waterColorMasks/corner/0020.webp)}.page .imageMaskCorner21{--wc:url(/assets/waterColorMasks/corner/0021.webp)}.page .imageMaskCorner22{--wc:url(/assets/waterColorMasks/corner/0022.webp)}.page .imageMaskCorner23{--wc:url(/assets/waterColorMasks/corner/0023.webp)}.page .imageMaskCorner24{--wc:url(/assets/waterColorMasks/corner/0024.webp)}.page .imageMaskCorner25{--wc:url(/assets/waterColorMasks/corner/0025.webp)}.page .imageMaskCorner26{--wc:url(/assets/waterColorMasks/corner/0026.webp)}.page .imageMaskCorner27{--wc:url(/assets/waterColorMasks/corner/0027.webp)}.page .imageMaskCorner28{--wc:url(/assets/waterColorMasks/corner/0028.webp)}.page .imageMaskCorner29{--wc:url(/assets/waterColorMasks/corner/0029.webp)}.page .imageMaskCorner30{--wc:url(/assets/waterColorMasks/corner/0030.webp)}.page .imageMaskCorner31{--wc:url(/assets/waterColorMasks/corner/0031.webp)}.page .imageMaskCorner32{--wc:url(/assets/waterColorMasks/corner/0032.webp)}.page .imageMaskCorner33{--wc:url(/assets/waterColorMasks/corner/0033.webp)}.page .imageMaskCorner34{--wc:url(/assets/waterColorMasks/corner/0034.webp)}.page .imageMaskCorner35{--wc:url(/assets/waterColorMasks/corner/0035.webp)}.page .imageMaskCorner36{--wc:url(/assets/waterColorMasks/corner/0036.webp)}.page .imageMaskCorner37{--wc:url(/assets/waterColorMasks/corner/0037.webp)}.page dl{padding-left:1em;white-space:pre-line}.page dt{display:inline;margin-right:.5ch;margin-left:-1em}.page dd{display:inline;margin-left:0;text-indent:0}.page .blank{height:1em;margin-top:0}.page .blank+*{margin-top:0}.page .wide{column-span:all;display:block;margin-bottom:1em}.page .wide+*{margin-top:0}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  @font-face{font-family:Overpass;src:url('./themes/fonts/5e/Overpass Medium.woff2');font-weight:500;font-style:normal}@font-face{font-family:Davek;src:url('./themes/fonts/5e/Davek.woff2');font-weight:500;font-style:normal}
18
  @font-face{font-family:Iokharic;src:url('./themes/fonts/5e/Iokharic.woff2');font-weight:500;font-style:normal}
19
  @font-face{font-family:Rellanic;src:url('./themes/fonts/5e/Rellanic.woff2');font-weight:500;font-style:normal}:root{--HB_Color_Background:#FFFFFF;--HB_Color_WatercolorStain:#000000}
20
+ @page {
21
+ margin: 0;
22
+ }
23
+
24
+ body {
25
+ counter-reset: phb-page-numbers;
26
+ }
27
+
28
+ * {
29
+ -webkit-print-color-adjust: exact;
30
+ }
31
+
32
+ .page .block {
33
+ break-inside: avoid;
34
+ display: inline-block;
35
+ width: 100%;
36
+ }
37
+
38
+ .page .block img {
39
+ z-index: 0;
40
+ }
41
+
42
+ .page .inline-block {
43
+ display: inline-block;
44
+ text-indent: initial;
45
+ }
46
+
47
+ .columnWrapper {
48
+ max-height: 100%;
49
+ column-span: all;
50
+ columns: inherit;
51
+ column-gap: inherit;
52
+ column-fill: inherit;
53
+ }
54
+
55
+ .page {
56
+ column-fill: auto;
57
+ column-count: 2;
58
+ height: 279.4mm;
59
+ width: 215.9mm;
60
+ padding: 1.4cm 1.9cm 1.7cm;
61
+ counter-increment: phb-page-numbers;
62
+ background-color: var(--HB_Color_Background);
63
+ position: relative;
64
+ z-index: 15;
65
+ box-sizing: border-box;
66
+ overflow: hidden;
67
+ text-rendering: optimizeLegibility;
68
+ page-break-before: always;
69
+ page-break-after: always;
70
+ contain: size;
71
+ }
72
+
73
+ .page p {
74
+ overflow-wrap: break-word;
75
+ display: block;
76
+ }
77
+
78
+ .page strong {
79
+ font-weight: bold;
80
+ }
81
+
82
+ .page em {
83
+ font-style: italic;
84
+ }
85
+
86
+ .page sup {
87
+ vertical-align: super;
88
+ font-size: smaller;
89
+ line-height: 0;
90
+ }
91
+
92
+ .page sub {
93
+ vertical-align: sub;
94
+ font-size: smaller;
95
+ line-height: 0;
96
+ }
97
+
98
+ .page ul {
99
+ list-style-position: outside;
100
+ list-style-type: disc;
101
+ padding-left: 1.4em;
102
+ }
103
+
104
+ .page ol {
105
+ list-style-position: outside;
106
+ list-style-type: decimal;
107
+ padding-left: 1.4em;
108
+ }
109
+
110
+ .page img {
111
+ z-index: -1;
112
+ }
113
+
114
+ .page h1,
115
+ .page h2,
116
+ .page h3,
117
+ .page h4,
118
+ .page h5,
119
+ .page h6 {
120
+ font-weight: bold;
121
+ line-height: 1.2em;
122
+ }
123
+
124
+ .page h1 {
125
+ font-size: 2em;
126
+ }
127
+
128
+ .page h2 {
129
+ font-size: 1.5em;
130
+ }
131
+
132
+ .page h3 {
133
+ font-size: 1.17em;
134
+ }
135
+
136
+ .page h4 {
137
+ font-size: 1em;
138
+ }
139
+
140
+ .page h5 {
141
+ font-size: 0.83em;
142
+ }
143
+
144
+ .page table {
145
+ width: 100%;
146
+ }
147
+
148
+ .page table thead {
149
+ display: table-row-group;
150
+ font-weight: bold;
151
+ }
152
+
153
+ .page div:not(.columnWrapper) > table + table {
154
+ margin-top: 0;
155
+ }
156
+
157
+ .page code {
158
+ font-family: "Courier New", Courier, monospace;
159
+ white-space: pre-wrap;
160
+ overflow-wrap: break-word;
161
+ }
162
+
163
+ .page pre code {
164
+ width: 100%;
165
+ display: inline-block;
166
+ }
167
+
168
+ .page .columnSplit {
169
+ visibility: hidden;
170
+ -webkit-column-break-after: always;
171
+ break-after: always;
172
+ -moz-column-break-after: always;
173
+ margin-top: 0;
174
+ }
175
+
176
+ .page .columnSplit + * {
177
+ margin-top: 0;
178
+ }
179
+
180
+ .page blockquote,
181
+ .page table {
182
+ z-index: 15;
183
+ -webkit-column-break-inside: avoid;
184
+ page-break-inside: avoid;
185
+ break-inside: avoid;
186
+ }
187
+
188
+ .page ul ul,
189
+ .page ol ol,
190
+ .page ul ol,
191
+ .page ol ul {
192
+ margin-bottom: 0px;
193
+ margin-left: 1.5em;
194
+ }
195
+
196
+ .page li {
197
+ -webkit-column-break-inside: avoid;
198
+ page-break-inside: avoid;
199
+ break-inside: avoid;
200
+ }
201
+
202
+ .page .watermark {
203
+ display: grid !important;
204
+ place-items: center;
205
+ justify-content: center;
206
+ position: absolute;
207
+ margin: 0;
208
+ top: 0;
209
+ left: 0;
210
+ width: 100%;
211
+ height: 100%;
212
+ font-size: 120px;
213
+ text-transform: uppercase;
214
+ mix-blend-mode: overlay;
215
+ opacity: 30%;
216
+ transform: rotate(-45deg);
217
+ z-index: 500;
218
+ }
219
+
220
+ .page .watermark p {
221
+ margin-bottom: none;
222
+ }
223
+
224
+ .page [class*="watercolor"] {
225
+ position: absolute;
226
+ width: 2000px;
227
+ height: 2000px;
228
+ -webkit-mask-image: var(--wc);
229
+ -webkit-mask-size: contain;
230
+ -webkit-mask-repeat: no-repeat;
231
+ mask-image: var(--wc);
232
+ mask-size: contain;
233
+ mask-repeat: no-repeat;
234
+ background-size: cover;
235
+ background-color: var(--HB_Color_WatercolorStain);
236
+ --wc: url('/assets/watercolor/watercolor1.png');
237
+ z-index: -2;
238
+ }
239
+
240
+ .page .watercolor1 {
241
+ --wc: url('/assets/watercolor/watercolor1.png');
242
+ }
243
+
244
+ .page .watercolor2 {
245
+ --wc: url('/assets/watercolor/watercolor2.png');
246
+ }
247
+
248
+ .page .watercolor3 {
249
+ --wc: url('/assets/watercolor/watercolor3.png');
250
+ }
251
+
252
+ .page .watercolor4 {
253
+ --wc: url('/assets/watercolor/watercolor4.png');
254
+ }
255
+
256
+ .page .watercolor5 {
257
+ --wc: url('/assets/watercolor/watercolor5.png');
258
+ }
259
+
260
+ .page .watercolor6 {
261
+ --wc: url('/assets/watercolor/watercolor6.png');
262
+ }
263
+
264
+ .page .watercolor7 {
265
+ --wc: url('/assets/watercolor/watercolor7.png');
266
+ }
267
+
268
+ .page .watercolor8 {
269
+ --wc: url('/assets/watercolor/watercolor8.png');
270
+ }
271
+
272
+ .page .watercolor9 {
273
+ --wc: url('/assets/watercolor/watercolor9.png');
274
+ }
275
+
276
+ .page .watercolor10 {
277
+ --wc: url('/assets/watercolor/watercolor10.png');
278
+ }
279
+
280
+ .page .watercolor11 {
281
+ --wc: url('/assets/watercolor/watercolor11.png');
282
+ }
283
+
284
+ .page .watercolor12 {
285
+ --wc: url('/assets/watercolor/watercolor12.png');
286
+ }
287
+
288
+ .page [class*="imageMask"] {
289
+ position: absolute;
290
+ height: 200%;
291
+ width: 200%;
292
+ left: 50%;
293
+ bottom: 50%;
294
+ --rotation: 0;
295
+ --revealer: none;
296
+ --checkerboard: none;
297
+ --scaleX: 1;
298
+ --scaleY: 1;
299
+ -webkit-mask-image: var(--wc), var(--revealer);
300
+ -webkit-mask-repeat: repeat-x;
301
+ -webkit-mask-size: 50%;
302
+ -webkit-mask-position: 50% calc(50% - var(--offset));
303
+ mask-image: var(--wc);
304
+ mask-repeat: repeat-x;
305
+ mask-size: 50%;
306
+ mask-position: 50% calc(50% - var(--offset));
307
+ background-image: var(--checkerboard);
308
+ background-size: 20px;
309
+ z-index: -1;
310
+ transform: translateY(50%) translateX(-50%) rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY));
311
+ }
312
+
313
+ .page [class*="imageMask"] > p:has(img) {
314
+ position: absolute;
315
+ width: 50%;
316
+ height: 50%;
317
+ bottom: 50%;
318
+ left: 50%;
319
+ transform: translateX(-50%) translateY(50%) rotate(calc(-1deg * var(--rotation))) scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY)));
320
+ }
321
+
322
+ .page [class*="imageMask"] img {
323
+ position: absolute;
324
+ display: block;
325
+ bottom: 0;
326
+ }
327
+
328
+ .page [class*="imageMask"].bottom {
329
+ --rotation: 0;
330
+ }
331
+
332
+ .page [class*="imageMask"].bottom img {
333
+ bottom: 0;
334
+ }
335
+
336
+ .page [class*="imageMask"].top {
337
+ --rotation: 180;
338
+ }
339
+
340
+ .page [class*="imageMask"].top img {
341
+ top: 0;
342
+ }
343
+
344
+ .page [class*="imageMask"].left {
345
+ --rotation: 90;
346
+ }
347
+
348
+ .page [class*="imageMask"].left img {
349
+ left: 0;
350
+ }
351
+
352
+ .page [class*="imageMask"].right {
353
+ --rotation: -90;
354
+ }
355
+
356
+ .page [class*="imageMask"].right img {
357
+ right: 0;
358
+ }
359
+
360
+ .page [class*="imageMask"].revealImage {
361
+ --revealer: linear-gradient(0deg, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.2));
362
+ --checkerboard: url(/assets/waterColorMasks/missingImage.png);
363
+ }
364
+
365
+ .page .imageMaskEdge1 {
366
+ --wc: url(/assets/waterColorMasks/edge/0001.webp);
367
+ }
368
+
369
+ .page .imageMaskEdge2 {
370
+ --wc: url(/assets/waterColorMasks/edge/0002.webp);
371
+ }
372
+
373
+ .page .imageMaskEdge3 {
374
+ --wc: url(/assets/waterColorMasks/edge/0003.webp);
375
+ }
376
+
377
+ .page .imageMaskEdge4 {
378
+ --wc: url(/assets/waterColorMasks/edge/0004.webp);
379
+ }
380
+
381
+ .page .imageMaskEdge5 {
382
+ --wc: url(/assets/waterColorMasks/edge/0005.webp);
383
+ }
384
+
385
+ .page .imageMaskEdge6 {
386
+ --wc: url(/assets/waterColorMasks/edge/0006.webp);
387
+ }
388
+
389
+ .page .imageMaskEdge7 {
390
+ --wc: url(/assets/waterColorMasks/edge/0007.webp);
391
+ }
392
+
393
+ .page .imageMaskEdge8 {
394
+ --wc: url(/assets/waterColorMasks/edge/0008.webp);
395
+ }
396
+
397
+ .page .imageMaskEdge9 {
398
+ --wc: url(/assets/waterColorMasks/edge/0009.webp);
399
+ }
400
+
401
+ .page .imageMaskEdge10 {
402
+ --wc: url(/assets/waterColorMasks/edge/0010.webp);
403
+ }
404
+
405
+ .page .imageMaskEdge11 {
406
+ --wc: url(/assets/waterColorMasks/edge/0011.webp);
407
+ }
408
+
409
+ .page .imageMaskEdge12 {
410
+ --wc: url(/assets/waterColorMasks/edge/0012.webp);
411
+ }
412
+
413
+ .page .imageMaskCorner1 {
414
+ --wc: url(/assets/waterColorMasks/corner/0001.webp);
415
+ }
416
+
417
+ .page .imageMaskCorner2 {
418
+ --wc: url(/assets/waterColorMasks/corner/0002.webp);
419
+ }
420
+
421
+ .page .imageMaskCorner3 {
422
+ --wc: url(/assets/waterColorMasks/corner/0003.webp);
423
+ }
424
+
425
+ .page .imageMaskCorner4 {
426
+ --wc: url(/assets/waterColorMasks/corner/0004.webp);
427
+ }
428
+
429
+ .page .imageMaskCorner5 {
430
+ --wc: url(/assets/waterColorMasks/corner/0005.webp);
431
+ }
432
+
433
+ .page .imageMaskCorner6 {
434
+ --wc: url(/assets/waterColorMasks/corner/0006.webp);
435
+ }
436
+
437
+ .page .imageMaskCorner7 {
438
+ --wc: url(/assets/waterColorMasks/corner/0007.webp);
439
+ }
440
+
441
+ .page .imageMaskCorner8 {
442
+ --wc: url(/assets/waterColorMasks/corner/0008.webp);
443
+ }
444
+
445
+ .page .imageMaskCorner9 {
446
+ --wc: url(/assets/waterColorMasks/corner/0009.webp);
447
+ }
448
+
449
+ .page .imageMaskCorner10 {
450
+ --wc: url(/assets/waterColorMasks/corner/0010.webp);
451
+ }
452
+
453
+ .page .imageMaskCorner11 {
454
+ --wc: url(/assets/waterColorMasks/corner/0011.webp);
455
+ }
456
+
457
+ .page .imageMaskCorner12 {
458
+ --wc: url(/assets/waterColorMasks/corner/0012.webp);
459
+ }
460
+
461
+ .page .imageMaskCorner13 {
462
+ --wc: url(/assets/waterColorMasks/corner/0013.webp);
463
+ }
464
+
465
+ .page .imageMaskCorner14 {
466
+ --wc: url(/assets/waterColorMasks/corner/0014.webp);
467
+ }
468
+
469
+ .page .imageMaskCorner15 {
470
+ --wc: url(/assets/waterColorMasks/corner/0015.webp);
471
+ }
472
+
473
+ .page .imageMaskCorner16 {
474
+ --wc: url(/assets/waterColorMasks/corner/0016.webp);
475
+ }
476
+
477
+ .page .imageMaskCorner17 {
478
+ --wc: url(/assets/waterColorMasks/corner/0017.webp);
479
+ }
480
+
481
+ .page .imageMaskCorner18 {
482
+ --wc: url(/assets/waterColorMasks/corner/0018.webp);
483
+ }
484
+
485
+ .page .imageMaskCorner19 {
486
+ --wc: url(/assets/waterColorMasks/corner/0019.webp);
487
+ }
488
+
489
+ .page .imageMaskCorner20 {
490
+ --wc: url(/assets/waterColorMasks/corner/0020.webp);
491
+ }
492
+
493
+ .page .imageMaskCorner21 {
494
+ --wc: url(/assets/waterColorMasks/corner/0021.webp);
495
+ }
496
+
497
+ .page .imageMaskCorner22 {
498
+ --wc: url(/assets/waterColorMasks/corner/0022.webp);
499
+ }
500
+
501
+ .page .imageMaskCorner23 {
502
+ --wc: url(/assets/waterColorMasks/corner/0023.webp);
503
+ }
504
+
505
+ .page .imageMaskCorner24 {
506
+ --wc: url(/assets/waterColorMasks/corner/0024.webp);
507
+ }
508
+
509
+ .page .imageMaskCorner25 {
510
+ --wc: url(/assets/waterColorMasks/corner/0025.webp);
511
+ }
512
+
513
+ .page .imageMaskCorner26 {
514
+ --wc: url(/assets/waterColorMasks/corner/0026.webp);
515
+ }
516
+
517
+ .page .imageMaskCorner27 {
518
+ --wc: url(/assets/waterColorMasks/corner/0027.webp);
519
+ }
520
+
521
+ .page .imageMaskCorner28 {
522
+ --wc: url(/assets/waterColorMasks/corner/0028.webp);
523
+ }
524
+
525
+ .page .imageMaskCorner29 {
526
+ --wc: url(/assets/waterColorMasks/corner/0029.webp);
527
+ }
528
+
529
+ .page .imageMaskCorner30 {
530
+ --wc: url(/assets/waterColorMasks/corner/0030.webp);
531
+ }
532
+
533
+ .page .imageMaskCorner31 {
534
+ --wc: url(/assets/waterColorMasks/corner/0031.webp);
535
+ }
536
+
537
+ .page .imageMaskCorner32 {
538
+ --wc: url(/assets/waterColorMasks/corner/0032.webp);
539
+ }
540
+
541
+ .page .imageMaskCorner33 {
542
+ --wc: url(/assets/waterColorMasks/corner/0033.webp);
543
+ }
544
+
545
+ .page .imageMaskCorner34 {
546
+ --wc: url(/assets/waterColorMasks/corner/0034.webp);
547
+ }
548
+
549
+ .page .imageMaskCorner35 {
550
+ --wc: url(/assets/waterColorMasks/corner/0035.webp);
551
+ }
552
+
553
+ .page .imageMaskCorner36 {
554
+ --wc: url(/assets/waterColorMasks/corner/0036.webp);
555
+ }
556
+
557
+ .page .imageMaskCorner37 {
558
+ --wc: url(/assets/waterColorMasks/corner/0037.webp);
559
+ }
560
+
561
+ .page dl {
562
+ padding-left: 1em;
563
+ white-space: normal;
564
+ }
565
+
566
+ .page dt {
567
+ display: inline;
568
+ margin-right: 0.5ch;
569
+ margin-left: -1em;
570
+ }
571
+
572
+ .page dd {
573
+ display: inline;
574
+ margin-left: 0;
575
+ text-indent: 0;
576
+ }
577
+
578
+ .page .blank {
579
+ height: 1em;
580
+ margin-top: 0;
581
+ }
582
+
583
+ .page .blank + * {
584
+ margin-top: 0;
585
+ }
586
+
587
+ .page .wide {
588
+ column-span: all;
589
+ display: block;
590
+ margin-bottom: 1em;
591
+ }
592
+
593
+ .page .wide + * {
594
+ margin-top: 0;
595
+ }
dependencies/themes/assets/parchmentBackground.jpg CHANGED

Git LFS Details

  • SHA256: fc153294b70f6aac4d2ce48c7e9d015945b87c48f584db29da30cbbd5b876d1c
  • Pointer size: 131 Bytes
  • Size of remote file: 174 kB
dependencies/themes/assets/parchmentBackgroundGrayscale.jpg CHANGED

Git LFS Details

  • SHA256: fee031d8f639fec593828ae14cea9bb8737d24620c51711dd1016ac3dbffb8cb
  • Pointer size: 131 Bytes
  • Size of remote file: 164 kB
description_helper.py CHANGED
@@ -1,105 +1,254 @@
1
-
2
- #import sys, os
3
- #sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
4
- import torch
5
- import random
6
- from random import randint
7
  import gc
8
  import os
9
- import utilities as u
10
- import time
11
- from exllamav2 import(
12
- ExLlamaV2,
13
- ExLlamaV2Config,
14
- ExLlamaV2Cache,
15
- ExLlamaV2Tokenizer,
16
- )
17
-
18
- from exllamav2.generator import (
19
- ExLlamaV2BaseGenerator,
20
- ExLlamaV2Sampler
21
- )
22
-
23
- # Creating userlog again? This needs to be merged and rebuilt in utilities
24
-
25
-
26
- # Declare globals
27
- device = "cuda:0"
28
- model_directory = "./models/Speechless-Llama2-Hermes-Orca-Platypus-WizardLM-13B-GPTQ"
29
- prompt_list = []
30
- sd_input = []
31
-
32
- def del_sd_input() :
33
- del sd_input[:]
34
- print(sd_input)
35
-
36
- # Initialize model and cache
37
-
38
-
39
-
40
- # Function to load the llm, pass user input into a prompt, pass that prompt to llm and log memory and time then cleanup
41
- def generate_monster_desc(monster_type, monster_color,monster_size,monster_loc):
42
- u.reclaim_mem()
43
- del_sd_input()
44
- print(sd_input)
45
- prompt_template = f"You are an intelligent, accomplished, expert writer of fantasy fiction that uses diverse and wide ranging vocabulary with a particular knack for painting mental images. Do not write instructions or tips, only write a concise, visually descriptive, detailed and colorful three to 5 sentences about the appearance of the {monster_type}, include description of its body and all of it's parts. This is a wholly orginial and unique creation {monster_color} is size {monster_size} and can be found {monster_loc}. The focus should be on describing it's appearance and it's very important it is FIVE sentences! Always start with the creatures name/type : {monster_type}"
46
- prompt_list.append(prompt_template)
47
 
48
- start_time = time.time()
49
- generate_monster_desc.monster_type = monster_type
50
- generate_monster_desc.monster_color = monster_color
51
- generate_monster_desc.monster_size = monster_size
52
- generate_monster_desc.monster_loc = monster_loc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
- config = ExLlamaV2Config()
55
- config.model_dir = model_directory
56
- config.prepare()
57
-
58
- model = ExLlamaV2(config)
59
- print("Loading model: " + model_directory)
60
- print(f"Model load took {time.time() - start_time}")
61
- print(f"Memory allocated : {torch.cuda.memory_allocated()}")
62
-
63
- cache = ExLlamaV2Cache(model, lazy = True)
64
- model.load_autosplit(cache)
65
-
66
- tokenizer = ExLlamaV2Tokenizer(config)
67
- # Initialize generator
68
-
69
- generator = ExLlamaV2BaseGenerator(model, cache, tokenizer)
70
- #generator.set_stop_conditions([tokenizer.eos_token_id])
71
 
72
- # Define settings
73
-
74
- settings = ExLlamaV2Sampler.Settings()
75
- settings.temperature = 1.10
76
- settings.top_k = 50
77
- settings.top_p = 0.8
78
- settings.token_repetition_penalty = 1.15
79
-
80
-
81
- max_new_tokens = 512
82
- start_time = time.time()
83
- generator.warmup()
84
 
85
-
86
- generate_monster_desc.response = generator.generate_simple(prompt_template, settings, max_new_tokens, seed = random.randint(0,10**20))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
- # Check if LLM output prompt as part of output, and remove.
89
- if prompt_template in generate_monster_desc.response:
90
- generate_monster_desc.response = generate_monster_desc.response.replace(prompt_template, '').replace('<s>','' ).replace('</s>', '')
91
-
92
- print(time.time() - start_time)
93
- print(f"Text generation took {time.time() - start_time}")
94
- print(f"Memory allocated : {torch.cuda.memory_allocated()}")
95
-
96
- # Clean up memory
97
- del model
98
- del tokenizer
99
- del generator
100
- del cache
101
- del config
102
- gc.collect()
103
- sd_input.append(generate_monster_desc.response)
104
- print(sd_input)
105
- return generate_monster_desc.response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import replicate
2
+ import ast
 
 
 
 
3
  import gc
4
  import os
5
+ from openai import OpenAI
6
+
7
+ api_key = os.getenv('REPLICATE_API_TOKEN')
8
+ client = OpenAI()
9
+
10
+ def load_llm(user_input, spellcaster, legendary_actions ):
11
+ prompt = f"the subject is {user_input}, Spellcaster : {spellcaster}, Legendary Actions : {legendary_actions}"
12
+ print(prompt)
13
+ response = client.chat.completions.create(
14
+ model="gpt-4o",
15
+ messages=[
16
+ {
17
+ "role": "user",
18
+ "content": f"{prompt_instructions} {prompt}"
19
+ }
20
+ ],
21
+ temperature=1,
22
+ max_tokens=2000,
23
+ top_p=1,
24
+ frequency_penalty=0,
25
+ presence_penalty=0
26
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
+ return response.choices[0].message.content
29
+
30
+ model_path = "meta/meta-llama-3-70b-instruct"
31
+ #def load_llm(user_input, spellcaster, legendary_actions):
32
+ # prompt = f"the subject is {user_input}, Spellcaster : {spellcaster}, Legendary Actions : {legendary_actions}"
33
+ #input = {"prompt" : f" {prompt_instructions} {prompt}","max_tokens":2000}
34
+ #print(f"Generation Started, \n Prompt = {prompt}")
35
+ #output = replicate.run(model_path,
36
+ #input=input
37
+
38
+ # )
39
+ #return output
40
+
41
+
42
+ def call_llm_and_cleanup(user_input,spellcaster, legendary_actions):
43
+ # Call the LLM and store its output
44
+ llm_output = load_llm(user_input, spellcaster, legendary_actions)
45
+ llm_output = "".join(llm_output)
46
+ print(llm_output)
47
+ llm_output = ast.literal_eval(llm_output)
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
+ gc.collect()
51
+
52
+ # llm_output is still available for use here
 
 
 
 
 
 
 
 
 
53
 
54
+ return llm_output
55
+
56
+
57
+ def convert_to_dict(string):
58
+ # Check if the input is already a dictionary
59
+ if isinstance(string, dict):
60
+ print("Input is already a dictionary.")
61
+ return string
62
+
63
+ # Function to try parsing the string to a dictionary
64
+ def try_parse(s):
65
+ try:
66
+ result = ast.literal_eval(s)
67
+ if isinstance(result, dict):
68
+ print("Item dictionary is valid")
69
+ return result
70
+ except SyntaxError as e:
71
+ error_message = str(e)
72
+ print("Syntax Error:", error_message)
73
+ # Check if the error message indicates an unclosed '{'
74
+ if "'{' was never closed" in error_message:
75
+ return try_parse(s + '}') # Attempt to fix by adding a closing '}'
76
+ except ValueError as e:
77
+ print("Value Error:", e)
78
+ return None
79
+
80
+ # First, try parsing the original string
81
+ result = try_parse(string)
82
+ if result is not None:
83
+ return result
84
+
85
+ # Check if braces are missing
86
+ if not string.startswith('{'):
87
+ string = '{' + string
88
+ if not string.endswith('}'):
89
+ string = string + '}'
90
+
91
+ # Try parsing again with added braces
92
+ return try_parse(string) or "Dictionary not valid"
93
+
94
+
95
+
96
+ # Instructions past 4 are not time tested and may need to be removed.
97
+ ### Meta prompted :
98
+ prompt_instructions = """ **Purpose**: ONLY Generate a structured json following the provided format. The job is to generate a balance, creative, interesting monster statblock in the rule style of Dungeons and Dragons. You do not need to stick strictly to the abilities and spells of the game, if it fits the style and flavor of the user input, get weird, scary, or silly with the details. You will also be writing a paragraph of interesting flavor text and description, and a brief one sentence image generation prompt.Include the type and subtype in the image prompt.
99
+
100
+ Image Generation Prompt Examples :
101
+ "A hooded stout dwarf necromancer, in black robes, emanating evil magic "
102
+ "A black and tan battle dog with spike collar, hackles up and ready to strike"
103
+ "a tanned human barbarian with bleeding red axes"
104
+ "a magical zombie tiger, colorful and decaying"
105
+
106
+ 1. Only output file structure starting with { and ending with } it is CRITICAL to end with a }, DO NOT say anything, don't add ''' or json"
107
+ 2. You tend to build over powered monsters, tend towards average to a bit underpowered. Damage Resistance can be left blank.
108
+ 3. All Actions should have explicit instructions on how to use and what dice and effects they have
109
+ 4. DO NOT type other_sense_type, other_skill_type or any other label or item.
110
+ 5. DO NOT use null, use "".
111
+ 5. If Spellcaster : False then Spells should not be printed. If Spellcaster
112
+ 6. If Legendary Actions : False then legendary_action should not be printed
113
+ 7. Review and stick closely to this table.
114
+
115
+ Monster Statistics by Challenge Rating
116
+ Fractions MUST be surrounded by double quotes ""
117
+ | CR | XP | Prof. Bonus | Armor Class | Hit Points | Attack Bonus | Damage/Round | Save DC | Prob. of Spell | Num. of Spells | Level of Spells | Prob. of Legendary Actions | Num. of Legendary Actions |
118
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
119
+ | 0 | 10 | 2 | 10-13 | 1-3 | 3 | 0-1 | 13 | 0% | 0 | - | 0% | 0 |
120
+ | "1/8" | 25 | 2 | 10-13 | 2-7 | 3 | 2-3 | 13 | 05% | 1 | 1st | 0% | 0 |
121
+ | "1/4" | 50 | 2 | 10-13 | 4-8 | 3 | 4-5 | 13 | 10% | 2 | 1st | 0% | 0 |
122
+ | "1/2" | 100 | 2 | 10-13 | 5-11 | 3 | 6-8 | 13 | 15% | 3 | 1st | 0% | 0 |
123
+ | 1 | 200 | 2 | 10-13 | 8-20 | 3 | 9-14 | 13 | 15% | 4 | 1st | 0% | 0 |
124
+ | 2 | 450 | 2 | 11-13 | 15-30 | 3 | 15-20 | 13 | 20% | 5 | 2nd | 0% | 0 |
125
+ | 3 | 700 | 2 | 11-13 | 25-50 | 4 | 21-26 | 13 | 25% | 6 | 2nd |05% | 1 |
126
+ | 4 | 1100 | 2 | 11-14 | 35-75 | 5 | 27-32 | 14 | 30% | 7 | 2nd | 10% | 1 |
127
+ | 5 | 1800 | 3 | 12-15 | 45-95 | 6 | 33-38 | 15 | 50% | 8 | 3rd | 10% | 1 |
128
+ | 6 | 2300 | 3 | 12-15 | 60-110 | 6 | 39-44 | 15 | 60% | 9 | 3rd | 15% | 2 |
129
+ | 7 | 2900 | 3 | 12-15 | 70-140 | 6 | 45-50 | 15 | 75% | 10 | 3rd | 20% | 2 |
130
+ | 8 | 3900 | 3 | 13-16 | 80-160 | 7 | 51-56 | 16 | 90% | 11 | 4th | 20% | 3 |
131
+ | 9 | 5000 | 4 | 13-16 | 105-205 | 7 | 57-62 | 16 | 90% | 12 | 4th | 20% | 3 |
132
+ | 10 | 5900 | 4 | 14-17 | 206-220 | 7 | 63-68 | 16 | 100% | 13 | 4th | 30% | 3 |
133
+ | 11 | 7200 | 4 | 14-17 | 221-235 | 8 | 69-74 | 17 | 100% | 14 | 5th | 40% | 3 |
134
+ | 12 | 8400 | 4 | 14-17 | 236-250 | 8 | 75-80 | 18 | 100% | 15 | 6th | 60% | 4 |
135
+ | 13 | 10000 | 5 | 15-18 | 251-265 | 8 | 81-86 | 18 | 100% | 16 | 7th | 80% | 4 |
136
+ | 14 | 11500 | 5 | 15-18 | 266-280 | 8 | 87-92 | 18 | 100% | 17 | 7th | 100% | 4 |
137
+ | 15 | 13000 | 5 | 15-18 | 281-295 | 8 | 93-98 | 18 | 100% | 18 | 8th | 100% | 5 |
138
+ | 16 | 15000 | 5 | 15-18 | 296-310 | 9 | 99-104 | 18 | 100% | 19 | 8th | 100% | 5 |
139
+ | 17 | 18000 | 6 | 16-19 | 311-325 | 10 | 105-110 | 19 | 100% | 20 | 8th | 100% | 5 |
140
+ | 18 | 20000 | 6 | 16-19 | 326-340 | 10 | 111-116 | 19 | 100% | 21 | 9th | 100% | 5 |
141
+ | 19 | 22000 | 6 | 16-19 | 341-355 | 10 | 117-122 | 19 | 100% | 22 | 9th | 100% | 5 |
142
+ | 20 | 25000 | 6 | 16-19 | 356-400 | 10 | 123-140 | 19 | 100% | 23 | 9th | 100% | 5 |
143
+ | 21 | 33000 | 7 | 16-19 | 401-445 | 11 | 141-158 | 20 | 100% | 24 | 9th | 100% | 5 |
144
+ | 22 | 41000 | 7 | 16-19 | 446-490 | 11 | 159-176 | 20 | 100% | 25 | 9th | 100% | 5 |
145
+ | 23 | 50000 | 7 | 16-19 | 491-535 | 11 | 177-194 | 20 | 100% | 26 | 9th | 100% | 5 |
146
+ | 24 | 62000 | 7 | 16-19 | 536-580 | 11 | 195-212 | 21 | 100% | 27 | 9th | 100% | 5 |
147
+ | 25 | 75000 | 8 | 16-19 | 581-625 | 12 | 213-230 | 21 | 100% | 28 | 9th | 100% | 5 |
148
+ | 26 | 90000 | 8 | 16-19 | 626-670 | 12 | 231-248 | 21 | 100% | 29 | 9th | 100% | 5 |
149
+ | 27 | 105000 | 8 | 16-19 | 671-715 | 13 | 249-266 | 22 | 100% | 30 | 9th | 100% | 5 |
150
+ | 28 | 120000 | 8 | 16-19 | 716-760 | 13 | 267-284 | 22 | 100% | 31 | 9th | 100% | 5 |
151
+ | 29 | 135000 | 9 | 16-19 | 760-805 | 13 | 285-302 | 22 | 100% | 32 | 9th | 100% | 5 |
152
+ | 30 | 155000 | 9 | 16-19 | 805-850 | 14 | 303-320 | 23 | 100% | 33 | 9th | 100% | 5 |
153
+
154
+ 8. If a creature has Legendary Actions they need to be fully described.
155
+ Example : "legendary_actions": {
156
+ "actions": "Hermione the Grumpy can take 3 legendary actions, choosing from the options below. Only one legendary action can be used at a time and only at the end of another creature's turn. Hermione regains spent legendary actions at the start of her turn.",
157
+ "options": [
158
+ {
159
+ "name": "Imperial Claw Strike",
160
+ "desc": "With a swift motion that belies her regal composure, Hermione unsheathes her gleaming claws, striking with the precision of a seasoned sovereign. She makes one attack with her claws that deal an extra 2d6 slashing damage. If the attack hits a creature, that creature also suffers a -2 penalty to AC until the start of Hermione's next turn, as her claws leave rending tears in their armor or flesh, symbolizing her disdain for any challenge to her rule."
161
+ },
162
+ {
163
+ "name": "Royal Command",
164
+ "desc": "Hermione gazes across the battlefield, her eyes glowing with an ethereal light. She issues a commanding meow that resonates with arcane power. All creatures within 60 feet that can hear her must succeed on a DC 20 Wisdom saving throw or become charmed by Hermione for 1 minute. A charmed creature regards Hermione as its beloved monarch and will protect her as if it were guarding its own life. This charm effect ends if the charmed creature suffers any harm or if Hermione dismisses it as unworthy with a wave of her paw."
165
+ },
166
+ {
167
+ "name": "Divine Whisker Quiver",
168
+ "desc": "Hermione channels the cosmic power of her royal lineage through her whiskers, which quiver with the energy of the universe itself. She chooses up to three creatures she can see within 100 feet of her. Each target must succeed on a DC 18 Constitution saving throw or be stunned by the overwhelming presence of the Empress until the end of their next turn. Creatures that fail their saving throw by 5 or more are also teleported up to 30 feet in any direction Hermione chooses, as she rearranges the battlefield to her liking with but a thought."
169
+ }
170
+ ]
171
+
172
+
173
+ Output format :
174
+ {
175
+ "name": "",
176
+ "size": "",
177
+ "type": "",
178
+ "subtype": "",
179
+ "alignment": "",
180
+ "armor_class": ,
181
+ "hit_points": ,
182
+ "hit_dice": "",
183
+ "speed": {
184
+ "walk":
185
 
186
+ },
187
+ "abilities": {
188
+ "str": ,
189
+ "dex": ,
190
+ "con": ,
191
+ "int": ,
192
+ "wis": ,
193
+ "cha":
194
+ },
195
+ "saving_throws": {
196
+ "str": ,
197
+ "dex": ,
198
+ "wis":
199
+ },
200
+ "skills": {
201
+ "perception":
202
+ },
203
+ "damage_resistance":
204
+ "senses": {
205
+ "darkvision":
206
+ },
207
+ "languages": "",
208
+ "challenge_rating": ,
209
+ "xp": ,
210
+ "actions": [
211
+ {
212
+ "name": "",
213
+ "desc": ""
214
+ }
215
+ ],
216
+ "spells": {
217
+ "cantrips": [
218
+ {
219
+ "name": "",
220
+ "desc": ""
221
+ }
222
+ ],
223
+ "known_spells": [
224
+ {
225
+ "name": "",
226
+ "level":"",
227
+ "desc": ""
228
+ }
229
+ ],
230
+ "spell_slots": {
231
+ "1st_level": ,
232
+ "2nd_level": ,
233
+ "3rd_level": ,
234
+ "4th_level": ,
235
+ "5th_level": ,
236
+ "6th_level": ,
237
+ "7th_level": ,
238
+ "8th_level": ,
239
+ "9th_level":
240
+ }
241
+ },
242
+ "legendary_actions": {
243
+ "actions": ,
244
+ "options": [
245
+ {
246
+ "name": "",
247
+ "desc": ""
248
+ }
249
+ ]
250
+ },
251
+ "description":"",
252
+ "sd_prompt":""
253
+ }
254
+ """
docker_build.log DELETED
The diff for this file is too large to render. See raw diff
 
flagged/log.csv ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ name,hp,ac,str_stat,dex_stat,con_stat,int_stat,wis_stat,cha_stat,output,flag,username,timestamp
2
+ ,,,,,,,,,"{""name"": """", ""hp"": """", ""ac"": """", ""str_stat"": """", ""dex_stat"": """", ""con_stat"": """", ""int_stat"": """", ""wis_stat"": """", ""cha_stat"": """"}",,,2024-05-21 22:22:06.438871
get-pip.py ADDED
The diff for this file is too large to render. See raw diff
 
main.py DELETED
@@ -1,158 +0,0 @@
1
- # this imports the code from files and modules
2
- import description_helper as dsh
3
- import gradio as gr
4
- import sd_generator as sd
5
- import statblock_helper as sth
6
- import utilities as u
7
- import process_html
8
- import os
9
- import ctypes
10
-
11
- print("Building app")
12
- # This is a fix for the way that python doesn't release system memory back to the OS and it was leading to locking up the system
13
- libc = ctypes.cdll.LoadLibrary("libc.so.6")
14
- M_MMAP_THRESHOLD = -3
15
-
16
- # Set malloc mmap threshold.
17
- libc.mallopt(M_MMAP_THRESHOLD, 2**20)
18
-
19
-
20
- # Build functions that will be called by Gradio UI
21
-
22
-
23
- # Take input from gradio user and call the llm in description_helper
24
- def gen_mon_desc(user_monster_type, user_monster_color,user_monster_size, user_monster_loc):
25
- u.reclaim_mem()
26
- # Delete the list of images in sd
27
- sd.del_image_list
28
- dsh.del_sd_input
29
-
30
- # Prompt template with user inputs added to be printed to log and terminal
31
- prompt_template = f"You are an intelligent and very good writer of fantasy fiction that uses a diverse and wide ranging vocabulary. Write a very concise, visually descriptive, detailed and colorful paragraph description of a {user_monster_type}, include description of its body, face, and limbs, it is {user_monster_color} and size {user_monster_size} at {user_monster_loc}. The {user_monster_color} {user_monster_type} "
32
- print("Prompt Template : " + prompt_template)
33
-
34
- # setting sd_input as global to be called and moved between models, this could probably be done better as a variable called from description helper
35
-
36
- response = dsh.generate_monster_desc(user_monster_type, user_monster_color, user_monster_size,user_monster_loc)
37
- print(response)
38
-
39
- # A list to hold user inputs and start user log,
40
- # Build static folders, this too needs to go to utilities
41
- #Create user log path all this needs to be moved to utilities
42
-
43
-
44
- u.reclaim_mem()
45
- return response
46
-
47
-
48
- # Pass sd_input and call function to generate image and save bu calling name making function in utilities
49
- def gen_mon_img():
50
- print(dsh.sd_input)
51
- print(type(dsh.sd_input))
52
- sd_input = dsh.sd_input[0]
53
- generated_images = sd.generate_image(sd_input)
54
-
55
- # return a list of image relative paths to pass to gallery
56
- return generated_images
57
-
58
- # Take the selected image in gallery and assign to global variable
59
-
60
-
61
- # Pass modified input to statblock generator
62
- def gen_mon_statblock(challenge_rating,abilities):
63
- input = dsh.sd_input[0] + f"it is very important it is built with a challenge rating {challenge_rating}, create and give it the abilities {abilities}"
64
- generated_statblock = sth.generate_statblock(input)
65
- return generated_statblock
66
-
67
- # Call the html process program and point to the file
68
- def mon_html_process(user_text):
69
- input_dir = sth.file_name_list[0]
70
- md_path = f"{input_dir}/my-brew.md"
71
- mon_file_name = sth.file_name_list[0]+'/' + sth.file_name_list[1] +'.html'
72
- sth.md_process(md_path, sth.file_name_list[1])
73
- print("Ouput path = " + mon_file_name)
74
- print(f"User Text : {user_text}")
75
- process_html.process_html(mon_file_name,user_text)
76
-
77
- # Build the html and file path to pass to gradio to output html file in gr.html
78
- def gen_link():
79
- mon_file_path = sth.file_name_list[0]+'/' + sth.file_name_list[1] +'.html'
80
- if not os.path.exists(mon_file_path):
81
- print(f"{mon_file_path} not found")
82
- else: print(f"{mon_file_path} found")
83
- iframe = iframe = f"""<iframe src="file={mon_file_path}" width="100%" height="500px"></iframe>"""
84
- link = f'<a href="file={mon_file_path}" target="_blank">{sth.file_name_list[1] +".html"}</a>'
85
- return link, iframe
86
-
87
- # Build gradio app
88
- demo = gr.Blocks()
89
- with gr.Blocks() as demo:
90
- # Title, eventually turn this into an updated variable with Monster name
91
- gr.HTML(""" <div id="inner"> <header>
92
- <h1>Monster Statblock Generator</h1>
93
- </div>""")
94
- with gr.Tab("Generator"):
95
- with gr.Row():
96
-
97
- with gr.Column(scale = 1):
98
- # Does this need to be a column? Building interface to receive input
99
-
100
- mon_name = gr.Textbox(label = "Step 1 : The Monster's Name or type", lines = 1, placeholder=f"Ex : A friendly skeletal lich", elem_id= "Monster Name")
101
- mon_color = gr.Textbox(label = "Step 2 : Colors and description", lines = 3, placeholder="Ex: wearing a glimmering robe ", elem_id= "Monster Color")
102
- mon_size = gr.Dropdown(['Tiny','Small','Medium','Large','Huge','Gigantic','Titanic'], label = 'Step 3 : Size ', elem_id="Monster Size")
103
- mon_loc = gr.Textbox(label = "Step 4 Optional : Describe it's location", lines = 3, placeholder="in a dusty library", elem_id = "Monster Location" )
104
- desc_gen = gr.Button(value = "Step 5. Generate Description")
105
- with gr.Column(scale = 1):
106
-
107
- mon_desc = gr.Textbox(label = 'Monster Description', lines = 16, interactive=True)
108
- desc_gen.click(fn = gen_mon_desc, inputs = [mon_name, mon_color, mon_size,mon_loc], outputs= mon_desc)
109
-
110
-
111
- # Output object for image, and button to trigger
112
- image_Gen = gr.Button(value = "Step 6 : Generate 4x Images about 1 minute" )
113
- output_gallery = gr.Gallery(label = "Generated Images",
114
- show_label = False,
115
- elem_id = "gallery",
116
- columns =[4], rows =[1],
117
- object_fit = "contain", height ="auto")
118
-
119
- image_Gen.click(gen_mon_img, outputs = output_gallery)
120
- output_gallery.select(fn = process_html.assign_img)
121
-
122
- # Create a tab to split off statblock generation
123
- with gr.Tab("Step 7 : Statblock"):
124
-
125
- # Block to take in
126
- gr.Interface(
127
- fn=gen_mon_statblock,
128
- inputs = [gr.Textbox(label="Challenge Rating, 1-20", lines =1),
129
- gr.Textbox(label="Names of Abilities seperated by commas", lines = 3)],
130
- outputs = gr.Textbox(label = "Statblock", lines = 20, interactive=True, elem_id= "User Input"),
131
- allow_flagging="never"
132
- )
133
-
134
- # Build buttons to modify to html and show html
135
- gen_html = gr.Button(value = "Step 8 : Generate html, click once then go to Step 9")
136
- gen_html.click(mon_html_process,inputs =[mon_desc], outputs=[])
137
- markdown = gr.Markdown(label="Output Box")
138
- html = gr.HTML(label="HTML preview", show_label=True)
139
- new_link_btn = gr.Button("Step 9: Display HTML and Link")
140
- new_link_btn.click(fn = gen_link, inputs = [], outputs = [markdown, html])
141
-
142
- def main() -> None:
143
- # run web server, expose port 8000, share to create web link, give app access folder path, gradio was updated for security and can no longer serve any directory not specified.
144
- if __name__ == '__main__':
145
- demo.launch(server_name = "0.0.0.0", server_port = 8000, share = False, allowed_paths = ["/home/user/app/output","/home/user/app/dependencies"])
146
-
147
- main()
148
-
149
-
150
-
151
-
152
-
153
-
154
-
155
-
156
-
157
-
158
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
normalize_then_alpha.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from PIL import Image, ImageOps
2
+ import numpy as np
3
+ import cv2
4
+
5
+ def apply_transparency(image):
6
+ # Load the image in RGBA format to handle transparency
7
+ print(f"Image Type is : {type(image)}")
8
+
9
+ # Ensure the image has an alpha channel
10
+ if image.mode != 'RGBA':
11
+ image = image.convert('RGBA')
12
+
13
+ original = image.copy() # Keep the original for final processing
14
+
15
+
16
+ # Convert the image to a grayscale NumPy array
17
+ gray_image = ImageOps.grayscale(image)
18
+ gray_array = np.array(gray_image)
19
+
20
+ # Normalize the background by setting near-white to white
21
+ normalized_background = np.where(gray_array > 200, 255, gray_array)
22
+ normalized_background_image = Image.fromarray(normalized_background.astype(np.uint8))
23
+
24
+
25
+ # Convert the normalized background image to a numpy array before thresholding
26
+ normalized_background_array = np.array(normalized_background_image)
27
+
28
+ # Threshold the image to isolate the white areas
29
+ _, binary_image = cv2.threshold(normalized_background_array, 240, 255, cv2.THRESH_BINARY)
30
+ binary_image = cv2.bitwise_not(binary_image) # Invert to make white areas black for flood fill
31
+ binary_image = Image.fromarray(binary_image)
32
+
33
+ # Convert RGB and Alpha to separate arrays for OpenCV processing
34
+ rgb_image = np.array(normalized_background_image.convert('RGB'))
35
+ alpha_channel = np.array(image)[:, :, 3] # Extract the alpha channel directly from the original RGBA image
36
+
37
+ h, w = rgb_image.shape[:2]
38
+ mask = np.zeros((h+2, w+2), np.uint8)
39
+
40
+ # Define the seed point for flood fill, assuming it's set to a point known to be within the background
41
+ seed_point = (0, 0) # Adjust this as needed
42
+
43
+ # Sample the color at the seed point from the RGB image
44
+ seed_color = rgb_image[seed_point[1], seed_point[0]].tolist()
45
+
46
+ # Set tolerance levels such that the fill will stop at or before hitting black
47
+ # Black in BGR is (0, 0, 0), and we set a very low tolerance to stop at any near-black color
48
+ # Lower numbers are more restrictive to fill, higher is more permissive
49
+ lo_diff = (3, 3, 3) # Lower bounds for color differences (can be adjusted)
50
+ up_diff = (3, 3, 3) # Upper bounds for color differences (can be adjusted)
51
+
52
+ # Perform the flood fill operation
53
+ cv2.floodFill(rgb_image, mask, seed_point, seed_color, lo_diff, up_diff, 8)
54
+ # Convert back to RGB and then to Image for saving
55
+ filled_image = cv2.cvtColor(rgb_image, cv2.COLOR_BGR2RGB)
56
+ filled_image = Image.fromarray(filled_image)
57
+
58
+ # Save the filled image to disk
59
+
60
+ # Optionally, save the mask to review which areas were filled
61
+ mask_image = Image.fromarray(mask[1:-1, 1:-1] * 255) # Scale mask to 0-255 for visibility
62
+
63
+
64
+
65
+ # Dilate the mask to extend the transparency slightly
66
+ kernel = np.ones((12,12), np.uint8) # You can adjust the kernel size for more/less dilation
67
+ dilated_mask = cv2.dilate(mask, kernel, iterations = 1)
68
+
69
+ # Apply Gaussian blur to the dilated mask to smooth the edges
70
+ blurred_mask = cv2.GaussianBlur(dilated_mask, (5, 5), 0)
71
+
72
+ # Update alpha channel based on the blurred mask
73
+ alpha_channel[blurred_mask[1:h+1, 1:w+1] == 1] = 0
74
+
75
+ # Combine RGB and modified Alpha into the final image
76
+ final_image = np.dstack((rgb_image, alpha_channel))
77
+
78
+ # Apply original colors back only to non-transparent areas
79
+ original_rgb = np.array(original.convert('RGB'))
80
+ final_rgb = final_image[:, :, :3] # Extract RGB channels
81
+ final_rgb[blurred_mask[1:h+1, 1:w+1] != 1] = original_rgb[blurred_mask[1:h+1, 1:w+1] != 1]
82
+
83
+ # Recombine with alpha channel
84
+ final_image_with_original_colors = np.dstack((final_rgb, alpha_channel))
85
+ final_image_with_original_colors = Image.fromarray(final_image_with_original_colors)
86
+
87
+ return final_image_with_original_colors
88
+
89
+
process_html.py CHANGED
@@ -1,108 +1,319 @@
1
- import os
2
  import re, fileinput, sys
3
- import statblock_helper as sth
4
  import utilities as u
5
  import description_helper as dsh
6
 
7
  import gradio as gr
8
 
9
-
 
 
 
 
 
 
 
 
 
 
10
 
11
  # Assigning strings to variables for replacing location of dependencies for the webpage to local static folders
12
  # Path is ../../ for the html files location in output/dated_folder/
13
  break_tag = "<br>"
14
- old_all_css = """<link href="https://use.fontawesome.com/releases/v5.15.1/css/all.css" rel="stylesheet" />"""
15
- new_all_css = """<link href="../../dependencies/all.css" rel="stylesheet" />"""
16
- old_fonts = """<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />"""
17
- new_fonts = """<link href="../../dependencies/css.css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />"""
18
- old_bundle = """<link href='/bundle.css' rel='stylesheet' />"""
19
- new_bundle = """<link href='../../dependencies/bundle.css' rel='stylesheet' />"""
20
- old_icon = """<link rel="icon" href="/assets/favicon.ico" type="image/x-icon" />"""
21
- new_icon = """<link rel="icon" href="../../dependencies/favicon.ico" type="image/x-icon" />"""
22
- old_style = """<link href='../build/themes/V3/Blank/style.css' rel='stylesheet' />"""
23
- new_style = """<link href='../../dependencies/style.css' rel='stylesheet' />"""
24
- old_5estyle = """<link href='../build/themes/V3/5ePHB/style.css' rel='stylesheet' />"""
25
- new_5estyle = """<link href='../../dependencies/5ePHBstyle.css' rel='stylesheet' />"""
26
-
27
-
28
- # creating global variable to pass user image selection to html file as variable usr_img
29
- def assign_img(evt: gr.SelectData):
30
- global usr_img
31
- img_dict = evt.value
32
- usr_img = img_dict['image']['url']
33
- print(usr_img)
34
-
35
- # function to collect the data in the description box if a user edits it.
36
-
 
 
 
 
37
 
38
- # Functions to insert input strings before and after to modify html file
39
- def find_all_insert_before(html_file, pattern, new_tag):
40
- file = fileinput.input(html_file, inplace=True)
41
- for line in file:
42
- replacement = new_tag + line
43
- line = re.sub(pattern,replacement,line)
44
- sys.stdout.write(line)
45
- file.close()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- def insert_tag_before(html_file, pattern, new_tag):
48
- index = html_file.find(pattern)
49
- if index != -1:
50
- output = html_file[:index] + new_tag + html_file[index:]
51
- return output
52
-
53
- def insert_tag_after(html_file, pattern, new_tag):
54
- len_new_tag = len(new_tag)
55
- print(len_new_tag)
56
- index = html_file.find(pattern)
57
- output = html_file[:index + len(pattern)] + new_tag + html_file[ index + len(pattern):]
58
- return output
59
-
60
- # Modify the output from the home/userbrewery process.js
61
- def process_html(self, user_text):
62
 
63
- # There is an encoding issue with the left and right quotation marks in the html files, they need to be replaced with single quotes
64
- html_path = self
65
- statblock_html = open(html_path, 'r')
66
- html_as_string = statblock_html.read()
67
- html_as_string = html_as_string.replace("’", "'").replace("‘", "'")
68
- html_as_string = html_as_string.replace(old_all_css, new_all_css)
69
- html_as_string = html_as_string.replace(old_fonts, new_fonts)
70
- html_as_string = html_as_string.replace(old_bundle, new_bundle)
71
- html_as_string = html_as_string.replace(old_icon, new_icon)
72
- html_as_string = html_as_string.replace(old_style, new_style)
73
- html_as_string = html_as_string.replace(old_5estyle, new_5estyle)
74
-
75
- # Strip out <br> so that it can be readded after strong marker to be bold, then remove it from Armor and Speed for reasons of formatting
76
- html_as_string = html_as_string.replace("<br><strong>","<strong>")
77
- html_as_string = html_as_string.replace("<strong>","<br><strong>")
78
- html_as_string = html_as_string.replace("<br><strong>Armor","<strong>Armor" )
79
- html_as_string = html_as_string.replace("<br><strong>Speed","<strong>Speed" )
80
- html_as_string = html_as_string.replace("</p>","" )
81
- html_as_string = html_as_string.replace("<p><br>","" )
82
- html_as_string = insert_tag_before(html_as_string,"<hr>", "</dl>" )
83
- html_as_string = insert_tag_before(html_as_string,"<strong>Armor Class</strong>", '<hr><dl>' )
84
 
85
- # Call global usr_img and assign a local variable
86
- input_img = usr_img
87
- print(usr_img)
88
-
89
- # Store image location string as variable to make it accesisble for locating and formatting
90
 
91
- img_location = f"""{input_img}" alt="{dsh.generate_monster_desc.monster_type}"""
 
 
 
 
 
 
 
 
 
 
92
 
93
- # Insert image before <hr>
94
- html_as_string = insert_tag_before(html_as_string,"<hr><dl>", f'<p><img class=" " style="width:330px; mix-blend-mode:multiply;" src="{img_location}"></p>' )
 
 
 
 
95
 
96
- # Use image to target where to insert description box
97
- user_desc = f""" <div class="block descriptive"><p>{user_text}</div></p> """
98
- html_as_string = insert_tag_before(html_as_string,"<hr><dl>", user_desc )
 
 
 
 
 
 
 
99
 
100
- # Re write and close html file
101
- with open(sth.file_name_list[0]+'/' + os.path.basename(html_path) , 'w') as clean_html:
102
- clean_html.write(html_as_string)
 
 
103
  clean_html.close()
104
- #clear link list and append with new entries
105
  del u.link_list[:]
106
- u.link_list.append(sth.file_name_list[0]+'/' + sth.file_name_list[1] +'.html')
107
- u.link_list.append(dsh.generate_monster_desc.monster_type)
108
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import re, fileinput, sys
 
2
  import utilities as u
3
  import description_helper as dsh
4
 
5
  import gradio as gr
6
 
7
+ # HTML section headers
8
+ actions_header = """<h4 id="actions">Actions</h4>
9
+ """
10
+ cantrips_header = """<h4 id="cantrips">Cantrips</h4>
11
+ """
12
+ spells_header = """<h4 id="known spells">Known Spells</h4>
13
+ """
14
+ spell_slot_header = """<h4 id="spell slots">Spell Slots</h4>
15
+ """
16
+ legendary_actions_header = """<h4 id="legendary actions">Legendary Actions</h4>
17
+ """
18
 
19
  # Assigning strings to variables for replacing location of dependencies for the webpage to local static folders
20
  # Path is ../../ for the html files location in output/dated_folder/
21
  break_tag = "<br>"
22
+ def build_html_base(
23
+ mon_name,
24
+ mon_size,
25
+ mon_type,
26
+ mon_subtype,
27
+ mon_alignment,
28
+ mon_armor_class,
29
+ mon_hp,
30
+ mon_hit_dice,
31
+ mon_speed,
32
+ mon_abilities,
33
+ mon_saving_throws,
34
+ mon_skills,
35
+ mon_damage_resistance,
36
+ mon_senses,
37
+ mon_languages,
38
+ mon_challenge_rating,
39
+ mon_xp,
40
+ mon_actions,
41
+ mon_description,
42
+ mon_image_path,
43
+ mon_cantrips = False,
44
+ mon_spells = False,
45
+ mon_spell_slots = False,
46
+ mon_legendary_actions = False
47
+
48
+ ) :
49
 
50
+
51
+
52
+ # Combine the properties that will go on a single line
53
+ if mon_subtype != "" :
54
+ mon_properties = f"{mon_size}, {mon_type}, {mon_subtype}, {mon_alignment}"
55
+ else: mon_properties = f"{mon_size}, {mon_type}, {mon_alignment}"
56
+ mon_abilities = parse_abilities_from_text(mon_abilities)
57
+
58
+ # Template for the page
59
+ html_base = f"""<!DOCTYPE html>
60
+ <html>
61
+ <head>
62
+
63
+ <link href="../../dependencies/all.css" rel="stylesheet" />
64
+ <link href="../../dependencies/css.css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
65
+ <link href='../../dependencies/bundle.css' rel='stylesheet' />
66
+ <link rel="icon" href="../../dependencies/favicon.ico" type="image/x-icon" />
67
+ <title>{mon_name}</title>
68
+ </head>
69
+ <body>
70
+ <link href='../../dependencies/style.css' rel='stylesheet' />
71
+ <link href='../../dependencies/5ePHBstyle.css' rel='stylesheet' />
72
+
73
+ <div class='brewRenderer'>
74
+ <style>undefined</style>
75
+ <div class='pages'>
76
+ <div class='page phb' id='p1' key='0' >
77
+ <div className='columnWrapper'>
78
+ <div class="block monster frame wide" >
79
+ <h4 id="user-monster-name">{mon_name}</h4>
80
+ <p><em>{mon_properties}</em>
81
+ <p><img class=" " style="width:330px; mix-blend-mode:multiply; border:3px solid black;" src={mon_image_path} alt="image"></p>
82
+ <div class="block descriptive">
83
+ <h5 id="user-monster-description">{mon_description}</h5>
84
+
85
+ </div>
86
+ <hr>
87
+ <dl>
88
+ <strong>Armor Class</strong> : {mon_armor_class}
89
+ <strong>Hit Points</strong>: {mon_hp} Hit Dice : {mon_hit_dice}
90
+ <strong>Speed</strong>: {mon_speed}
91
+ </dl>
92
+ <hr>
93
+ <table>
94
+ <thead>
95
+ <tr>
96
+ <th align=center>STR</th>
97
+ <th align=center>DEX</th>
98
+ <th align=center>CON</th>
99
+ <th align=center>INT</th>
100
+ <th align=center>WIS</th>
101
+ <th align=center>CHA</th>
102
+ </tr>
103
+ </thead>
104
+ <tbody>
105
+ <tr>
106
+ <td align=center>{mon_abilities[0]}</td>
107
+ <td align=center>{mon_abilities[1]}</td>
108
+ <td align=center>{mon_abilities[2]}</td>
109
+ <td align=center>{mon_abilities[3]}</td>
110
+ <td align=center>{mon_abilities[4]}</td>
111
+ <td align=center>{mon_abilities[5]}</td>
112
+ </tr>
113
+ </tbody>
114
+ </table>
115
+ <hr>
116
+ <strong>Saving Throws</strong> : {mon_saving_throws}
117
+ <br><strong>Skills</strong> : {mon_skills}
118
+ <br><strong>Resistances</strong> : {mon_damage_resistance}
119
+ <br><strong>Senses</strong> : {mon_senses}
120
+ <br><strong>Languages</strong> : {mon_languages}
121
+ <br><strong>Challenge Rating</strong> : {mon_challenge_rating} ({mon_xp})"""
122
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
+ if mon_actions :
125
+ print("Actions : True")
126
+ parsed_actions = parse_actions_from_text(mon_actions)
127
+
128
+ html_file_as_text = f"""{html_base} <hr> {actions_header}
129
+ {''.join(parsed_actions)}"""
130
+ else:
131
+ print("Actions : False")
132
+ html_file_as_text = html_base
 
 
 
 
 
 
 
 
 
 
 
 
133
 
 
 
 
 
 
134
 
135
+ if mon_cantrips:
136
+ print(mon_cantrips)
137
+ mon_cantrips = mon_cantrips.replace("Cantrips", '')
138
+ mon_cantrips = parse_cantrips_from_text(mon_cantrips)
139
+ html_file_as_text = html_file_as_text + cantrips_header + mon_cantrips
140
+
141
+ if mon_spells :
142
+ print(mon_spells)
143
+ mon_spells = mon_spells.replace("Known Spells",'')
144
+ mon_spells = parse_spells_from_text(mon_spells)
145
+ html_file_as_text = html_file_as_text + spells_header + mon_spells
146
 
147
+ if mon_spell_slots:
148
+ print(mon_spell_slots)
149
+ mon_spell_slots = mon_spell_slots.replace("Spell Slots", '')
150
+ mon_spell_slots = parse_spell_slots_from_text(mon_spell_slots)
151
+ html_file_as_text = html_file_as_text + spell_slot_header + mon_spell_slots
152
+
153
 
154
+ else:
155
+ print("Spells : False")
156
+
157
+ if mon_legendary_actions:
158
+ print("Legendary Actions : True")
159
+ mon_legendary_actions = mon_legendary_actions.replace("Legendary Actions \n\n", '')
160
+ mon_legendary_actions = parse_legendary_action_from_text(mon_legendary_actions)
161
+ html_file_as_text = html_file_as_text +legendary_actions_header + mon_legendary_actions
162
+ else:
163
+ print("Legendary Actions : False")
164
 
165
+ # Open a file path that will receive the processed text
166
+ u.gen_file_name(mon_name)
167
+ mon_file_path = f"{u.file_name_list[0]}/{u.file_name_list[1]}.html"
168
+ with open(mon_file_path, 'w') as clean_html:
169
+ clean_html.write(html_file_as_text)
170
  clean_html.close()
171
+ # Clear link list and append with new entries
172
  del u.link_list[:]
173
+ u.link_list.append(u.file_name_list[0]+'/' + u.file_name_list[1] +'.html')
174
+ u.link_list.append(mon_type)
175
+
176
+ #Passing back a file path that Gradio can access and is local
177
+ return mon_file_path
178
+
179
+ def parse_actions_from_text(edited_text):
180
+ html_content = '<dl>'
181
+ actions = []
182
+ action_entries = edited_text.strip().split('\n\n')
183
+ print(action_entries)
184
+ for entry in action_entries:
185
+ parts = entry.split(';')
186
+ action_dict = {
187
+ "name": parts[0].split(": ")[1].strip(),
188
+ "desc": parts[1].split("Description: ")[1].strip()
189
+ }
190
+ actions.append(action_dict)
191
+
192
+ for action in actions:
193
+ html_content += f"<dt><em><strong>{action['name']}</strong></em> :</dt><dd>‘{action['desc']}</dd>"
194
+ html_content += "<br>"
195
+ html_content=html_content.rstrip('br')
196
+
197
+ html_content += '</dl>'
198
+ return html_content
199
+
200
+ def parse_abilities_from_text(abilities):
201
+ abilities_list = []
202
+ ability_entries = abilities.strip().split('\n')
203
+ for entry in ability_entries:
204
+ parts = entry.split(':')
205
+ ability_value = parts[1]
206
+ abilities_list.append(ability_value)
207
+ return abilities_list
208
+
209
+ def parse_cantrips_from_text(cantrips):
210
+ html_content = '<dl>'
211
+ cantrips_list = []
212
+ cantrip_entries = cantrips.strip().split('\n\n')
213
+ for entry in cantrip_entries:
214
+ parts = entry.split(';')
215
+ cantrip_dict = {
216
+ "name": parts[0],
217
+ "desc": parts[1].split("Description: ")[1].strip()
218
+ }
219
+ cantrips_list.append(cantrip_dict)
220
+ for cantrip in cantrips_list:
221
+ html_content += f"<dt><em><strong>{cantrip['name']}</strong></em> :</dt> <dd> ‘{cantrip['desc']}’</dd>"
222
+ html_content += "<br>"
223
+ html_content=html_content.rstrip('br')
224
+ html_content += '</dl>'
225
+ return html_content
226
+
227
+ def parse_spells_from_text(spells):
228
+ html_content = '<dl>'
229
+ spells_list = []
230
+ spell_entries = spells.strip().split('\n\n')
231
+ for entry in spell_entries:
232
+ print(f"Spell entry = {entry}")
233
+ parts = entry.split(';')
234
+ spell_name_part = parts[0]
235
+ level_desc_part = parts[1]
236
+
237
+ # Extract the spell's name (before 'level:')
238
+ name = spell_name_part.strip()
239
+
240
+ # Further split level and description
241
+ level_part = level_desc_part.split(", Description:")[0].strip()
242
+ description_part = level_desc_part.split(", Description:")[1].strip() if ", Description:" in level_desc_part else ""
243
+
244
+ # Extract the level (assuming it follows 'Level: ' directly)
245
+ level = level_part.replace("Level: ", "").strip()
246
+ print(f"Level = {level}")
247
+
248
+
249
+ # Assemble the dictionary for this spell
250
+ spell_dict = {
251
+ "name": name,
252
+ "level": level,
253
+ "desc": description_part
254
+ }
255
+ spells_list.append(spell_dict)
256
+ for spell in spells_list:
257
+ html_content += f"<dt><em><strong>{spell['name']}</strong></em> :</dt> <dd> ‘Level : {spell['level']} {spell['desc']}’</dd>"
258
+ html_content += " <br>"
259
+ html_content=html_content.rstrip('br')
260
+ html_content += '</dl>'
261
+ return html_content
262
+
263
+ def parse_spell_slots_from_text(spell_slots):
264
+ html_content = '<dl>'
265
+ spell_slots_list = []
266
+ spell_slot_entries = spell_slots.strip().split('\n\n')
267
+ for entry in spell_slot_entries:
268
+
269
+ if '0' not in entry:
270
+ parts = entry.split(':')
271
+ spell_slot_dict = {
272
+ "level": parts[0],
273
+ "number": parts[1]
274
+
275
+ }
276
+
277
+ spell_slots_list.append(spell_slot_dict)
278
+ for spell_slot in spell_slots_list:
279
+ html_content += f"<dt><em><strong>{spell_slot['level']}</strong></em>:</dt> <dd>‘{spell_slot['number']}’</dd>"
280
+ html_content += " <br>"
281
+ html_content=html_content.rstrip(' br')
282
+ html_content += '</dl>'
283
+ return html_content
284
+
285
+ def parse_legendary_action_from_text(legendary_actions):
286
+ html_content = '<dl>'
287
+ parts = legendary_actions.split('\n\n')
288
+ description = parts[0].strip()
289
+ description = description + "<br>"
290
+ print(f"Description : {description}")
291
+ actions_text = parts[1:]
292
+ actions = ';'.join(actions_text)
293
+ actions = actions.split(';')
294
+ print(f"actions : {actions}")
295
+
296
+ legendary_actions_dict = {
297
+ "description": description,
298
+ "actions":[]
299
+ }
300
+ html_content += description
301
+ # Process each action
302
+ for action in actions:
303
+ print(f"action to be parsed: {action}")
304
+ action_split = action.split(':')
305
+ print(f"Split Action : {action_split}")
306
+ legendary_actions_dict['actions'].append({
307
+ "name": action_split[0],
308
+ "desc": action_split[1]
309
+ })
310
+
311
+ for action in legendary_actions_dict['actions']:
312
+ print(action)
313
+ html_content += f"<dt><em><strong>{action['name']}</strong></em>:</dt> <dd> ‘{action['desc']}’</dd>"
314
+ html_content += " <br>"
315
+ html_content=html_content.rstrip(' br')
316
+ html_content += '</dl>'
317
+ return html_content
318
+
319
+
process_text.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ #Function to process text from Key Value pairs into User Friendly text
3
+
4
+ def format_mon_qualities(qualities):
5
+ formatted_text = ""
6
+ for key, value in qualities.items():
7
+ formatted_text += f" {key} : {value}, "
8
+ formatted_text = formatted_text.rstrip(", ")
9
+ return formatted_text
10
+
11
+ def format_actions_for_editing(actions):
12
+ formatted_text = ""
13
+ for action in actions:
14
+ formatted_text += f"Action Name: {action['name']}; Description: {action['desc']} \n\n"
15
+ formatted_text = formatted_text.rstrip(", ")
16
+ return formatted_text
17
+
18
+ def format_abilities_for_editing(abilities):
19
+ formatted_text = ""
20
+ key_list = list(abilities)
21
+ for key in key_list:
22
+ formatted_text += f"{key} : {abilities[key]}\n"
23
+ return formatted_text
24
+
25
+ def format_spells_for_editing(spells):
26
+ print(f"Spells in format_spells function : {spells}")
27
+ formatted_cantrips = ""
28
+ formatted_spells = ""
29
+ formatted_spell_slots = ""
30
+ if spells['cantrips'] and len(spells['cantrips']) >= 1:
31
+ print(f"Cantrips : {spells['cantrips']}")
32
+ formatted_cantrips += "Cantrips \n\n"
33
+ for cantrip in spells['cantrips']:
34
+ formatted_cantrips += f"{cantrip['name']}; Description: {cantrip['desc']}, \n\n"
35
+ if spells['known_spells'] and len(spells['known_spells']) >= 1:
36
+ formatted_spells += "Known Spells \n\n"
37
+ for spell in spells['known_spells']:
38
+ formatted_spells += f"{spell['name']}; Level: {spell['level']}, Description: {spell['desc']}, \n\n"
39
+ if spells['spell_slots'] and len(spells['spell_slots']) >= 1:
40
+ print (f"Spell Slots : {spells['spell_slots']}")
41
+ formatted_spell_slots += "Spell Slots \n\n"
42
+ for key, value in spells['spell_slots'].items():
43
+ if value != 0:
44
+ formatted_spell_slots += f"{key.replace('_',' ')}: {value}, \n\n"
45
+ formatted_cantrips = formatted_cantrips.rstrip(", ")
46
+ formatted_spells = formatted_spells.rstrip(", ")
47
+ formatted_spell_slots = formatted_spell_slots.rstrip(", ")
48
+ return formatted_cantrips, formatted_spells, formatted_spell_slots
49
+
50
+ def format_legendaries_for_editing(legendary_actions):
51
+ formatted_text = ""
52
+ if legendary_actions['actions'] and len(legendary_actions['actions']) >= 1:
53
+ formatted_text += "Legendary Actions \n\n"
54
+ formatted_text += f"{legendary_actions['actions']} \n\n"
55
+ if legendary_actions['options']:
56
+ for option in legendary_actions['options']:
57
+ formatted_text += f"{option['name']} : {option['desc']}, \n\n"
58
+ formatted_text = formatted_text.rstrip(", \n\n")
59
+ return formatted_text
60
+
requirements.txt DELETED
@@ -1,158 +0,0 @@
1
- # install last
2
- # pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
3
- # pip install flash-attn
4
- #pip install auto-gptq
5
- accelerate
6
- aiofiles
7
- aiohttp
8
- aiosignal
9
- altair
10
- annotated-types
11
- antlr4-python3-runtime
12
- anyio
13
- async-timeout
14
- attrs
15
- backoff
16
- blinker
17
- cachetools
18
- certifi
19
- charset-normalizer
20
- clarifai
21
- clarifai-grpc
22
- click
23
- cmake
24
- cohere
25
- colorama
26
- coloredlogs
27
- compel
28
- contextlib2
29
- contourpy
30
- ctransformers
31
- cycler
32
- dataclasses-json
33
- datasets
34
- diffusers
35
- dill
36
- einops
37
- exceptiongroup
38
- exllamav2
39
- fastapi
40
- fastavro
41
- ffmpy
42
- filelock
43
- fonttools
44
- frozenlist
45
- fsspec
46
- gekko
47
- gitdb
48
- GitPython
49
- googleapis-common-protos
50
- gradio
51
- gradio_client
52
- greenlet
53
- grpcio
54
- h11
55
- httpcore
56
- httpx
57
- huggingface-hub
58
- humanfriendly
59
- idna
60
- importlib-metadata
61
- importlib-resources
62
- iniconfig
63
- Jinja2
64
- jsonpatch
65
- jsonpointer
66
- jsonschema
67
- jsonschema-specifications
68
- kiwisolver
69
- manifest-ml
70
- Markdown
71
- markdown-it-py
72
- MarkupSafe
73
- marshmallow
74
- matplotlib
75
- mdurl
76
- mpmath
77
- multidict
78
- multiprocess
79
- mypy-extensions
80
- networkx
81
- ninja
82
- nlpcloud
83
- numpy
84
- omegaconf
85
- openai
86
- openlm
87
- optimum
88
- orjson
89
- packaging
90
- pandas
91
- peft
92
- Pillow
93
- pluggy
94
- protobuf
95
- psutil
96
- py-cpuinfo
97
- pyarrow
98
- pydantic
99
- pydantic_core
100
- pydeck
101
- pydub
102
- Pygments
103
- pyparsing
104
- pyreadline3
105
- pytest
106
- python-dateutil
107
- python-dotenv
108
- python-multipart
109
- python-rapidjson
110
- python_on_whales
111
- pytz
112
- PyYAML
113
- qinfer
114
- redis
115
- referencing
116
- regex
117
- reportlab
118
- requests
119
- rich
120
- rouge
121
- rpds-py
122
- safetensors
123
- schema
124
- semantic-version
125
- sentencepiece
126
- six
127
- smmap
128
- sniffio
129
- soupsieve
130
- SQLAlchemy
131
- sqlitedict
132
- starlette
133
- streamlit
134
- sympy
135
- tenacity
136
- tokenizers
137
- toml
138
- tomli
139
- toolz
140
-
141
- tornado
142
- tqdm
143
- transformers
144
- tritonclient
145
- typing-inspect
146
- typing_extensions
147
- tzdata
148
- tzlocal
149
- urllib3
150
- uvicorn
151
- validators
152
- watchdog
153
- websockets
154
- wheel
155
- xxhash
156
- yarl
157
- zipp
158
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sd_generator.py CHANGED
@@ -1,73 +1,32 @@
1
- from diffusers import StableDiffusionXLPipeline
2
- import torch
3
- from compel import Compel, ReturnedEmbeddingsType
4
- import utilities as u
5
  import time
 
 
 
 
6
 
7
 
8
- image_list = []
9
- def del_image_list() :
10
- del image_list
11
 
12
- # batch size
13
- num_img = 4
14
- # Assign path to model to be used and tell torch to be ready for 32 bit
15
- torch.backends.cuda.matmul.allow_tf32 = True
16
- model_path = ("/home/user/app/models/stable-diffusion/SDXLFaetastic_v24.safetensors")
 
 
 
17
 
18
- def generate_image(sd_input) :
19
- u.reclaim_mem()
20
-
21
- start_time = time.time()
22
-
23
 
24
- # create variable that calls SD model and work in float 16
25
- # from_single_file is critical for loading a local file
26
- pipeline = StableDiffusionXLPipeline.from_single_file(model_path, custom_pipeline="lpw_stable_diffusion", torch_dtype=torch.float16, variant="fp16" ).to("cuda")
27
-
28
- # enable vae slicing ton prevent Out Of Memory Error when generating batches
29
- # pipeline.enable_vae_slicing()
30
 
31
- # Compel is a module that could allow longer than 77 token prompts AND adding weights to specific tokens
32
- compel = Compel(tokenizer=[pipeline.tokenizer, pipeline.tokenizer_2] ,
33
- text_encoder=[pipeline.text_encoder, pipeline.text_encoder_2],
34
- returned_embeddings_type=ReturnedEmbeddingsType.PENULTIMATE_HIDDEN_STATES_NON_NORMALIZED,
35
- requires_pooled=[False, True],
36
- truncate_long_prompts=False)
37
-
38
-
39
- # assign prompt as global sd_input
40
- prompt = sd_input
41
-
42
-
43
- # Not sure what conditioning or pooled means, but it's in the demo code from here https://github.com/damian0815/compel/blob/main/compel-demo-sdxl.ipynb
44
- negative_prompt = "sex, lingerie, midriff, watermark, text, fastnegative2, blurry, ugly, low quality, worst quality, 3d"
45
- conditioning, pooled = compel([prompt, negative_prompt])
46
- print(conditioning.shape, pooled.shape)
47
 
48
- # generate image
49
- # image = pipe(prompt=prompt,num_inference_steps=50).images[0]
50
- for x in range(num_img):
51
- image = pipeline(prompt_embeds=conditioning[0:1], pooled_prompt_embeds=pooled[0:1],
52
- negative_prompt_embeds=conditioning[1:2], negative_pooled_prompt_embeds=pooled[1:2],
53
- num_inference_steps=30, width=1024, height=1024).images[0]
54
- image_name = u.make_image_name()
55
- image.save(image_name)
56
- image_list.append(image_name)
57
- del image
58
-
59
-
60
- del pipeline
61
- del compel
62
-
63
- del image_name
64
- del prompt
65
-
66
- u.reclaim_mem()
67
- print(image_list)
68
- stop_time = time.time()
69
- run_time = stop_time - start_time
70
- print(f"Time to generate : {run_time}")
71
 
72
- return image_list
73
 
 
 
 
 
 
1
  import time
2
+ import utilities as u
3
+ from PIL import Image
4
+ import replicate
5
+ from pathlib import Path
6
 
7
 
8
+ start_time = time.time()
9
+ temp_image_path = "./image_temp/"
 
10
 
11
+ def preview_and_generate_image(character_name,sd_prompt, token):
12
+ img_start = time.time()
13
+ output=replicate.run(
14
+ "drakosfire/dnd_monster_generator:80c9d4247c1e1bd92084af24fa61e88b6a809a42585e343af9722d90337c9ada",
15
+ input={
16
+ "character_name":character_name,
17
+ "sd_prompt":sd_prompt,
18
+ "token":token
19
 
20
+ }
21
+ )
 
 
 
22
 
 
 
 
 
 
 
23
 
24
+ img_time = time.time() - img_start
25
+ img_its = 35/img_time
26
+ print(f"image gen time = {img_time} and {img_its} it/s")
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
+ total_time = time.time() - start_time
29
+ print(total_time)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ return output
32
 
statblock_helper.py DELETED
@@ -1,165 +0,0 @@
1
- import description_helper as dsh
2
- import time
3
- import os
4
- import subprocess
5
- import utilities as u
6
- from python_on_whales import docker
7
-
8
- from exllamav2 import (
9
- ExLlamaV2,
10
- ExLlamaV2Config,
11
- ExLlamaV2Cache,
12
- ExLlamaV2Tokenizer,
13
- ExLlamaV2Lora,
14
- )
15
-
16
- from exllamav2.generator import (
17
- ExLlamaV2BaseGenerator,
18
- ExLlamaV2StreamingGenerator,
19
- ExLlamaV2Sampler
20
- )
21
-
22
- file_name_list = []
23
- output_list = []
24
-
25
- # This needs to be moved to utilities
26
-
27
- def gen_file_name():
28
- del file_name_list[:]
29
- timestr = time.strftime("%H%M%S")
30
- input_dir = f"/home/user/app/output/{u.make_folder()}"
31
-
32
- mon_file_name = dsh.generate_monster_desc.monster_type.replace(' ', '')
33
- file_name = mon_file_name + "_" + timestr
34
- file_name_list.append(input_dir)
35
- file_name_list.append(file_name)
36
- file_name_list.append(mon_file_name)
37
-
38
- # Function to find the beginning of the output markdown and remove the prompt before it
39
-
40
- def rembeforestart(text):
41
- where_start = text.find('{{')
42
- if where_start == -1:
43
- return text
44
- return text[where_start:]
45
-
46
- # Function to remove any extra after the end of the Markdown
47
-
48
- def remafterend(text):
49
- where_end = text.find('}}')
50
- if where_end == -1:
51
- return text
52
- return text[:where_end + 2]
53
-
54
- def generate_statblock(input):
55
- gen_file_name()
56
- run_time = time.time()
57
- generate_statblock.input = input
58
-
59
- # Initialize model and cache
60
-
61
- model_directory = "/home/user/app/models/Speechless-Llama2-Hermes-Orca-Platypus-WizardLM-13B-GPTQ"
62
- config = ExLlamaV2Config()
63
- config.model_dir = model_directory
64
- config.prepare()
65
-
66
- model = ExLlamaV2(config)
67
- print("Loading model: " + model_directory)
68
- model.load()
69
-
70
- tokenizer = ExLlamaV2Tokenizer(config)
71
-
72
- cache = ExLlamaV2Cache(model)
73
-
74
- # Load LoRA
75
-
76
- lora_directory = "/home/user/app/models/statblock-alpha"
77
- lora = ExLlamaV2Lora.from_directory(model, lora_directory)
78
-
79
- # Initialize generators
80
-
81
-
82
- simple_generator = ExLlamaV2BaseGenerator(model, cache, tokenizer)
83
-
84
- # Sampling settings
85
-
86
- settings = ExLlamaV2Sampler.Settings()
87
- settings.temperature = 0.85
88
- settings.top_k = 50
89
- settings.top_p = 0.8
90
- settings.token_repetition_penalty = 1.1
91
-
92
- max_new_tokens = 1100
93
-
94
- # Build prompt
95
-
96
- monster_frame_wide ="""{{monster,frame,wide"""
97
- statblock_start = f"""{monster_frame_wide} \n## {dsh.generate_monster_desc.monster_type} """
98
- input_context = f"Write a .brewery formatted dungeons and dragons statblock of {generate_statblock.input} any abilities that reference actions need to have those actions defined \n" + statblock_start
99
- print(input_context)
100
- prompt = input_context
101
-
102
-
103
- generate_time = time.time()
104
- output_text = simple_generator.generate_simple(prompt, settings, max_new_tokens, loras = lora)
105
- output_text = rembeforestart(output_text)
106
-
107
- generate_statblock.output_text = remafterend(output_text)
108
- print(generate_statblock.output_text)
109
- print("statblock generation time : " + str(time.time() - generate_time))
110
-
111
- input_dir = file_name_list[0]
112
- input_md = open(f"{input_dir}/my-brew.md", 'w')
113
- print(generate_statblock.output_text, file = input_md)
114
-
115
- #md_process(md_path, file_name_list[1])
116
-
117
-
118
- del model
119
- del tokenizer
120
- del output_text
121
- del simple_generator
122
- del cache
123
- del lora
124
- u.reclaim_mem()
125
- print("total statblock generation time : " + str(time.time() - run_time))
126
- output_list.append(input_context)
127
- output_list.append(generate_statblock.output_text)
128
- u.make_user_log()
129
- return generate_statblock.output_text
130
-
131
- # Function to process the my-brew.md file into a named html inside docker using process.js
132
- def md_process(input_md,output_name):
133
-
134
- file_name = output_name
135
- # Passing in file name to derive absolute directory, and the desired output name
136
- abs_path = os.path.abspath(input_md)
137
- input_dir = os.path.dirname(abs_path)
138
-
139
- print(abs_path)
140
- print(input_dir)
141
- # Subprocess to pass the docker command to the command line
142
- # Docker compused from this https://github.com/G-Ambatte/.brewery/tree/experimentalCommandLineBrewProcess
143
- process_call = f"node /home/user/app/homebrewery/cli/process.js --input {abs_path} --output {input_dir}/{file_name}.html --renderer v3 --overwrite"
144
- print(process_call)
145
- subprocess.run(process_call, shell=True)
146
-
147
- # beginning of code to run html process and display in one shot
148
- #while True:
149
- #if os.path.isfile(file_name_list[0]+'/' + file_name_list[1] +'.html'):
150
- #gen_html()
151
- #break
152
- #else:
153
- #print("File not found, waiting.")
154
- #time.sleep(1) # wait for 1 second before checking again
155
-
156
-
157
-
158
-
159
-
160
-
161
-
162
-
163
-
164
-
165
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
style.css ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ font-family: 'Libre Baskerville', serif;
3
+ background-color: #f9f6f1;
4
+ margin: 0;
5
+ padding: 0;
6
+ }
7
+
8
+ .stat-block {
9
+ max-width: 800px;
10
+ margin: 20px auto;
11
+ padding: 20px;
12
+ background-color: #fff;
13
+ border: 2px solid #333;
14
+ border-radius: 10px;
15
+ }
16
+
17
+ .stat-block h1, .stat-block h2 {
18
+ text-align: center;
19
+ color: #b33;
20
+ margin: 0;
21
+ }
22
+
23
+ .stat-block h1 {
24
+ font-size: 2em;
25
+ }
26
+
27
+ .stat-block h2 {
28
+ font-size: 1.5em;
29
+ margin-top: 10px;
30
+ }
31
+
32
+ .stat-section {
33
+ display: flex;
34
+ justify-content: space-between;
35
+ margin: 10px 0;
36
+ }
37
+
38
+ .stat-section div {
39
+ flex: 1;
40
+ padding: 10px;
41
+ margin: 5px;
42
+ background-color: #eee;
43
+ border-radius: 5px;
44
+ text-align: center;
45
+ }
46
+
47
+ .stat-section div label {
48
+ display: block;
49
+ margin-bottom: 5px;
50
+ color: #666;
51
+ }
52
+
53
+ .stat-section div input {
54
+ width: 100%;
55
+ padding: 5px;
56
+ border: 1px solid #333;
57
+ border-radius: 5px;
58
+ text-align: center;
59
+ font-family: 'Roboto', sans-serif;
60
+ }
tripo3d.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ import time
4
+ from pathlib import Path
5
+
6
+ # Load API key from environment variable
7
+ API_KEY = os.getenv('TRIPO3D_API_KEY')
8
+ if not API_KEY:
9
+ raise EnvironmentError("TRIPO3D_API_KEY not found in environment variables")
10
+
11
+ BASE_URL = "https://api.tripo3d.ai/v2/openapi"
12
+ headers = {
13
+ "Authorization": f"Bearer {API_KEY}"
14
+ }
15
+
16
+ def download_image(url, local_filename):
17
+ response = requests.get(url, stream=True)
18
+ if response.status_code == 200:
19
+ print("download of original image successful")
20
+ with open(local_filename, 'wb') as f:
21
+ for chunk in response.iter_content(1024):
22
+ f.write(chunk)
23
+ return local_filename
24
+ else:
25
+ raise Exception(f"Failed to download image from {url}, status code: {response.status_code}")
26
+
27
+ def upload_image(file_path):
28
+ url = f"{BASE_URL}/upload"
29
+ files = {'file': open(file_path, 'rb')}
30
+
31
+ response = requests.post(url, headers=headers, files=files)
32
+
33
+ if response.status_code == 200:
34
+ print(f"upload successful \n {response}")
35
+ return response.json()
36
+ else:
37
+ print(f"Error: {response.status_code}, {response.json()}")
38
+ return None
39
+
40
+ def create_image_to_model_task(image_token):
41
+ url = f"{BASE_URL}/task"
42
+ payload = {
43
+ "type": "image_to_model",
44
+ "file": {
45
+ "type": "png", # Adjust this based on the actual file type
46
+ "file_token": image_token
47
+ }
48
+ }
49
+
50
+ response = requests.post(url, headers=headers, json=payload)
51
+
52
+ if response.status_code == 200:
53
+ print(f"generation successful \n {response}")
54
+ return response.json()
55
+ else:
56
+ print(f"Error: {response.status_code}, {response.json()}")
57
+ return None
58
+
59
+ def check_task_status(task_id):
60
+ url = f"{BASE_URL}/task/{task_id}"
61
+ response = requests.get(url, headers=headers)
62
+
63
+ if response.status_code == 200:
64
+ return response.json()
65
+ else:
66
+ print(f"Error: {response.status_code}, {response.json()}")
67
+ return None
68
+
69
+ def download_file(url, local_filename):
70
+ with requests.get(url, headers=headers, stream=True) as r:
71
+ r.raise_for_status()
72
+ with open(local_filename, 'wb') as f:
73
+ for chunk in r.iter_content(chunk_size=8192):
74
+ f.write(chunk)
75
+ return local_filename
76
+
77
+ def process_image(image_url):
78
+
79
+ local_image_path = download_image(image_url, "temp_image.jpg")
80
+ # Step 1: Upload the image
81
+ upload_response = upload_image(local_image_path)
82
+ if not upload_response or upload_response.get('code') != 0:
83
+ return "Error uploading image", None
84
+
85
+ image_token = upload_response['data']['image_token']
86
+
87
+ # Step 2: Create image-to-model task
88
+ task_response = create_image_to_model_task(image_token)
89
+ if not task_response or task_response.get('code') != 0:
90
+ return "Error creating task", None
91
+
92
+ task_id = task_response['data']['task_id']
93
+
94
+ # Step 3: Check task status
95
+ while True:
96
+ status_response = check_task_status(task_id)
97
+ if status_response and status_response.get('code') == 0:
98
+ status = status_response['data']['status']
99
+ progress = status_response['data']['progress']
100
+ print(f"Task Status: {status}, Progress: {progress}%")
101
+
102
+ if status == "success":
103
+ output = status_response['data']['output']
104
+ model_url = output['model'] # Assuming 'model' key contains the URL
105
+
106
+ # Step 4: Download the .glb file
107
+ local_filename = f"{task_id}.glb"
108
+ download_file(model_url, local_filename)
109
+ print(local_filename)
110
+
111
+ return local_filename
112
+ elif status in ["failed", "cancelled"]:
113
+ return f"Task {status}", None
114
+ else:
115
+ time.sleep(5) # Wait for 5 seconds before checking again
116
+ else:
117
+ return "Failed to check task status", None
118
+
119
+ # Gradio Interface
120
+ def generate_model(file):
121
+ model_path = process_image(file)
122
+ if model_path:
123
+ return model_path
124
+ else:
125
+ return None
126
+
utilities.py CHANGED
@@ -4,10 +4,11 @@ import os
4
  import gc
5
  import torch
6
  import description_helper as dsh
7
- import statblock_helper as sth
8
- import ctypes
9
- import psutil
10
 
 
 
 
 
11
  image_name_list = []
12
  link_list =['something','Link to monster statblock once generated']
13
  random_prompt_list = []
@@ -36,7 +37,10 @@ def reclaim_mem():
36
  torch.cuda.ipc_collect()
37
  gc.collect()
38
  torch.cuda.empty_cache()
 
39
  time.sleep(0.01)
 
 
40
  print(f"Memory Allocated after del {mem_alloc}")
41
  print(f"Memory Cached after del {mem_cache}")
42
 
@@ -48,17 +52,17 @@ def generate_datetime():
48
 
49
  def make_folder():
50
  foldertimestr = time.strftime("%Y%m%d_%H")
51
- folder_path = f"/home/user/app/output/{foldertimestr}"
52
- if not os.path.exists("/home/user/app/output"):
53
- os.mkdir("/home/user/app/output")
54
  if not os.path.exists(folder_path):
55
  os.mkdir(folder_path)
56
  return foldertimestr
57
 
58
- def make_image_name():
59
  del image_name_list[:]
60
  timestr = time.strftime("%Y%m%d-%H%M%S")
61
- image_name = f"/home/user/app/output/{make_folder()}/{dsh.generate_monster_desc.monster_type}{timestr}.png"
62
  image_name = image_name.replace(' ', '_')
63
  image_name_list.append(image_name)
64
  print("Image name is : " + image_name_list[-1])
@@ -67,7 +71,7 @@ def make_image_name():
67
  def make_user_log() :
68
  del user_log[:]
69
  timestr = time.strftime("%Y%m%d-%H%M%S")
70
- folder_path = f"/home/user/app/output/{make_folder()}"
71
  user_log_file = open(f"{folder_path}/userlog-{timestr}.txt","w")
72
  user_log.append(dsh.prompt_list[0])
73
  user_log.append(f"Output from LLM: {dsh.sd_input[0]}")
@@ -79,9 +83,43 @@ def make_user_log() :
79
  print(user_log, file= user_log_file)
80
  user_log_file.close()
81
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
- #def gen_random_prompt(prompt_in):
84
-
85
-
86
-
87
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  import gc
5
  import torch
6
  import description_helper as dsh
 
 
 
7
 
8
+ # Utility scripts for all modules
9
+
10
+ # List for file locations to point at
11
+ file_name_list = []
12
  image_name_list = []
13
  link_list =['something','Link to monster statblock once generated']
14
  random_prompt_list = []
 
37
  torch.cuda.ipc_collect()
38
  gc.collect()
39
  torch.cuda.empty_cache()
40
+ torch.cuda.synchronize()
41
  time.sleep(0.01)
42
+ allocated_memory = torch.cuda.memory_allocated()
43
+ cached_memory = torch.cuda.memory_reserved()
44
  print(f"Memory Allocated after del {mem_alloc}")
45
  print(f"Memory Cached after del {mem_cache}")
46
 
 
52
 
53
  def make_folder():
54
  foldertimestr = time.strftime("%Y%m%d_%H")
55
+ folder_path = f"/media/drakosfire/Shared/Docker/StatblockGenerator/output/{foldertimestr}"
56
+ if not os.path.exists("/media/drakosfire/Shared/Docker/StatblockGenerator/output"):
57
+ os.mkdir("/media/drakosfire/Shared/Docker/StatblockGenerator/output")
58
  if not os.path.exists(folder_path):
59
  os.mkdir(folder_path)
60
  return foldertimestr
61
 
62
+ def make_image_name(name):
63
  del image_name_list[:]
64
  timestr = time.strftime("%Y%m%d-%H%M%S")
65
+ image_name = f"/media/drakosfire/Shared/Docker/StatblockGenerator/output/{make_folder()}/{name}{timestr}.png"
66
  image_name = image_name.replace(' ', '_')
67
  image_name_list.append(image_name)
68
  print("Image name is : " + image_name_list[-1])
 
71
  def make_user_log() :
72
  del user_log[:]
73
  timestr = time.strftime("%Y%m%d-%H%M%S")
74
+ folder_path = f"/media/drakosfire/Shared/Docker/StatblockGenerator/output/{make_folder()}"
75
  user_log_file = open(f"{folder_path}/userlog-{timestr}.txt","w")
76
  user_log.append(dsh.prompt_list[0])
77
  user_log.append(f"Output from LLM: {dsh.sd_input[0]}")
 
83
  print(user_log, file= user_log_file)
84
  user_log_file.close()
85
 
86
+ # Create a unique time stamped file name
87
+ def gen_file_name(mon_name):
88
+ del file_name_list[:]
89
+ timestr = time.strftime("%H%M%S")
90
+ input_dir = f"/media/drakosfire/Shared/Docker/StatblockGenerator/output/{make_folder()}"
91
+
92
+ mon_file_name = mon_name
93
+ file_name = mon_file_name + "_" + timestr
94
+ file_name_list.append(input_dir)
95
+ file_name_list.append(file_name)
96
+ file_name_list.append(mon_file_name)
97
 
98
+ def make_folder():
99
+ foldertimestr = time.strftime("%Y%m%d_%H")
100
+ folder_path = f"/media/drakosfire/Shared/Docker/StatblockGenerator/output/{foldertimestr}"
101
+ if not os.path.exists("/media/drakosfire/Shared/Docker/StatblockGenerator/output"):
102
+ os.mkdir("/media/drakosfire/Shared/Docker/StatblockGenerator/output")
103
+ if not os.path.exists(folder_path):
104
+ os.mkdir(folder_path)
105
+ return foldertimestr
106
+
107
+ # Create a list of a directory if directory exists
108
+ def directory_contents(directory_path):
109
+ if os.path.isdir(directory_path) :
110
+ contents = os.listdir(directory_path)
111
+ return contents
112
+ else : pass
113
+
114
+
115
+ # Delete a list of file
116
+ def delete_files(file_paths):
117
+ if file_paths:
118
+
119
+ for file_path in file_paths:
120
+ try:
121
+ os.remove(f"./image_temp/{file_path}")
122
+ print(f"Remove : ./image_temp/{file_path}")
123
+ except OSError as e:
124
+ print(f"Error: {file_path} : {e.strerror}")
125
+ file_paths.clear()