OzanSevindir commited on
Commit
7c07793
·
verified ·
1 Parent(s): 634c406

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. deep_research.py +49 -7
  2. model_config.py +66 -0
  3. research_manager.py +40 -13
deep_research.py CHANGED
@@ -7,7 +7,7 @@ import re
7
  load_dotenv(override=True)
8
 
9
 
10
- async def run_research(query: str, progress=gr.Progress()):
11
  """Run research and yield updates for both report and references"""
12
  status_messages = []
13
  final_report_md = ""
@@ -39,7 +39,7 @@ async def run_research(query: str, progress=gr.Progress()):
39
  '''
40
 
41
  # Collect all chunks and parse structured messages
42
- async for chunk in ResearchManager().run(query):
43
  # Parse structured messages (format: TYPE|data)
44
  if "|" in chunk:
45
  msg_type, msg_data = chunk.split("|", 1)
@@ -468,11 +468,36 @@ textarea:focus {
468
  font-size: 1.125rem;
469
  }
470
 
471
- /* Primary Run Button - Minimal & Modern */
472
  .button-row-bottom {
473
  display: flex;
474
  gap: 0.75rem;
475
  margin-top: 1rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  }
477
 
478
  button[variant="primary"] {
@@ -762,7 +787,24 @@ with gr.Blocks(theme=luntre_theme, css=custom_css, title="Luntre AI - Deep Resea
762
  )
763
 
764
  with gr.Row(elem_classes="button-row-bottom"):
765
- run_btn = gr.Button("🚀 Run Research", variant="primary", scale=1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
766
  # Hidden buttons for programmatic access
767
  edit_btn = gr.Button("Edit", variant="secondary", visible=False, elem_id="edit-query-btn")
768
  rewrite_btn = gr.Button("Rewrite", variant="secondary", visible=False, elem_id="rewrite-btn")
@@ -793,7 +835,7 @@ with gr.Blocks(theme=luntre_theme, css=custom_css, title="Luntre AI - Deep Resea
793
  queue=False
794
  ).then(
795
  fn=run_research,
796
- inputs=[current_query_state],
797
  outputs=[report_output, references_output]
798
  )
799
 
@@ -808,7 +850,7 @@ with gr.Blocks(theme=luntre_theme, css=custom_css, title="Luntre AI - Deep Resea
808
  # Rewrite (run again with same query)
809
  rewrite_event = rewrite_btn.click(
810
  fn=run_research,
811
- inputs=[current_query_state],
812
  outputs=[report_output, references_output]
813
  )
814
 
@@ -825,7 +867,7 @@ with gr.Blocks(theme=luntre_theme, css=custom_css, title="Luntre AI - Deep Resea
825
  queue=False
826
  ).then(
827
  fn=run_research,
828
- inputs=[current_query_state],
829
  outputs=[report_output, references_output]
830
  )
831
 
 
7
  load_dotenv(override=True)
8
 
9
 
10
+ async def run_research(query: str, model_choice: str, progress=gr.Progress()):
11
  """Run research and yield updates for both report and references"""
12
  status_messages = []
13
  final_report_md = ""
 
39
  '''
40
 
41
  # Collect all chunks and parse structured messages
42
+ async for chunk in ResearchManager(model_choice).run(query):
43
  # Parse structured messages (format: TYPE|data)
44
  if "|" in chunk:
45
  msg_type, msg_data = chunk.split("|", 1)
 
468
  font-size: 1.125rem;
469
  }
470
 
471
+ /* Input Row with Button and Dropdown */
472
  .button-row-bottom {
473
  display: flex;
474
  gap: 0.75rem;
475
  margin-top: 1rem;
476
+ align-items: center;
477
+ }
478
+
479
+ /* Model Selector Dropdown */
480
+ .model-selector select {
481
+ background: rgba(45, 45, 45, 0.6) !important;
482
+ border: 1px solid rgba(55, 65, 81, 0.5) !important;
483
+ color: #9CA3AF !important;
484
+ border-radius: 10px !important;
485
+ padding: 0.875rem 1rem !important;
486
+ font-size: 0.875rem !important;
487
+ font-weight: 500 !important;
488
+ transition: all 0.2s ease !important;
489
+ cursor: pointer !important;
490
+ }
491
+
492
+ .model-selector select:hover {
493
+ border-color: rgba(16, 185, 129, 0.4) !important;
494
+ background: rgba(45, 45, 45, 0.8) !important;
495
+ }
496
+
497
+ .model-selector select:focus {
498
+ border-color: rgba(16, 185, 129, 0.6) !important;
499
+ box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1) !important;
500
+ outline: none !important;
501
  }
502
 
503
  button[variant="primary"] {
 
787
  )
788
 
789
  with gr.Row(elem_classes="button-row-bottom"):
