Spaces:
Running
Running
import os | |
import json | |
import re | |
import gradio as gr | |
from openai import OpenAI | |
############################################################################## | |
# 1. 读取外部文件 | |
############################################################################## | |
try: | |
with open("furry_species.json", "r", encoding="utf-8") as ff: | |
FURRY_DATA = json.load(ff) | |
except: | |
FURRY_DATA = {} | |
try: | |
with open("gender_rules.json", "r", encoding="utf-8") as gf: | |
GENDER_RULES = json.load(gf) | |
except: | |
GENDER_RULES = {} | |
try: | |
with open("transform_rules.json", "r", encoding="utf-8") as tf: | |
TRANSFORM_DICT = json.load(tf) | |
except: | |
TRANSFORM_DICT = {} | |
############################################################################## | |
# 2. 多级菜单函数 | |
############################################################################## | |
def get_top_categories(furry_data): | |
return sorted(list(furry_data.keys())) | |
def get_sub_categories(furry_data, top_category): | |
if top_category in furry_data: | |
return sorted(list(furry_data[top_category].keys())) | |
return [] | |
def get_species_list(furry_data, top_category, sub_category): | |
if top_category in furry_data and sub_category in furry_data[top_category]: | |
return sorted(furry_data[top_category][sub_category]) | |
return [] | |
############################################################################## | |
# 3. 合并规则文本 | |
############################################################################## | |
def merge_transform_rules_into_prompt(rules_json): | |
if not rules_json: | |
return "(No transform rules loaded)" | |
gt = rules_json.get("gender_transform", {}) | |
sp = rules_json.get("shared_preferences", {}) | |
td = rules_json.get("table_details", {}) | |
text_parts = [] | |
text_parts.append("==== GENDER TRANSFORM RULES ====") | |
text_parts.append(str(gt)) | |
text_parts.append("==== SHARED PREFERENCES ====") | |
text_parts.append(str(sp)) | |
text_parts.append("==== TABLE DETAILS (PRO ACTIONS) ====") | |
text_parts.append(str(td)) | |
return "\n".join(text_parts) | |
RULES_TEXT_FULL = merge_transform_rules_into_prompt(TRANSFORM_DICT) | |
############################################################################## | |
# 4. 强制替换:根据 override_conflicting_descriptors | |
############################################################################## | |
transform_map = { | |
"Trans_to_Male": "female_to_male", | |
"Trans_to_Female": "male_to_female", | |
"Trans_to_Mannequin": "any_to_genderless", | |
"Trans_to_Intersex": "any_to_intersex", | |
"Trans_to_Furry": "trans_to_furry" | |
} | |
def forced_replace(prompt, direction): | |
if not TRANSFORM_DICT: | |
return prompt | |
override_section = TRANSFORM_DICT.get("override_conflicting_descriptors", {}) | |
replacements = override_section.get(direction, {}) | |
if not replacements: | |
return prompt | |
for old, new in replacements.items(): | |
pattern = r"(?i)\b" + re.escape(old) + r"\b" | |
prompt = re.sub(pattern, new, prompt) | |
# 针对复杂句子的补充替换 | |
if direction == "female_to_male": | |
prompt = re.sub(r"\bshe\b", "he", prompt, flags=re.IGNORECASE) | |
prompt = re.sub(r"\bher\b", "his", prompt, flags=re.IGNORECASE) | |
prompt = re.sub(r"\bherself\b", "himself", prompt, flags=re.IGNORECASE) | |
return prompt | |
############################################################################## | |
# 5. 核心 GPT/DeepSeek 调用 | |
############################################################################## | |
def generate_transformed_output(prompt, gender_option, top_cat, sub_cat, species_item, api_mode, api_key): | |
if not api_key: | |
return "Error: No API Key provided." | |
if api_mode == "GPT": | |
base_url = None | |
model_name = "gpt-4o" | |
else: | |
base_url = "https://api.deepseek.com" | |
model_name = "deepseek-chat" | |
client = OpenAI(api_key=api_key) | |
if base_url: | |
client.base_url = base_url | |
if gender_option == "Trans_to_Furry": | |
furry_path = f"{top_cat} > {sub_cat} > {species_item}" if (top_cat and sub_cat and species_item) else "unknown" | |
extra_line = f"\nFurry chosen: {furry_path}\n" | |
else: | |
extra_line = "" | |
gender_specific_rule = "" | |
if gender_option == "Trans_to_Male": | |
gender_specific_rule = GENDER_RULES.get("male", "") | |
elif gender_option == "Trans_to_Female": | |
gender_specific_rule = GENDER_RULES.get("female", "") | |
elif gender_option == "Trans_to_Mannequin": | |
gender_specific_rule = GENDER_RULES.get("genderless", "") | |
elif gender_option == "Trans_to_Intersex": | |
gender_specific_rule = GENDER_RULES.get("intersex", "") | |
system_prompt = f""" | |
You are a creative assistant that transforms the user's base prompt | |
to reflect correct gender/furry/genderless/intersex transformations. Follow these references: | |
1) Detailed Transform Rules (transform_rules.json): | |
{RULES_TEXT_FULL} | |
2) Additional short gender rules (gender_rules.json): | |
{gender_specific_rule} | |
{extra_line} | |
Instructions: | |
- Remove any female-specific terms if the target is male. | |
- Ensure all gender-specific terms are replaced correctly based on the rules. | |
- Original prompt tags: {prompt} | |
- Convert them into NEW combined tags, removing or replacing conflicting ones. | |
- Only output two parts: | |
1) One line of final tags in parentheses, e.g. (male, short hair, dynamic pose, ...) | |
2) A blank line. | |
3) Then 3~6 sentences of imaginative scene description in English. | |
- No extra lines, no headings, no 'gender:' or 'base_prompt:'. | |
- End of instructions. | |
""".strip() | |
try: | |
resp = client.chat.completions.create( | |
model=model_name, | |
messages=[ | |
{"role": "system", "content": system_prompt}, | |
{"role": "user", "content": "Generate final tags and description now."} | |
], | |
) | |
return resp.choices[0].message.content.strip() | |
except Exception as e: | |
return f"{api_mode} generation failed. Error: {e}" | |
############################################################################## | |
# 6. 翻译函数 | |
############################################################################## | |
def translate_text(content, lang, api_mode, api_key): | |
if not api_key: | |
return "Error: No API Key provided." | |
if not content.strip(): | |
return "" | |
if api_mode == "GPT": | |
base_url = None | |
model_name = "gpt-3.5-turbo" | |
else: | |
base_url = "https://api.deepseek.com" | |
model_name = "deepseek-chat" | |
client = OpenAI(api_key=api_key) | |
if base_url: | |
client.base_url = base_url | |
translate_system_prompt = f""" | |
You are a translator. Translate the following text to {lang}, | |
keeping parentheses line and blank line if present. | |
No extra headings. | |
""".strip() | |
try: | |
resp = client.chat.completions.create( | |
model=model_name, | |
messages=[ | |
{"role": "system", "content": translate_system_prompt}, | |
{"role": "user", "content": content} | |
], | |
) | |
return resp.choices[0].message.content.strip() | |
except Exception as e: | |
return f"{api_mode} translation failed. Error: {e}" | |
############################################################################## | |
# 7. Gradio 界面 | |
############################################################################## | |
def build_interface(): | |
with gr.Blocks() as demo: | |
gr.Markdown("## Prompt Trans-Tool - 提示词物种性别转换器") | |
with gr.Row(): | |
with gr.Column(): | |
api_mode = gr.Radio( | |
label="Select API 选择API厂商 (GPT/DeepSeek)", | |
choices=["GPT", "DeepSeek"], | |
value="GPT" | |
) | |
api_key = gr.Textbox( | |
label="API Key", | |
type="password", | |
placeholder="Input your GPT or DeepSeek Key" | |
) | |
gender_option = gr.Radio( | |
label="Trans-Option 选择转换目标", | |
choices=[ | |
"Trans_to_Male", | |
"Trans_to_Female", | |
"Trans_to_Mannequin", | |
"Trans_to_Intersex", | |
"Trans_to_Furry" | |
], | |
value="Trans_to_Male" | |
) | |
top_cat_dd = gr.Dropdown( | |
label="Furry: Top Category", | |
choices=get_top_categories(FURRY_DATA), | |
value=None, | |
visible=False | |
) | |
sub_cat_dd = gr.Dropdown( | |
label="Furry: Sub Category", | |
choices=[], | |
value=None, | |
visible=False | |
) | |
species_dd = gr.Dropdown( | |
label="Furry: Species", | |
choices=[], | |
value=None, | |
visible=False | |
) | |
def show_furry_options(opt): | |
if opt == "Trans_to_Furry": | |
return (gr.update(visible=True), | |
gr.update(visible=True), | |
gr.update(visible=True)) | |
else: | |
return (gr.update(visible=False), | |
gr.update(visible=False), | |
gr.update(visible=False)) | |
gender_option.change( | |
fn=show_furry_options, | |
inputs=[gender_option], | |
outputs=[top_cat_dd, sub_cat_dd, species_dd] | |
) | |
def on_top_cat_select(selected): | |
subs = get_sub_categories(FURRY_DATA, selected) | |
return gr.update(choices=subs, value=None) | |
top_cat_dd.change( | |
fn=on_top_cat_select, | |
inputs=[top_cat_dd], | |
outputs=[sub_cat_dd] | |
) | |
def on_sub_cat_select(top_c, sub_c): | |
sp = get_species_list(FURRY_DATA, top_c, sub_c) | |
return gr.update(choices=sp, value=None) | |
sub_cat_dd.change( | |
fn=on_sub_cat_select, | |
inputs=[top_cat_dd, sub_cat_dd], | |
outputs=[species_dd] | |
) | |
with gr.Column(): | |
user_prompt = gr.Textbox( | |
label="Original Prompt 原始提示词 (e.g. 1girl, butterfly, solo, ...)", | |
lines=5 | |
) | |
final_output = gr.Textbox( | |
label="Transformed Output 翻译结果 (tags + description)", | |
lines=10 | |
) | |
with gr.Row(): | |
translate_lang = gr.Dropdown( | |
label="Translate to Language 翻译语言", | |
choices=[ | |
"English", "Chinese", "Japanese", "French", "German", | |
"Italian", "Spanish", "Russian", "Dutch", "Persian", "Arabic", "Thai" | |
], | |
value="English" | |
) | |
translated_text = gr.Textbox( | |
label="Translated Result", | |
lines=10 | |
) | |
###################################################################### | |
# 生成 | |
###################################################################### | |
def on_generate(prompt, gender, tc, sc, spc, mode, key, lang): | |
# Step 1: 强制替换用户输入 | |
direction = transform_map.get(gender, None) | |
if direction: | |
prompt = forced_replace(prompt, direction) # 替换必须在生成之前 | |
# Debug 替换后的 Prompt | |
print(f"Debug Prompt After Replacement: {prompt}") | |
# Step 2: 提交到生成器 | |
merged_output = generate_transformed_output(prompt, gender, tc, sc, spc, mode, key) | |
# Debug 生成器输出 | |
print(f"Debug Merged Output: {merged_output}") | |
# Step 3: 翻译生成的结果 | |
translated_output = translate_text(merged_output, lang, mode, key) | |
# Step 4: 返回结果 | |
return merged_output, translated_output | |
user_prompt.submit( | |
fn=on_generate, | |
inputs=[user_prompt, gender_option, top_cat_dd, sub_cat_dd, species_dd, api_mode, api_key, translate_lang], | |
outputs=[final_output, translated_text] | |
) | |
gen_btn = gr.Button("Generate") | |
gen_btn.click( | |
fn=on_generate, | |
inputs=[user_prompt, gender_option, top_cat_dd, sub_cat_dd, species_dd, api_mode, api_key, translate_lang], | |
outputs=[final_output, translated_text] | |
) | |
return demo | |
if __name__ == "__main__": | |
demo = build_interface() | |
demo.launch() |