kshitijthakkar commited on
Commit
dab7275
Β·
1 Parent(s): a85f4e8

feat: Add Settings screen for API key configuration

Browse files

- Create new Settings screen for user-provided API keys (Gemini & HuggingFace)
- Add comprehensive UI with key validation, testing, and usage guides
- Prevent API key exposure via Gradio API using api_name=False
- Add settings navigation button in sidebar
- Update all navigation functions to include settings screen
- Session-only storage for security (no server-side persistence)
- Addresses hackathon judge feedback for credit safety

Files changed (2) hide show
  1. app.py +67 -14
  2. screens/settings.py +214 -0
app.py CHANGED
@@ -60,6 +60,7 @@ from screens.chat import (
60
  on_quick_action
61
  )
62
  from screens.documentation import create_documentation_screen
 
63
  from screens.mcp_helpers import (
64
  call_analyze_leaderboard_sync,
65
  call_debug_trace_sync,
@@ -1593,6 +1594,7 @@ with gr.Blocks(title="TraceMind-AI", theme=theme) as app:
1593
  chat_nav_btn = gr.Button("πŸ€– Agent Chat", variant="secondary", size="lg")
1594
  synthetic_data_nav_btn = gr.Button("πŸ”¬ Synthetic Data", variant="secondary", size="lg")
1595
  docs_nav_btn = gr.Button("πŸ“š Documentation", variant="secondary", size="lg")
 
1596
 
1597
  gr.Markdown("---")
1598
 
@@ -2419,6 +2421,11 @@ with gr.Blocks(title="TraceMind-AI", theme=theme) as app:
2419
  # ============================================================================
2420
  documentation_screen = create_documentation_screen()
2421
 
 
 
 
 
 
2422
  # ============================================================================
2423
  # Evaluation Helper Functions
2424
  # ============================================================================
@@ -2790,6 +2797,7 @@ No historical data available for **{model}**.
2790
  synthetic_data_screen: gr.update(visible=False),
2791
  new_evaluation_screen: gr.update(visible=False),
2792
  documentation_screen: gr.update(visible=False),
 
2793
  dashboard_nav_btn: gr.update(variant="primary"),
2794
  leaderboard_nav_btn: gr.update(variant="secondary"),
2795
  new_eval_nav_btn: gr.update(variant="secondary"),
@@ -2797,6 +2805,7 @@ No historical data available for **{model}**.
2797
  chat_nav_btn: gr.update(variant="secondary"),
2798
  synthetic_data_nav_btn: gr.update(variant="secondary"),
2799
  docs_nav_btn: gr.update(variant="secondary"),
 
2800
  }
2801
  result.update(dashboard_updates)
2802
  return result
@@ -2813,6 +2822,7 @@ No historical data available for **{model}**.
2813
  synthetic_data_screen: gr.update(visible=False),
2814
  new_evaluation_screen: gr.update(visible=False),
2815
  documentation_screen: gr.update(visible=False),
 
2816
  dashboard_nav_btn: gr.update(variant="secondary"),
2817
  leaderboard_nav_btn: gr.update(variant="primary"),
2818
  new_eval_nav_btn: gr.update(variant="secondary"),
@@ -2820,6 +2830,7 @@ No historical data available for **{model}**.
2820
  chat_nav_btn: gr.update(variant="secondary"),
2821
  synthetic_data_nav_btn: gr.update(variant="secondary"),
2822
  docs_nav_btn: gr.update(variant="secondary"),
 
2823
  }
2824
 
2825
  def navigate_to_new_evaluation():
@@ -2834,6 +2845,7 @@ No historical data available for **{model}**.
2834
  synthetic_data_screen: gr.update(visible=False),
2835
  new_evaluation_screen: gr.update(visible=True),
2836
  documentation_screen: gr.update(visible=False),
 
2837
  dashboard_nav_btn: gr.update(variant="secondary"),
2838
  leaderboard_nav_btn: gr.update(variant="secondary"),
2839
  new_eval_nav_btn: gr.update(variant="primary"),
@@ -2841,6 +2853,7 @@ No historical data available for **{model}**.
2841
  chat_nav_btn: gr.update(variant="secondary"),
2842
  synthetic_data_nav_btn: gr.update(variant="secondary"),
2843
  docs_nav_btn: gr.update(variant="secondary"),
 
2844
  }
