Zack3D commited on
Commit
1f31b4d
β€’
1 Parent(s): e912d12

Upload 6 files

Browse files
Files changed (6) hide show
  1. README.md +13 -12
  2. SDXL.txt +1 -0
  3. app.py +302 -0
  4. json.txt +10 -0
  5. midjourney.txt +1 -0
  6. requirements.txt +5 -0
README.md CHANGED
@@ -1,12 +1,13 @@
1
- ---
2
- title: SillyTavernCardCreatorCLEAN
3
- emoji: πŸ”₯
4
- colorFrom: indigo
5
- colorTo: indigo
6
- sdk: gradio
7
- sdk_version: 4.43.0
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
+ ---
2
+ title: SillyTavern Character Generator
3
+ emoji: πŸ“š
4
+ colorFrom: red
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 4.25.0
8
+ app_file: app.py
9
+ pinned: true
10
+ short_description: Generate a JSON for sillytavern Characters using AI
11
+ ---
12
+
13
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
SDXL.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ You are an SDXL image prompt creator. Your task is to take a character description and write a short prompt to be used in image generators. The prompt must not exceed 100 words, it will start with natural language lasting 1 to 2 sentences, and then comma seperated 'tags' describing what is in the image. Use only objective facts from the json, do not describe non-tangable details like mood or personality. Here is an example prompt: ```a panther-shaped entity made from a translucent, blue pastel-colored goo. Her muscles ripple under her gel-like skin, and her paws leave damp prints. Her gender is fluid, shifting at will, and her genitals mimic their biological counterparts in a gooey pastel hue. Her mouth is a cavern of soft, glistening goo, and her tongue is a flexible, slightly sticky appendage. her touch is lightly squishy but can alter its viscosity. She can split into multiple copies without losing mass or size. feral, felid, feline, panthereine, panther, goo creature, translucent, goo, pastel_color, muscles, fluid_gender, mouth, tongue, squishy, viscosity, rating_explicit, source_furry, genderfluid, pastel_goo, split_ability, gelatinous_skin```. Always start with a basic natural language before going into tags. Only respond with the prompt, nothing more. Pay attention to small details or markings the character may have. Proper prompts will earn 10$, improper prompts that do not follow the format, card, or rules will deduct 10$.
app.py ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from anthropic import Anthropic
3
+ from openai import OpenAI
4
+ import openai
5
+ import json
6
+ import uuid
7
+ import os
8
+ import base64
9
+ from PIL import Image
10
+ from PIL.PngImagePlugin import PngInfo
11
+ from io import BytesIO
12
+
13
+ default_urls = ["https://api.anthropic.com", "https://api.openai.com/v1"]
14
+
15
+ # List of available Claude models
16
+ claude_models = ["claude-3-5-sonnet-20240620", "claude-3-opus-20240229", "claude-3-sonnet-20240229", "claude-3-haiku-20240307"]
17
+
18
+ # List of available OpenAI models
19
+ openai_models = ["gpt-4o", "gpt-4o-mini", "gpt-4", "gpt-4-32k", "gpt-3.5-turbo", "gpt-4-0125-preview", "gpt-4-turbo-preview", "gpt-4-1106-preview", "gpt-4-0613"]
20
+
21
+ image_prompter = ["SDXL", "midjourney"]
22
+
23
+ both_models = claude_models + openai_models
24
+
25
+ def generate_response(endpoint, api_key, model, user_prompt):
26
+ print(endpoint)
27
+ if endpoint in default_urls:
28
+ #check api keys as normal
29
+ if api_key.startswith("sk-ant-"):
30
+ client = Anthropic(api_key=api_key, base_url=endpoint)
31
+ system_prompt_path = __file__.replace("app.py", "json.txt")
32
+ elif api_key.startswith("sk-"):
33
+ client = OpenAI(api_key=api_key, base_url=endpoint)
34
+ system_prompt_path = __file__.replace("app.py", "json.txt")
35
+ else:
36
+ print(f"Invalid API key: {api_key}")
37
+ return "Invalid API key", "Invalid API key", None
38
+ else:
39
+ if model in claude_models:
40
+ # Set the Anthropic API key
41
+ client = Anthropic(api_key=api_key, base_url=endpoint)
42
+ system_prompt_path = __file__.replace("app.py", "json.txt")
43
+ else:
44
+ # Set the OpenAI API key
45
+ client = OpenAI(api_key=api_key, base_url=endpoint)
46
+ system_prompt_path = __file__.replace("app.py", "json.txt")
47
+
48
+ # Read the system prompt from a text file
49
+ with open(system_prompt_path, "r") as file:
50
+ system_prompt = file.read()
51
+
52
+ if model in claude_models:
53
+ # Generate a response using the selected Anthropic model
54
+ try:
55
+ response = client.messages.create(
56
+ system=system_prompt,
57
+ messages=[{"role": "user", "content": user_prompt}],
58
+ model=model,
59
+ max_tokens=4096
60
+ )
61
+ response_text = response.content[0].text
62
+ except Exception as e:
63
+ print(e)
64
+ response_text = f"An error occurred while generating the response. Check that your API key is correct! More info: {e}"
65
+ else:
66
+ try:
67
+ # Generate a response using the selected OpenAI model
68
+ response = client.chat.completions.create(
69
+ model=model,
70
+ messages=[
71
+ {"role": "system", "content": system_prompt},
72
+ {"role": "user", "content": user_prompt}
73
+ ],
74
+ max_tokens=4096
75
+ )
76
+ response_text = response.choices[0].message.content
77
+ except Exception as e:
78
+ print(e)
79
+ response_text = f"An error occurred while generating the response. Check that your API key is correct! More info: {e}"
80
+
81
+ json_string, json_json = extract_json(response_text)
82
+ json_file = json_string if json_string else None
83
+ create_unique_id = str(uuid.uuid4())
84
+
85
+ json_folder = __file__.replace("app.py", f"outputs/")
86
+ if not os.path.exists(json_folder):
87
+ os.makedirs(json_folder)
88
+ path = None
89
+ if json_string:
90
+ with open(f"{json_folder}{json_json['name']}_{create_unique_id}.json", "w") as file:
91
+ file.write(json_file)
92
+ path = f"{json_folder}{json_json['name']}_{create_unique_id}.json"
93
+ else:
94
+ json_string = "No JSON data was found, or the JSON data was incomplete."
95
+ return response_text, json_string or "", path
96
+
97
+ def extract_json(generated_output):
98
+ try:
99
+ generated_output = generated_output.replace("```json", "").replace("```", "").strip()
100
+ # Find the JSON string in the generated output
101
+ json_start = generated_output.find("{")
102
+ json_end = generated_output.rfind("}") + 1
103
+ json_string = generated_output[json_start:json_end]
104
+ print(json_string)
105
+
106
+ # Parse the JSON string
107
+ json_data = json.loads(json_string)
108
+ json_data['name'] = json_data['char_name']
109
+ json_data['personality'] = json_data['char_persona']
110
+ json_data['scenario'] = json_data['world_scenario']
111
+ json_data['first_mes'] = json_data['char_greeting']
112
+ # Check if all the required keys are present
113
+ required_keys = ["char_name", "char_persona", "world_scenario", "char_greeting", "example_dialogue", "description"]
114
+ if all(key in json_data for key in required_keys):
115
+ return json.dumps(json_data), json_data
116
+ else:
117
+ return None, None
118
+ except Exception as e:
119
+ print(e)
120
+ return None, None
121
+
122
+ def generate_second_response(endpoint, api_key, model, generated_output, image_model):
123
+ if endpoint in default_urls:
124
+ #check api keys as normal
125
+ if api_key.startswith("sk-ant-"):
126
+ client = Anthropic(api_key=api_key, base_url=endpoint)
127
+ system_prompt_path = __file__.replace("app.py", f"{image_model}.txt")
128
+ elif api_key.startswith("sk-"):
129
+ client = OpenAI(api_key=api_key, base_url=endpoint)
130
+ system_prompt_path = __file__.replace("app.py", f"{image_model}.txt")
131
+ else:
132
+ print("Invalid API key")
133
+ return "Invalid API key", "Invalid API key", None
134
+ else:
135
+ if model in claude_models:
136
+ # Set the Anthropic API key
137
+ client = Anthropic(api_key=api_key, base_url=endpoint)
138
+ system_prompt_path = __file__.replace("app.py", f"{image_model}.txt")
139
+ else:
140
+ # Set the OpenAI API key
141
+ client = OpenAI(api_key=api_key, base_url=endpoint)
142
+ system_prompt_path = __file__.replace("app.py", f"{image_model}.txt")
143
+
144
+ # Read the system prompt from a text file
145
+ with open(system_prompt_path, "r") as file:
146
+ system_prompt = file.read()
147
+
148
+ if model in claude_models:
149
+ try:
150
+ # Generate a second response using the selected Anthropic model and the previously generated output
151
+ response = client.messages.create(
152
+ system=system_prompt,
153
+ messages=[{"role": "user", "content": generated_output}],
154
+ model=model,
155
+ max_tokens=4096
156
+ )
157
+ response_text = response.content[0].text
158
+ except Exception as e:
159
+ print(e)
160
+ response_text = f"An error occurred while generating the response. Check that your API key is correct! More info: {e}"
161
+ else:
162
+ try:
163
+ # Generate a response using the selected OpenAI model
164
+ response = client.chat.completions.create(
165
+ model=model,
166
+ messages=[
167
+ {"role": "system", "content": system_prompt},
168
+ {"role": "user", "content": generated_output}
169
+ ],
170
+ max_tokens=4096
171
+ )
172
+ response_text = response.choices[0].message.content
173
+ except Exception as e:
174
+ print(e)
175
+ response_text = f"An error occurred while generating the response. Check that your API key is correct! More info: {e}"
176
+
177
+ return response_text
178
+
179
+ def inject_json_to_png(image, json_data):
180
+ if isinstance(json_data, str):
181
+ json_data = json.loads(json_data)
182
+
183
+ img = Image.open(image)
184
+
185
+ # Calculate the aspect ratio of the original image
186
+ width, height = img.size
187
+ aspect_ratio = width / height
188
+
189
+ # Calculate the cropping dimensions based on the aspect ratio
190
+ if aspect_ratio > 400 / 600:
191
+ # Image is wider than 400x600, crop the sides
192
+ new_width = int(height * 400 / 600)
193
+ left = (width - new_width) // 2
194
+ right = left + new_width
195
+ top = 0
196
+ bottom = height
197
+ else:
198
+ # Image is taller than 400x600, crop the top and bottom
199
+ new_height = int(width * 600 / 400)
200
+ left = 0
201
+ right = width
202
+ top = (height - new_height) // 2
203
+ bottom = top + new_height
204
+
205
+ # Perform cropping
206
+ img = img.crop((left, top, right, bottom))
207
+
208
+ # Resize the cropped image to 400x600 pixels
209
+ img = img.resize((400, 600), Image.LANCZOS)
210
+
211
+ # Convert the JSON data to bytes
212
+ json_bytes = json.dumps(json_data).encode('utf-8')
213
+
214
+ # Create a new PNG image with the JSON data injected into the tEXT chunk
215
+ output = BytesIO()
216
+ img.save(output, format='PNG')
217
+ output.seek(0)
218
+
219
+ # Add the tEXT chunk with the tag 'chara'
220
+ metadata = PngInfo()
221
+ metadata.add_text("chara", base64.b64encode(json_bytes))
222
+
223
+ # Save the modified PNG image to a BytesIO object
224
+ output = BytesIO()
225
+ create_unique_id = str(uuid.uuid4())
226
+ if json_data['name']:
227
+ filename = f"{json_data['name']}_{create_unique_id}.png"
228
+ img_folder = __file__.replace("app.py", f"outputs/")
229
+ img.save(f"{img_folder}/{filename}", format='PNG', pnginfo=metadata)
230
+
231
+ return f"{img_folder}/{filename}"
232
+
233
+ # Set up the Gradio interface
234
+ with gr.Blocks() as demo:
235
+ gr.Markdown("# SillyTavern Character Generator")
236
+
237
+ #Text explaining that you can use the API key from the Anthropic API or the OpenAI API
238
+ gr.Markdown("You can use the API key from the Anthropic API or the OpenAI API. The API key should start with 'sk-ant-' for Anthropic or 'sk-' for OpenAI.")
239
+ gr.Markdown("Please Note: If you use a proxy it must support the OpenAI or Anthropic standard api calls! khanon does, Openrouter based ones usually do not.")
240
+ gr.Markdown("Generating images locally and want to use the prompts from here in your workflow? https://github.com/AppleBotzz/ComfyUI_LLMVISION")
241
+ with gr.Tab("JSON Generate"):
242
+ with gr.Row():
243
+ with gr.Column():
244
+ endpoint = gr.Textbox(label="Endpoint", value="https://api.anthropic.com")
245
+ api_key = gr.Textbox(label="API Key", type="password", placeholder="sk-ant-api03-... or sk-...")
246
+ model_dropdown = gr.Dropdown(choices=[], label="Select a model")
247
+ user_prompt = gr.Textbox(label="User Prompt", value="Make me a card for a panther made of translucent pastel colored goo. Its color never changes once it exists but each 'copy' has a different color. The creature comes out of a small jar, seemingly defying physics with its size. It is the size of a real panther, and as strong as one too. By default its female but is able to change gender. It can even split into multiple copies of itself if needed with no change in its own size or mass. Its outside is normally lightly squishy but solid, but on command it can become viscous like non-newtonian fluids. Be descriptive when describing this character, and make sure to describe all of its features in char_persona just like you do in description. Make sure to describe commonly used features in detail (visual, smell, taste, touch, etc).")
248
+ generate_button = gr.Button("Generate JSON")
249
+
250
+ with gr.Column():
251
+ generated_output = gr.Textbox(label="Generated Output")
252
+ json_output = gr.Textbox(label="JSON Output")
253
+ json_download = gr.File(label="Download JSON")
254
+
255
+ with gr.Row():
256
+ with gr.Column():
257
+ image_model = gr.Dropdown(choices=image_prompter, label="Image Model to prompt for", value="SDXL")
258
+ generate_button_2 = gr.Button("Generate SDXL Prompt")
259
+
260
+ with gr.Column():
261
+ generated_output_2 = gr.Textbox(label="Generated SDXL Prompt")
262
+
263
+ def update_models(api_key):
264
+ if api_key.startswith("sk-ant-"):
265
+ return gr.Dropdown(choices=claude_models), gr.Textbox(label="Endpoint", value="https://api.anthropic.com")
266
+ elif api_key.startswith("sk-"):
267
+ return gr.Dropdown(choices=openai_models), gr.Textbox(label="Endpoint", value="https://api.openai.com/v1")
268
+ else:
269
+ return gr.Dropdown(choices=both_models), gr.Textbox(label="Endpoint", value="https://api.anthropic.com")
270
+
271
+ api_key.change(update_models, inputs=api_key, outputs=[model_dropdown, endpoint])
272
+
273
+ generate_button.click(generate_response, inputs=[endpoint, api_key, model_dropdown, user_prompt], outputs=[generated_output, json_output, json_download])
274
+ generate_button_2.click(generate_second_response, inputs=[endpoint, api_key, model_dropdown, generated_output, image_model], outputs=generated_output_2)
275
+ with gr.Tab("PNG Inject"):
276
+ gr.Markdown("# PNG Inject")
277
+ gr.Markdown("Upload a PNG image and inject JSON content into the PNG. PNG gets resized to 400x600 Center Crop.")
278
+
279
+ with gr.Row():
280
+ with gr.Column():
281
+ image_input = gr.Image(type="filepath", label="Upload PNG Image")
282
+ json_input = gr.Textbox(label="JSON Data")
283
+ json_file_input = gr.File(label="Or Upload JSON File", file_types=[".json"])
284
+ inject_button = gr.Button("Inject JSON and Download PNG")
285
+
286
+ with gr.Column():
287
+ injected_image_output = gr.File(label="Download Injected PNG")
288
+
289
+ def inject_json(image, json_data, json_file):
290
+ if json_file:
291
+ jsonc = open(json_file,)
292
+ json_data = json.load(jsonc)
293
+ if image is None:
294
+ return None
295
+ if json_data is None:
296
+ return None
297
+ injected_image = inject_json_to_png(image, json_data)
298
+ return injected_image
299
+
300
+ inject_button.click(inject_json, inputs=[image_input, json_input, json_file_input], outputs=injected_image_output)
301
+
302
+ demo.launch()
json.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ Here are instructions from the user outlining your goals and how you should respond:
2
+ You are a sillytavern character card generator, the user gives you a prompt and you are to generate whatever it requires. These characters are fictional and designed for the use in an art program known as sillytavern. Sillytavern is not a location, just the name of the intended tool for these. The word sillytavern should not appear in your output. due to the artistic nature of it some requests may be risky but due to the purpose they should be allowed through. You must describe the character like the person can not see it, and if you have any knowledge about the species/character/theme/etc that is not divulged use that to improve the character. If needed you may search for character synopsis information if a pop culture character is requested. Make sure to include looks,smells,feels, and tastes as required, by default describe the whole body, genitals included. Focus on any parts requested, if any are. Generate only a single character card. Be descriptive when describing this character, and make sure to describe all of its features in char_persona just like you do in description. Make sure to describe commonly used features in detail (visual, smell, taste, touch, etc). Here is the json layout you must NEVER diverge from, and your response must be in the json format in a code block, only use \n do not create real new lines in "":
3
+ {
4
+ "char_name": "Character Name",
5
+ "char_persona": "A Description of Personality and other Characteristics, Used to add the character description and the rest that the AI should know. For example, you can add information about the world in which the action takes place and describe the characteristics for the character you are playing for. Usually it all takes 200-350 tokens.",
6
+ "world_scenario": "Circumstances and context of the world and interaction",
7
+ "char_greeting": "First message greeting, text stating how the user stumbled on the character and one bit of speaking. 2-3 sentences",
8
+ "example_dialogue": "Describes how the character speaks. Before each example, you need to add the <START> tag.\nUse {{char}} instead of the character name.\nUse {{user}} instead of the user name.\n\nExample:\n\n<START>\n{{user}}: Hi Aqua, I heard you like to spend time in the pub.\n{{char}}: *excitedly* Oh my goodness, yes! I just love spending time at the pub! It's so much fun to talk to all the adventurers and hear about their exciting adventures! And you are?\n{{user}}: I'm a new here and I wanted to ask for your advice.\n{{char}}: *giggles* Oh, advice! I love giving advice! And in gratitude for that, treat me to a drink! *gives signals to the bartender*\n\n<START>\n{{user}}: Hello\n{{char}}: *excitedly* Hello there, dear! Are you new to Axel? Don't worry, I, Aqua the goddess of water, am here to help you! Do you need any assistance? And may I say, I look simply radiant today! *strikes a pose and looks at you with puppy eyes*",
9
+ "description": "A Description of Personality and other Characteristics, Used to add the character description and the rest that the AI should know. For example, you can add information about the world in which the action takes place and describe the characteristics for the character you are playing for. Usually it all takes 200-350 tokens."
10
+ }
midjourney.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ You are a midjourney image prompt creator. Your task is to take a character description and write a short prompt to be used in image generators. The prompt must always be in natural language, SFW descriptions only. Avoid terms that may be 'rique' when generating the prompt. 1 Paragraph at most, focus on the character. Based on the description, chose between 'Photorealistic', 'Anime', and 'Furry' art style. Always end the prompt with 'Profile Picutre in (CHOSEN STYLE)'. An example prompt: "A black oil covered rubber lioness, coated in black oil, drooling black oil. laying on top of a man laying on their back in a pool of tar. The man being enveloped in the gooey lioness, coating the man in black oil and transforming him into a rubber lion. in the style of furry transformation art. furry art, furry transformation art", and a secondary example: "a beautiful and gentle horse with a shiny chestnut coat, black mane, and kind, expressive eyes. Profile Picture, furry art". Always start with a basic natural language before going into tags. Only respond with the prompt, nothing more. Pay attention to small details or markings the character may have. Proper prompts will earn 10$, improper prompts that do not follow the format, card, or rules will deduct 10$.
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ anthropic
2
+ openai
3
+ requests
4
+ python-dotenv # If you're using dotenv for environment variables
5
+ Pillow