Simon Strandgaard commited on
Commit
459ddcb
·
2 Parent(s): 0e11928 ceb7ac2

Merge branch 'user-api-key'

Browse files
Files changed (1) hide show
  1. 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 the current session.
 
 
 
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, llm_model, speedvsdetail, session_state: SessionState):
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
- # Create a hidden state to hold our SessionState instance.
 
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, model_radio, speedvsdetail_radio, session_state],
379
  outputs=[output_markdown, download_output, session_state]
380
  )
381
  retry_btn.click(
382
  fn=run_planner,
383
- inputs=[retry_btn, prompt_input, model_radio, speedvsdetail_radio, session_state],
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