2845
 
2846
  def navigate_to_compare():
@@ -2867,6 +2880,7 @@ No historical data available for **{model}**.
2867
  synthetic_data_screen: gr.update(visible=False),
2868
  new_evaluation_screen: gr.update(visible=False),
2869
  documentation_screen: gr.update(visible=False),
 
2870
  dashboard_nav_btn: gr.update(variant="secondary"),
2871
  leaderboard_nav_btn: gr.update(variant="secondary"),
2872
  new_eval_nav_btn: gr.update(variant="secondary"),
@@ -2896,6 +2910,7 @@ No historical data available for **{model}**.
2896
  chat_nav_btn: gr.update(variant="secondary"),
2897
  synthetic_data_nav_btn: gr.update(variant="secondary"),
2898
  docs_nav_btn: gr.update(variant="secondary"),
 
2899
  }
2900
 
2901
  def navigate_to_chat():
@@ -2910,6 +2925,7 @@ No historical data available for **{model}**.
2910
  synthetic_data_screen: gr.update(visible=False),
2911
  new_evaluation_screen: gr.update(visible=False),
2912
  documentation_screen: gr.update(visible=False),
 
2913
  dashboard_nav_btn: gr.update(variant="secondary"),
2914
  leaderboard_nav_btn: gr.update(variant="secondary"),
2915
  new_eval_nav_btn: gr.update(variant="secondary"),
@@ -2917,6 +2933,7 @@ No historical data available for **{model}**.
2917
  chat_nav_btn: gr.update(variant="primary"),
2918
  synthetic_data_nav_btn: gr.update(variant="secondary"),
2919
  docs_nav_btn: gr.update(variant="secondary"),
 
2920
  }
2921
 
2922
  def navigate_to_synthetic_data():
@@ -2931,6 +2948,7 @@ No historical data available for **{model}**.
2931
  synthetic_data_screen: gr.update(visible=True),
2932
  new_evaluation_screen: gr.update(visible=False),
2933
  documentation_screen: gr.update(visible=False),
 
2934
  dashboard_nav_btn: gr.update(variant="secondary"),
2935
  leaderboard_nav_btn: gr.update(variant="secondary"),
2936
  new_eval_nav_btn: gr.update(variant="secondary"),
@@ -2938,6 +2956,7 @@ No historical data available for **{model}**.
2938
  chat_nav_btn: gr.update(variant="secondary"),
2939
  synthetic_data_nav_btn: gr.update(variant="primary"),
2940
  docs_nav_btn: gr.update(variant="secondary"),
 
2941
  }
2942
 
2943
  def navigate_to_documentation():
@@ -2952,6 +2971,7 @@ No historical data available for **{model}**.
2952
  synthetic_data_screen: gr.update(visible=False),
2953
  new_evaluation_screen: gr.update(visible=False),
2954
  documentation_screen: gr.update(visible=True),
 
2955
  dashboard_nav_btn: gr.update(variant="secondary"),
2956
  leaderboard_nav_btn: gr.update(variant="secondary"),
2957
  new_eval_nav_btn: gr.update(variant="secondary"),
@@ -2959,6 +2979,30 @@ No historical data available for **{model}**.
2959
  chat_nav_btn: gr.update(variant="secondary"),
2960
  synthetic_data_nav_btn: gr.update(variant="secondary"),
2961
  docs_nav_btn: gr.update(variant="primary"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2962
  }
2963
 
2964
  # Synthetic Data Generator Callbacks
@@ -3277,24 +3321,24 @@ Result: {result}
3277
  fn=navigate_to_dashboard,
3278
  outputs=[
3279
  dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen,
3280
- new_evaluation_screen, documentation_screen,
3281
- dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn
3282
  ] + list(dashboard_components.values())
3283
  )
3284
 
3285
  leaderboard_nav_btn.click(
3286
  fn=navigate_to_leaderboard,
3287
  outputs=[
3288
- dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen, new_evaluation_screen, documentation_screen,
3289
- dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn
3290
  ]
3291
  )
3292
 
3293
  new_eval_nav_btn.click(
3294
  fn=navigate_to_new_evaluation,
3295
  outputs=[
3296
- dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen, new_evaluation_screen, documentation_screen,
3297
- dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn
3298
  ]
3299
  )
3300
 