790
+ run_btn = gr.Button("🚀 Run Research", variant="primary", scale=2)
791
+
792
+ model_selector = gr.Dropdown(
793
+ choices=[
794
+ "gemini-2.5-flash (Default)",
795
+ "gemini-2.0-flash-exp",
796
+ "gemini-2.0-flash-thinking-exp",
797
+ "llama-3.3-70b-versatile (Groq)",
798
+ ],
799
+ value="gemini-2.5-flash (Default)",
800
+ label="",
801
+ show_label=False,
802
+ container=False,
803
+ elem_classes="model-selector",
804
+ scale=1,
805
+ interactive=True
806
+ )
807
+
808
  # Hidden buttons for programmatic access
809
  edit_btn = gr.Button("Edit", variant="secondary", visible=False, elem_id="edit-query-btn")
810
  rewrite_btn = gr.Button("Rewrite", variant="secondary", visible=False, elem_id="rewrite-btn")
 
835
  queue=False
836
  ).then(
837
  fn=run_research,
838
+ inputs=[current_query_state, model_selector],
839
  outputs=[report_output, references_output]
840
  )
841
 
 
850
  # Rewrite (run again with same query)
851
  rewrite_event = rewrite_btn.click(
852
  fn=run_research,
853
+ inputs=[current_query_state, model_selector],
854
  outputs=[report_output, references_output]
855
  )
856
 
 
867
  queue=False
868
  ).then(
869
  fn=run_research,
870
+ inputs=[current_query_state, model_selector],
871
  outputs=[report_output, references_output]
872
  )
873
 
model_config.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ from agents import AsyncOpenAI, OpenAIChatCompletionsModel
4
+
5
+ load_dotenv(override=True)
6
+
7
+
8
+ def get_model(model_choice: str):
9
+ """
10
+ Get the appropriate model based on user selection
11
+
12
+ Args:
13
+ model_choice: String from dropdown (e.g., "gemini-2.5-flash (Default)")
14
+
15
+ Returns:
16
+ OpenAIChatCompletionsModel configured for the selected model
17
+ """
18
+ # Extract model name from dropdown choice
19
+ if "gemini-2.5-flash" in model_choice.lower():
20
+ model_name = "gemini-2.5-flash"
21
+ api_key = os.getenv('GEMINI_API_KEY')
22
+ base_url = "https://generativelanguage.googleapis.com/v1beta/openai/"
23
+
24
+ elif "gemini-2.0-flash-thinking" in model_choice.lower():
25
+ model_name = "gemini-2.0-flash-thinking-exp-01-21"
26
+ api_key = os.getenv('GEMINI_API_KEY')
27
+ base_url = "https://generativelanguage.googleapis.com/v1beta/openai/"
28
+
29
+ elif "gemini-2.0-flash-exp" in model_choice.lower():
30
+ model_name = "gemini-2.0-flash-exp"
31
+ api_key = os.getenv('GEMINI_API_KEY')
32
+ base_url = "https://generativelanguage.googleapis.com/v1beta/openai/"
33
+
34
+ elif "llama" in model_choice.lower():
35
+ model_name = "llama-3.3-70b-versatile"
36
+ api_key = os.getenv('GROQ_API_KEY')
37
+ base_url = "https://api.groq.com/openai/v1"
38
+
39
+ else:
40
+ # Default to gemini-2.5-flash
41
+ model_name = "gemini-2.5-flash"
42
+ api_key = os.getenv('GEMINI_API_KEY')
43
+ base_url = "https://generativelanguage.googleapis.com/v1beta/openai/"
44
+
45
+ # Create client
46
+ client = AsyncOpenAI(
47
+ api_key=api_key,
48
+ base_url=base_url
49
+ )
50
+
51
+ # Return model
52
+ return OpenAIChatCompletionsModel(
53
+ model=model_name,
54
+ openai_client=client
55
+ )
56
+
57
+
58
+ def get_model_display_name(model_choice: str) -> str:
59
+ """Get a clean display name for the model"""
60
+ model_map = {
61
+ "gemini-2.5-flash (Default)": "Gemini 2.5 Flash",
62
+ "gemini-2.0-flash-exp": "Gemini 2.0 Flash",
63
+ "gemini-2.0-flash-thinking-exp": "Gemini 2.0 Flash Thinking",
64
+ "llama-3.3-70b-versatile (Groq)": "Llama 3.3 70B (Groq)"
65
+ }
66
+ return model_map.get(model_choice, model_choice)
research_manager.py CHANGED
@@ -1,18 +1,27 @@
1
- from agents import Runner, trace, gen_trace_id
2
  from search_agent import search_agent
3
  from planner_agent import planner_agent, WebSearchItem, WebSearchPlan
4
- from writer_agent import writer_agent_plain, writer_agent, ReportData
5
  from email_agent import email_agent
6
  import asyncio
7
  import time
8
  from pydantic import BaseModel, Field
 
9
 
10
  class ResearchManager:
11
- def __init__(self):
12
  # Track request timestamps for intelligent rate limiting
13
  self.request_times = []
14
- self.max_rpm = 10 # Gemini 2.5 Flash free tier: 10 requests per minute
15
  self.rate_limit_window = 60 # seconds
 
 
 
 
 
 
 
 
16
 
17
  async def wait_for_rate_limit(self):
18
  """Intelligent rate limiting: only sleep as much as needed"""
