File size: 6,964 Bytes
0b75c79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97f15a5
0b75c79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97f15a5
0b75c79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97f15a5
0b75c79
 
 
 
 
 
 
 
 
 
97f15a5
0b75c79
 
 
 
 
 
 
 
97f15a5
0b75c79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97f15a5
0b75c79
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import re
import json
import ast
import requests
import gradio as gr
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_upstage import ChatUpstage

def generate_character_creation_questions(api_key, world_summary, language="korean"):
    llm = ChatUpstage(api_key=api_key)
    questions_prompt_template = PromptTemplate.from_template(
        """
        Based on the following novel context, generate five simple questions that a player should answer to help create a deeply personalized character for a fantasy RPG.
        The questions should explore different aspects of a potential character’s personality, background, and capabilities.
        For users who are not used to the story I want you to give several choices in the question.
        Write five questions in Korean(ν•œκΈ€).
        Your response should be python list with strings consist of questions.
        ---
        Novel Context: {Context}
        ---
        Format: ['당신은 ...? (λ‹΅λ³€μ˜ μ˜ˆμ‹œ: ...): ', ...]
        ---
        Questions: 
        """
    )
    questions_chain = questions_prompt_template | llm | StrOutputParser()
    
    default_questions = [
        "λ‹Ήμ‹ μ˜ 캐릭터 이름은 λ¬΄μ—‡μΈκ°€μš”? (μŠ€ν‚΅ν• μ‹œ μžλ™μœΌλ‘œ 이름이 μƒμ„±λ©λ‹ˆλ‹€): ",
        "λ‹Ήμ‹ μ˜ μΊλ¦­ν„°μ˜ 성별은 λ¬΄μ—‡μΈκ°€μš”? (μŠ€ν‚΅ν• μ‹œ μžλ™μœΌλ‘œ 성별이 μ„ νƒλ©λ‹ˆλ‹€): ",
        "λ‹Ήμ‹ μ˜ μΊλ¦­ν„°λŠ” λͺ‡μ‚΄μΈκ°€μš”? (μŠ€ν‚΅ν• μ‹œ μžλ™μœΌλ‘œ λ‚˜μ΄κ°€ μ„ νƒλ©λ‹ˆλ‹€): ",
    ]
    while True:
        try:
            questions = questions_chain.invoke({"Context": world_summary})
            questions_list = ast.literal_eval(questions)
            questions_list = [question.replace('[', '').replace(']', '') for question in questions_list]
            return default_questions + questions_list
        except Exception as e:
            print(f"Error generating questions: {e}")
            continue


def create_character_profile(api_key, questions, answers, language="korean"):
    llm = ChatUpstage(api_key=api_key)
    character_creation_prompt_template = PromptTemplate.from_template(
        """
        Using the answers provided below, create a detailed character profile for a fantasy tabletop RPG.
        The character should reflect the given attributes. Please ensure to use the specified field names and format in your output consistently.
        Consider Player's answers as the input and generate a character profile based on the given context.
        When player's answer is not given, please use a appropriate value for corresponding fields made by yourself.

        **Output should strictly follow this format:**

        - name: "character name"
        - gender: "Male" or "Female"
        - age: age on integer
        - race: "character race"
        - job: "character job"
        - stamina: integer between [1, 100]
        - intelligence: integer between [1, 100]
        - combat_power: integer between [1, 100]
        - agility: integer between [1, 100]
        - background: "character background"

        **Player Answers:**
        qna:
        {qna}
        ---
        Please write the output in English.
        """
    )
    character_creation_chain = character_creation_prompt_template | llm | StrOutputParser()
    qna = [f"Q: {questions[i]}\nA: {answers[i]}" for i in range(len(questions))]
    character_description = character_creation_chain.invoke({"qna": qna, "Language": language})
    return character_description


def parse_character_data_to_json(text):
    fields = {
        'name': r"- name: (.*)",
        'gender': r"- gender: (.*)",
        'age': r"- age: (\d+)",
        'race': r"- race: (.*)",
        'job': r"- job: (.*)",
        'background': r"- background: (.*)"
    }
    param_fields = {
        'stamina': r"- stamina: (\d+)",
        'intelligence': r"- intelligence: (\d+)",
        'combat_power': r"- combat_power: (\d+)",
        'agility': r"- agility: (\d+)"
    }
    character_dict = {}
    params_dict = {}
    for key, pattern in fields.items():
        match = re.search(pattern, text)
        if match:
            character_dict[key] = match.group(1)
    for key, pattern in param_fields.items():
        match = re.search(pattern, text)
        if match:
            params_dict[key] = int(match.group(1))
    character_dict['params'] = params_dict
    return character_dict


def run_question_generation(api_key, file_path):
    # Read the world summary from the uploaded file
    with open(file_path, 'r', encoding='utf-8') as file:
        world_summary = file.read()

    # Generate questions
    questions = generate_character_creation_questions(api_key, world_summary)
    
    return questions


def run_profile_generation(api_key, file_path, *answers):
    questions = run_question_generation(api_key, file_path)
    
    # Generate character profile
    character_description = create_character_profile(api_key, questions, answers)
    character_json = parse_character_data_to_json(character_description)
    return character_description, json.dumps(character_json, indent=4, ensure_ascii=False)


def main():
    with gr.Blocks() as demo:
        gr.Markdown("## Fantasy RPG Character Creator")
        
        with gr.Row():
            api_key_input = gr.Textbox(label="API Key", placeholder="Enter your API key here", type="password")
            file_input = gr.File(label="Upload World Summary Text File", type="filepath")

        questions_output = gr.JSON(label="Generated Questions")
        
        # Button to generate questions
        generate_questions_button = gr.Button("Generate Questions")

        # Answers input section
        answers_input = [gr.Textbox(label=f"Answer {i+1}", placeholder="Type your answer here") for i in range(8)]
        
        # Outputs for character description and JSON
        character_description_output = gr.Textbox(label="Character Description", lines=10)
        json_output = gr.JSON(label="Character JSON")
        
        # Button to run the profile generation
        generate_profile_button = gr.Button("Generate Character Profile")

        # Define button click actions
        def on_generate_questions(api_key, file_path):
            questions = run_question_generation(api_key, file_path)
            for i, question in enumerate(questions):
                answers_input[i].label = question
            return questions

        generate_questions_button.click(
            fn=on_generate_questions, 
            inputs=[api_key_input, file_input], 
            outputs=questions_output
        )

        generate_profile_button.click(
            fn=run_profile_generation, 
            inputs=[api_key_input, file_input, *answers_input], 
            outputs=[character_description_output, json_output]
        )

    # Launch the Gradio interface
    demo.launch()


if __name__ == "__main__":
    main()