@@ -3302,7 +3346,7 @@ Result: {result}
3302
  fn=navigate_to_compare,
3303
  outputs=[
3304
  dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen,
3305
- new_evaluation_screen, documentation_screen,
3306
  dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn,
3307
  compare_components['compare_run_a_dropdown'], compare_components['compare_run_b_dropdown']
3308
  ]
@@ -3312,16 +3356,16 @@ Result: {result}
3312
  fn=navigate_to_chat,
3313
  outputs=[
3314
  dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen,
3315
- new_evaluation_screen, documentation_screen,
3316
- dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn
3317
  ]
3318
  )
3319
  synthetic_data_nav_btn.click(
3320
  fn=navigate_to_synthetic_data,
3321
  outputs=[
3322
  dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen,
3323
- new_evaluation_screen, documentation_screen,
3324
- dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn
3325
  ]
3326
  )
3327
 
@@ -3329,8 +3373,17 @@ Result: {result}
3329
  fn=navigate_to_documentation,
3330
  outputs=[
3331
  dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen,
3332
- new_evaluation_screen, documentation_screen,
3333
- dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn
 
 
 
 
 
 
 
 
 
3334
  ]
3335
  )
3336
 
@@ -3352,7 +3405,7 @@ Result: {result}
3352
  fn=navigate_to_leaderboard,
3353
  outputs=[
3354
  dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen, new_evaluation_screen, documentation_screen,
3355
- dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn
3356
  ]
3357
  )
3358
 
 
60
  on_quick_action
61
  )
62
  from screens.documentation import create_documentation_screen
63
+ from screens.settings import create_settings_screen
64
  from screens.mcp_helpers import (
65
  call_analyze_leaderboard_sync,
66
  call_debug_trace_sync,
 
1594
  chat_nav_btn = gr.Button("πŸ€– Agent Chat", variant="secondary", size="lg")
1595
  synthetic_data_nav_btn = gr.Button("πŸ”¬ Synthetic Data", variant="secondary", size="lg")
1596
  docs_nav_btn = gr.Button("πŸ“š Documentation", variant="secondary", size="lg")
1597
+ settings_nav_btn = gr.Button("βš™οΈ Settings", variant="secondary", size="lg")
1598
 
1599
  gr.Markdown("---")
1600
 
 
2421
  # ============================================================================
2422
  documentation_screen = create_documentation_screen()
2423
 
2424
+ # ============================================================================
2425
+ # Screen 10: Settings
2426
+ # ============================================================================
2427
+ settings_screen, gemini_api_key_input, hf_token_input = create_settings_screen()
2428
+
2429
  # ============================================================================
2430
  # Evaluation Helper Functions
2431
  # ============================================================================
 
2797
  synthetic_data_screen: gr.update(visible=False),
2798
  new_evaluation_screen: gr.update(visible=False),
2799
  documentation_screen: gr.update(visible=False),
2800
+ settings_screen: gr.update(visible=False),
2801
  dashboard_nav_btn: gr.update(variant="primary"),
2802
  leaderboard_nav_btn: gr.update(variant="secondary"),
2803
  new_eval_nav_btn: gr.update(variant="secondary"),
 
2805
  chat_nav_btn: gr.update(variant="secondary"),
2806
  synthetic_data_nav_btn: gr.update(variant="secondary"),
2807
  docs_nav_btn: gr.update(variant="secondary"),
2808
+ settings_nav_btn: gr.update(variant="secondary"),
2809
  }
2810
  result.update(dashboard_updates)
2811
  return result
 
2822
  synthetic_data_screen: gr.update(visible=False),
2823
  new_evaluation_screen: gr.update(visible=False),
2824
  documentation_screen: gr.update(visible=False),
2825
+ settings_screen: gr.update(visible=False),
2826
  dashboard_nav_btn: gr.update(variant="secondary"),
2827
  leaderboard_nav_btn: gr.update(variant="primary"),
2828
  new_eval_nav_btn: gr.update(variant="secondary"),
 
2830
  chat_nav_btn: gr.update(variant="secondary"),
2831
  synthetic_data_nav_btn: gr.update(variant="secondary"),
2832
  docs_nav_btn: gr.update(variant="secondary"),
2833
+ settings_nav_btn: gr.update(variant="secondary"),
2834
  }
2835
 
2836
  def navigate_to_new_evaluation():
 