@@ -36,15 +45,11 @@ class ResearchManager:
36
  async def run(self, query: str):
37
  """ Run the deep research process, yielding the status updates and the final report"""
38
  trace_id = gen_trace_id()
39
-
40
- # Closure to yield progress updates
41
- async def yield_progress(message):
42
- # This is a workaround - we'll store messages in a list
43
- pass
44
 
45
  with trace("Research trace", trace_id=trace_id):
46
- print(f"Using Brave Search API and Gemini 2.5 Flash")
47
- yield f"INIT|Using Brave Search API and Gemini 2.5 Flash"
48
  print("Starting research...")
49
 
50
  search_plan = await self.plan_searches(query)
@@ -78,8 +83,18 @@ class ResearchManager:
78
  """ Plan the searches to perform for the query """
79
  print("Planning searches...")
80
  await self.wait_for_rate_limit()
 
 
 
 
 
 
 
 
 
 
81
  result = await Runner.run(
82
- planner_agent,
83
  f"Query: {query}",
84
  )
85
  print(f"Will perform {len(result.final_output.searches)} searches")
@@ -140,6 +155,18 @@ class ResearchManager:
140
  print("Thinking about report...")
141
  input = f"Original query: {query}\nSummarized search results: {search_results}"
142
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  max_retries = 3
144
  for attempt in range(max_retries):
145
  try:
@@ -147,7 +174,7 @@ class ResearchManager:
147
  await self.wait_for_rate_limit()
148
  # Use plain text agent to avoid structured output truncation
149
  result = await Runner.run(
150
- writer_agent_plain,
151
  input,
152
  )
153
  print("Finished writing report")
 
1
+ from agents import Runner, trace, gen_trace_id, Agent, ModelSettings
2
  from search_agent import search_agent
3
  from planner_agent import planner_agent, WebSearchItem, WebSearchPlan
4
+ from writer_agent import writer_agent_plain, writer_agent, ReportData, INSTRUCTIONS as WRITER_INSTRUCTIONS
5
  from email_agent import email_agent
6
  import asyncio
7
  import time
8
  from pydantic import BaseModel, Field
9
+ from model_config import get_model, get_model_display_name
10
 
11
  class ResearchManager:
12
+ def __init__(self, model_choice="gemini-2.5-flash (Default)"):
13
  # Track request timestamps for intelligent rate limiting
14
  self.request_times = []
15
+ self.max_rpm = 10 # Default: 10 requests per minute
16
  self.rate_limit_window = 60 # seconds
17
+ self.model_choice = model_choice
18
+ self.model = get_model(model_choice)
19
+
20
+ # Adjust rate limits based on model
21
+ if "groq" in model_choice.lower():
22
+ self.max_rpm = 30 # Groq has higher limits
23
+ elif "thinking" in model_choice.lower():
24
+ self.max_rpm = 5 # Experimental models have lower limits
25
 
26
  async def wait_for_rate_limit(self):
27
  """Intelligent rate limiting: only sleep as much as needed"""
 
45
  async def run(self, query: str):
46
  """ Run the deep research process, yielding the status updates and the final report"""
47
  trace_id = gen_trace_id()
48
+ model_display = get_model_display_name(self.model_choice)
 
 
 
 
49
 
50
  with trace("Research trace", trace_id=trace_id):
51
+ print(f"Using Brave Search API and {model_display}")
52
+ yield f"INIT|Using Brave Search API and {model_display}"
53
  print("Starting research...")
54
 
55
  search_plan = await self.plan_searches(query)
 
83
  """ Plan the searches to perform for the query """
84
  print("Planning searches...")
85
  await self.wait_for_rate_limit()
86
+
87
+ # Use selected model for planning
88
+ from planner_agent import INSTRUCTIONS as PLANNER_INSTRUCTIONS, WebSearchPlan
89
+ dynamic_planner = Agent(
90
+ name="PlannerAgent",
91
+ instructions=PLANNER_INSTRUCTIONS,
92
+ model=self.model,
93
+ output_type=WebSearchPlan,
94
+ )
95
+
96
  result = await Runner.run(
97
+ dynamic_planner,
98
  f"Query: {query}",
99
  )
100
  print(f"Will perform {len(result.final_output.searches)} searches")
 
155
  print("Thinking about report...")
156
  input = f"Original query: {query}\nSummarized search results: {search_results}"
157
 
158
+ # Create writer with selected model
159
+ dynamic_writer = Agent(
160
+ name="WriterAgentPlain",
161
+ instructions=WRITER_INSTRUCTIONS,
162
+ model=self.model,
163
+ output_type=str,
164
+ model_settings=ModelSettings(
165
+ max_tokens=32000,
166
+ temperature=0.7,
167
+ ),
168
+ )
169
+
170
  max_retries = 3
171
  for attempt in range(max_retries):
172
  try:
 
174
  await self.wait_for_rate_limit()
175
  # Use plain text agent to avoid structured output truncation
176
  result = await Runner.run(
177
+ dynamic_writer,
178
  input,
179
  )
180
  print("Finished writing report")