Merge branch 'user-api-key'
Browse files- src/plan/app_text2plan.py +76 -14
src/plan/app_text2plan.py
CHANGED
@@ -97,9 +97,18 @@ class MarkdownBuilder:
|
|
97 |
class SessionState:
|
98 |
"""
|
99 |
In a multi-user environment (e.g. Hugging Face Spaces), this class hold each users state.
|
100 |
-
In a single-user environment, this class is used to hold the state of
|
|
|
|
|
|
|
101 |
"""
|
102 |
def __init__(self):
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
# Holds the subprocess.Popen object for the currently running pipeline process.
|
104 |
self.active_proc = None
|
105 |
# A threading.Event used to signal that the running process should stop.
|
@@ -117,14 +126,11 @@ class SessionState:
|
|
117 |
"""
|
118 |
return self
|
119 |
|
120 |
-
def run_planner(submit_or_retry_button, plan_prompt,
|
121 |
"""
|
122 |
Generator function for launching the pipeline process and streaming updates.
|
123 |
The session state is carried in a SessionState instance.
|
124 |
"""
|
125 |
-
# Initialize session_state if needed.
|
126 |
-
if session_state is None:
|
127 |
-
session_state = SessionState()
|
128 |
|
129 |
# Clear any previous stop signal.
|
130 |
session_state.stop_event.clear()
|
@@ -163,8 +169,15 @@ def run_planner(submit_or_retry_button, plan_prompt, llm_model, speedvsdetail, s
|
|
163 |
# Set environment variables for the pipeline.
|
164 |
env = os.environ.copy()
|
165 |
env["RUN_ID"] = run_id
|
166 |
-
env["LLM_MODEL"] = llm_model
|
167 |
-
env["SPEED_VS_DETAIL"] = speedvsdetail
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
|
169 |
start_time = time.perf_counter()
|
170 |
# Initialize the last zip creation time to be ZIP_INTERVAL_SECONDS in the past
|
@@ -278,8 +291,6 @@ def stop_planner(session_state: SessionState):
|
|
278 |
"""
|
279 |
Sets a stop flag in the session_state and attempts to terminate the active process.
|
280 |
"""
|
281 |
-
if session_state is None:
|
282 |
-
session_state = SessionState()
|
283 |
|
284 |
session_state.stop_event.set()
|
285 |
|
@@ -299,8 +310,6 @@ def open_output_dir(session_state: SessionState):
|
|
299 |
"""
|
300 |
Opens the latest output directory in the native file explorer.
|
301 |
"""
|
302 |
-
if session_state is None:
|
303 |
-
session_state = SessionState()
|
304 |
|
305 |
latest_run_dir = session_state.latest_run_dir
|
306 |
if not latest_run_dir or not os.path.exists(latest_run_dir):
|
@@ -317,6 +326,29 @@ def open_output_dir(session_state: SessionState):
|
|
317 |
except Exception as e:
|
318 |
return f"Failed to open directory: {e}", session_state
|
319 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
320 |
# Build the Gradio UI using Blocks.
|
321 |
with gr.Blocks(title="PlanExe") as demo_text2plan:
|
322 |
gr.Markdown("# PlanExe: crack open pandora’s box of ideas")
|
@@ -363,24 +395,31 @@ with gr.Blocks(title="PlanExe") as demo_text2plan:
|
|
363 |
label="Speed vs Detail",
|
364 |
interactive=True
|
365 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
366 |
|
367 |
with gr.Tab("Join the community"):
|
368 |
gr.Markdown("""
|
369 |
- [GitHub](https://github.com/neoneye/PlanExe) the source code.
|
370 |
- [Discord](https://neoneye.github.io/PlanExe-web/discord) join the community. Suggestions, feedback, and questions are welcome.
|
371 |
""")
|
372 |
-
|
|
|
373 |
session_state = gr.State(SessionState())
|
374 |
|
375 |
# Submit and Retry buttons call run_planner and update the state.
|
376 |
submit_btn.click(
|
377 |
fn=run_planner,
|
378 |
-
inputs=[submit_btn, prompt_input,
|
379 |
outputs=[output_markdown, download_output, session_state]
|
380 |
)
|
381 |
retry_btn.click(
|
382 |
fn=run_planner,
|
383 |
-
inputs=[retry_btn, prompt_input,
|
384 |
outputs=[output_markdown, download_output, session_state]
|
385 |
)
|
386 |
# The Stop button uses the state to terminate the running process.
|
@@ -396,6 +435,29 @@ with gr.Blocks(title="PlanExe") as demo_text2plan:
|
|
396 |
outputs=[status_markdown, session_state]
|
397 |
)
|
398 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
399 |
if __name__ == "__main__":
|
400 |
# print("Environment variables Gradio:\n" + get_env_as_string() + "\n\n\n")
|
401 |
|
|
|
97 |
class SessionState:
|
98 |
"""
|
99 |
In a multi-user environment (e.g. Hugging Face Spaces), this class hold each users state.
|
100 |
+
In a single-user environment, this class is used to hold the state of that lonely user.
|
101 |
+
|
102 |
+
IDEA: Persist the user settings for longer. The settings survive a page refresh, but not a server restart.
|
103 |
+
The browser has a local storage. Can Gradio access that, so that the settings are remembered between sessions?
|
104 |
"""
|
105 |
def __init__(self):
|
106 |
+
# Settings: the user's OpenRouter API key.
|
107 |
+
self.openrouter_api_key = "" # Initialize to empty string
|
108 |
+
# Settings: The model that the user has picked.
|
109 |
+
self.llm_model = available_model_names[0]
|
110 |
+
# Settings: The speedvsdetail that the user has picked.
|
111 |
+
self.speedvsdetail = SpeedVsDetailEnum.ALL_DETAILS_BUT_SLOW
|
112 |
# Holds the subprocess.Popen object for the currently running pipeline process.
|
113 |
self.active_proc = None
|
114 |
# A threading.Event used to signal that the running process should stop.
|
|
|
126 |
"""
|
127 |
return self
|
128 |
|
129 |
+
def run_planner(submit_or_retry_button, plan_prompt, session_state: SessionState):
|
130 |
"""
|
131 |
Generator function for launching the pipeline process and streaming updates.
|
132 |
The session state is carried in a SessionState instance.
|
133 |
"""
|
|
|
|
|
|
|
134 |
|
135 |
# Clear any previous stop signal.
|
136 |
session_state.stop_event.clear()
|
|
|
169 |
# Set environment variables for the pipeline.
|
170 |
env = os.environ.copy()
|
171 |
env["RUN_ID"] = run_id
|
172 |
+
env["LLM_MODEL"] = session_state.llm_model
|
173 |
+
env["SPEED_VS_DETAIL"] = session_state.speedvsdetail
|
174 |
+
|
175 |
+
# If there is a non-empty OpenRouter API key, set it as an environment variable.
|
176 |
+
if session_state.openrouter_api_key and len(session_state.openrouter_api_key) > 0:
|
177 |
+
print("Setting OpenRouter API key as environment variable.")
|
178 |
+
env["OPENROUTER_API_KEY"] = session_state.openrouter_api_key
|
179 |
+
else:
|
180 |
+
print("No OpenRouter API key provided.")
|
181 |
|
182 |
start_time = time.perf_counter()
|
183 |
# Initialize the last zip creation time to be ZIP_INTERVAL_SECONDS in the past
|
|
|
291 |
"""
|
292 |
Sets a stop flag in the session_state and attempts to terminate the active process.
|
293 |
"""
|
|
|
|
|
294 |
|
295 |
session_state.stop_event.set()
|
296 |
|
|
|
310 |
"""
|
311 |
Opens the latest output directory in the native file explorer.
|
312 |
"""
|
|
|
|
|
313 |
|
314 |
latest_run_dir = session_state.latest_run_dir
|
315 |
if not latest_run_dir or not os.path.exists(latest_run_dir):
|
|
|
326 |
except Exception as e:
|
327 |
return f"Failed to open directory: {e}", session_state
|
328 |
|
329 |
+
def update_openrouter_api_key(openrouter_api_key, session_state: SessionState):
|
330 |
+
"""Updates the OpenRouter API key in the session state."""
|
331 |
+
session_state.openrouter_api_key = openrouter_api_key
|
332 |
+
return openrouter_api_key, session_state
|
333 |
+
|
334 |
+
def update_model_radio(llm_model, session_state: SessionState):
|
335 |
+
"""Updates the llm_model in the session state"""
|
336 |
+
session_state.llm_model = llm_model
|
337 |
+
return llm_model, session_state
|
338 |
+
|
339 |
+
def update_speedvsdetail_radio(speedvsdetail, session_state: SessionState):
|
340 |
+
"""Updates the speedvsdetail in the session state"""
|
341 |
+
session_state.speedvsdetail = speedvsdetail
|
342 |
+
return speedvsdetail, session_state
|
343 |
+
|
344 |
+
def initialize_settings(session_state: SessionState):
|
345 |
+
"""Initializes the settings from session_state, if available."""
|
346 |
+
return (session_state.openrouter_api_key,
|
347 |
+
session_state.llm_model,
|
348 |
+
session_state.speedvsdetail,
|
349 |
+
session_state)
|
350 |
+
|
351 |
+
|
352 |
# Build the Gradio UI using Blocks.
|
353 |
with gr.Blocks(title="PlanExe") as demo_text2plan:
|
354 |
gr.Markdown("# PlanExe: crack open pandora’s box of ideas")
|
|
|
395 |
label="Speed vs Detail",
|
396 |
interactive=True
|
397 |
)
|
398 |
+
openrouter_api_key_text = gr.Textbox(
|
399 |
+
label="OpenRouter API Key",
|
400 |
+
type="password",
|
401 |
+
placeholder="Enter your OpenRouter API key (optional)",
|
402 |
+
info="Sign up at [OpenRouter](https://openrouter.ai/) to get an API key. A small top-up (e.g. 5 USD) is needed to access paid models."
|
403 |
+
)
|
404 |
|
405 |
with gr.Tab("Join the community"):
|
406 |
gr.Markdown("""
|
407 |
- [GitHub](https://github.com/neoneye/PlanExe) the source code.
|
408 |
- [Discord](https://neoneye.github.io/PlanExe-web/discord) join the community. Suggestions, feedback, and questions are welcome.
|
409 |
""")
|
410 |
+
|
411 |
+
# Manage the state of the current user
|
412 |
session_state = gr.State(SessionState())
|
413 |
|
414 |
# Submit and Retry buttons call run_planner and update the state.
|
415 |
submit_btn.click(
|
416 |
fn=run_planner,
|
417 |
+
inputs=[submit_btn, prompt_input, session_state],
|
418 |
outputs=[output_markdown, download_output, session_state]
|
419 |
)
|
420 |
retry_btn.click(
|
421 |
fn=run_planner,
|
422 |
+
inputs=[retry_btn, prompt_input, session_state],
|
423 |
outputs=[output_markdown, download_output, session_state]
|
424 |
)
|
425 |
# The Stop button uses the state to terminate the running process.
|
|
|
435 |
outputs=[status_markdown, session_state]
|
436 |
)
|
437 |
|
438 |
+
openrouter_api_key_text.change(
|
439 |
+
fn=update_openrouter_api_key,
|
440 |
+
inputs=[openrouter_api_key_text, session_state],
|
441 |
+
outputs=[openrouter_api_key_text, session_state]
|
442 |
+
)
|
443 |
+
model_radio.change(
|
444 |
+
fn=update_model_radio,
|
445 |
+
inputs=[model_radio, session_state],
|
446 |
+
outputs=[model_radio, session_state]
|
447 |
+
)
|
448 |
+
speedvsdetail_radio.change(
|
449 |
+
fn=update_speedvsdetail_radio,
|
450 |
+
inputs=[speedvsdetail_radio, session_state],
|
451 |
+
outputs=[speedvsdetail_radio, session_state]
|
452 |
+
)
|
453 |
+
|
454 |
+
|
455 |
+
demo_text2plan.load(
|
456 |
+
fn=initialize_settings,
|
457 |
+
inputs=[session_state],
|
458 |
+
outputs=[openrouter_api_key_text, model_radio, speedvsdetail_radio, session_state]
|
459 |
+
)
|
460 |
+
|
461 |
if __name__ == "__main__":
|
462 |
# print("Environment variables Gradio:\n" + get_env_as_string() + "\n\n\n")
|
463 |
|