2845
  synthetic_data_screen: gr.update(visible=False),
2846
  new_evaluation_screen: gr.update(visible=True),
2847
  documentation_screen: gr.update(visible=False),
2848
+ settings_screen: gr.update(visible=False),
2849
  dashboard_nav_btn: gr.update(variant="secondary"),
2850
  leaderboard_nav_btn: gr.update(variant="secondary"),
2851
  new_eval_nav_btn: gr.update(variant="primary"),
 
2853
  chat_nav_btn: gr.update(variant="secondary"),
2854
  synthetic_data_nav_btn: gr.update(variant="secondary"),
2855
  docs_nav_btn: gr.update(variant="secondary"),
2856
+ settings_nav_btn: gr.update(variant="secondary"),
2857
  }
2858
 
2859
  def navigate_to_compare():
 
2880
  synthetic_data_screen: gr.update(visible=False),
2881
  new_evaluation_screen: gr.update(visible=False),
2882
  documentation_screen: gr.update(visible=False),
2883
+ settings_screen: gr.update(visible=False),
2884
  dashboard_nav_btn: gr.update(variant="secondary"),
2885
  leaderboard_nav_btn: gr.update(variant="secondary"),
2886
  new_eval_nav_btn: gr.update(variant="secondary"),
 
2910
  chat_nav_btn: gr.update(variant="secondary"),
2911
  synthetic_data_nav_btn: gr.update(variant="secondary"),
2912
  docs_nav_btn: gr.update(variant="secondary"),
2913
+ settings_nav_btn: gr.update(variant="secondary"),
2914
  }
2915
 
2916
  def navigate_to_chat():
 
2925
  synthetic_data_screen: gr.update(visible=False),
2926
  new_evaluation_screen: gr.update(visible=False),
2927
  documentation_screen: gr.update(visible=False),
2928
+ settings_screen: gr.update(visible=False),
2929
  dashboard_nav_btn: gr.update(variant="secondary"),
2930
  leaderboard_nav_btn: gr.update(variant="secondary"),
2931
  new_eval_nav_btn: gr.update(variant="secondary"),
 
2933
  chat_nav_btn: gr.update(variant="primary"),
2934
  synthetic_data_nav_btn: gr.update(variant="secondary"),
2935
  docs_nav_btn: gr.update(variant="secondary"),
2936
+ settings_nav_btn: gr.update(variant="secondary"),
2937
  }
2938
 
2939
  def navigate_to_synthetic_data():
 
2948
  synthetic_data_screen: gr.update(visible=True),
2949
  new_evaluation_screen: gr.update(visible=False),
2950
  documentation_screen: gr.update(visible=False),
2951
+ settings_screen: gr.update(visible=False),
2952
  dashboard_nav_btn: gr.update(variant="secondary"),
2953
  leaderboard_nav_btn: gr.update(variant="secondary"),
2954
  new_eval_nav_btn: gr.update(variant="secondary"),
 
2956
  chat_nav_btn: gr.update(variant="secondary"),
2957
  synthetic_data_nav_btn: gr.update(variant="primary"),
2958
  docs_nav_btn: gr.update(variant="secondary"),
2959
+ settings_nav_btn: gr.update(variant="secondary"),
2960
  }
2961
 
2962
  def navigate_to_documentation():
 
2971
  synthetic_data_screen: gr.update(visible=False),
2972
  new_evaluation_screen: gr.update(visible=False),
2973
  documentation_screen: gr.update(visible=True),
2974
+ settings_screen: gr.update(visible=False),
2975
  dashboard_nav_btn: gr.update(variant="secondary"),
2976
  leaderboard_nav_btn: gr.update(variant="secondary"),
2977
  new_eval_nav_btn: gr.update(variant="secondary"),
 
2979
  chat_nav_btn: gr.update(variant="secondary"),
2980
  synthetic_data_nav_btn: gr.update(variant="secondary"),
2981
  docs_nav_btn: gr.update(variant="primary"),
