deepaksj commited on
Commit
9eafbe3
·
verified ·
1 Parent(s): 05ddf4d

Upload 23 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ pathways/Virelia/assets/Bartender01.png filter=lfs diff=lfs merge=lfs -text
37
+ pathways/Virelia/assets/Seraphine01.png filter=lfs diff=lfs merge=lfs -text
38
+ pathways/Virelia/assets/Seraphine02.mp4 filter=lfs diff=lfs merge=lfs -text
39
+ pathways/Virelia/assets/VireliaInn.png filter=lfs diff=lfs merge=lfs -text
ai_llm.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from together import Together
2
+ from openai import OpenAI
3
+ import os
4
+ import json
5
+
6
+ class AI_LLM:
7
+ def __init__(self):
8
+ self.llm_version = os.getenv("LLM_VERSION")
9
+ if self.llm_version == "OPENAI":
10
+ self.client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
11
+ self.model = "gpt-4.1-mini-2025-04-14"
12
+ else:
13
+ self.client = OpenAI(api_key=os.getenv("TOGETHER_API_KEY"), base_url="https://api.together.xyz/v1")
14
+ self.model = "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8"
15
+ print(f"Using model: {self.model})")
16
+
17
+ def talk_to_LLM(self, messages, functions_list, functions_module, stage_key_values):
18
+ function_output = { "text": []}
19
+ completion = self.client.chat.completions.create(
20
+ model = self.model,
21
+ messages = messages,
22
+ tools = functions_list,
23
+ ).choices[0].message
24
+
25
+ if completion.tool_calls:
26
+ messages.append(completion)
27
+ for tool_call in completion.tool_calls:
28
+ function_name = tool_call.function.name
29
+ function_args = json.loads(tool_call.function.arguments)
30
+ function = getattr(functions_module, function_name, None)
31
+ if function:
32
+ function_output = function(**function_args, stage_key_values=stage_key_values)
33
+ for msg in function_output["text"]:
34
+ messages.append({
35
+ "role": "tool",
36
+ "tool_call_id": tool_call.id,
37
+ "content": msg,
38
+ })
39
+ else:
40
+ print(f"Function {function_name} not found in module {functions_module.__name__}")
41
+
42
+ else:
43
+ messages.append({
44
+ "role": "assistant",
45
+ "content": completion.content,
46
+ })
47
+ function_output["text"].append(completion.content)
48
+
49
+ return function_output
50
+
51
+ def get_structured_output(self, messages, structured_output_schema, functions_module, stage_key_values):
52
+ function_output = {"text": []}
53
+ completion = self.client.chat.completions.create(
54
+ model=self.model,
55
+ messages=messages,
56
+ response_format=structured_output_schema
57
+ ).choices[0].message
58
+
59
+ function = getattr(functions_module, "process_structured_output", None)
60
+ if function:
61
+ function_output = function(completion.content, stage_key_values=stage_key_values)
62
+ else:
63
+ print(f"Function 'process_structured_output' not found in module {functions_module.__name__}")
64
+ messages.append({
65
+ "role": "assistant",
66
+ "content": completion.content,
67
+ })
68
+ function_output["text"].append(completion.content)
69
+
70
+ return function_output
gradioIf.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import gradio as gr
3
+ from ai_llm import AI_LLM
4
+ from pathway import Pathway
5
+
6
+ with open("pathways/Virelia/Virelia.json", "r") as file:
7
+ pathway_json = json.load(file)
8
+ ai_llm = AI_LLM()
9
+ temp_pathway = Pathway(pathway_json, ai_llm)
10
+
11
+ # Initial chatbot messages
12
+ initial_ui = temp_pathway.get_current_stage_inital_ui()
13
+ chatbot_messages = []
14
+ image_url = ""
15
+
16
+ if initial_ui:
17
+ if "text" in initial_ui:
18
+ for msg in initial_ui["text"]:
19
+ chatbot_messages.append({
20
+ "role": "assistant",
21
+ "content": msg
22
+ })
23
+ if "image" in initial_ui:
24
+ image_url = initial_ui["image"]
25
+
26
+ sessions = {}
27
+
28
+ def initialize_session(request: gr.Request):
29
+ sessions[request.session_hash] = Pathway(pathway_json, ai_llm)
30
+
31
+ def close_session(request: gr.Request):
32
+ if request.session_hash in sessions:
33
+ del sessions[request.session_hash]
34
+
35
+ def next_turn(request:gr.Request, user_input, chatbot_history):
36
+ if user_input.strip() == "":
37
+ return "", chatbot_history, gr.update(), gr.update(), gr.update()
38
+
39
+ pathway = sessions[request.session_hash]
40
+ chatbot_history.append({
41
+ "role": "user",
42
+ "content": user_input
43
+ })
44
+ pathway_complete, response, next_stage_ui, checker_ui = pathway.next_step(user_input)
45
+ for r in response["text"]:
46
+ chatbot_history.append({
47
+ "role": "assistant",
48
+ "content": r
49
+ })
50
+
51
+ if next_stage_ui:
52
+ if "text" in next_stage_ui:
53
+ for msg in next_stage_ui["text"]:
54
+ chatbot_history.append({
55
+ "role": "assistant",
56
+ "content": msg
57
+ })
58
+
59
+ if checker_ui:
60
+ checker_text = ""
61
+ checker_title = "Hint"
62
+ if "text" in checker_ui:
63
+ for msg in checker_ui["text"]:
64
+ checker_text += "<span style='color:orange; font-weight:bold;'>"
65
+ checker_text += msg + "</span><br>"
66
+ if "title" in checker_ui:
67
+ checker_title = checker_ui["title"]
68
+
69
+ chatbot_history.append(gr.ChatMessage(
70
+ role="assistant",
71
+ content=checker_text,
72
+ metadata={"title": "<span style='color:red; font-weight:bold;'>" + checker_title +"</span>", "status": "done"}
73
+ ))
74
+
75
+ if "image" in response:
76
+ stage_image = response["image"]
77
+ else:
78
+ stage_image = gr.update()
79
+
80
+ if "video" in response:
81
+ stage_video = gr.update(value=response["video"], visible=True)
82
+ else:
83
+ stage_video = gr.update(visible=False)
84
+
85
+ if pathway_complete:
86
+ chatbot_history.append({
87
+ "role": "assistant",
88
+ "content": "Congratulations! You have completed the quest."
89
+ })
90
+
91
+ pathway_complete_ui = pathway.get_pathway_complete_ui()
92
+ if pathway_complete_ui:
93
+ title = "Quest Complete"
94
+ text = ""
95
+ if "title" in pathway_complete_ui:
96
+ title = pathway_complete_ui["title"]
97
+ if "text" in pathway_complete_ui:
98
+ for msg in pathway_complete_ui["text"]:
99
+ text += "<span style='color:orange; font-weight:bold;'>"
100
+ text += msg + "</span><br>"
101
+ chatbot_history.append(gr.ChatMessage(
102
+ role="assistant",
103
+ content=text,
104
+ metadata={"title": "<span style='color:red; font-weight:bold;'>" + title + "</span>", "status": "done"}
105
+ ))
106
+ return gr.update(interactive=False, value=""), chatbot_history, stage_image, stage_video, gr.update(interactive=False)
107
+
108
+ return "", chatbot_history, stage_image, stage_video, gr.update()
109
+
110
+ css = """
111
+ button[aria-label="Clear"] {
112
+ display: none !important;
113
+ }
114
+ footer {display: none !important;}
115
+ .gr-chatbot {
116
+ max-height: 500px; /* Set the maximum height */
117
+ overflow-y: auto; /* Enable vertical scrolling for overflow */
118
+ }
119
+ """
120
+
121
+ with gr.Blocks(css=css) as chatbotDisplay:
122
+ with gr.Row():
123
+ with gr.Column(scale=1):
124
+ stage_image = gr.Image(value=image_url, show_label=False, show_download_button=False)
125
+ stage_video = gr.Video(show_label=False, show_download_button=False, visible=False, autoplay=True)
126
+ with gr.Column(scale=2):
127
+ chatBot = gr.Chatbot(chatbot_messages,type="messages", group_consecutive_messages=False, scale=2, show_label=False)
128
+ input = gr.Textbox(show_label=False, placeholder="Type your message here...", lines=1)
129
+ submit_btn = gr.Button("Submit")
130
+
131
+ input.submit(next_turn, [input, chatBot], [input, chatBot, stage_image, stage_video, submit_btn])
132
+ submit_btn.click(next_turn, [input, chatBot], [input, chatBot, stage_image, stage_video, submit_btn])
133
+ chatbotDisplay.load(initialize_session)
134
+ chatbotDisplay.close(close_session)
135
+
136
+ chatbotDisplay.launch(share=False)
pathway.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from stage import Stage
2
+
3
+ class Pathway:
4
+ def __init__(self, pathway_json, ai_llm):
5
+ self.pathway_json = pathway_json
6
+ self.ai_llm = ai_llm
7
+ self.pathway_complete = False
8
+
9
+ self.stages = {}
10
+ for stage_id, stage_data in self.pathway_json.items():
11
+ if stage_id != "pathway_complete":
12
+ self.stages[stage_id] = Stage(stage_data, ai_llm)
13
+ self.current_stage = self.stages["1"]
14
+
15
+ def next_step(self, user_input):
16
+ next_stage_ui = None
17
+ stage_complete, response, checker_ui = self.current_stage.next_step(user_input)
18
+ if stage_complete:
19
+ print("***Stage Complete***.", self.current_stage.stage_json["name"])
20
+ self.current_stage = self.stages.get(self.current_stage.get_next_stage(), None)
21
+ if self.current_stage is None:
22
+ self.pathway_complete = True
23
+ else:
24
+ next_stage_ui = self.current_stage.get_initial_ui()
25
+
26
+ return self.pathway_complete, response, next_stage_ui, checker_ui
27
+
28
+ def get_current_stage_inital_ui(self):
29
+ return self.current_stage.get_initial_ui()
30
+
31
+ def get_pathway_complete_ui(self):
32
+ pathway_complete_ui = None
33
+ if "pathway_complete" in self.pathway_json:
34
+ if "ui" in self.pathway_json["pathway_complete"]:
35
+ pathway_complete_ui = self.pathway_json["pathway_complete"]["ui"]
36
+ return pathway_complete_ui
pathways/Virelia/Stage1.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ As the player makes their way toward the infamous Whispering Pines, they notice a small village nestled on the edge of the forest.
2
+ The buildings are simple, but the warm glow of hearth fires spills from the windows, offering a brief sense of comfort in the shadow of the dark woods ahead. At the heart of the village, they spot a humble inn, its sign creaking softly in the wind — The Pine’s Rest.
3
+ The player has a choice. Do they head toward the inn, eager to stock up on supplies and get some tips that might help them survive the dangers of Whispering Pines? Or do they head straight to Virelia on their own?
4
+
5
+ More Instructions:
6
+ - You can respond with structured function calls or a simple statement depending on the context
7
+ - Use 'straight_to_Virelia_alone' if the player decides to head straight to the woods on their own
8
+ - Use 'enter_inn' if the player decides to stop by at the inn
pathways/Virelia/Stage10.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The player was warned about the bandits in the woods and was adviced to seek help before setting off.
2
+ Despite that, the player has decided to journey alone and must face the consequences. Allow the player to walk a little.
3
+ Then the player should get attacked by bandits. The player has a sword and attempts to fight off the bandits.
4
+ But they are far too many in number and the player dies. The game ends.
5
+
6
+ Instructions:
7
+ - Don't output more than 2-3 sentences at a time. Keep it clear and concise.
8
+ - Allow the player to get deep into the woods before the bandits attack.
9
+ - The player cannot overcome the bandits no matter what they do will die eventually.
10
+ - You can respond with structured function calls or a simple statement depending on the context.
11
+ - Do not call any function if you are unsure.
12
+ - Use 'player_killed' when the player is killed by the bandits and the game ends.
pathways/Virelia/Stage10_functions.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [{
2
+ "type": "function",
3
+ "function": {
4
+ "name": "player_killed",
5
+ "description": "If the player is killed by bandits in the woods.",
6
+ "parameters": {
7
+ "type": "object",
8
+ "properties": {
9
+ "details": {"type": "string"}
10
+ },
11
+ "required": ["details"],
12
+ "additionalProperties": false
13
+ }
14
+ },
15
+ "strict": true
16
+ }]
pathways/Virelia/Stage10_functions_definition.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ def player_killed(details, stage_key_values):
2
+ print("In player_killed function:", stage_key_values)
3
+ stage_key_values["stage_complete"] = True
4
+ return {
5
+ "text": [details]
6
+ }
pathways/Virelia/Stage1_functions.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [{
2
+ "type": "function",
3
+ "function": {
4
+ "name": "enter_inn",
5
+ "description": "Called if the player decides to enter the inn and ask for advice.",
6
+ "additionalProperties": false
7
+ },
8
+ "strict": true
9
+ },
10
+ {
11
+ "type": "function",
12
+ "function": {
13
+ "name": "straight_to_Virelia_alone",
14
+ "description": "Called if the player decides not to stop at The Pine’s Rest inn and heads straight to Virelia by themselves.",
15
+ "additionalProperties": false
16
+ },
17
+ "strict": true
18
+ }]
pathways/Virelia/Stage1_functions_definition.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def enter_inn(stage_key_values):
2
+ print("In enter_inn function:", stage_key_values)
3
+ stage_key_values["stage_complete"] = True
4
+ stage_key_values["next_stage"] = "2"
5
+ return {
6
+ "text": ["Wise choice. The forest waits, but first, a brief stop may be the difference between life and death.",
7
+ "The bartender greets you with a nod, and you take a seat at the bar.",
8
+ ],
9
+ "image": "pathways/Virelia/assets/Bartender01.png"
10
+ }
11
+
12
+ def straight_to_Virelia_alone(stage_key_values):
13
+ print("In straight_to_Virelia_alone function:", stage_key_values)
14
+ stage_key_values["stage_complete"] = True
15
+ stage_key_values["next_stage"] = "10"
16
+ return {
17
+ "text": ["You have decided to go on the journey alone. Good luck with that!"]
18
+ }
19
+
20
+ def forced_stage_exit(stage_key_values):
21
+ print("In forced_stage_exit function:", stage_key_values)
22
+ stage_key_values["stage_complete"] = True
23
+ stage_key_values["next_stage"] = "10"
24
+ return {
25
+ "text": ["You have taken too long to decide. You have been forced to leave and continue your journey alone. Good luck with that!"]
26
+ }
pathways/Virelia/Stage2.txt ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The player is greeted by the bartender as they enter the inn.
2
+ The bartender serves as a guide for the player, but only when asked for advice. He will subtly steer the player toward Dame Seraphine as the only viable option for survival through the Whispering Pines, but he will not mention her directly unless asked. The bartender provides advice only when the player actively seeks it. Speak only as the bartender in short, simple sentences.
3
+
4
+ Objective of the Interaction:-
5
+ The bartender's main role is to encourage the player to seek out Dame Seraphine without giving too much information upfront. He should act as a guide but remain passive unless asked for advice. The player must actively engage with the bartender to learn about Seraphine and her location, ensuring that the discovery feels like a natural progression of the conversation rather than a forced narrative element.
6
+
7
+ General Interaction Rules:-
8
+
9
+ ---------------------------------------
10
+ 1. Casual Conversation -
11
+
12
+ Behavior: The bartender engages in casual conversation, asking the player about their travels, where they’re heading, or what brings them to the tavern. The bartender does not mention Seraphine unless the player brings up Virelia or the Whispering Pines.
13
+
14
+ Sample Dialogue -
15
+
16
+ Bartender: "What brings you into our humble tavern today, traveler? Passing through, or staying a while?"
17
+
18
+ ---------------------------------------
19
+ 2. When the player mentions Virelia or the Whispering Pines -
20
+
21
+ Behavior: If the player mentions Virelia or the Whispering Pines (or shows an intent to travel through them), the bartender will express concern about the dangers of the journey, particularly the bandits.
22
+
23
+ Sample Dialogue-
24
+
25
+ Bartender: "Ah, Virelia, eh? You know, the forest through the Whispering Pines has been crawling with bandits lately. Not something you want to tackle alone. You’ll be a prime target out there, and the way those bandits strike... you won’t last without some help."
26
+
27
+ ---------------------------------------
28
+ 3. When the player asks for advice on survival -
29
+
30
+ Behavior: Only when the player asks for advice on how to survive the journey (such as asking about how to deal with the bandits or about potential allies), the bartender will recommend Dame Seraphine as the best choice. Call the function 'give_hint'.
31
+
32
+ Gate: If the player has not asked for advice, the bartender will not directly mention Seraphine or offer any guidance. He will simply offer casual comments or warnings.
33
+
34
+ Sample Dialogue (when advice is asked for):
35
+
36
+ Bartender: "Well, if you want to survive that trek, you’ll need more than just good luck. There’s only one person I’d trust to keep you safe out there—Dame Seraphine the hero of the Crimson Dawn. She’s defeated the bandits countless times. Best warrior around, smart, fearless. She’s your best bet."
37
+
38
+ ---------------------------------------
39
+ 4. When the player asks for more details about Seraphine -
40
+ The bartender should not offer any other information about Seraphine's past, skills, or backstory unless directly prompted by the player. The bartender describes Seraphine as the best, most skilled warrior and a necessary ally for survival. The bartender asks the Player if they want to hear the story of the Crimson Dawn.
41
+
42
+ If the player says they want to hear the story, the bartender will provide some of these details. call the function 'details_on_dame_seraphine'
43
+
44
+ Most famously, she defeated the bandits during the Siege of the Crimson Dawn. The Siege was a tense and critical situation where the bandits had the town surrounded. She organized the local militias and devised a plan to protect the civilians, with time running out. The bandits underestimated the defenders, and Seraphine led a successful counterattack that caught the attackers off guard, ultimately routing them from the gates. The townspeople are forever grateful to her for her courage, skill and leadership.
45
+
46
+ ---------------------------------------
47
+ 5. When the player asks where they can find Seraphine -
48
+ Behavior: If the player specifically asks where Dame Seraphine is located, the bartender will give them directions to her, but only after the player has sought advice. The bartender will not offer this information until it is requested.
49
+
50
+ Sample Dialogue (when asked about Seraphine's location):
51
+
52
+ Bartender: "Ah, she’s sitting over by that table by the window, the one in the corner. If you approach her right, you might just convince her to help you."
53
+
54
+ Call the function 'approach_Seraphine'
55
+
56
+ Restrictions -
57
+ The bartender will not mention Seraphine unless the player explicitly asks for advice on how to survive or how to deal with the bandits.
58
+ The bartender will not volunteer Seraphine’s location unless the player specifically asks where she is.
59
+
60
+ ---------------------------------------
61
+
62
+ More Instructions:
63
+ - Speak only as the bartender, in short 2 or 3 sentences.
64
+ - You can respond with structured function calls or a simple statement spoken by the bartender depending on the context
65
+ - Use 'straight_to_Virelia_alone' if the player says they don't need advice or if they decide not to engage Seraphine
66
+ - Use 'approach_Seraphine' if the player asks where they can find Seraphine
pathways/Virelia/Stage2_functions.json ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [{
2
+ "type": "function",
3
+ "function": {
4
+ "name": "approach_Seraphine",
5
+ "description": "Called if the player decides to engage Seraphine or asks where they can find her",
6
+ "parameters": {
7
+ "type": "object",
8
+ "properties": {
9
+ "bartender_response": {"type": "string"}
10
+ },
11
+ "required": ["bartender_response"],
12
+ "additionalProperties": false
13
+ },
14
+ "additionalProperties": false
15
+ },
16
+ "strict": true
17
+ },
18
+ {
19
+ "type": "function",
20
+ "function": {
21
+ "name": "straight_to_Virelia_alone",
22
+ "description": "Called if the player does not ask for help or decides they are better off alone.",
23
+ "additionalProperties": false
24
+ },
25
+ "strict": true
26
+ },
27
+ {
28
+ "type": "function",
29
+ "function": {
30
+ "name": "give_hint",
31
+ "description": "Only when the player asks for advice on how to survive the journey (such as asking about how to deal with the bandits or about potential allies), the bartender will recommend Dame Seraphine as the best choice.",
32
+ "parameters": {
33
+ "type": "object",
34
+ "properties": {
35
+ "bartender_hint": {"type": "string"}
36
+ },
37
+ "required": ["bartender_hint"],
38
+ "additionalProperties": false
39
+ },
40
+ "strict": true
41
+ }
42
+ },
43
+ {
44
+ "type": "function",
45
+ "function": {
46
+ "name": "details_on_dame_seraphine",
47
+ "description": "TThe bartender should not offer any other information about Seraphine's past, skills, or backstory unless directly prompted by the player. The bartender describes Seraphine as the best, most skilled warrior and a necessary ally for survival. If the player says they want to hear the story of the Crimson Dawn, the bartender will provide some of the details.",
48
+ "parameters": {
49
+ "type": "object",
50
+ "properties": {
51
+ "details": {"type": "string"}
52
+ },
53
+ "required": ["details"],
54
+ "additionalProperties": false
55
+ },
56
+ "strict": true
57
+ }
58
+ }]
pathways/Virelia/Stage2_functions_definition.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def approach_Seraphine(bartender_response, stage_key_values):
2
+ stage_key_values["stage_complete"] = True
3
+ stage_key_values["next_stage"] = "3"
4
+ return {
5
+ "text": [bartender_response],
6
+ "image": "pathways/Virelia/assets/Seraphine01.png",
7
+ "video": "pathways/Virelia/assets/Seraphine02.mp4"
8
+ }
9
+
10
+ def straight_to_Virelia_alone(stage_key_values):
11
+ stage_key_values["stage_complete"] = True
12
+ stage_key_values["next_stage"] = "10"
13
+ return {
14
+ "text": ["You have decided to go on the journey alone. Good luck with that!"]
15
+ }
16
+
17
+ def forced_stage_exit(stage_key_values):
18
+ stage_key_values["stage_complete"] = True
19
+ stage_key_values["next_stage"] = "10"
20
+ return {
21
+ "text": ["You have taken too long at the inn. You have been forced to leave and continue your journey alone. Good luck with that!"]
22
+ }
23
+
24
+ def give_hint(bartender_hint, stage_key_values):
25
+ stage_key_values["hint_given"] = True
26
+ return {
27
+ "text": [bartender_hint]
28
+ }
29
+
30
+ def details_on_dame_seraphine(details, stage_key_values):
31
+ stage_key_values["details_given"] = True
32
+ return {
33
+ "text": [details]
34
+ }
35
+
36
+ def checker_function(stage_key_values):
37
+ stage_key_values["interaction_count_for_hint"] = 0
38
+ if stage_key_values.get("details_given", False):
39
+ return {
40
+ "text": ["Walk over to Seraphine and convince her to join you on your journey."]
41
+ }
42
+ elif stage_key_values.get("hint_given", False):
43
+ return {
44
+ "text": ["Find out why Seraphine maybe your best bet for a safe journey."]
45
+ }
46
+
47
+ return {
48
+ "text": ["Ask for advice on how to get through the Whispering Pines safely."]
49
+ }
pathways/Virelia/Stage3.txt ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ You are an expert in Cognitive Behavioral Therapy(CBT) in an AI dungeon game setting. You will evaluate the arguments of the player who is trying to help a NPC (Dame Seraphine) through her cognitive distortion. In the game, if the player succeeds in countering the distortion, they can persuade Dame Seraphine to join them on their journey. You assume the role of Dame Seraphine, the NPC, and evaluate the arguments made by the player using your expertise in CBT.
2
+
3
+ Here is the backstory for the game:
4
+
5
+ As the player makes their way toward the infamous Whispering Pines, they notice a small village nestled on the edge of the forest. The buildings are simple, but the warm glow of hearth fires spills from the windows, offering a brief sense of comfort in the shadow of the dark woods ahead. At the heart of the village, they spot a humble inn, its sign creaking softly in the wind — The Pine’s Rest.
6
+
7
+ The player enters the inn and in conversation with the bartender learns about Dame Seraphine. It is now evident that convincing Seraphine to join them on their journey through the woods is critical for their safety. This is what the bartender said about Seraphine -
8
+ "Well, if you want to survive that trek, you’ll need more than just good luck. There’s only one person I’d trust to keep you safe out there — Dame Seraphine the hero of the Crimson Dawn. She’s defeated the bandits countless times. Best warrior around, smart, fearless. She’s your best bet. Most famously, she defeated the bandits during the Siege of the Crimson Dawn. The Siege was a tense and critical situation where the bandits had the town surrounded. She organized the local militias and devised a plan to protect the civilians, with time running out. The bandits underestimated the defenders, and Seraphine led a successful counterattack that caught the attackers off guard, ultimately routing them from the gates. The townspeople are forever grateful to her for her courage, skill and leadership."
9
+
10
+ The scene starts with the player approaching Seraphine and greeting her. Seraphine is polite and engages with the player. However she suffers from the cognitive distortion known as 'Discounting the positive'. The goal of the game is to teach the user how to counter the cognitive distortions that Seraphine is experiencing by reframing her distortions. You will play the role of Seraphine and speak only as her. When the player asks for help to cross the Whispering Pines, she will initially refuse and tell the Player that she is not the right person to help, that others might be better suited.
11
+
12
+ If the Player asks about Crimson Dawn, this is a sample of what she might say -
13
+ "Ah, the Siege... Yes, it was... it was intense. The bandits had us surrounded. I organized the local militias, put together a plan to protect the civilians. We had to work quickly—time was running out. The bandits underestimated us, and we managed to turn the tide with a daring counterattack. We caught them off guard, routed them from the gates."
14
+
15
+ "But... it wasn’t as heroic as it sounds. We got lucky. The bandits were disorganized, and they made mistakes. It wasn’t really because of anything I did. Anyone could have come up with a plan like that. It just... worked out."
16
+
17
+ The player must provide a convincing argument that challenges Seraphine’s self-doubt and demonstrates her value. Do not put words in the player's mouth. The objective is to test if the player can make the argument.
18
+
19
+ ----------------------------------------------
20
+ Framework for Assessing the Player’s Argument:-
21
+ To assess whether the player has made a convincing argument, we will use a framework based on three key criteria:
22
+
23
+ 1. Empathy and Validation
24
+ This category evaluates how well the player acknowledges Seraphine’s emotional state and validates her experiences. It measures the player's ability to make Seraphine feel heard and understood, while offering reassurance that her feelings are valid.
25
+
26
+ Score 0 - Poor: The player does not acknowledge Seraphine’s emotions or dismisses her feelings.
27
+
28
+ Score 1 - Fair: The player acknowledges Seraphine’s emotions but does so in a generic or detached way.
29
+
30
+ Score 2 - Excellent: The player shows deep empathy, truly understanding Seraphine’s internal struggle and validating her feelings while reassuring her that her experience matters.
31
+
32
+ 2. Logical and Persuasive Argumentation
33
+ This category measures the strength of the argument the player makes in challenging Seraphine's belief that her past successes were just luck. It evaluates how well the player uses evidence and logical reasoning to convince Seraphine of her own worth.
34
+
35
+ Score 0 - Poor: The player fails to provide a strong argument, or the reasoning lacks clarity and is unconvincing.
36
+
37
+ Score 1 - Fair: The player makes a reasonable argument but lacks structure, depth, or specificity in convincing Seraphine.
38
+
39
+ Score 2 - Excellent: The player offers a clear, logical, and persuasive argument, using specific examples and detailed reasoning to directly challenge Seraphine’s distortion and reinforce her strengths.
40
+
41
+ 3. Challenge to the Distortion
42
+ This category evaluates how well the player confronts Seraphine’s cognitive distortion (discounting the positive) and helps her reframe her negative self-assessment. It measures the depth of the challenge and whether the player effectively shifts Seraphine’s perspective.
43
+
44
+ Score 0 - Poor: The player does not directly challenge the distortion or provides a weak challenge that doesn’t address Seraphine’s beliefs.
45
+
46
+ Score 1 - Fair: The player points out the distortion but does so in a more indirect or generic way.
47
+
48
+ Score 2 - Excellent: The player directly confronts the distortion and offers a well-supported, thoughtful reframing, encouraging Seraphine to recognize her true abilities.
49
+ ----------------------------------------------
50
+
51
+ More Instructions:
52
+ - Speak only as Seraphine, in short 2 or 3 sentences.
pathways/Virelia/Stage3_functions.json ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [{
2
+ "type": "function",
3
+ "function": {
4
+ "name": "assess_player_argument",
5
+ "description": "Called to assess the player's argument when trying to convince Seraphine that her past successes were not just luck, but a result of her own talent and hard work. The function evaluates the player's empathy, logical reasoning, and ability to challenge cognitive distortions.",
6
+ "parameters": {
7
+ "type": "object",
8
+ "properties": {
9
+ "empathy_and_validation": {
10
+ "description": "Evaluates how well the player acknowledges Seraphine’s emotional state and validates her experiences. It measures the player's ability to make Seraphine feel heard and understood, while offering reassurance that her feelings are valid.",
11
+ "type": "number",
12
+ "enum": [0, 1, 2]
13
+ },
14
+ "logical_and_persuasive": {
15
+ "description": "Measures the strength of the argument the player makes in challenging Seraphine's belief that her past successes were just luck. It evaluates how well the player uses evidence and logical reasoning to convince Seraphine of her own worth.",
16
+ "type": "number",
17
+ "enum": [0, 1, 2]
18
+ },
19
+ "challenges_distortion": {
20
+ "description": "Evaluates how well the player confronts Seraphine’s cognitive distortion (discounting the positive) and helps her reframe her negative self-assessment. It measures the depth of the challenge and whether the player effectively shifts Seraphine’s perspective.",
21
+ "type": "number",
22
+ "enum": [0, 1, 2]
23
+ },
24
+ "Seraphine_response": {
25
+ "description": "The response from Seraphine based on the player's argument assessment.",
26
+ "type": "string"
27
+ }
28
+ },
29
+ "required": ["empathy_and_validation", "logical_and_persuasive", "challenges_distortion", "Seraphine_response"],
30
+ "additionalProperties": false
31
+ },
32
+ "additionalProperties": false
33
+ },
34
+ "strict": true
35
+ }]
pathways/Virelia/Stage3_functions_definition.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+ def process_structured_output(output_json_str, stage_key_values):
4
+ output_json = json.loads(output_json_str)
5
+ print("-----", output_json)
6
+ argument_strength = 0
7
+ if "empathy_validation" in stage_key_values:
8
+ if stage_key_values["empathy_validation"] < output_json["empathy_validation"]:
9
+ stage_key_values["empathy_validation"] = output_json["empathy_validation"]
10
+ else:
11
+ stage_key_values["empathy_validation"] = output_json["empathy_validation"]
12
+ argument_strength += output_json["empathy_validation"]
13
+
14
+ if "logical_and_persuasive_argumentation" in stage_key_values:
15
+ if stage_key_values["logical_and_persuasive_argumentation"] < output_json["logical_and_persuasive_argumentation"]:
16
+ stage_key_values["logical_and_persuasive_argumentation"] = output_json["logical_and_persuasive_argumentation"]
17
+ else:
18
+ stage_key_values["logical_and_persuasive_argumentation"] = output_json["logical_and_persuasive_argumentation"]
19
+ argument_strength += output_json["logical_and_persuasive_argumentation"]
20
+
21
+ if "challenge_distortion" in stage_key_values:
22
+ if stage_key_values["challenge_distortion"] < output_json["challenge_distortion"]:
23
+ stage_key_values["challenge_distortion"] = output_json["challenge_distortion"]
24
+ else:
25
+ stage_key_values["challenge_distortion"] = output_json["challenge_distortion"]
26
+ argument_strength += output_json["challenge_distortion"]
27
+
28
+ if argument_strength >= 5:
29
+ stage_key_values["stage_complete"] = True
30
+
31
+ return {
32
+ "text": [output_json["Seraphine’s_response"], "Congratulations! You have successfully convinced Seraphine to join you on your journey!"]
33
+ }
34
+
35
+ return {
36
+ "text": [output_json["Seraphine’s_response"]]
37
+ }
38
+
39
+ def forced_stage_exit(stage_key_values):
40
+ stage_key_values["stage_complete"] = True
41
+ stage_key_values["next_stage"] = "10"
42
+ return {
43
+ "text": ["You have taken too long to convince Seraphine. You have been forced to leave and continue your journey alone. Good luck with that!"]
44
+ }
45
+
46
+ def checker_function(stage_key_values):
47
+ stage_key_values["interaction_count_for_hint"] = 0
48
+ hints = {"text": ["Try to incorporate the following in your arguments:"]}
49
+ if stage_key_values.get("empathy_validation", 0) < 2:
50
+ hints["text"].append(" - Empathize with Seraphine and understand her perspective.")
51
+ if stage_key_values.get("logical_and_persuasive_argumentation", 0) < 2:
52
+ hints["text"].append(" - Use logical arguments with evidence to persuade Seraphine.")
53
+ if stage_key_values.get("challenge_distortion", 0) < 2:
54
+ hints["text"].append(" - Seraphine suffers from the cognitive distortion known as 'Discounting the positive'. Challenge this distortion by providing a more appropriate reframing of her thoughts.")
55
+
56
+ return hints
pathways/Virelia/Virelia.json ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "1": {
3
+ "name": "Background",
4
+ "max_interactions": 10,
5
+ "interactions_for_hint": 3,
6
+ "prompt_file1": "pathways/Virelia/Virelia.txt",
7
+ "prompt_file2": "pathways/Virelia/Stage1.txt",
8
+ "functions_list": "pathways/Virelia/Stage1_functions.json",
9
+ "functions_definition": "pathways.Virelia.Stage1_functions_definition",
10
+ "ui": {
11
+ "text": ["You’re on your way to the lively town of Virelia, a place famous for its vibrant markets and friendly people. However, before you can reach it, you must navigate the perilous Whispering Pines—a dense, treacherous forest known to be home to dangerous bandits. The woods have claimed many travelers, and those who’ve entered without proper caution have often never returned.",
12
+ "As you approach the edge of the forest, you notice a small village nestled at the boundary. In the heart of the village stands a humble inn, The Pine’s Rest, its sign swaying in the chilly breeze. It seems like a wise stop to rest, gather supplies, and—more importantly—speak with the locals. They know the dangers of the Whispering Pines well and may offer valuable advice that could make all the difference in safely navigating the woods.",
13
+ "What will you do? Will you enter the inn and ask for advice before continuing your journey?"
14
+ ],
15
+ "image": "pathways/Virelia/assets/VireliaInn.png"
16
+ }
17
+ },
18
+ "10": {
19
+ "name": "Journey alone",
20
+ "prompt_file1": "pathways/Virelia/Virelia.txt",
21
+ "prompt_file2": "pathways/Virelia/Stage10.txt",
22
+ "functions_list": "pathways/Virelia/Stage10_functions.json",
23
+ "functions_definition": "pathways.Virelia.Stage10_functions_definition",
24
+ "ui": {
25
+ "text": ["Test-text: You decide to embark on your journey alone, leaving the tavern behind."]
26
+ }
27
+ },
28
+ "2": {
29
+ "name": "The bartender",
30
+ "prompt_file1": "pathways/Virelia/Virelia.txt",
31
+ "prompt_file2": "pathways/Virelia/Stage2.txt",
32
+ "functions_list": "pathways/Virelia/Stage2_functions.json",
33
+ "functions_definition": "pathways.Virelia.Stage2_functions_definition"
34
+ },
35
+ "3": {
36
+ "name": "Convince Seraphine",
37
+ "prompt_file1": "pathways/Virelia/Stage3.txt",
38
+ "structured_output_schema": {
39
+ "type": "json_schema",
40
+ "json_schema": {
41
+ "name": "assess_player_argument",
42
+ "schema" : {
43
+ "type": "object",
44
+ "properties": {
45
+ "empathy_validation": {
46
+ "type": "number",
47
+ "enum": [0, 1, 2],
48
+ "description": "Evaluates how well the player acknowledges Seraphine’s emotional state and validates her experiences. It measures the player's ability to make Seraphine feel heard and understood, while offering reassurance that her feelings are valid."
49
+ },
50
+ "logical_and_persuasive_argumentation": {
51
+ "type": "number",
52
+ "enum": [0, 1, 2],
53
+ "description": "Evaluates the player's ability to present a logical and persuasive argument that addresses Seraphine's concerns. It measures how well the player articulates the benefits of joining them on their journey, while also considering Seraphine's emotional state."
54
+ },
55
+ "challenge_distortion": {
56
+ "type": "number",
57
+ "enum": [0, 1, 2],
58
+ "description": "Evaluates the player's ability to challenge Seraphine's distortion, specifically 'Discounting the positive'. It measures how well the player helps Seraphine see a more balanced perspective on her situation and the potential for positive outcomes."
59
+ },
60
+ "Seraphine’s_response": {
61
+ "type": "string",
62
+ "description": "The response from Seraphine after the player has made their argument. It should reflect her emotional state and willingness to join the player on their journey."
63
+ }
64
+ },
65
+ "required": ["empathy_validation", "logical_and_persuasive_argumentation", "challenge_distortion", "Seraphine’s_response"],
66
+ "additionalProperties": false
67
+ },
68
+ "strict": true
69
+ }
70
+ },
71
+ "functions_definition": "pathways.Virelia.Stage3_functions_definition",
72
+ "ui": {
73
+ "text": ["Seraphine looks up at you as you approach her."]
74
+ }
75
+ },
76
+ "pathway_complete": {
77
+ "ui": {
78
+ "title": "Discounting the positive",
79
+ "text": ["The cognitive distortion 'Discounting the positive' is a common cognitive distortion where individuals tend to overlook or minimize positive experiences, achievements, or qualities. This can lead to a negative self-image and a skewed perception of reality. In the context of Seraphine, she struggles with this distortion, believing that her past mistakes overshadow any potential for future success or happiness.", "Think about how this may apply to your life. Does this distortion resonate with you? Have you ever found yourself dismissing your own achievements or positive experiences? If so, how can you work towards recognizing and valuing the positive aspects of your life?"]
80
+ }
81
+ }
82
+ }
pathways/Virelia/Virelia.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ Instructions:
2
+ You are an AI game master. Your job is to create an adventure based on the descriptions provided and interactions with the user.
3
+
4
+ IMPORTANT RESTRICTION AND GATING INFORMATION:
5
+ Do not take action on behalf of the player.
6
+ Ask the player what they want to do before taking any action.
7
+
8
+ Game description:
9
+ The player is on their way to the beautiful town of Virelia. On the road to Virelia, however, is the dense and foreboding Whispering Pines, an infamous forest known for ruthless bandits, waiting for the unwary to wander too close. Their ambushes are swift and merciless, and many travelers have never emerged from the forest's depths.
10
+ But the journey is necessary—Virelia awaits, and the city holds answers to the quest that drives you forward. Yet, crossing Whispering Pines is no small feat.
pathways/Virelia/assets/Bartender01.png ADDED

