Update app.py
Browse files
app.py
CHANGED
|
@@ -11,7 +11,15 @@ from langchain_core.messages import HumanMessage, SystemMessage
|
|
| 11 |
# --- Configuration ---
|
| 12 |
NEBIUS_API_KEY = os.getenv("NEBIUS_API_KEY")
|
| 13 |
NEBIUS_BASE_URL = "https://api.studio.nebius.ai/v1/"
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
# --- System Prompt ---
|
| 17 |
SYSTEM_PROMPT = """You are a 'Vibe Coding' Python Tutor.
|
|
@@ -77,9 +85,9 @@ def parse_agent_response(full_text):
|
|
| 77 |
|
| 78 |
return chat_content, videos, articles, quiz
|
| 79 |
|
| 80 |
-
async def
|
| 81 |
"""
|
| 82 |
-
|
| 83 |
"""
|
| 84 |
server_params = StdioServerParameters(
|
| 85 |
command=sys.executable,
|
|
@@ -96,22 +104,33 @@ async def run_tutor_dashboard(user_message):
|
|
| 96 |
api_key=NEBIUS_API_KEY,
|
| 97 |
base_url=NEBIUS_BASE_URL,
|
| 98 |
model=MODEL_NAME,
|
| 99 |
-
temperature=0.7
|
|
|
|
| 100 |
)
|
| 101 |
|
| 102 |
agent_executor = create_react_agent(llm, tools)
|
| 103 |
|
| 104 |
-
# Prepend the SystemMessage to ensure the agent follows instructions
|
| 105 |
inputs = {
|
| 106 |
"messages": [
|
| 107 |
SystemMessage(content=SYSTEM_PROMPT),
|
| 108 |
HumanMessage(content=user_message)
|
| 109 |
]
|
| 110 |
}
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
|
| 116 |
# --- Gradio Dashboard UI ---
|
| 117 |
# Professional "Slate" theme
|
|
@@ -144,7 +163,7 @@ with gr.Blocks(title="AI Python Tutor", theme=theme, fill_height=True) as demo:
|
|
| 144 |
|
| 145 |
chatbot = gr.Chatbot(
|
| 146 |
height=600,
|
| 147 |
-
show_label=False,
|
| 148 |
type="messages",
|
| 149 |
bubble_full_width=False,
|
| 150 |
show_copy_button=True,
|
|
@@ -211,17 +230,26 @@ with gr.Blocks(title="AI Python Tutor", theme=theme, fill_height=True) as demo:
|
|
| 211 |
# Placeholder for AI
|
| 212 |
history.append({"role": "assistant", "content": "Thinking..."})
|
| 213 |
|
| 214 |
-
#
|
| 215 |
-
# Structure: history, msg, 3x Side Boxes, 3x Bottom Boxes
|
| 216 |
yield history, "", "", "", "", "", "", ""
|
| 217 |
|
| 218 |
-
|
| 219 |
-
chat_text, video_text, article_text, quiz_text = await run_tutor_dashboard(user_message)
|
| 220 |
|
| 221 |
-
#
|
| 222 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
|
| 224 |
-
#
|
|
|
|
| 225 |
yield history, "", video_text, article_text, quiz_text, video_text, article_text, quiz_text
|
| 226 |
|
| 227 |
# --- Focus Mode Logic ---
|
|
@@ -229,14 +257,9 @@ with gr.Blocks(title="AI Python Tutor", theme=theme, fill_height=True) as demo:
|
|
| 229 |
|
| 230 |
def toggle_fullscreen(current_state):
|
| 231 |
new_state = not current_state
|
| 232 |
-
# If new_state is True (Fullscreen): Hide side col, Show bottom row
|
| 233 |
-
# If False (Normal): Show side col, Hide bottom row
|
| 234 |
-
|
| 235 |
side_visible = not new_state
|
| 236 |
bottom_visible = new_state
|
| 237 |
-
|
| 238 |
btn_text = "↩ Exit Focus" if new_state else "⛶ Focus Mode"
|
| 239 |
-
|
| 240 |
return new_state, gr.Column(visible=side_visible), gr.Row(visible=bottom_visible), btn_text
|
| 241 |
|
| 242 |
fullscreen_btn.click(
|
|
@@ -245,7 +268,7 @@ with gr.Blocks(title="AI Python Tutor", theme=theme, fill_height=True) as demo:
|
|
| 245 |
outputs=[is_fullscreen, right_col, bottom_dashboard, fullscreen_btn]
|
| 246 |
)
|
| 247 |
|
| 248 |
-
# Actions
|
| 249 |
outputs_list = [
|
| 250 |
chatbot, msg,
|
| 251 |
video_box_side, article_box_side, quiz_box_side,
|
|
|
|
| 11 |
# --- Configuration ---
|
| 12 |
NEBIUS_API_KEY = os.getenv("NEBIUS_API_KEY")
|
| 13 |
NEBIUS_BASE_URL = "https://api.studio.nebius.ai/v1/"
|
| 14 |
+
|
| 15 |
+
# OPTION 1: Best Balance (Speed + Intelligence) -> RECOMMENDED
|
| 16 |
+
MODEL_NAME = "Qwen/Qwen3-30B-A3B-Thinking-2507"
|
| 17 |
+
|
| 18 |
+
# OPTION 2: Maximum Speed (Good for simple tasks)
|
| 19 |
+
# MODEL_NAME = "Qwen/Qwen3-30B-A3B-Thinking-2507"
|
| 20 |
+
|
| 21 |
+
# OPTION 3: Maximum Intelligence (Slowest)
|
| 22 |
+
# MODEL_NAME = "openai/gpt-oss-120b"
|
| 23 |
|
| 24 |
# --- System Prompt ---
|
| 25 |
SYSTEM_PROMPT = """You are a 'Vibe Coding' Python Tutor.
|
|
|
|
| 85 |
|
| 86 |
return chat_content, videos, articles, quiz
|
| 87 |
|
| 88 |
+
async def run_tutor_dashboard_stream(user_message):
|
| 89 |
"""
|
| 90 |
+
Runs the agent loop and yields streaming text updates.
|
| 91 |
"""
|
| 92 |
server_params = StdioServerParameters(
|
| 93 |
command=sys.executable,
|
|
|
|
| 104 |
api_key=NEBIUS_API_KEY,
|
| 105 |
base_url=NEBIUS_BASE_URL,
|
| 106 |
model=MODEL_NAME,
|
| 107 |
+
temperature=0.7,
|
| 108 |
+
streaming=True # Critical for speed
|
| 109 |
)
|
| 110 |
|
| 111 |
agent_executor = create_react_agent(llm, tools)
|
| 112 |
|
|
|
|
| 113 |
inputs = {
|
| 114 |
"messages": [
|
| 115 |
SystemMessage(content=SYSTEM_PROMPT),
|
| 116 |
HumanMessage(content=user_message)
|
| 117 |
]
|
| 118 |
}
|
| 119 |
+
|
| 120 |
+
# Stream the response token by token
|
| 121 |
+
final_text = ""
|
| 122 |
+
async for event in agent_executor.astream_events(inputs, version="v1"):
|
| 123 |
+
kind = event["event"]
|
| 124 |
+
|
| 125 |
+
# Stream actual tokens from the LLM
|
| 126 |
+
if kind == "on_chat_model_stream":
|
| 127 |
+
content = event["data"]["chunk"].content
|
| 128 |
+
if content:
|
| 129 |
+
final_text += content
|
| 130 |
+
yield final_text
|
| 131 |
+
|
| 132 |
+
# Once done, yield the final text one last time to ensure completeness
|
| 133 |
+
yield final_text
|
| 134 |
|
| 135 |
# --- Gradio Dashboard UI ---
|
| 136 |
# Professional "Slate" theme
|
|
|
|
| 163 |
|
| 164 |
chatbot = gr.Chatbot(
|
| 165 |
height=600,
|
| 166 |
+
show_label=False,
|
| 167 |
type="messages",
|
| 168 |
bubble_full_width=False,
|
| 169 |
show_copy_button=True,
|
|
|
|
| 230 |
# Placeholder for AI
|
| 231 |
history.append({"role": "assistant", "content": "Thinking..."})
|
| 232 |
|
| 233 |
+
# Initial yield
|
|
|
|
| 234 |
yield history, "", "", "", "", "", "", ""
|
| 235 |
|
| 236 |
+
full_response = ""
|
|
|
|
| 237 |
|
| 238 |
+
# STREAMING LOOP
|
| 239 |
+
async for chunk in run_tutor_dashboard_stream(user_message):
|
| 240 |
+
full_response = chunk
|
| 241 |
+
|
| 242 |
+
# Update chat history LIVE
|
| 243 |
+
history[-1]["content"] = full_response
|
| 244 |
+
|
| 245 |
+
# Yield update just for chat, keeping others blank for now
|
| 246 |
+
yield history, "", "", "", "", "", "", ""
|
| 247 |
+
|
| 248 |
+
# Final Parse (Split into 4 components)
|
| 249 |
+
chat_text, video_text, article_text, quiz_text = parse_agent_response(full_response)
|
| 250 |
|
| 251 |
+
# Update everything one last time
|
| 252 |
+
history[-1]["content"] = chat_text
|
| 253 |
yield history, "", video_text, article_text, quiz_text, video_text, article_text, quiz_text
|
| 254 |
|
| 255 |
# --- Focus Mode Logic ---
|
|
|
|
| 257 |
|
| 258 |
def toggle_fullscreen(current_state):
|
| 259 |
new_state = not current_state
|
|
|
|
|
|
|
|
|
|
| 260 |
side_visible = not new_state
|
| 261 |
bottom_visible = new_state
|
|
|
|
| 262 |
btn_text = "↩ Exit Focus" if new_state else "⛶ Focus Mode"
|
|
|
|
| 263 |
return new_state, gr.Column(visible=side_visible), gr.Row(visible=bottom_visible), btn_text
|
| 264 |
|
| 265 |
fullscreen_btn.click(
|
|
|
|
| 268 |
outputs=[is_fullscreen, right_col, bottom_dashboard, fullscreen_btn]
|
| 269 |
)
|
| 270 |
|
| 271 |
+
# Actions
|
| 272 |
outputs_list = [
|
| 273 |
chatbot, msg,
|
| 274 |
video_box_side, article_box_side, quiz_box_side,
|