2982
+ settings_nav_btn: gr.update(variant="secondary"),
2983
+ }
2984
+
2985
+ def navigate_to_settings():
2986
+ """Navigate to settings screen"""
2987
+ return {
2988
+ dashboard_screen: gr.update(visible=False),
2989
+ leaderboard_screen: gr.update(visible=False),
2990
+ run_detail_screen: gr.update(visible=False),
2991
+ trace_detail_screen: gr.update(visible=False),
2992
+ compare_screen: gr.update(visible=False),
2993
+ chat_screen: gr.update(visible=False),
2994
+ synthetic_data_screen: gr.update(visible=False),
2995
+ new_evaluation_screen: gr.update(visible=False),
2996
+ documentation_screen: gr.update(visible=False),
2997
+ settings_screen: gr.update(visible=True),
2998
+ dashboard_nav_btn: gr.update(variant="secondary"),
2999
+ leaderboard_nav_btn: gr.update(variant="secondary"),
3000
+ new_eval_nav_btn: gr.update(variant="secondary"),
3001
+ compare_nav_btn: gr.update(variant="secondary"),
3002
+ chat_nav_btn: gr.update(variant="secondary"),
3003
+ synthetic_data_nav_btn: gr.update(variant="secondary"),
3004
+ docs_nav_btn: gr.update(variant="secondary"),
3005
+ settings_nav_btn: gr.update(variant="primary"),
3006
  }
3007
 
3008
  # Synthetic Data Generator Callbacks
 
3321
  fn=navigate_to_dashboard,
3322
  outputs=[
3323
  dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen,
3324
+ new_evaluation_screen, documentation_screen, settings_screen,
3325
+ dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn, settings_nav_btn
3326
  ] + list(dashboard_components.values())
3327
  )
3328
 
3329
  leaderboard_nav_btn.click(
3330
  fn=navigate_to_leaderboard,
3331
  outputs=[
3332
+ dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen, new_evaluation_screen, documentation_screen, settings_screen,
3333
+ dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn, settings_nav_btn
3334
  ]
3335
  )
3336
 
3337
  new_eval_nav_btn.click(
3338
  fn=navigate_to_new_evaluation,
3339
  outputs=[
3340
+ dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen, new_evaluation_screen, documentation_screen, settings_screen,
3341
+ dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn, settings_nav_btn
3342
  ]
3343
  )
3344
 
 
3346
  fn=navigate_to_compare,
3347
  outputs=[
3348
  dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen,
3349
+ new_evaluation_screen, documentation_screen, settings_screen,
3350
  dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn,
3351
  compare_components['compare_run_a_dropdown'], compare_components['compare_run_b_dropdown']
3352
  ]
 
3356
  fn=navigate_to_chat,
3357
  outputs=[
3358
  dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen,
3359
+ new_evaluation_screen, documentation_screen, settings_screen,
3360
+ dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn, settings_nav_btn
3361
  ]
3362
  )
3363
  synthetic_data_nav_btn.click(
3364
  fn=navigate_to_synthetic_data,
3365
  outputs=[
3366
  dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen,
3367
+ new_evaluation_screen, documentation_screen, settings_screen,
3368
+ dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn, settings_nav_btn
3369
  ]
3370
  )
3371
 
 
3373
  fn=navigate_to_documentation,
3374
  outputs=[
3375
  dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen,
3376
+ new_evaluation_screen, documentation_screen, settings_screen,
3377
+ dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn, settings_nav_btn
3378
+ ]
3379
+ )
3380
+
3381
+ settings_nav_btn.click(
3382
+ fn=navigate_to_settings,
3383
+ outputs=[
3384
+ dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen,
3385
+ new_evaluation_screen, documentation_screen, settings_screen,
3386
+ dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn, settings_nav_btn
3387
  ]
3388
  )
3389
 
 
3405
  fn=navigate_to_leaderboard,
3406
  outputs=[
3407
  dashboard_screen, leaderboard_screen, run_detail_screen, trace_detail_screen, compare_screen, chat_screen, synthetic_data_screen, new_evaluation_screen, documentation_screen,
3408
+ dashboard_nav_btn, leaderboard_nav_btn, new_eval_nav_btn, compare_nav_btn, chat_nav_btn, synthetic_data_nav_btn, docs_nav_btn, settings_nav_btn
3409
  ]
3410
  )
3411
 