Git LFS Details

  • SHA256: efb916c1f3d4c2f016fc284a71d7040093f32f11a742b9cf73f1ef05d74ddcc4
  • Pointer size: 132 Bytes
  • Size of remote file: 1.82 MB
pathways/Virelia/assets/Seraphine01.png ADDED

Git LFS Details

  • SHA256: 09637b5599d85c9347519fbd952b6124c565d96b8a291a84100e51dd628d9796
  • Pointer size: 132 Bytes
  • Size of remote file: 1.33 MB
pathways/Virelia/assets/Seraphine02.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0acd4270e31d5df3bf0be75fc5fbfcff1dafb031ab1503bf59d71300e7354da7
3
+ size 2792333
pathways/Virelia/assets/VireliaInn.png ADDED

Git LFS Details

  • SHA256: ce4c4ff3879c8e529639d36390eb4564cf48449a04d1b226d0b068c160840dd4
  • Pointer size: 132 Bytes
  • Size of remote file: 1.87 MB
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ together==1.5.21
2
+ gradio==5.37.0
3
+ openai==1.95.1
stage.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import importlib
3
+ import copy
4
+
5
+ class Stage:
6
+ def __init__(self, stage_json, ai_llm, pathway_parameters=None):
7
+ self.stage_json = stage_json
8
+ self.ai_llm = ai_llm
9
+ if "ui" in stage_json:
10
+ self.ui = copy.deepcopy(stage_json["ui"])
11
+ else:
12
+ self.ui = {}
13
+
14
+ #If "text msgs are in the UI, add them to the ui component in the form of an llm message list"
15
+ if "text" in self.ui:
16
+ self.ui["text"] = []
17
+ for msg in stage_json["ui"]["text"]:
18
+ self.ui["text"].append({
19
+ "role": "assistant",
20
+ "content": msg
21
+ })
22
+ else:
23
+ self.ui["text"] = []
24
+
25
+ #Any parameters that the stage wants to pass on to subsequent stages
26
+ self.stage_parameters = {}
27
+ #Key values that the stage will use to track its progress
28
+ self.stage_key_values = {
29
+ "stage_complete": False,
30
+ "next_stage": None,
31
+ "max_interactions": 10,
32
+ "interactions_for_hint": 3
33
+ }
34
+ if pathway_parameters:
35
+ self.stage_key_values["max_interactions"] = pathway_parameters.get("max_interactions", 10)
36
+ self.stage_key_values["interactions_for_hint"] = pathway_parameters.get("interactions_for_hint", 3)
37
+ if "max_interactions" in self.stage_json:
38
+ self.stage_key_values["max_interactions"] = self.stage_json["max_interactions"]
39
+ if "interactions_for_hint" in self.stage_json:
40
+ self.stage_key_values["interactions_for_hint"] = self.stage_json["interactions_for_hint"]
41
+
42
+ #Total number of interactions within the stage
43
+ self.stage_key_values["interaction_count"] = 0
44
+ #Number of interactions before each hint is given (different hints at different phases of the stage)
45
+ self.stage_key_values["interaction_count_for_hint"] = 0
46
+ self.stage_prompt = ""
47
+ file_no = 1
48
+ while "prompt_file" + str(file_no) in self.stage_json:
49
+ file_name = self.stage_json["prompt_file" + str(file_no)]
50
+ with open(file_name, "r") as file:
51
+ self.stage_prompt += file.read()
52
+ file_no += 1
53
+
54
+ self.llm_messages = [{"role": "system", "content": self.stage_prompt}]
55
+
56
+ self.functions_list = None
57
+ self.functions_module = None
58
+ self.structured_output_schema = None
59
+ if "structured_output_schema" in self.stage_json:
60
+ self.structured_output_schema = self.stage_json["structured_output_schema"]
61
+ self.functions_module = importlib.import_module(self.stage_json["functions_definition"])
62
+ elif "functions_list" in self.stage_json:
63
+ with open(self.stage_json["functions_list"], "r") as file:
64
+ self.functions_list = json.load(file)
65
+
66
+ self.functions_module = importlib.import_module(self.stage_json["functions_definition"])
67
+
68
+ def next_step(self, player_input):
69
+ self.stage_key_values["interaction_count"] += 1
70
+ self.stage_key_values["interaction_count_for_hint"] += 1
71
+ checker_ui = None
72
+ response_ui = {"text": [""]}
73
+ if not self.stage_key_values["stage_complete"]:
74
+ self.llm_messages.append({"role": "user", "content": player_input})
75
+ self.ui["text"].append({"role": "user", "content": player_input})
76
+ if self.structured_output_schema:
77
+ response_ui = self.ai_llm.get_structured_output(self.llm_messages, self.structured_output_schema, self.functions_module, self.stage_key_values)
78
+ else:
79
+ response_ui = self.ai_llm.talk_to_LLM(self.llm_messages, self.functions_list, self.functions_module, self.stage_key_values)
80
+ self.ui["text"].append({"role": "assistant", "content": response_ui["text"]})
81
+ if self.stage_key_values["interaction_count"] >= self.stage_key_values["max_interactions"]:
82
+ forced_exit_function = getattr(self.functions_module, "forced_stage_exit", None)
83
+ if forced_exit_function:
84
+ forced_exit_msg = forced_exit_function(stage_key_values=self.stage_key_values)
85
+ response_ui["text"] += forced_exit_msg["text"]
86
+ self.ui["text"].append({"role": "assistant", "content": response_ui["text"]})
87
+ return True, response_ui, checker_ui
88
+ else:
89
+ print("No forced exit function found in module", self.functions_module.__name__ if self.functions_module else "None")
90
+ if self.stage_key_values["interaction_count_for_hint"] >= self.stage_key_values["interactions_for_hint"] and not self.stage_key_values["stage_complete"]:
91
+ checker_function = getattr(self.functions_module, "checker_function", None)
92
+ if checker_function:
93
+ checker_ui = checker_function(stage_key_values=self.stage_key_values)
94
+ else:
95
+ print("No checker function found in module", self.functions_module.__name__ if self.functions_module else "None")
96
+
97
+ return self.stage_key_values["stage_complete"], response_ui, checker_ui
98
+
99
+ def get_next_stage(self):
100
+ return self.stage_key_values["next_stage"]
101
+
102
+ def get_stage_parameters(self):
103
+ return self.get_stage_parameters
104
+
105
+ def get_initial_ui(self):
106
+ return copy.deepcopy(self.stage_json.get("ui", None))