suchith83 commited on
Commit
68b80a4
·
1 Parent(s): 2a82dce

research app

Browse files
.gitignore CHANGED
@@ -1,3 +1,6 @@
1
  venv/
2
  __pycache__/
3
  .env
 
 
 
 
1
  venv/
2
  __pycache__/
3
  .env
4
+ tools/__pycache__
5
+
6
+ .gradio/
README.md CHANGED
@@ -11,3 +11,47 @@ short_description: Searchs through web and returns related links
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
14
+
15
+ # Deep Research Assistant
16
+
17
+ A Gradio web application that performs comprehensive research on any query using advanced AI models and web search capabilities.
18
+
19
+ ## Features
20
+
21
+ - Interactive web interface using Gradio
22
+ - Comprehensive research capabilities using multiple tools
23
+ - Well-structured research reports with executive summaries, main findings, analysis, and sources
24
+ - Support for a wide range of research topics
25
+
26
+ ## Setup
27
+
28
+ 1. Clone the repository
29
+ 2. Install dependencies:
30
+ ```bash
31
+ pip install -r requirements.txt
32
+ ```
33
+ 3. Create a `.env` file in the root directory with your API key:
34
+ ```
35
+ CEREBRAS_API_KEY=your_api_key_here
36
+ ```
37
+
38
+ ## Running the Application
39
+
40
+ 1. Start the Gradio web interface:
41
+ ```bash
42
+ python app.py
43
+ ```
44
+ 2. Open your web browser and navigate to the URL shown in the terminal (typically http://localhost:7860)
45
+ 3. Enter your research query in the text box and click submit
46
+ 4. The application will generate a comprehensive research report based on your query
47
+
48
+ ## Usage Examples
49
+
50
+ The application comes with built-in examples that you can try:
51
+ - Latest developments in quantum computing
52
+ - Current state of climate change and its impacts
53
+ - Emerging trends in artificial intelligence
54
+
55
+ ## Note
56
+
57
+ Make sure you have a valid Cerebras API key set in your environment variables. The application uses the Cerebras AI model for generating high-quality research reports.
app.py CHANGED
@@ -1,47 +1,63 @@
1
  import gradio as gr
 
2
  import os
3
- import requests
4
  from dotenv import load_dotenv
 
 
 
5
 
6
- load_dotenv(".env")
 
 
7
 
8
- API_KEY = os.getenv("GOOGLE_API_KEY")
9
- CSE_ID = os.getenv("GOOGLE_CSE_ID")
 
 
 
 
 
 
 
 
10
 
11
- def search_web(query):
12
- if not API_KEY or not CSE_ID:
13
- return "Missing API key or Search Engine ID in .env"
14
 
15
- params = {
16
- "q": query,
17
- "key": API_KEY,
18
- "cx": CSE_ID
19
- }
20
 
21
- try:
22
- response = requests.get("https://www.googleapis.com/customsearch/v1", params=params)
23
- response.raise_for_status()
24
- data = response.json()
25
- results = data.get("items", [])
26
- if not results:
27
- return "No results found."
28
-
29
- formatted = ""
30
- for i, result in enumerate(results[:3], 1):
31
- title = result.get("title", "No Title")
32
- link = result.get("link", "No Link")
33
- snippet = result.get("snippet", "No Snippet")
34
- formatted += f"**Result {i}**\n[{title}]({link})\n\n{snippet}\n\n---\n"
35
- return formatted
36
 
 
 
 
 
 
37
  except Exception as e:
38
- return f"Error: {str(e)}"
 
39
 
40
- # Gradio UI
41
- gr.Interface(
42
- fn=search_web,
43
- inputs=gr.Textbox(label="Search Query", placeholder="e.g. IPL 2025 predictions"),
44
- outputs=gr.Markdown(label="Results"),
45
- title="Google Search Tool",
46
- description="Uses Google Custom Search API to fetch top 3 web results"
47
- ).launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ from research_agent import research
3
  import os
 
4
  from dotenv import load_dotenv
5
+ import re
6
+ # Load environment variables
7
+ load_dotenv()
8
 
9
+ def format_as_markdown(raw: str) -> str:
10
+ # 1. Remove <think>...</think> and everything inside
11
+ raw = re.sub(r"<think>.*?</think>", "", raw, flags=re.DOTALL)
12
 
13
+ # 2. Replace section headers with markdown equivalents
14
+ replacements = {
15
+ "[EXECUTIVE_SUMMARY]": "## Executive Summary",
16
+ "[MAIN_FINDINGS]": "## Main Findings",
17
+ "[ANALYSIS]": "## Analysis",
18
+ "[CONCLUSION]": "## Conclusion",
19
+ "[SOURCES]": "## Sources",
20
+ }
21
+ for tag, header in replacements.items():
22
+ raw = raw.replace(tag, f"\n\n{header}\n\n")
23
 
24
+ # 3. Optional: clean up extra whitespace
25
+ raw = re.sub(r"\n{3,}", "\n\n", raw).strip()
 
26
 
27
+ return raw
 
 
 
 
28
 
29
+
30
+ def process_query(query: str) -> str:
31
+ """Process the user query and return research results."""
32
+ if not query.strip():
33
+ return "Please enter a valid query."
 
 
 
 
 
 
 
 
 
 
34
 
35
+ try:
36
+ result = research(query)
37
+ # print("returning result", result)
38
+ result = format_as_markdown(result)
39
+ return result
40
  except Exception as e:
41
+ return f"Error occurred: {str(e)}"
42
+
43
 
44
+ # Create Gradio interface
45
+ demo = gr.Interface(
46
+ fn=process_query,
47
+ inputs=gr.Textbox(
48
+ lines=3,
49
+ placeholder="Enter your research query here...",
50
+ label="Research Query"
51
+ ),
52
+ outputs=gr.Markdown(
53
+ label="Research Results"
54
+ ),
55
+ title="Deep Research Assistant",
56
+ description="Enter any query and get a comprehensive research report based on the latest information.",
57
+ examples=[
58
+ ["What are the latest developments in quantum computing?"],
59
+ ["Explain the current state of climate change and its impacts"],
60
+ ["What are the emerging trends in artificial intelligence?"]
61
+ ],
62
+ theme=gr.themes.Soft()
63
+ ).launch(mcp_server=True)
requirements.txt CHANGED
@@ -5,4 +5,5 @@ markdownify
5
  mcp[cli]
6
  httpx
7
  gradio[mcp]
8
- textblob
 
 
5
  mcp[cli]
6
  httpx
7
  gradio[mcp]
8
+ textblob
9
+ firecrawl-py
research_agent.py ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import List, Dict, Any, Optional
3
+ from openai import OpenAI
4
+ import json
5
+ from tools import SearchTool, FetchTool, SummarizeTool, FirecrawlScrapeTool
6
+ from dotenv import load_dotenv
7
+ from openai.types.chat import ChatCompletionMessage
8
+ from openai.types.chat.chat_completion import ChatCompletion
9
+
10
+ load_dotenv()
11
+
12
+ def print_section(title: str, content: str):
13
+ """Print a section with a clear separator."""
14
+ print(f"\n{'='*80}")
15
+ print(f"{title}")
16
+ print(f"{'='*80}")
17
+ print(content)
18
+ print(f"{'='*80}\n")
19
+
20
+ class PromptRefiner:
21
+ def __init__(self, client):
22
+ self.client = client
23
+ self.model = "qwen-3-32b"
24
+
25
+ def refine(self, query: str) -> str:
26
+ """Refine the user's query into a structured research prompt."""
27
+ #print_section("PROMPT REFINER", f"Original query: {query}")
28
+
29
+ response = self.client.chat.completions.create(
30
+ model=self.model,
31
+ messages=[
32
+ {"role": "system", "content": """You are a "Prompt Architect" for a Deep Research Tool. Your job is to take an informal user query and turn it into a clear, comprehensive, and structured research prompt.
33
+
34
+ Your output MUST follow this exact format:
35
+
36
+ [RESEARCH_OBJECTIVE]
37
+ A clear, single-sentence statement of what needs to be researched.
38
+
39
+ [CONTEXT]
40
+ - Domain/field of research
41
+ - Required background knowledge
42
+ - Any specific constraints or boundaries
43
+
44
+ [KEY_QUESTIONS]
45
+ 1. First specific question to answer
46
+ 2. Second specific question to answer
47
+ 3. Third specific question to answer
48
+ (Add more if needed)
49
+
50
+ [OUTPUT_REQUIREMENTS]
51
+ - Format (e.g., structured report, bullet points)
52
+ - Depth of analysis
53
+ - Required citations or sources
54
+ - Length constraints
55
+
56
+ [KEY_TERMS]
57
+ - Term 1
58
+ - Term 2
59
+ - Term 3
60
+ (Add more if needed)
61
+
62
+ [CLARIFICATIONS_NEEDED]
63
+ - Any questions that need to be asked to the user
64
+ - Any assumptions made
65
+ """},
66
+ {"role": "user", "content": query}
67
+ ]
68
+ )
69
+ refined_query = response.choices[0].message.content
70
+ #print_section("REFINED QUERY", refined_query)
71
+ return refined_query
72
+
73
+ class ResearcherAgent:
74
+ def __init__(self, client):
75
+ self.client = client
76
+ self.model = "qwen-3-32b"
77
+ self.tools = [
78
+ SearchTool(),
79
+ # FetchTool(),
80
+ SummarizeTool(),
81
+ FirecrawlScrapeTool()
82
+ ]
83
+ self.tools_json = [
84
+ {
85
+ "type": "function",
86
+ "function": tool.to_json()
87
+ }
88
+ for tool in self.tools
89
+ ]
90
+ self.tools_map = {tool.name: tool for tool in self.tools}
91
+
92
+ def research(self, query: str) -> str:
93
+ """Perform web research on the given query and return summarized findings."""
94
+ #print_section("RESEARCHER", f"Starting research on: {query}")
95
+
96
+ conversation_history = [
97
+ {"role": "system", "content": """You are a research agent that searches the web, reads contents of the urls, and summarizes findings.
98
+ Use below tools if you think you are not up to date with the latest information:
99
+ - search tool - to find relevant URLs
100
+ - firecrawl_scrape tool - to get content from the most promising URLs in markdown format
101
+ - summarize tool - to extract key information
102
+
103
+ Organize findings in a clear, structured format
104
+
105
+ Your final response should be a well-organized summary of all findings, with clear sections and bullet points where appropriate."""},
106
+ {"role": "user", "content": query}
107
+ ]
108
+
109
+ while True:
110
+ response = self.client.chat.completions.create(
111
+ model=self.model,
112
+ messages=conversation_history,
113
+ tools=self.tools_json,
114
+ )
115
+
116
+ message = response.choices[0].message
117
+ conversation_history.append({
118
+ "role": "assistant",
119
+ "content": message.content if message.content else "",
120
+ "tool_calls": message.tool_calls
121
+ })
122
+
123
+ if not message.tool_calls:
124
+ #print_section("RESEARCH FINDINGS", message.content or "No findings generated")
125
+ return message.content or "No findings generated"
126
+
127
+ tool_results = []
128
+ for tool_call in message.tool_calls:
129
+ tool_name = tool_call.function.name
130
+ arguments = json.loads(tool_call.function.arguments)
131
+
132
+ #print_section("TOOL CALL", f"Tool: {tool_name}\nArguments: {json.dumps(arguments, indent=2)}")
133
+
134
+ if tool_name not in self.tools_map:
135
+ continue
136
+
137
+ tool = self.tools_map[tool_name]
138
+ result = tool(**arguments)
139
+
140
+ #print_section("TOOL RESULT", f"Tool: {tool_name}\nResult: {result}")
141
+
142
+ tool_results.append({
143
+ "tool_call_id": tool_call.id,
144
+ "role": "tool",
145
+ "name": tool_name,
146
+ "content": result
147
+ })
148
+
149
+ conversation_history.extend(tool_results)
150
+
151
+ class PlannerAgent:
152
+ def __init__(self, client):
153
+ self.client = client
154
+ self.model = "qwen-3-32b"
155
+ self.scratchpad = ""
156
+ self.researcher = ResearcherAgent(client)
157
+
158
+ def plan(self, refined_query: str) -> str:
159
+ """Plan the research process and manage the scratchpad."""
160
+ #print_section("PLANNER", f"Starting research planning for:\n{refined_query}")
161
+
162
+ conversation_history = [
163
+ {"role": "system", "content": """
164
+ You are a research planner that manages the research process.
165
+
166
+ Your responses MUST follow this exact format:
167
+
168
+ If you need more research:
169
+ NEED_RESEARCH
170
+ RESEARCH_QUERY: [specific query to research]
171
+ REASON: [why this research is needed]
172
+
173
+ If you have enough information:
174
+ ENOUGH_INFORMATION
175
+ SUMMARY: [brief summary of what we've learned]
176
+ NEXT_STEPS: [what should be done with this information]
177
+
178
+ Always evaluate:
179
+ 1. Have we answered all key questions from the research objective?
180
+ 2. Do we have enough depth and breadth of information?
181
+ 3. Are there any gaps in our understanding?
182
+ 4. Do we need to verify any information?
183
+
184
+ Current date is 2025-06-04.
185
+ """},
186
+ {"role": "user", "content": f"Query: {refined_query}\nCurrent scratchpad:\n{self.scratchpad}"}
187
+ ]
188
+
189
+ while True:
190
+ response = self.client.chat.completions.create(
191
+ model=self.model,
192
+ messages=conversation_history
193
+ )
194
+
195
+ message = response.choices[0].message
196
+ #print_section("PLANNER DECISION", message.content)
197
+
198
+ conversation_history.append({"role": "assistant", "content": message.content})
199
+
200
+ # Parse the planner's decision
201
+ if "ENOUGH_INFORMATION" in message.content:
202
+ #print_section("PLANNER", "Research complete. Moving to report generation.")
203
+ return self.scratchpad
204
+ elif "NEED_RESEARCH" in message.content:
205
+ # Extract research query from the message
206
+ research_query = message.content.split("RESEARCH_QUERY:")[1].split("\n")[0].strip()
207
+ findings = self.researcher.research(research_query)
208
+ self.scratchpad += f"\n\nNew findings:\n{findings}"
209
+ #print_section("UPDATED SCRATCHPAD", self.scratchpad)
210
+ conversation_history.append({
211
+ "role": "user",
212
+ "content": f"Updated scratchpad:\n{self.scratchpad}"
213
+ })
214
+
215
+ class ReporterAgent:
216
+ def __init__(self, client):
217
+ self.client = client
218
+ self.model = "qwen-3-32b"
219
+
220
+ def generate_report(self, scratchpad: str, original_query: str) -> str:
221
+ """Generate a final report based on the scratchpad content."""
222
+ #print_section("REPORTER", "Generating final report")
223
+
224
+ response = self.client.chat.completions.create(
225
+ model=self.model,
226
+ messages=[
227
+ {"role": "system", "content": """You are a research reporter that generates clear, well-structured reports.
228
+
229
+ Your report MUST follow this format:
230
+
231
+ [EXECUTIVE_SUMMARY]
232
+ A concise overview of the key findings and conclusions.
233
+
234
+ [MAIN_FINDINGS]
235
+ 1. First major finding
236
+ - Supporting details
237
+ - Sources/references
238
+ 2. Second major finding
239
+ - Supporting details
240
+ - Sources/references
241
+ (Add more as needed)
242
+
243
+ [ANALYSIS]
244
+ - Interpretation of the findings
245
+ - Connections between different pieces of information
246
+ - Implications or significance
247
+
248
+ [CONCLUSION]
249
+ - Summary of key takeaways
250
+ - Any remaining questions or areas for further research
251
+
252
+ [SOURCES]
253
+ - List of all sources used in the research"""},
254
+ {"role": "user", "content": f"Original query: {original_query}\n\nResearch findings:\n{scratchpad}\n\nGenerate a comprehensive report that answers the original query."}
255
+ ]
256
+ )
257
+ report = response.choices[0].message.content
258
+ # #print_section("FINAL REPORT", report)
259
+ return report
260
+
261
+ def research(query: str) -> str:
262
+ """Main research function that orchestrates the entire research process."""
263
+ try:
264
+ api_key = os.environ.get("CEREBRAS_API_KEY")
265
+ if not api_key:
266
+ return "Error: Please set CEREBRAS_API_KEY environment variable"
267
+
268
+ client = OpenAI(
269
+ base_url="https://api.cerebras.ai/v1",
270
+ api_key=api_key
271
+ )
272
+
273
+ # Step 1: Refine the prompt
274
+ refiner = PromptRefiner(client)
275
+ refined_query = refiner.refine(query)
276
+
277
+ # Step 2: Plan and execute research
278
+ planner = PlannerAgent(client)
279
+ scratchpad = planner.plan(refined_query)
280
+
281
+ # Step 3: Generate final report
282
+ reporter = ReporterAgent(client)
283
+ final_report = reporter.generate_report(scratchpad, query)
284
+
285
+ return final_report
286
+
287
+ except Exception as e:
288
+ return f"Error in research process: {str(e)}"
289
+
290
+ # if __name__ == "__main__":
291
+ # while True:
292
+ # query = input("Enter your query: ")
293
+ # if query == "exit":
294
+ # break
295
+ # print(research(query))
tools/__init__.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ from .search import SearchTool
2
+ from .fetch import FetchTool
3
+ from .summarize import SummarizeTool
4
+ from .firecrawl_scrape import FirecrawlScrapeTool
5
+ from .tool import Tool
6
+
7
+ __all__ = ["SearchTool", "FetchTool", "SummarizeTool", "Tool", "FirecrawlScrapeTool"]
tools/fetch.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .tool import Tool
2
+ from markdownify import markdownify
3
+ import requests
4
+
5
+ class FetchTool(Tool):
6
+ def __init__(self):
7
+ super().__init__(
8
+ name="fetch",
9
+ description="Fetch the content of a URL and return the markdownified version of the content",
10
+ inputSchema={
11
+ "type": "object",
12
+ "properties": {
13
+ "url": {"type": "string", "description": "The URL to fetch"}
14
+ }
15
+ }
16
+ )
17
+
18
+ def __call__(self, url: str):
19
+ try:
20
+ if not url:
21
+ return "Error: URL parameter is required"
22
+
23
+ resp = requests.get(url)
24
+ resp.raise_for_status() # Raise an exception for bad status codes
25
+
26
+ return markdownify(resp.text)
27
+
28
+ except requests.exceptions.RequestException as e:
29
+ return f"Error fetching URL: {str(e)}"
30
+ except Exception as e:
31
+ return f"Unexpected error while processing URL: {str(e)}"
tools/firecrawl_scrape.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .tool import Tool
2
+ from firecrawl import FirecrawlApp
3
+ from dotenv import load_dotenv
4
+ import os
5
+
6
+ load_dotenv()
7
+
8
+ class FirecrawlScrapeTool(Tool):
9
+ def __init__(self):
10
+ super().__init__(
11
+ name="firecrawl_scrape",
12
+ description="Scrape a website and return the markdownified version of the content",
13
+ inputSchema={
14
+ "type": "object",
15
+ "properties": {
16
+ "url": {"type": "string", "description": "The URL to scrape"}
17
+ }
18
+ }
19
+ )
20
+
21
+ def __call__(self, url: str):
22
+ try:
23
+ if not url:
24
+ return "Error: URL parameter is required"
25
+
26
+ app = FirecrawlApp(api_key=os.getenv("FIRECRAWL_API_KEY"))
27
+
28
+ scrape_result = app.scrape_url(url, formats=['markdown', 'html'])
29
+ return scrape_result["data"]["markdown"]
30
+
31
+ except Exception as e:
32
+ return f"Error scraping URL: {str(e)}"
33
+
tools/search.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ from dotenv import load_dotenv
3
+ import os
4
+ from .tool import Tool
5
+
6
+ load_dotenv("./.env")
7
+
8
+ class SearchTool(Tool):
9
+ def __init__(self):
10
+ super().__init__(
11
+ name="search",
12
+ description="Search the web for information",
13
+ inputSchema={
14
+ "type": "object",
15
+ "properties": {
16
+ "query": {"type": "string", "description": "The search query"}
17
+ }
18
+ }
19
+ )
20
+
21
+ self.api_key = os.environ.get("GOOGLE_API_KEY")
22
+ self.search_engine_id = os.environ.get("GOOGLE_CSE_ID")
23
+
24
+ if not self.api_key:
25
+ raise ValueError("Please set GOOGLE_API_KEY environment variable")
26
+ if not self.search_engine_id:
27
+ raise ValueError("Please set GOOGLE_CSE_ID environment variable")
28
+
29
+ def __call__(self, query: str):
30
+ try:
31
+ if not query:
32
+ return "Error: Query parameter is required"
33
+
34
+ params = {
35
+ "q": query,
36
+ "key": self.api_key,
37
+ "cx": self.search_engine_id
38
+ }
39
+
40
+ resp = requests.get("https://www.googleapis.com/customsearch/v1", params=params)
41
+ resp.raise_for_status() # Raise an exception for bad status codes
42
+
43
+ _results = resp.json().get("items", [])
44
+ results = []
45
+ for result in _results[:3]:
46
+ results.append({
47
+ "title": result.get("title", "No title"),
48
+ "link": result.get("link", "No link"),
49
+ "snippet": result.get("snippet", "No snippet")
50
+ })
51
+
52
+ if not results:
53
+ return "No results found for the given query."
54
+
55
+ # Format results as a string
56
+ formatted_results = []
57
+ for i, result in enumerate(results, 1):
58
+ formatted_results.append(f"Result {i}:\nTitle: {result['title']}\nLink: {result['link']}\nSnippet: {result['snippet']}\n")
59
+
60
+ return "\n".join(formatted_results)
61
+
62
+ except requests.exceptions.RequestException as e:
63
+ return f"Error during search: {str(e)}"
64
+ except Exception as e:
65
+ return f"Unexpected error during search: {str(e)}"
tools/summarize.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .tool import Tool
2
+ from openai import OpenAI
3
+ from dotenv import load_dotenv
4
+ import os
5
+
6
+ load_dotenv("./.env")
7
+
8
+ class SummarizeTool(Tool):
9
+ def __init__(self):
10
+ super().__init__(
11
+ name="summarize",
12
+ description="Summarize the content of a URL",
13
+ inputSchema={
14
+ "type": "object",
15
+ "properties": {
16
+ "content": {"type": "string", "description": "The content to summarize"}
17
+ }
18
+ }
19
+ )
20
+
21
+ api_key = os.environ.get("CEREBRAS_API_KEY")
22
+ if not api_key:
23
+ raise ValueError("Please set CEREBRAS_API_KEY environment variable")
24
+
25
+ self.client = OpenAI(base_url="https://api.cerebras.ai/v1", api_key=api_key)
26
+
27
+ def __call__(self, **kwargs):
28
+ try:
29
+ content = kwargs.get("content")
30
+ if not content:
31
+ return "Error: Content parameter is required"
32
+
33
+ response = self.client.chat.completions.create(
34
+ model="qwen-3-32b",
35
+ messages=[
36
+ {"role": "system", "content": "You are a helpful assistant that summarizes content while keeping the all important information."},
37
+ {"role": "user", "content": content}
38
+ ]
39
+ )
40
+ return response.choices[0].message.content
41
+ except Exception as e:
42
+ return f"Error during summarization: {str(e)}"
tools/tool.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class Tool:
2
+ def __init__(self, name: str, description: str, inputSchema: dict):
3
+ self.name = name
4
+ self.description = description
5
+ self.inputSchema = inputSchema
6
+
7
+ def __repr__(self):
8
+ return f"Tool(name={self.name}, description={self.description}, inputSchema={self.inputSchema})"
9
+
10
+ def to_json(self):
11
+ return {
12
+ "name": self.name,
13
+ "description": self.description,
14
+ "parameters": self.inputSchema
15
+ }