screens/settings.py ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Settings Screen for TraceMind-AI
3
+ Allows users to configure API keys for Gemini and HuggingFace
4
+ """
5
+
6
+ import gradio as gr
7
+ import os
8
+
9
+
10
+ def create_settings_screen():
11
+ """
12
+ Create the settings screen for API key configuration
13
+
14
+ Returns:
15
+ gr.Column: Gradio Column component for settings (can be shown/hidden)
16
+ """
17
+ with gr.Column(visible=False) as settings_interface:
18
+ gr.Markdown("""
19
+ # βš™οΈ Settings
20
+
21
+ Configure your API keys to use TraceMind features. These keys are stored only in your browser session and are never saved to our servers.
22
+ """)
23
+
24
+ with gr.Accordion("πŸ”‘ API Key Configuration", open=True):
25
+ gr.Markdown("""
26
+ ### Why provide API keys?
27
+
28
+ TraceMind uses external services to provide intelligent analysis and insights:
29
+ - **Google Gemini API**: Powers the MCP server for leaderboard analysis, cost estimation, and trace debugging
30
+ - **HuggingFace Token**: Required to access evaluation datasets and results
31
+
32
+ **For Judges & Visitors**: Please enter your own API keys to prevent credit issues during evaluation.
33
+ """)
34
+
35
+ # Gemini API Key
36
+ with gr.Row():
37
+ with gr.Column(scale=4):
38
+ gemini_api_key = gr.Textbox(
39
+ label="Google Gemini API Key",
40
+ placeholder="Enter your Gemini API key (starts with 'AIza...')",
41
+ type="password",
42
+ value=os.environ.get("GEMINI_API_KEY", ""),
43
+ info="Get your free API key at: https://ai.google.dev/"
44
+ )
45
+ with gr.Column(scale=1):
46
+ gemini_status = gr.Markdown("βšͺ Not configured")
47
+
48
+ # HuggingFace Token
49
+ with gr.Row():
50
+ with gr.Column(scale=4):
51
+ hf_token = gr.Textbox(
52
+ label="HuggingFace Token",
53
+ placeholder="Enter your HF token (starts with 'hf_...')",
54
+ type="password",
55
+ value=os.environ.get("HF_TOKEN", ""),
56
+ info="Get your token at: https://huggingface.co/settings/tokens"
57
+ )
58
+ with gr.Column(scale=1):
59
+ hf_status = gr.Markdown("βšͺ Not configured")
60
+
61
+ # Save button
62
+ with gr.Row():
63
+ save_btn = gr.Button("πŸ’Ύ Save API Keys", variant="primary")
64
+ test_btn = gr.Button("πŸ§ͺ Test Connection", variant="secondary")
65
+
66
+ # Status message
67
+ status_message = gr.Markdown("")
68
+
69
+ with gr.Accordion("πŸ“– How to Get API Keys", open=False):
70
+ gr.Markdown("""
71
+ ### Google Gemini API Key
72
+
73
+ 1. Go to [Google AI Studio](https://ai.google.dev/)
74
+ 2. Click "Get API Key" in the top right
75
+ 3. Create a new project or select an existing one
76
+ 4. Generate an API key
77
+ 5. Copy the key (starts with `AIza...`)
78
+
79
+ **Free Tier**: 60 requests per minute, suitable for testing and demos
80
+
81
+ ---
82
+
83
+ ### HuggingFace Token
84
+
85
+ 1. Go to [HuggingFace Settings](https://huggingface.co/settings/tokens)
86
+ 2. Click "New token"
87
+ 3. Give it a name (e.g., "TraceMind Access")
88
+ 4. Select "Read" permissions
89
+ 5. Create and copy the token (starts with `hf_...`)
90
+
91
+ **Note**: Read-only access is sufficient for viewing datasets
92
+ """)
93
+
94
+ with gr.Accordion("πŸ”’ Privacy & Security", open=False):
95
+ gr.Markdown("""
96
+ ### Your Privacy Matters
97
+
98
+ - βœ… **Session-only storage**: API keys are stored only in your browser session
99
+ - βœ… **No server storage**: Keys are never saved to our servers or databases
100
+ - βœ… **HTTPS encryption**: All API calls are made over secure connections
101
+ - βœ… **No logging**: API keys are not logged or tracked
102
+
103
+ ### Best Practices
104
+
105
+ - πŸ” Use dedicated API keys for testing/demos
106
+ - πŸ”„ Rotate your keys regularly
107
+ - 🚫 Don't share your keys publicly
108
+ - πŸ“Š Monitor your API usage on provider dashboards
109
+
110
+ ### Rate Limits
111
+
112
+ **Gemini API (Free Tier)**:
113
+ - 60 requests per minute
114
+ - 1,500 requests per day
115
+
116
+ **HuggingFace**:
117
+ - Read access: No strict limits
118
+ - Public datasets: Unlimited reads
119
+ """)
120
+
121
+ # Define save functionality
122
+ def save_api_keys(gemini_key, hf_key):
123
+ """Save API keys to session"""
124
+ messages = []
125
+
126
+ # Validate and save Gemini API key
127
+ if gemini_key and gemini_key.strip():
128
+ if gemini_key.startswith("AIza"):
129
+ os.environ["GEMINI_API_KEY"] = gemini_key.strip()
130
+ messages.append("βœ… Gemini API key saved")
131
+ gemini_status_text = "βœ… Configured"
132
+ else:
133
+ messages.append("⚠️ Invalid Gemini API key format (should start with 'AIza')")
134
+ gemini_status_text = "❌ Invalid format"
135
+ else:
136
+ messages.append("⚠️ Gemini API key not provided")
137
+ gemini_status_text = "βšͺ Not configured"
138
+
139
+ # Validate and save HuggingFace token
140
+ if hf_key and hf_key.strip():
141
+ if hf_key.startswith("hf_"):
142
+ os.environ["HF_TOKEN"] = hf_key.strip()
143
+ messages.append("βœ… HuggingFace token saved")
144
+ hf_status_text = "βœ… Configured"
145
+ else:
146
+ messages.append("⚠️ Invalid HuggingFace token format (should start with 'hf_')")
147
+ hf_status_text = "❌ Invalid format"
148
+ else:
149
+ messages.append("⚠️ HuggingFace token not provided")
150
+ hf_status_text = "βšͺ Not configured"
151
+
152
+ status_msg = "\n\n".join(messages)
153
+ status_msg += "\n\n**Note**: Keys are saved for this session only and will be used for MCP server calls."
154
+
155
+ return status_msg, gemini_status_text, hf_status_text
156
+
157
+ def test_api_keys(gemini_key, hf_key):
158
+ """Test API key connections"""
159
+ results = []
160
+
161
+ # Test Gemini API
162
+ if gemini_key and gemini_key.strip():
163
+ try:
164
+ import google.generativeai as genai
165
+ genai.configure(api_key=gemini_key.strip())
166
+ # Try to list models as a test
167
+ models = genai.list_models()
168
+ results.append("βœ… **Gemini API**: Connection successful!")
169
+ except Exception as e:
170
+ results.append(f"❌ **Gemini API**: Connection failed - {str(e)}")
171
+ else:
172
+ results.append("⚠️ **Gemini API**: No key provided")
173
+
174
+ # Test HuggingFace token
175
+ if hf_key and hf_key.strip():
176
+ try:
177
+ from huggingface_hub import HfApi
178
+ api = HfApi(token=hf_key.strip())
179
+ # Try to get user info as a test
180
+ user_info = api.whoami()
181
+ results.append(f"βœ… **HuggingFace**: Connection successful! (User: {user_info['name']})")
182
+ except Exception as e:
183
+ results.append(f"❌ **HuggingFace**: Connection failed - {str(e)}")
184
+ else:
185
+ results.append("⚠️ **HuggingFace**: No token provided")
186
+
187
+ return "\n\n".join(results)
188
+
189
+ # Wire up button events (api_name=False to prevent API key exposure)
190
+ save_btn.click(
191
+ fn=save_api_keys,
192
+ inputs=[gemini_api_key, hf_token],
193
+ outputs=[status_message, gemini_status, hf_status],
194
+ api_name=False # IMPORTANT: Prevents API key exposure via Gradio API
195
+ )
196
+
197
+ test_btn.click(
198
+ fn=test_api_keys,
199
+ inputs=[gemini_api_key, hf_token],
200
+ outputs=[status_message],
201
+ api_name=False # IMPORTANT: Prevents API key exposure via Gradio API
202
+ )
203
+
204
+ # Return both the interface and the input components for external access
205
+ return settings_interface, gemini_api_key, hf_token
206
+
207
+
208
+ if __name__ == "__main__":
209
+ # For standalone testing
210
+ with gr.Blocks() as demo:
211
+ settings_screen, _, _ = create_settings_screen()
212
+ # Make it visible for standalone testing
213
+ settings_screen.visible = True
214
+ demo.launch()