Decision-Fish commited on
Commit
6e19a5c
·
verified ·
1 Parent(s): 44fada6

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -180
app.py DELETED
@@ -1,180 +0,0 @@
1
- import os
2
- import gradio as gr
3
- from pathlib import Path
4
-
5
- UNIVERSAL_PROMPT_PATH = "CAT_universal_prompt.txt"
6
- MODULE_DIR = "modules" # <-- now using /modules subfolder
7
-
8
- from openai import OpenAI
9
- from dotenv import load_dotenv
10
- load_dotenv()
11
- client = OpenAI()
12
-
13
- # Type aliases
14
- from typing import List, cast
15
- from openai.types.chat import ChatCompletionMessageParam
16
-
17
-
18
- def call_model(system_prompt: str, history: list[dict[str, str]]) -> str:
19
- # Build as simple dicts first
20
- msgs: list[dict[str, str]] = [{"role": "system", "content": system_prompt}]
21
- for m in history:
22
- role = m.get("role")
23
- content = m.get("content")
24
- if role in ("user", "assistant") and isinstance(content, str):
25
- msgs.append({"role": role, "content": content})
26
-
27
- # Cast once at the call site to satisfy the SDK types
28
- typed_msgs = cast(List[ChatCompletionMessageParam], msgs)
29
-
30
- resp = client.chat.completions.create(
31
- model="gpt-4o-mini",
32
- messages=typed_msgs,
33
- temperature=0.4,
34
- )
35
- return resp.choices[0].message.content or ""
36
-
37
-
38
- def load_text(path: str) -> str:
39
- with open(path, "r", encoding="utf-8") as f:
40
- return f.read()
41
-
42
- def assemble_prompt(universal_prompt_text: str, module_text: str) -> str:
43
- def extract(label: str) -> str:
44
- marker = label + ":"
45
- start = module_text.find(marker)
46
- if start == -1:
47
- return ""
48
- start += len(marker)
49
- next_markers = ["\nLEARNING OBJECTIVES:", "\nRUBRIC:", "\nMODULE NAME:"]
50
- end_positions = [module_text.find(m, start) for m in next_markers if module_text.find(m, start) != -1]
51
- end = min(end_positions) if end_positions else len(module_text)
52
- return module_text[start:end].strip()
53
-
54
- learning_objectives = extract("LEARNING OBJECTIVES")
55
- rubric = extract("RUBRIC")
56
-
57
- prompt = universal_prompt_text.replace("{LEARNING_OBJECTIVES}", learning_objectives)
58
- prompt = prompt.replace("{RUBRIC}", rubric)
59
- return prompt
60
-
61
- def init_state():
62
- return {
63
- "assembled_prompt": "",
64
- "history": [],
65
- "mode": "roleplay",
66
- "student_turns": 0,
67
- "invited_wrap": False,
68
- "mentor_step": 0,
69
- "student_name": ""
70
- }
71
-
72
- def start_session(module_file, student_name=""):
73
- state = init_state()
74
- state["student_name"] = student_name
75
- universal = load_text(UNIVERSAL_PROMPT_PATH)
76
- module_text = load_text(Path(MODULE_DIR) / module_file)
77
-
78
- # Give the model the name so it can personalize the dialogue (without overusing it)
79
- name_hint = f"\n\n[Student first name: {student_name}. Use it naturally once in the opening; don’t overuse.]" if student_name else ""
80
- state["assembled_prompt"] = assemble_prompt(universal, module_text) + name_hint
81
-
82
- state["history"].append({"role": "system", "content": state["assembled_prompt"]})
83
- opening = call_model(state["assembled_prompt"], state["history"])
84
- state["history"].append({"role": "assistant", "content": opening})
85
- return state, state["history"]
86
-
87
- def chat(user_msg, state):
88
- if not user_msg.strip():
89
- return "", state["history"], state
90
-
91
- if state["mode"] == "roleplay":
92
- state["student_turns"] += 1
93
- state["history"].append({"role": "user", "content": user_msg})
94
-
95
- invite_now = False
96
- force_now = False
97
-
98
- if state["mode"] == "roleplay":
99
- if state["student_turns"] >= 10:
100
- force_now = True
101
- elif (7 <= state["student_turns"] <= 9) and not state["invited_wrap"]:
102
- invite_now = True
103
-
104
- if state["mode"] == "roleplay" and (invite_now or force_now):
105
- if not state["invited_wrap"]:
106
- invite = ('Thanks for walking through this with me. We’ve covered a lot. '
107
- 'Before we wrap up, is there anything else you’d like me to consider '
108
- 'before I give you a preliminary assessment?')
109
- state["history"].append({"role": "assistant", "content": invite})
110
- state["invited_wrap"] = True
111
- return "", state["history"], state
112
- else:
113
- ack = "I appreciate you sharing that. I’ll take it into account. Let’s step back and review how you approached this situation."
114
- state["history"].append({"role": "assistant", "content": ack})
115
- state["mode"] = "mentor"
116
-
117
- if state["mode"] == "roleplay" and not state["invited_wrap"]:
118
- reply = call_model(state["assembled_prompt"], state["history"])
119
- state["history"].append({"role": "assistant", "content": reply})
120
- return "", state["history"], state
121
-
122
- if state["mode"] == "mentor":
123
- # Step 1: general intro (no assumption of tools)
124
- if state.get("mentor_step", 0) == 0:
125
- eval_intro = (
126
- "Before we wrap up: name two specific concepts, tools, or frameworks you used in this scenario, "
127
- "and in one short sentence each say how you applied them. If you didn’t use any, name two insights "
128
- "you learned and how you would apply them next time."
129
- )
130
- state["history"].append({"role": "assistant", "content": eval_intro})
131
- state["mentor_step"] = 1
132
- return "", state["history"], state
133
-
134
- # Step 2: concise rubric-based evaluation
135
- else:
136
- eval_request = (
137
- "Evaluate the student's performance using the module rubric. Provide these sections: "
138
- "Overall rating (Unsatisfactory, Satisfactory, or Excellent) with a one-sentence justification; "
139
- 'Career competencies; Uniquely human capacities; Argument analysis; Ethical frameworks; ESG awareness; '
140
- "Application; Interaction quality; Strength; Area to improve; Advice for next time; Fictional consequence. "
141
- "Quote at least one student phrase. Keep the whole evaluation under 180 words and end with 'END OF SCENE'."
142
- )
143
- state["history"].append({"role": "user", "content": eval_request})
144
- reply = call_model(state["assembled_prompt"], state["history"])
145
- if "END OF SCENE" not in reply:
146
- reply += "\n\nEND OF SCENE"
147
- state["history"].append({"role": "assistant", "content": reply})
148
- state["mode"] = "done"
149
- return "", state["history"], state
150
-
151
-
152
- with gr.Blocks(title="CAT (MVP)") as demo:
153
- gr.Markdown("## 😼Conversational Assessment Tool (CAT) — MVP")
154
- with gr.Row():
155
- module_file = gr.Dropdown(
156
- label="Select Module File",
157
- choices=[p.name for p in sorted(Path(MODULE_DIR).glob("module*.txt"))],
158
- value="module01.txt",
159
- interactive=True
160
- )
161
- name_tb = gr.Textbox(label="Your first name", placeholder="e.g., Maya", value="", interactive=True)
162
- start_btn = gr.Button("Start") # fine to keep inside the row (optional)
163
-
164
- chatbot = gr.Chatbot(label="CAT Conversation", type="messages")
165
- user_in = gr.Textbox(label="Your message", placeholder="Type here and press Enter")
166
- state = gr.State(init_state())
167
-
168
- def _start(module_name, student_name):
169
- student_name = student_name.strip()
170
- if not student_name:
171
- # Return a valid state object plus a warning message in the chat
172
- return init_state(), [{"role": "assistant", "content": "⚠ Please enter your first name before starting."}]
173
- st, hist = start_session(module_name, student_name)
174
- return st, hist
175
-
176
- start_btn.click(_start, [module_file, name_tb], [state, chatbot])
177
- user_in.submit(chat, [user_in, state], [user_in, chatbot, state])
178
-
179
- if __name__ == "__main__":
180
- demo.launch()