rameshbasina commited on
Commit
aa73f1c
·
1 Parent(s): bed418d

Enhance README with detailed Google Custom Search API cost information and automatic fallback to DuckDuckGo. Introduce a new TEST_QUESTIONS.md file with comprehensive sample queries for various functionalities, including math, web search, and algorithms. Update agent configuration to improve news query handling and enhance calculator capabilities with natural language processing for percentages, divisions, and geometry queries. Improve logging for better debugging and user experience.

Browse files
Files changed (4) hide show
  1. README.md +54 -0
  2. TEST_QUESTIONS.md +696 -0
  3. config/agent_config.json +359 -6
  4. core/meetara_agent.py +624 -31
README.md CHANGED
@@ -252,6 +252,41 @@ meeTARA Agent supports multiple search providers for web searches:
252
  - Search Engine ID is not sensitive (designed to be public)
253
  - No risk of hackers accessing your credentials through the Space URL
254
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  ### Enabling Agent Mode
256
 
257
  Agent Mode is **always enabled by default**. Just select your preferred model from the dropdown and ask questions:
@@ -261,6 +296,25 @@ Agent Mode is **always enabled by default**. Just select your preferred model fr
261
  - **Combined**: *"What's 2^10 and search for news about quantum computing in 2025"*
262
  - **Real-time**: *"Tell me current US stock trends and present day crypto prices"*
263
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  ## 🚀 Usage
265
 
266
  1. Click "Initialize" to load meeTARA (first time may take a few minutes to download models)
 
252
  - Search Engine ID is not sensitive (designed to be public)
253
  - No risk of hackers accessing your credentials through the Space URL
254
 
255
+ ### 💰 Google Custom Search API - Cost Information
256
+
257
+ **Free Tier:**
258
+ - ✅ **100 queries per day** - **FREE** (no credit card needed initially, but billing account required)
259
+ - ⚠️ **Billing account required** even for free tier (you won't be charged for first 100/day)
260
+
261
+ **Automatic Fallback (When Quota Exceeded):**
262
+ - 🔄 **Automatic fallback to DuckDuckGo** when Google quota is exceeded (429 or quota-related 403 errors)
263
+ - DuckDuckGo is **free and unlimited** (no API key required)
264
+ - Seamless transition - users won't notice interruption
265
+ - Logs will indicate: `⚠️ Google Search quota exceeded → Auto-fallback to DuckDuckGo`
266
+ - **No code changes needed** - fallback happens automatically
267
+ - **No paid subscription required** - system gracefully handles quota limits
268
+
269
+ **Paid Tier (Optional - Beyond 100 queries/day):**
270
+ - 💵 **$5 per 1,000 queries** (after free tier)
271
+ - 📊 **Maximum**: 10,000 queries per day (requires billing enabled)
272
+ - **Note**: If you don't set up paid billing, the system automatically falls back to DuckDuckGo after 100 queries/day
273
+
274
+ **Cost Examples:**
275
+ - **100 queries/day** (3,000/month): **FREE** ✅ (auto-fallback after quota)
276
+ - **500 queries/day** (15,000/month): ~$70/month (14,000 additional queries × $5/1,000 = $70) OR use DuckDuckGo (free)
277
+ - **1,000 queries/day** (30,000/month): ~$145/month (29,000 additional queries × $5/1,000 = $145) OR use DuckDuckGo (free)
278
+ - **10,000 queries/day** (300,000/month): ~$1,485/month (299,000 additional queries × $5/1,000 = $1,485) OR use DuckDuckGo (free)
279
+
280
+ **Comparison with DuckDuckGo:**
281
+ - **DuckDuckGo**: ✅ **FREE, unlimited queries** (automatic fallback - excellent results!)
282
+ - **Google Custom Search**: Free for 100/day, then $5 per 1,000 queries (optional upgrade)
283
+
284
+ **Recommendation:**
285
+ - ✅ **For Development/Testing**: DuckDuckGo is perfect (free, unlimited, good results)
286
+ - 💰 **For Production**: Use Google API only if you need better results AND can afford $5 per 1,000 queries
287
+ - ✅ **Current Setup**: Hybrid approach - tries Google first, automatically falls back to DuckDuckGo when quota exceeded (best of both worlds!)
288
+ - 🎯 **Best Practice**: Let the system automatically fallback to DuckDuckGo - no paid subscription needed unless you specifically need Google's superior results
289
+
290
  ### Enabling Agent Mode
291
 
292
  Agent Mode is **always enabled by default**. Just select your preferred model from the dropdown and ask questions:
 
296
  - **Combined**: *"What's 2^10 and search for news about quantum computing in 2025"*
297
  - **Real-time**: *"Tell me current US stock trends and present day crypto prices"*
298
 
299
+ ### 📝 Sample Test Questions
300
+
301
+ For comprehensive test questions covering all areas (Math, Web Search, Current Events, Stock Market, Technology, etc.), see **[TEST_QUESTIONS.md](TEST_QUESTIONS.md)**.
302
+
303
+ The test questions file includes:
304
+ - 🧮 **Math & Calculator queries** (basic, geometry, advanced)
305
+ - 📰 **Current Events & News** (today's news, specific topics, global news)
306
+ - 💼 **Stock Market & Financial** (market trends, specific markets, combined queries)
307
+ - 💻 **Technology & AI** (AI trends, tech news, emerging technologies)
308
+ - 🎓 **Educational & Research** (science, academic topics, general knowledge)
309
+ - 🔄 **Combined Queries** (Math + Search in one question)
310
+ - 🌍 **Real-time Information** (time-sensitive queries, specific dates/years)
311
+ - 🎯 **Edge Cases** (complex multi-part questions, natural language variations)
312
+
313
+ **Quick Start Examples:**
314
+ - *"Calculate 25 * 48"* - Tests calculator tool
315
+ - *"Search for latest AI trends"* - Tests web search tool
316
+ - *"What's 2^10 and also search for current stock market trends"* - Tests combined tool usage
317
+
318
  ## 🚀 Usage
319
 
320
  1. Click "Initialize" to load meeTARA (first time may take a few minutes to download models)
TEST_QUESTIONS.md ADDED
@@ -0,0 +1,696 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # meeTARA Agent - Sample Test Questions
2
+
3
+ This file contains sample questions organized by category to test all meeTARA Agent capabilities (Calculator + Web Search + AI Processing).
4
+
5
+ ---
6
+
7
+ ## 🧮 Math & Calculator Queries
8
+
9
+ ### Basic Calculations
10
+ - "Calculate 25 * 48"
11
+ - "What's 15% of 340?"
12
+ - "Solve: 2^10 + 3^5"
13
+ - "What is 144 divided by 12?"
14
+ - "Multiply 123 by 456"
15
+ - "What is 999 divided by 3?"
16
+ - "Add 50, 25, and 75"
17
+ - "Compute 1000 - 456 + 234 * 3"
18
+ - "Solve: (12 + 8) * (15 - 7)"
19
+ - "What is 2^8? Also calculate 3^4"
20
+
21
+ ### Percentage & Division
22
+ - "What's 15% of 340?"
23
+ - "Calculate 25% of 200"
24
+ - "What is 20% times 150?"
25
+ - "999 divided by 3"
26
+ - "100 divided by 4"
27
+ - "50 ÷ 2"
28
+ - "What is 144 / 12?"
29
+
30
+ ### Geometry & Formulas
31
+ - "Find the surface area of a rectangular prism with dimensions: length = 6 cm, width = 4 cm, height = 5 cm"
32
+ - "Calculate the area of a circle with radius 7 cm"
33
+ - "What's the volume of a cylinder with radius 5 cm and height 10 cm?"
34
+ - "Find the perimeter of a rectangle with length 12 cm and width 8 cm"
35
+ - "Calculate the circumference of a circle with radius 10 cm"
36
+ - "What is the volume of a sphere with radius 5 cm?"
37
+
38
+ ### Powers & Roots
39
+ - "What is the square root of 256?"
40
+ - "Calculate square root of 144"
41
+ - "Find the square root of 16"
42
+ - "What is 2^8?"
43
+ - "Calculate 2 to the power of 10"
44
+ - "What's 3^4?"
45
+ - "Find cube root of 27"
46
+ - "Calculate cbrt(8)"
47
+
48
+ ### Scientific Functions
49
+
50
+ #### Trigonometry
51
+ - "Calculate sine of pi/2"
52
+ - "What is sin(90)?" (Note: expects radians, but natural language may use degrees)
53
+ - "Find cosine of 0"
54
+ - "What is cos(pi/2)?"
55
+ - "Calculate tangent of pi/4"
56
+ - "What is tan(45)?"
57
+ - "Find arcsin of 1"
58
+ - "Calculate arccos(0)"
59
+ - "What is arctan(1)?"
60
+ - "Sine of pi/2"
61
+
62
+ #### Hyperbolic Functions
63
+ - "Calculate sinh(2)"
64
+ - "What is cosh(3)?"
65
+ - "Find tanh(1)"
66
+ - "Compute asinh(1.5)"
67
+
68
+ #### Logarithms
69
+ - "What is log(10)?"
70
+ - "Calculate natural log of e"
71
+ - "Find ln(e)"
72
+ - "What is log base 10 of 100?"
73
+ - "Calculate log10(100)"
74
+ - "What's log base 2 of 8?"
75
+ - "Find log2(8)"
76
+ - "What is exponential of 2?"
77
+ - "Calculate exp(2)"
78
+ - "What's e^2?"
79
+
80
+ #### Advanced Functions
81
+ - "Calculate factorial of 5"
82
+ - "What is 5!?"
83
+ - "Find factorial(5)"
84
+ - "Calculate gcd of 48 and 18"
85
+ - "What is gcd(48, 18)?"
86
+ - "Find greatest common divisor of 48 and 18"
87
+ - "Calculate lcm of 12 and 18"
88
+ - "What is lcm(12, 18)?"
89
+ - "Find least common multiple of 12 and 18"
90
+ - "What is floor(3.7)?"
91
+ - "Calculate ceil(3.2)"
92
+ - "Find trunc(3.9)"
93
+ - "Convert 180 degrees to radians"
94
+ - "What is degrees(pi)?"
95
+ - "Calculate radians(180)"
96
+
97
+ ---
98
+
99
+ ## 🧬 Algorithms & Data Structures (Calculator)
100
+
101
+ ### Fibonacci Sequence
102
+ - "Calculate fibonacci of 10"
103
+ - "What is the 10th fibonacci number?"
104
+ - "Find fibonacci(10)"
105
+ - "What is fib(10)?"
106
+ - "Calculate the 15th fibonacci number"
107
+ - "Find fibonacci of 20"
108
+
109
+ ### Permutations (nPr)
110
+ - "Calculate permutations of 5 taken 3"
111
+ - "What is npr(5, 3)?"
112
+ - "Find 5P3"
113
+ - "Calculate npr for 5 taken 3"
114
+ - "What are the permutations of 5 items taken 3 at a time?"
115
+ - "Compute perm(5, 3)"
116
+ - "Calculate number of ways to arrange 5 items taken 3 at a time"
117
+
118
+ ### Combinations (nCr)
119
+ - "Calculate combinations of 5 choose 3"
120
+ - "What is ncr(5, 3)?"
121
+ - "Find 5C3"
122
+ - "Calculate ncr for 5 choose 3"
123
+ - "What are the combinations of 5 items choose 3?"
124
+ - "Compute comb(5, 3)"
125
+ - "Calculate choose(5, 3)"
126
+ - "How many ways to choose 3 items from 5?"
127
+
128
+ ### Base Conversions
129
+ - "Convert 255 to binary"
130
+ - "What is 255 in binary?"
131
+ - "Calculate bin(255)"
132
+ - "Find binary of 255"
133
+ - "Convert 255 to hexadecimal"
134
+ - "What is 255 in hex?"
135
+ - "Calculate hex(255)"
136
+ - "Find hexadecimal of 255"
137
+ - "Convert 255 to octal"
138
+ - "What is 255 in octal?"
139
+ - "Calculate oct(255)"
140
+ - "Find octal of 255"
141
+ - "Convert 1010 from binary to decimal"
142
+ - "What is hex('ff') in decimal?"
143
+ - "Convert 377 from octal to decimal"
144
+
145
+ ### Factorial & Advanced
146
+ - "Calculate factorial of 5"
147
+ - "What is 5!?"
148
+ - "Find factorial(5)"
149
+ - "Calculate factorial of 10"
150
+ - "What is 7!?"
151
+ - "Calculate factorial(20)"
152
+
153
+ ### Complexity & Analysis (Detection Keywords)
154
+ - "What is the time complexity of binary search?"
155
+ - "Explain big O notation for merge sort"
156
+ - "What is the space complexity of quick sort?"
157
+ - "Analyze the complexity of bubble sort"
158
+ - "What is the time complexity of binary tree traversal?"
159
+ - "Explain big O for hash table operations"
160
+ - "What is the complexity of heap insert?"
161
+ - "Analyze tree height calculation complexity"
162
+ - "What is the time complexity of graph traversal?"
163
+ - "Explain complexity analysis for array operations"
164
+
165
+ ### Data Structure Operations (Detection Keywords)
166
+ - "Calculate binary tree height with 15 nodes"
167
+ - "What is the depth of a tree with 31 nodes?"
168
+ - "Find the number of leaves in a binary tree"
169
+ - "Explain hash table time complexity"
170
+ - "What is the complexity of trie search?"
171
+ - "Analyze heap operations complexity"
172
+ - "What is binary search time complexity?"
173
+ - "Explain linear search complexity"
174
+ - "Calculate tree nodes for a given height"
175
+ - "What is the complexity of stack operations?"
176
+
177
+ ### Algorithm Concepts
178
+ - "Explain recursion depth for fibonacci"
179
+ - "What is recursive complexity for factorial?"
180
+ - "Analyze dynamic programming complexity"
181
+ - "What is greedy algorithm time complexity?"
182
+ - "Explain backtracking space complexity"
183
+ - "What is divide and conquer complexity?"
184
+ - "Analyze sorting algorithm complexity"
185
+ - "What is searching algorithm complexity?"
186
+
187
+ ---
188
+
189
+ ## 📰 Current Events & News (Web Search)
190
+
191
+ **Note**: meeTARA Agent is configured to extract ACTUAL NEWS HEADLINES and CONTENT, not just descriptions of news sources. The agent filters out meta pages about news websites and focuses on real headlines and stories.
192
+
193
+ ### Today's News (Headlines Extraction)
194
+ - "What are today's top news headlines?"
195
+ - "What happened in the news today?"
196
+ - "Tell me the latest breaking news"
197
+ - "Search for today's most important news stories"
198
+ - "What are the latest news headlines?"
199
+ - "Show me today's breaking news"
200
+ - "What's happening in the news right now?"
201
+
202
+ ### Specific Topics
203
+ - "What are the latest AI developments in 2025?"
204
+ - "Search for news about quantum computing breakthroughs"
205
+ - "What are current climate change updates?"
206
+ - "Latest space exploration news"
207
+ - "What's happening in renewable energy?"
208
+ - "Current updates on electric vehicles"
209
+
210
+ ### Global News
211
+ - "What are the latest global news stories?"
212
+ - "Search for international news today"
213
+ - "Tell me about current world events"
214
+
215
+ ---
216
+
217
+ ## 💼 Stock Market & Financial (Web Search)
218
+
219
+ ### Market Trends
220
+ - "What is the current US stock market trend?"
221
+ - "What are today's stock market trends?"
222
+ - "Current stock market performance"
223
+ - "How is the S&P 500 performing today?"
224
+ - "What are the latest stock market trends?"
225
+ - "Search for current market conditions"
226
+
227
+ ### Specific Markets
228
+ - "What is the current oil price?"
229
+ - "Tell me present day crypto prices"
230
+ - "Current Bitcoin and Ethereum prices"
231
+ - "What are the latest NASDAQ trends?"
232
+ - "How is the Dow Jones performing?"
233
+ - "Search for current commodity prices"
234
+ - "What are today's forex rates?"
235
+
236
+ ### Combined Market Queries
237
+ - "What are current US stock market trends and oil market prices?"
238
+ - "Search for stock market news and cryptocurrency trends"
239
+ - "Tell me about current stock trends and present day crypto prices"
240
+ - "What are today's market trends and commodity prices?"
241
+
242
+ ---
243
+
244
+ ## 💻 Technology & AI (Web Search)
245
+
246
+ ### AI Trends
247
+ - "What are the latest AI trends in 2025?"
248
+ - "Search for latest AI developments"
249
+ - "Current trends in artificial intelligence"
250
+ - "What's new in machine learning?"
251
+ - "Latest developments in neural networks"
252
+ - "What are the current AI breakthroughs?"
253
+ - "Search for recent AI research findings"
254
+
255
+ ### Tech News
256
+ - "Latest technology news"
257
+ - "What are current tech industry trends?"
258
+ - "Search for quantum computing updates"
259
+ - "Latest developments in cloud computing"
260
+ - "What's new in software development?"
261
+ - "Current trends in cybersecurity"
262
+
263
+ ### Emerging Technologies
264
+ - "Search for latest developments in quantum computing"
265
+ - "What are the newest trends in blockchain technology?"
266
+ - "Latest updates on 5G and 6G networks"
267
+ - "Current progress in autonomous vehicles"
268
+
269
+ ---
270
+
271
+ ## 🎓 Educational & Research (Web Search)
272
+
273
+ ### Science
274
+ - "Latest research on climate change"
275
+ - "Recent discoveries in space exploration"
276
+ - "What are the newest medical breakthroughs?"
277
+ - "Current research on renewable energy"
278
+ - "Latest findings in marine biology"
279
+ - "Recent discoveries in astronomy"
280
+
281
+ ### Academic Topics
282
+ - "Search for latest research on quantum physics"
283
+ - "What are recent findings in neuroscience?"
284
+ - "Latest developments in biotechnology"
285
+ - "Current research on artificial intelligence ethics"
286
+ - "Recent studies on renewable energy"
287
+
288
+ ### General Knowledge
289
+ - "Search for educational content about ancient civilizations"
290
+ - "What are the latest findings in archaeology?"
291
+ - "Current research on human psychology"
292
+
293
+ ---
294
+
295
+ ## 🔬 Science & Health (Web Search)
296
+
297
+ ### Health & Medicine
298
+ - "What are the latest medical breakthroughs?"
299
+ - "Search for current health research"
300
+ - "Latest developments in cancer treatment"
301
+ - "What's new in vaccine research?"
302
+ - "Current trends in mental health research"
303
+
304
+ ### Science News
305
+ - "Latest scientific discoveries"
306
+ - "What are recent space exploration achievements?"
307
+ - "Current research on climate solutions"
308
+ - "Latest findings in physics"
309
+ - "Recent breakthroughs in chemistry"
310
+
311
+ ---
312
+
313
+ ## 🏢 Business & Economics (Web Search)
314
+
315
+ ### Business News
316
+ - "What are current business trends?"
317
+ - "Latest corporate news"
318
+ - "Search for startup news"
319
+ - "What are today's business headlines?"
320
+ - "Current trends in entrepreneurship"
321
+
322
+ ### Economic Data
323
+ - "What are current economic conditions?"
324
+ - "Search for latest economic indicators"
325
+ - "Tell me about present day economic conditions"
326
+ - "What are current inflation rates?"
327
+ - "Latest GDP growth data"
328
+
329
+ ---
330
+
331
+ ## 🌍 Real-time Information (Web Search)
332
+
333
+ ### Time-Sensitive Queries
334
+ - "What's happening right now in the stock market?"
335
+ - "Current trends as of now"
336
+ - "Present day market conditions"
337
+ - "What are this week's tech trends?"
338
+ - "This month's biggest news stories"
339
+ - "What happened this year in AI?"
340
+ - "Search for right now's top stories"
341
+ - "As of now, what are the trends?"
342
+
343
+ ### Specific Dates/Years
344
+ - "Latest news from 2025"
345
+ - "What are the trends in 2026?"
346
+ - "Search for 2025 technology predictions"
347
+ - "Current 2025 market analysis"
348
+ - "What's happening in January 2025?"
349
+
350
+ ---
351
+
352
+ ## 🔄 Combined Queries (Math + Search)
353
+
354
+ ### Business + Finance
355
+ - "What are current market trends and stock prices? Also calculate ROI for an investment of $5000 with 8% return"
356
+ - "If a company's stock price is $150 and it increased by 15%, what's the new price? Also search for current market trends"
357
+ - "Search for today's stock market performance. Also calculate: if I invest $10,000 and it grows by 5%, what's my new total?"
358
+ - "Find current commodity prices and calculate 15% of 5000"
359
+
360
+ ### Tech + Math
361
+ - "Search for latest AI trends and calculate: if AI adoption grows by 25% annually, what's the growth after 5 years from a base of 100?"
362
+ - "Calculate 100 * 45 and also search for current stock market trends"
363
+ - "What's 2^8? Also tell me the latest AI news"
364
+ - "Search for quantum computing updates and calculate factorial of 5"
365
+ - "Find latest tech trends and calculate pow(2, 10)"
366
+
367
+ ### Scientific Calculator + Search
368
+ - "Calculate sin(pi/2) and search for latest mathematics research"
369
+ - "What is factorial(10)? Also find current computer science news"
370
+ - "Calculate fibonacci(15) and search for algorithm research"
371
+ - "Find permutations of 10 taken 3. Also search for combinatorics news"
372
+ - "Calculate gcd(48, 18) and search for number theory developments"
373
+ - "What is log10(1000)? Also find latest scientific discoveries"
374
+
375
+ ### Algorithms + Search
376
+ - "Calculate fibonacci(20) and search for latest algorithm research"
377
+ - "Find ncr(10, 3) and search for combinatorics applications"
378
+ - "What is npr(5, 3)? Also find permutation algorithms in practice"
379
+ - "Calculate factorial(7) and search for factorial applications"
380
+ - "Find binary of 255 and search for binary computation methods"
381
+
382
+ ### Current Events + Math
383
+ - "What's today's stock market performance? Also, if I invest $10,000 and it grows by 5%, what's my new total?"
384
+ - "Search for today's crypto prices and calculate: if Bitcoin is $50,000 and I have 0.5 BTC, what's my total value?"
385
+ - "Calculate 50 * 20 and search for today's cryptocurrency prices"
386
+ - "What are today's news headlines? Also calculate 25 * 48"
387
+
388
+ ### Real-world Problems
389
+ - "If a portfolio worth $100,000 grows by 12% this year, what's the new value? Also search for current investment trends"
390
+ - "Calculate compound interest on $10,000 at 5% for 3 years, and search for investment trends"
391
+ - "Search for current market conditions and calculate: if a stock increases by 8% from $200, what's the new price?"
392
+ - "Find present day economic conditions and calculate 15% of 2500"
393
+
394
+ ---
395
+
396
+ ## 🎯 Edge Cases & Complex Queries
397
+
398
+ ### Complex Detection
399
+ - "Calculate 100 * 5 and search for latest trends"
400
+ - "What's 25 * 48? Also tell me about present day economic conditions"
401
+ - "Solve 2^10 and search for current global market trends"
402
+
403
+ ### Multi-Part Questions
404
+ - "Calculate the surface area of a 6x4x5 cm box. Also search for latest technology trends"
405
+ - "What's 15% of 340? Also tell me about current stock market performance"
406
+ - "Find the area of a circle with radius 7 cm. Also search for today's news"
407
+
408
+ ### Natural Language Variations
409
+ - "Can you compute 123 * 456? Also find me the latest AI news"
410
+ - "I need to know what 25 * 48 equals. Also search for current market trends"
411
+ - "Please calculate 2^10 and tell me about present day trends"
412
+
413
+ ---
414
+
415
+ ## 🔍 Testing Specific Features
416
+
417
+ ### Calculator Detection Patterns - Basic Math
418
+ - "What is 25 * 48?"
419
+ - "Compute 100 / 4"
420
+ - "Solve: 50 + 25 - 10"
421
+ - "Calculate 2^8"
422
+ - "What's the square root of 144?"
423
+ - "Multiply 15 by 20"
424
+ - "Divide 1000 by 25"
425
+ - "What's 15% of 340?"
426
+ - "999 divided by 3"
427
+
428
+ ### Calculator Detection Patterns - Scientific Functions
429
+ - "Calculate sin(pi/2)"
430
+ - "What is cosine of 0?"
431
+ - "Find tangent of pi/4"
432
+ - "Calculate log10(100)"
433
+ - "What is natural log of e?"
434
+ - "Find factorial of 5"
435
+ - "Calculate sqrt(144)"
436
+ - "What is pow(2, 8)?"
437
+ - "Convert 180 degrees to radians"
438
+
439
+ ### Calculator Detection Patterns - Algorithms
440
+ - "Calculate fibonacci of 10"
441
+ - "What is npr(5, 3)?"
442
+ - "Find ncr(10, 3)"
443
+ - "Calculate permutations of 5 taken 3"
444
+ - "What are combinations of 10 choose 3?"
445
+ - "Convert 255 to binary"
446
+ - "What is 255 in hexadecimal?"
447
+ - "Find binary of 128"
448
+
449
+ ### Calculator Detection Patterns - Geometry
450
+ - "Find the surface area of a rectangular prism with length = 6, width = 4, height = 5"
451
+ - "Calculate the area of a circle with radius 7"
452
+ - "What's the volume of a cylinder with radius 5 and height 10?"
453
+
454
+ ### Web Search Detection Patterns
455
+ - "Search for latest AI trends"
456
+ - "Find current stock market news"
457
+ - "Tell me about today's events"
458
+ - "What are present day conditions?"
459
+ - "Search for 2025 technology predictions"
460
+ - "Tell me the latest news"
461
+ - "What's happening right now?"
462
+ - "Current trends in technology"
463
+ - "Presently, what are the top stories?"
464
+ - "What are today's top news headlines?"
465
+ - "Search for latest breaking news"
466
+ - "Tell me about present day market conditions"
467
+
468
+ ### Combined Detection - Basic Math + Search
469
+ - "Calculate 100 * 5 and search for latest trends"
470
+ - "What's 25 * 48? Also find me current stock prices"
471
+ - "Solve 2^10 and tell me about today's news"
472
+ - "Compute 50 * 20 and search for latest AI developments"
473
+ - "Calculate 15% of 340 and search for current trends"
474
+
475
+ ### Combined Detection - Scientific Calculator + Search
476
+ - "Calculate sin(pi/2) and search for latest mathematics news"
477
+ - "What is factorial(10)? Also find algorithm research"
478
+ - "Calculate fibonacci(15) and search for computer science developments"
479
+ - "Find ncr(10, 3) and search for combinatorics applications"
480
+ - "Convert 255 to binary and search for binary computation methods"
481
+
482
+ ### Combined Detection - Multiple Tools
483
+ - "Calculate 25 * 48, factorial of 5, and search for latest trends"
484
+ - "What's fibonacci(10) and npr(5, 3)? Also find algorithm news"
485
+ - "Calculate sqrt(144) and log10(100). Also search for current events"
486
+
487
+ ---
488
+
489
+ ## 📊 Performance Test Questions
490
+
491
+ ### Quick Tests - Basic
492
+ 1. "Calculate 25 * 48"
493
+ 2. "Search for latest AI trends"
494
+ 3. "What's 2^10 and also search for current stock market trends"
495
+
496
+ ### Quick Tests - Scientific Calculator
497
+ 1. "Calculate sin(pi/2)"
498
+ 2. "What is factorial(5)?"
499
+ 3. "Calculate fibonacci(10)"
500
+
501
+ ### Quick Tests - Algorithms
502
+ 1. "Calculate npr(5, 3)"
503
+ 2. "What is ncr(10, 3)?"
504
+ 3. "Convert 255 to binary"
505
+
506
+ ### Comprehensive Tests - Basic Math + Search
507
+ 1. "Find the surface area of a rectangular prism with dimensions: length = 6 cm, width = 4 cm, height = 5 cm. Also search for today's technology news"
508
+ 2. "Calculate compound interest on $10,000 at 5% for 3 years. Also tell me about present day market conditions"
509
+ 3. "What are current US stock market trends and oil market prices? Also calculate 100 * 45"
510
+
511
+ ### Comprehensive Tests - Scientific Calculator + Search
512
+ 1. "Calculate sin(pi/2), factorial(10), and gcd(48, 18). Also search for latest mathematics research"
513
+ 2. "Find fibonacci(15) and npr(5, 3). Also search for algorithm applications"
514
+ 3. "Calculate log10(1000), sqrt(144), and pow(2, 8). Also find current scientific developments"
515
+
516
+ ### Comprehensive Tests - Algorithms + Search
517
+ 1. "Calculate fibonacci(20), factorial(7), and ncr(10, 3). Also search for combinatorics research"
518
+ 2. "Find permutations of 10 taken 4 and combinations of 10 choose 4. Also search for algorithm complexity analysis"
519
+ 3. "Convert 255 to binary, hex, and octal. Also search for base conversion applications"
520
+
521
+ ### Edge Case Tests
522
+ 1. "Calculate 25 * 48, 15% of 340, and 999 divided by 3"
523
+ 2. "Calculate sin(pi/2), cos(0), tan(pi/4), and log10(100)"
524
+ 3. "Find fibonacci(10), npr(5, 3), ncr(5, 3), and factorial(5)"
525
+ 4. "Calculate sqrt(144), pow(2, 8), gcd(48, 18), and lcm(12, 18)"
526
+ 5. "Convert 255 to binary, hex, and octal. Also calculate factorial of 5"
527
+
528
+ ---
529
+
530
+ ## 🎓 Example Responses Expected
531
+
532
+ ### Example 1: Basic Math + Search
533
+ When you ask: *"Calculate 25 * 48 and search for latest AI trends"*
534
+
535
+ meeTARA should:
536
+ 1. ✅ Detect calculator need (matches "25 * 48")
537
+ 2. ✅ Detect web search need (matches "latest" + "trends")
538
+ 3. ✅ Execute calculator: `25 * 48 = 1200`
539
+ 4. ✅ Execute web search: Get current AI trends from Google/DuckDuckGo
540
+ 5. ✅ Feed both results to AI model
541
+ 6. ✅ Generate structured response:
542
+ ```
543
+ 🎯 Answer: 25 × 48 = 1200. Based on recent search results...
544
+ 📊 Details: Latest AI trends in 2025 include...
545
+ ⚡ Steps: [Calculation steps + key findings]
546
+ 💡 Note: [Additional insights]
547
+ ```
548
+
549
+ ### Example 2: Scientific Calculator
550
+ When you ask: *"Calculate sin(pi/2) and factorial of 5"*
551
+
552
+ meeTARA should:
553
+ 1. ✅ Detect calculator need (matches "sin" and "factorial")
554
+ 2. ✅ Execute calculator: `sin(pi/2) = 1.0` and `factorial(5) = 120`
555
+ 3. ✅ Feed results to AI model
556
+ 4. ✅ Generate structured response:
557
+ ```
558
+ 🎯 Answer: sin(π/2) = 1.0, factorial(5) = 120
559
+ 📊 Details: [Mathematical context]
560
+ ⚡ Steps: [Calculation steps]
561
+ 💡 Note: [Additional insights]
562
+ ```
563
+
564
+ ### Example 3: Algorithms
565
+ When you ask: *"Calculate fibonacci(10) and npr(5, 3)"*
566
+
567
+ meeTARA should:
568
+ 1. ✅ Detect calculator need (matches "fibonacci" and "npr")
569
+ 2. ✅ Execute calculator: `fibonacci(10) = 55` and `npr(5, 3) = 60`
570
+ 3. ✅ Feed results to AI model
571
+ 4. ✅ Generate structured response:
572
+ ```
573
+ 🎯 Answer: fibonacci(10) = 55, npr(5, 3) = 60
574
+ 📊 Details: [Algorithm context]
575
+ ⚡ Steps: [Calculation steps]
576
+ 💡 Note: [Additional insights]
577
+ ```
578
+
579
+ ### Example 4: Base Conversion
580
+ When you ask: *"Convert 255 to binary and hexadecimal"*
581
+
582
+ meeTARA should:
583
+ 1. ✅ Detect calculator need (matches "convert" and "binary"/"hexadecimal")
584
+ 2. ✅ Execute calculator: `bin(255) = 0b11111111` and `hex(255) = 0xff`
585
+ 3. ✅ Feed results to AI model
586
+ 4. ✅ Generate structured response:
587
+ ```
588
+ 🎯 Answer: 255 in binary = 0b11111111, in hexadecimal = 0xff
589
+ 📊 Details: [Base conversion context]
590
+ ⚡ Steps: [Conversion steps]
591
+ 💡 Note: [Additional insights]
592
+ ```
593
+
594
+ ### Example 5: Complex Combined Query
595
+ When you ask: *"Calculate fibonacci(15), factorial(7), and search for latest algorithm research"*
596
+
597
+ meeTARA should:
598
+ 1. ✅ Detect calculator need (matches "fibonacci" and "factorial")
599
+ 2. ✅ Detect web search need (matches "latest" + "research")
600
+ 3. ✅ Execute calculator: `fibonacci(15) = 610` and `factorial(7) = 5040`
601
+ 4. ✅ Execute web search: Get algorithm research from Google/DuckDuckGo
602
+ 5. ✅ Feed all results to AI model
603
+ 6. ✅ Generate structured response with all calculations and search results
604
+
605
+ ---
606
+
607
+ ## 📝 Notes for Testing
608
+
609
+ ### Calculator Testing
610
+ - ✅ **Basic math queries** should return accurate results
611
+ - ✅ **Percentage queries** should convert correctly (e.g., "15% of 340" → 51)
612
+ - ✅ **Division text** should convert correctly (e.g., "999 divided by 3" → 333)
613
+ - ✅ **Scientific functions** should handle radians, degrees, logarithms correctly
614
+ - ✅ **Algorithm functions** should calculate Fibonacci, Permutations, Combinations accurately
615
+ - ✅ **Base conversions** should return properly formatted results (0b, 0x, 0o prefixes)
616
+ - ✅ **Geometry queries** should detect and provide context (model calculates formulas)
617
+ - ✅ **Complex expressions** should respect order of operations (PEMDAS/BODMAS)
618
+
619
+ ### Web Search Testing
620
+ - ✅ **Web search queries** should fetch current/recent information
621
+ - ✅ **Time-sensitive queries** should trigger search (today, latest, current, present day)
622
+ - ✅ **Stock market queries** should detect and search for market data
623
+ - ✅ **News queries** should detect and search for current events
624
+ - ✅ **Fallback behavior** should work if Google API is unavailable (DuckDuckGo)
625
+ - ✅ **Error handling** should be graceful if search returns no results
626
+
627
+ ### Combined Queries Testing
628
+ - ✅ **Combined queries** should handle both calculator and web search correctly
629
+ - ✅ **Multiple calculator operations** should execute sequentially
630
+ - ✅ **Calculator + Search** should combine results properly
631
+ - ✅ **Response format** should follow meeTARA's structured format (🎯📊⚡💡)
632
+
633
+ ### Detection Testing
634
+ - ✅ **Calculator detection** should catch: basic math, percentage, division, scientific, algorithms
635
+ - ✅ **Web search detection** should catch: today, latest, current, trends, news, stock market
636
+ - ✅ **Natural language** should be converted correctly (e.g., "sine of 90" → sin(90))
637
+ - ✅ **Pattern matching** should work for variations (e.g., "5P3", "5C3", "npr(5,3)")
638
+
639
+ ### Error Handling
640
+ - ✅ **Invalid expressions** should return error messages gracefully
641
+ - ✅ **Division by zero** should be handled safely
642
+ - ✅ **Out of range** (e.g., factorial(1000)) should be handled
643
+ - ✅ **Search failures** should fallback to DuckDuckGo automatically
644
+ - ✅ **No search results** should still provide model response
645
+
646
+ ---
647
+
648
+ ## 🎯 Testing Checklist
649
+
650
+ ### Basic Calculator ✅
651
+ - [ ] Basic arithmetic (+, -, *, /)
652
+ - [ ] Percentage calculations
653
+ - [ ] Division text conversion
654
+ - [ ] Order of operations
655
+
656
+ ### Scientific Calculator ✅
657
+ - [ ] Trigonometry (sin, cos, tan, arcsin, arccos, arctan)
658
+ - [ ] Hyperbolic functions (sinh, cosh, tanh)
659
+ - [ ] Logarithms (log, log10, log2, ln, exp)
660
+ - [ ] Powers and roots (sqrt, cbrt, pow)
661
+ - [ ] Advanced (factorial, gcd, lcm, floor, ceil)
662
+ - [ ] Degree/radian conversion
663
+
664
+ ### Algorithms & Data Structures ✅
665
+ - [ ] Fibonacci sequence
666
+ - [ ] Permutations (nPr)
667
+ - [ ] Combinations (nCr)
668
+ - [ ] Base conversions (binary, hex, octal)
669
+ - [ ] Complexity keywords (detection)
670
+ - [ ] Data structure keywords (detection)
671
+
672
+ ### Geometry ✅
673
+ - [ ] Surface area
674
+ - [ ] Area of circle
675
+ - [ ] Volume calculations
676
+ - [ ] Perimeter calculations
677
+
678
+ ### Web Search ✅
679
+ - [ ] Today's news
680
+ - [ ] Current trends
681
+ - [ ] Stock market data
682
+ - [ ] Latest developments
683
+ - [ ] Time-sensitive queries
684
+
685
+ ### Combined Queries ✅
686
+ - [ ] Calculator + Search
687
+ - [ ] Multiple calculations + Search
688
+ - [ ] Scientific + Search
689
+ - [ ] Algorithms + Search
690
+
691
+ ---
692
+
693
+ **Happy Testing! 🚀**
694
+
695
+ The meeTARA Agent now supports comprehensive mathematical calculations, scientific functions, algorithms, data structures, and intelligent web search!
696
+
config/agent_config.json CHANGED
@@ -11,6 +11,15 @@
11
  "trend",
12
  "trends",
13
  "news",
 
 
 
 
 
 
 
 
 
14
  "2024",
15
  "2025",
16
  "2026",
@@ -49,6 +58,50 @@
49
  "breaking news",
50
  "latest developments"
51
  ],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  "search_patterns": [
53
  {
54
  "pattern": "(?:search|find)\\s+(?:for\\s+)?(.+?)(?:\\s+and|\\s+also|$)",
@@ -96,7 +149,86 @@
96
  "provider": "duckduckgo",
97
  "google_custom_search": {
98
  "enabled": false,
99
- "note": "Set GOOGLE_CUSTOM_SEARCH_API_KEY and GOOGLE_CUSTOM_SEARCH_ENGINE_ID environment variables to enable. Free tier: 100 queries/day. See README for setup instructions."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  }
101
  },
102
 
@@ -123,20 +255,241 @@
123
  "sum",
124
  "difference",
125
  "product",
126
- "quotient"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  ],
128
  "math_patterns": [
129
  {
130
- "pattern": "\\b(calculate|compute|what is|what's|solve|evaluate|multiply|divide|add|subtract)\\s+([0-9+\\-*/().\\s]+?)(?:\\s+and|\\s+also|$)",
131
- "description": "calculate 25 * 48, what is 25 * 48, compute X"
132
  },
133
  {
134
- "pattern": "\\b([0-9]+\\s*[+\\-*/×÷]\\s*[0-9]+)",
135
- "description": "25 * 48 or 25 × 48"
136
  },
137
  {
138
  "pattern": "\\b(sqrt|square root|power|exponent)\\s*\\(?\\s*([0-9]+)\\s*\\)?",
139
  "description": "sqrt(16) or square root 16"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  }
141
  ],
142
  "enabled": true
 
11
  "trend",
12
  "trends",
13
  "news",
14
+ "headlines",
15
+ "headline",
16
+ "breaking news",
17
+ "latest news",
18
+ "today's news",
19
+ "top news",
20
+ "current news",
21
+ "what happened",
22
+ "happening",
23
  "2024",
24
  "2025",
25
  "2026",
 
58
  "breaking news",
59
  "latest developments"
60
  ],
61
+ "news_query_keywords": [
62
+ "news",
63
+ "headlines",
64
+ "headline",
65
+ "breaking news",
66
+ "latest news",
67
+ "today's news",
68
+ "top news",
69
+ "current news",
70
+ "what happened",
71
+ "happening",
72
+ "breaking",
73
+ "developing"
74
+ ],
75
+ "meta_page_filter_phrases": [
76
+ "provides",
77
+ "offers",
78
+ "visit",
79
+ "check",
80
+ "read",
81
+ "explore",
82
+ "go to",
83
+ "subscribe",
84
+ "newsletter",
85
+ "website",
86
+ "at cnn",
87
+ "at fox",
88
+ "at bbc",
89
+ "you can find",
90
+ "available across",
91
+ "including cnn",
92
+ "including fox",
93
+ "visit cnn",
94
+ "check fox",
95
+ "view",
96
+ "on cnn.com",
97
+ "on foxnews.com",
98
+ "on bbc.com"
99
+ ],
100
+ "news_enhancement_keywords": [
101
+ "headlines",
102
+ "breaking",
103
+ "latest"
104
+ ],
105
  "search_patterns": [
106
  {
107
  "pattern": "(?:search|find)\\s+(?:for\\s+)?(.+?)(?:\\s+and|\\s+also|$)",
 
149
  "provider": "duckduckgo",
150
  "google_custom_search": {
151
  "enabled": false,
152
+ "auto_fallback_on_quota": true,
153
+ "note": "Set GOOGLE_CUSTOM_SEARCH_API_KEY and GOOGLE_CUSTOM_SEARCH_ENGINE_ID environment variables to enable. Free tier: 100 queries/day. When quota exceeded, automatically falls back to DuckDuckGo (free, unlimited). Paid tier: $5 per 1,000 queries after free tier. See README for setup instructions.",
154
+ "quota_info": {
155
+ "free_tier_quota": 100,
156
+ "free_tier_period": "per day",
157
+ "paid_tier_cost": "$5 per 1,000 queries",
158
+ "auto_fallback": "DuckDuckGo (free, unlimited)"
159
+ }
160
+ },
161
+ "news_query_instructions": {
162
+ "enabled": true,
163
+ "instruction_template": "CRITICAL INSTRUCTIONS FOR NEWS/HEADLINES QUERY:\n1. Extract and list the ACTUAL NEWS HEADLINES and STORIES from the search results above.\n2. Do NOT just describe news sources (e.g., 'CNN provides...' or 'Fox News offers...').\n3. Extract the REAL HEADLINES and NEWS CONTENT (e.g., 'President announces...', 'Stock market rises...', 'New study finds...').\n4. Focus on actual events, developments, and stories mentioned in the search results.\n5. You may mention source names briefly for attribution (e.g., 'According to CNN...'), but the primary content should be the actual news headlines and stories.\n6. List specific headlines and news items, not generic descriptions of news websites.\n\nExample of what to extract: 'Breaking: Stock market surges 2% today' (actual news)\nNOT: 'CNN provides breaking news updates' (source description)\n\nNow extract the actual news headlines and content from the search results above.",
164
+ "regular_instruction": "Use the tool results above to provide your response."
165
+ },
166
+ "safety_filter": {
167
+ "enabled": true,
168
+ "blocked_keywords": [
169
+ "how to hack",
170
+ "how to crack",
171
+ "how to break into",
172
+ "how to bypass",
173
+ "how to exploit",
174
+ "how to harm",
175
+ "how to hurt",
176
+ "how to kill",
177
+ "how to attack",
178
+ "how to destroy",
179
+ "how to steal",
180
+ "how to scam",
181
+ "how to cheat",
182
+ "how to manipulate",
183
+ "illegal activities",
184
+ "illegal content",
185
+ "piracy",
186
+ "warez",
187
+ "crack software",
188
+ "hack account",
189
+ "ddos attack",
190
+ "phishing",
191
+ "malware",
192
+ "virus download",
193
+ "trojan",
194
+ "keylogger",
195
+ "spyware",
196
+ "ransomware",
197
+ "drugs buy",
198
+ "drugs online",
199
+ "weapons buy",
200
+ "weapons online",
201
+ "violence instructions",
202
+ "terrorism",
203
+ "extremist content",
204
+ "hate speech",
205
+ "discriminatory content",
206
+ "explicit adult content",
207
+ "adult websites",
208
+ "nsfw content",
209
+ "inappropriate content"
210
+ ],
211
+ "blocked_patterns": [
212
+ {
213
+ "pattern": "how\\s+to\\s+(?:hack|crack|break|bypass|exploit|harm|hurt|attack|destroy|steal|scam|cheat|manipulate)",
214
+ "description": "How-to queries for harmful activities"
215
+ },
216
+ {
217
+ "pattern": "(?:download|get|buy)\\s+(?:malware|virus|trojan|keylogger|spyware|ransomware|warez|cracked)",
218
+ "description": "Downloads of malicious software or pirated content"
219
+ },
220
+ {
221
+ "pattern": "(?:buy|purchase|get)\\s+(?:drugs|weapons|illegal)",
222
+ "description": "Illegal goods purchase queries"
223
+ },
224
+ {
225
+ "pattern": "(?:instructions|guide|tutorial)\\s+(?:for|on)\\s+(?:violence|attack|harm|illegal)",
226
+ "description": "Instructions for harmful activities"
227
+ }
228
+ ],
229
+ "safe_search_enabled": true,
230
+ "safe_search_param": "safe=active",
231
+ "blocked_response_message": "I cannot search for content that may involve harmful, illegal, or inappropriate material. Please try a different query related to news, current events, technology, education, or general information."
232
  }
233
  },
234
 
 
255
  "sum",
256
  "difference",
257
  "product",
258
+ "quotient",
259
+ "percentage",
260
+ "percent",
261
+ "%",
262
+ "of",
263
+ "surface area",
264
+ "area",
265
+ "volume",
266
+ "perimeter",
267
+ "circumference",
268
+ "radius",
269
+ "diameter",
270
+ "find the",
271
+ "find",
272
+ "rectangular prism",
273
+ "circle",
274
+ "rectangle",
275
+ "triangle",
276
+ "cylinder",
277
+ "sphere",
278
+ "sine",
279
+ "cosine",
280
+ "tangent",
281
+ "sin",
282
+ "cos",
283
+ "tan",
284
+ "arcsin",
285
+ "arccos",
286
+ "arctan",
287
+ "logarithm",
288
+ "log",
289
+ "ln",
290
+ "natural log",
291
+ "exponential",
292
+ "exp",
293
+ "factorial",
294
+ "gcd",
295
+ "lcm",
296
+ "degrees",
297
+ "radians",
298
+ "square root",
299
+ "cube root",
300
+ "sqrt",
301
+ "power",
302
+ "exponent",
303
+ "pow",
304
+ "algorithm",
305
+ "algorithms",
306
+ "data structure",
307
+ "data structures",
308
+ "big o",
309
+ "complexity",
310
+ "time complexity",
311
+ "space complexity",
312
+ "fibonacci",
313
+ "fibonacci sequence",
314
+ "permutation",
315
+ "permutations",
316
+ "combination",
317
+ "combinations",
318
+ "ncr",
319
+ "npr",
320
+ "factorial",
321
+ "fact",
322
+ "binary search",
323
+ "linear search",
324
+ "sorting",
325
+ "quick sort",
326
+ "merge sort",
327
+ "bubble sort",
328
+ "graph",
329
+ "tree",
330
+ "binary tree",
331
+ "array",
332
+ "linked list",
333
+ "stack",
334
+ "queue",
335
+ "hash",
336
+ "hash table",
337
+ "heap",
338
+ "binary heap",
339
+ "trie",
340
+ "recursion",
341
+ "recursive",
342
+ "dynamic programming",
343
+ "greedy",
344
+ "backtracking",
345
+ "divide and conquer",
346
+ "binary",
347
+ "hexadecimal",
348
+ "hex",
349
+ "decimal",
350
+ "octal",
351
+ "base conversion"
352
  ],
353
  "math_patterns": [
354
  {
355
+ "pattern": "\\b(calculate|compute|what is|what's|solve|evaluate|multiply|divide|add|subtract|find|find the)\\s+([0-9+\\-*/().\\s]+?)(?:\\s+and|\\s+also|$)",
356
+ "description": "calculate 25 * 48, what is 25 * 48, compute X, find the area"
357
  },
358
  {
359
+ "pattern": "\\b([0-9]+\\s*[+\\-*/×÷]\\s*[0-9]+(?:\\s*[+\\-*/×÷]\\s*[0-9]+)*)",
360
+ "description": "25 * 48 or 25 × 48 or 1000 - 456 + 234 * 3"
361
  },
362
  {
363
  "pattern": "\\b(sqrt|square root|power|exponent)\\s*\\(?\\s*([0-9]+)\\s*\\)?",
364
  "description": "sqrt(16) or square root 16"
365
+ },
366
+ {
367
+ "pattern": "\\b([0-9]+(?:\\.[0-9]+)?)\\s*%\\s*(?:of|times|×)\\s*([0-9]+)",
368
+ "description": "15% of 340, 25% times 100"
369
+ },
370
+ {
371
+ "pattern": "(?:what is|what's|calculate|compute|find|find the)\\s+([0-9]+(?:\\.[0-9]+)?)\\s*%\\s*(?:of|times|×)?\\s*([0-9]+)",
372
+ "description": "what's 15% of 340"
373
+ },
374
+ {
375
+ "pattern": "\\b([0-9]+)\\s+(?:divided by|divided|÷|/)\\s+([0-9]+)",
376
+ "description": "999 divided by 3, 100 / 4, 50 ÷ 2"
377
+ },
378
+ {
379
+ "pattern": "(?:find|calculate|compute|what is|what's|the)\\s+(?:surface area|area|volume|perimeter|circumference)\\s+(?:of|for)?",
380
+ "description": "find the surface area of, calculate the area of a circle, volume of a cylinder"
381
+ },
382
+ {
383
+ "pattern": "(?:surface area|area|volume|perimeter|circumference)\\s+(?:of|for|with)\\s+.*?(?:radius|diameter|length|width|height|dimensions)\\s*=",
384
+ "description": "area of a circle with radius 7 cm, surface area with dimensions length = 6"
385
+ },
386
+ {
387
+ "pattern": "\\b(sin|cos|tan|asin|acos|atan|arcsin|arccos|arctan|sine|cosine|tangent)\\s*\\(\\s*([0-9]+(?:\\.[0-9]+)?|pi|e|pi/[0-9]+)\\s*\\)",
388
+ "description": "sin(90), cos(pi/2), tan(45), sine(90), arcsin(1)"
389
+ },
390
+ {
391
+ "pattern": "(?:calculate|compute|what is|what's|find|find the)\\s+(?:the\\s+)?(sin|cos|tan|sine|cosine|tangent|arcsin|arccos|arctan)\\s+(?:of|at)?\\s*([0-9]+(?:\\.[0-9]+)?|pi|e|pi/[0-9]+)",
392
+ "description": "calculate sine of 90, what is cos of pi/2, find tangent of 45"
393
+ },
394
+ {
395
+ "pattern": "\\b(sinh|cosh|tanh|asinh|acosh|atanh)\\s*\\(\\s*([0-9]+(?:\\.[0-9]+)?)\\s*\\)",
396
+ "description": "sinh(2), cosh(3), tanh(1)"
397
+ },
398
+ {
399
+ "pattern": "\\b(log|ln|log10|log2|natural log|logarithm)\\s*\\(\\s*([0-9]+(?:\\.[0-9]+)?|e|10|2)\\s*\\)",
400
+ "description": "log(10), ln(e), log10(100), log2(8), natural log of e"
401
+ },
402
+ {
403
+ "pattern": "(?:calculate|compute|what is|what's|find)\\s+(?:the\\s+)?(log|ln|natural log|logarithm|log base 10|log base 2)\\s+(?:of|of the number)?\\s*([0-9]+(?:\\.[0-9]+)?|e|10|2)",
404
+ "description": "calculate log of 10, what is natural log of e, find log base 10 of 100"
405
+ },
406
+ {
407
+ "pattern": "\\bexp\\s*\\(\\s*([0-9]+(?:\\.[0-9]+)?)\\s*\\)|exponential\\s+of\\s+([0-9]+(?:\\.[0-9]+)?)|e\\^([0-9]+(?:\\.[0-9]+)?)",
408
+ "description": "exp(2), exponential of 3, e^2"
409
+ },
410
+ {
411
+ "pattern": "\\bfactorial\\s*\\(\\s*([0-9]+)\\s*\\)|([0-9]+)!",
412
+ "description": "factorial(5), 5!"
413
+ },
414
+ {
415
+ "pattern": "(?:calculate|compute|what is|what's|find)\\s+(?:the\\s+)?factorial\\s+of\\s+([0-9]+)|([0-9]+)\\s*factorial",
416
+ "description": "calculate factorial of 5, what is 5 factorial"
417
+ },
418
+ {
419
+ "pattern": "\\bgcd\\s*\\(\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*\\)|greatest common divisor\\s+of\\s+([0-9]+)\\s+and\\s+([0-9]+)",
420
+ "description": "gcd(48, 18), greatest common divisor of 48 and 18"
421
+ },
422
+ {
423
+ "pattern": "\\blcm\\s*\\(\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*\\)|least common multiple\\s+of\\s+([0-9]+)\\s+and\\s+([0-9]+)",
424
+ "description": "lcm(12, 18), least common multiple of 12 and 18"
425
+ },
426
+ {
427
+ "pattern": "\\b(pow|power|\\^)\\s*\\(\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*\\)|([0-9]+)\\s*\\^\\s*([0-9]+)|([0-9]+)\\s+to\\s+the\\s+power\\s+of\\s+([0-9]+)",
428
+ "description": "pow(2, 8), 2^8, 2 to the power of 8"
429
+ },
430
+ {
431
+ "pattern": "\\b(sqrt|square root|√)\\s*\\(\\s*([0-9]+(?:\\.[0-9]+)?)\\s*\\)|square root of\\s+([0-9]+(?:\\.[0-9]+)?)|√([0-9]+(?:\\.[0-9]+)?)",
432
+ "description": "sqrt(144), square root(16), square root of 144, √16"
433
+ },
434
+ {
435
+ "pattern": "\\b(cbrt|cube root)\\s*\\(\\s*([0-9]+)\\s*\\)|cube root of\\s+([0-9]+)",
436
+ "description": "cbrt(27), cube root(8), cube root of 27"
437
+ },
438
+ {
439
+ "pattern": "(?:convert|what is|calculate|compute)\\s+([0-9]+(?:\\.[0-9]+)?)\\s*(?:degrees?|°)\\s+(?:to|in)\\s+radians?",
440
+ "description": "convert 180 degrees to radians, what is 90° in radians"
441
+ },
442
+ {
443
+ "pattern": "(?:convert|what is|calculate|compute)\\s+([0-9]+(?:\\.[0-9]+)?|pi|pi/[0-9]+)\\s+radians?\\s+(?:to|in)\\s+degrees?",
444
+ "description": "convert pi radians to degrees, what is pi/2 in degrees"
445
+ },
446
+ {
447
+ "pattern": "\\b(degrees|radians)\\s*\\(\\s*([0-9]+(?:\\.[0-9]+)?|pi|pi/[0-9]+)\\s*\\)",
448
+ "description": "degrees(pi), radians(180)"
449
+ },
450
+ {
451
+ "pattern": "\\b(floor|ceil|trunc|round)\\s*\\(\\s*([0-9]+\\.[0-9]+)\\s*\\)",
452
+ "description": "floor(3.7), ceil(3.2), trunc(3.9), round(3.5)"
453
+ },
454
+ {
455
+ "pattern": "(?:calculate|compute|what is|what's|find|find the|nth term of)\\s+(?:the\\s+)?fibonacci\\s+(?:number|sequence|of|at)?\\s*([0-9]+)|fibonacci\\s*\\(\\s*([0-9]+)\\s*\\)",
456
+ "description": "calculate fibonacci of 10, what is the 10th fibonacci number, fibonacci(10)"
457
+ },
458
+ {
459
+ "pattern": "(?:calculate|compute|what is|what's|find)\\s+(?:the\\s+)?(?:number of\\s+)?permutations?\\s+(?:of|for|with)?\\s*([0-9]+)\\s+(?:taken|choose|selected|at a time|per group)?\\s*([0-9]+)?|npr\\s*\\(\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*\\)|([0-9]+)p([0-9]+)",
460
+ "description": "permutations of 5 taken 3, npr(5,3), 5P3, number of permutations for 5 items"
461
+ },
462
+ {
463
+ "pattern": "(?:calculate|compute|what is|what's|find)\\s+(?:the\\s+)?(?:number of\\s+)?combinations?\\s+(?:of|for|with)?\\s*([0-9]+)\\s+(?:taken|choose|selected|at a time|per group)?\\s*([0-9]+)?|ncr\\s*\\(\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*\\)|([0-9]+)c([0-9]+)",
464
+ "description": "combinations of 5 choose 3, ncr(5,3), 5C3, number of combinations for 5 items"
465
+ },
466
+ {
467
+ "pattern": "(?:what is|what's|calculate|compute|find|analyze|explain)\\s+(?:the\\s+)?(?:time|space)?\\s*complexity\\s+(?:of|for)?|big\\s*o\\s+(?:of|for|notation)?|o\\s*\\(|complexity\\s+analysis",
468
+ "description": "what is the time complexity of, big o of, o(n), complexity analysis"
469
+ },
470
+ {
471
+ "pattern": "(?:calculate|compute|what is|what's|find|explain)\\s+(?:binary|linear|quick|merge|bubble|heap|selection|insertion)\\s+search\\s+(?:complexity|time|space|algorithm)?",
472
+ "description": "binary search complexity, time complexity of merge sort, quick sort big o"
473
+ },
474
+ {
475
+ "pattern": "(?:calculate|compute|what is|what's|find|explain)\\s+(?:binary|linear|quick|merge|bubble|heap|selection|insertion)\\s+sort\\s+(?:complexity|time|space|algorithm)?",
476
+ "description": "merge sort time complexity, big o of quick sort, bubble sort space complexity"
477
+ },
478
+ {
479
+ "pattern": "(?:calculate|compute|what is|what's|find|explain|analyze)\\s+(?:binary\\s+)?tree\\s+(?:height|depth|nodes|leaves|complexity|traversal|algorithm)?|tree\\s+(?:height|depth|nodes|leaves)\\s+(?:of|for)?",
480
+ "description": "binary tree height, tree depth calculation, tree nodes count, tree complexity"
481
+ },
482
+ {
483
+ "pattern": "(?:calculate|compute|what is|what's|find|explain|analyze)\\s+(?:graph|hash|heap|trie|array|linked list|stack|queue)\\s+(?:complexity|operations|algorithm|time|space)?",
484
+ "description": "graph complexity, hash table operations, heap insert complexity, trie search time"
485
+ },
486
+ {
487
+ "pattern": "(?:convert|what is|calculate|compute)\\s+([0-9]+|binary|hex|hexadecimal|decimal|octal)\\s+(?:to|in|as)\\s+(binary|hex|hexadecimal|decimal|octal|base\\s+[0-9]+)",
488
+ "description": "convert 255 to binary, what is 10 in hex, 1010 to decimal, base conversion"
489
+ },
490
+ {
491
+ "pattern": "(?:calculate|compute|what is|what's|find)\\s+(?:recursive|recursion)\\s+(?:complexity|time|space|depth|calls)?|recursion\\s+(?:depth|complexity|for)?",
492
+ "description": "recursive complexity, recursion depth, recursive fibonacci complexity"
493
  }
494
  ],
495
  "enabled": true
core/meetara_agent.py CHANGED
@@ -54,27 +54,242 @@ def calculator(expression: str) -> str:
54
  Evaluates a mathematical expression and returns the result.
55
  Use this for any calculations to ensure accuracy.
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  Args:
58
- expression: A mathematical expression like "25 * 48" or "sqrt(144)"
59
 
60
  Returns:
61
  The calculated result as a string
62
  """
63
  import math
 
 
64
  try:
65
- # Safe evaluation with math functions available
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  allowed_names = {
67
- "abs": abs, "round": round, "min": min, "max": max,
68
- "sum": sum, "pow": pow, "sqrt": math.sqrt,
 
 
 
 
 
69
  "sin": math.sin, "cos": math.cos, "tan": math.tan,
70
- "log": math.log, "log10": math.log10, "exp": math.exp,
71
- "pi": math.pi, "e": math.e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  logger.info(f"[AGENT] 🧮 Calculator API call: expression='{expression}'")
74
- result = eval(expression, {"__builtins__": {}}, allowed_names)
75
- result_str = f"{expression} = {result}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  logger.info(f"[AGENT] 📥 Calculator result: {result_str}")
77
  return result_str
 
78
  except Exception as e:
79
  error_msg = f"Error calculating '{expression}': {str(e)}"
80
  logger.error(f"[AGENT] ❌ Calculator error: {error_msg}")
@@ -98,18 +313,113 @@ if USE_GOOGLE_SEARCH:
98
  else:
99
  logger.debug("[AGENT] ℹ️ Google Custom Search API not configured - using DuckDuckGo (free)")
100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  # Define Google Search function first (doesn't require DDGS imports)
102
- def web_search_google(query: str, max_results: int = 5) -> str:
103
  """
104
  Search using Google Custom Search API (requires API key - free tier: 100 queries/day)
105
  Returns better, more relevant results than DuckDuckGo
 
 
 
 
 
106
  """
107
  if not USE_GOOGLE_SEARCH:
108
  return None
109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  try:
111
  import requests
112
 
 
 
 
 
 
 
 
 
 
113
  # Google Custom Search API endpoint
114
  url = "https://www.googleapis.com/customsearch/v1"
115
  params = {
@@ -119,34 +429,136 @@ def web_search_google(query: str, max_results: int = 5) -> str:
119
  "num": min(max_results, 10) # Google allows max 10 per request
120
  }
121
 
 
 
 
 
 
 
122
  logger.info(f"[AGENT] 🔍 Google Custom Search API call: query='{query}', max_results={max_results}")
123
  response = requests.get(url, params=params, timeout=10)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  response.raise_for_status()
125
 
 
126
  data = response.json()
127
  items = data.get("items", [])
128
 
129
  logger.info(f"[AGENT] 📥 Google Search API returned {len(items)} results")
130
 
131
  if not items:
 
132
  return None
133
 
134
- # Format results for model
135
- formatted = f"Web search results for '{query}':\n\n"
 
 
 
 
 
 
 
136
  for i, item in enumerate(items[:max_results], 1):
137
  title = item.get("title", "No title")
138
  snippet = item.get("snippet", "No description")
139
  link = item.get("link", "")
140
- formatted += f"{i}. {title}\n {snippet[:200]}...\n Source: {link}\n\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
  formatted_response = formatted.strip()
143
  logger.info(f"[AGENT] ✅ Google search formatted response: {len(formatted_response)} chars")
 
 
144
  return formatted_response
145
 
 
 
 
 
 
 
 
146
  except Exception as e:
147
- logger.error(f"[AGENT] Google Search API error: {e}")
 
 
 
148
  import traceback
149
- logger.error(f"[AGENT] Google search traceback: {traceback.format_exc()}")
150
  return None
151
 
152
  # Initialize DDGS availability flag
@@ -168,7 +580,7 @@ try:
168
  DDGS_AVAILABLE = False
169
  logger.warning("[AGENT] ⚠️ Neither ddgs nor duckduckgo_search available")
170
 
171
- def web_search(query: str, max_results: int = 5) -> str:
172
  """
173
  Search the web for current information.
174
  Uses Google Custom Search API if available (better results), otherwise DuckDuckGo (free).
@@ -177,17 +589,54 @@ try:
177
  Args:
178
  query: Search query string
179
  max_results: Maximum number of results to return (default: 5)
 
180
 
181
  Returns:
182
  Formatted search results as a string (or None if no results)
183
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  # Try Google Custom Search first (if API key configured - better results)
185
  if USE_GOOGLE_SEARCH:
186
  logger.info("[AGENT] 🔍 Using Google Custom Search API (AI-enhanced results)")
187
- google_results = web_search_google(query, max_results)
188
- if google_results:
 
 
 
 
 
 
 
189
  return google_results
190
- logger.warning("[AGENT] ⚠️ Google Search returned no results, falling back to DuckDuckGo")
 
 
191
 
192
  # Fallback to DuckDuckGo (free, no API key needed)
193
  if not DDGS_AVAILABLE:
@@ -236,6 +685,13 @@ try:
236
  if ' and ' in query.lower():
237
  logger.info(f"[AGENT] 🔄 Trying simpler query (splitting on 'and')...")
238
  simpler_query = query.split(' and ', 1)[0].strip()
 
 
 
 
 
 
 
239
  logger.info(f"[AGENT] 🔄 Retry with query: '{simpler_query}'")
240
  time.sleep(0.5) # Add delay for retry
241
  results = list(ddgs.text(simpler_query, max_results=max_results))
@@ -245,19 +701,67 @@ try:
245
  logger.warning(f"[AGENT] ⚠️ No search results found after all attempts for: {query}")
246
  return f"No results found for: {query}"
247
 
248
- # Format results for model
249
- formatted = f"Web search results for '{query}':\n\n"
 
 
 
 
 
 
 
250
  for i, result in enumerate(results, 1):
251
  title = result.get('title', 'No title')
252
  snippet = result.get('body', 'No description')
253
  url = result.get('href', result.get('url', ''))
254
- if url:
255
- formatted += f"{i}. {title}\n {snippet[:200]}...\n Source: {url}\n\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  else:
257
- formatted += f"{i}. {title}\n {snippet[:200]}...\n\n"
 
 
 
 
258
 
259
  formatted_response = formatted.strip()
260
  logger.info(f"[AGENT] ✅ Web search formatted response: {len(formatted_response)} chars")
 
 
261
  logger.debug(f"[AGENT] 📝 Formatted response preview (first 500 chars):\n{formatted_response[:500]}...")
262
 
263
  return formatted_response
@@ -406,9 +910,15 @@ class MeeTARAAgent:
406
  needs = {"calculator": False, "web_search": False, "calc_expression": None, "search_query": None}
407
 
408
  # Check for math/calculation needs
409
- # Load patterns from config or use defaults
410
  query_lower = query.lower()
411
  calculator_config = self.agent_config.get("calculator", {})
 
 
 
 
 
 
412
  math_patterns_config = calculator_config.get("math_patterns", [])
413
 
414
  # Extract patterns from config (list of dicts with "pattern" key) or use defaults
@@ -422,6 +932,7 @@ class MeeTARAAgent:
422
  r'\b(sqrt|square root|power|exponent)\s*\(?\s*([0-9]+)\s*\)?',
423
  ]
424
 
 
425
  for pattern in math_patterns:
426
  match = re.search(pattern, query, re.IGNORECASE)
427
  if match:
@@ -429,7 +940,7 @@ class MeeTARAAgent:
429
  # Extract the expression - try all groups
430
  expr = None
431
  for i in range(1, len(match.groups()) + 1):
432
- if match.group(i) and re.search(r'[0-9+\-*/().×÷]', match.group(i)):
433
  expr = match.group(i).strip()
434
  break
435
 
@@ -438,12 +949,42 @@ class MeeTARAAgent:
438
  expr = expr.replace('×', '*').replace('÷', '/')
439
  needs["calc_expression"] = expr
440
  else:
441
- # Fallback: extract numbers with operators from the match
442
- numbers_ops = re.search(r'([0-9+\-*/().\s]+)', match.group(0))
443
- if numbers_ops:
444
- needs["calc_expression"] = numbers_ops.group(1).strip().replace('×', '*').replace('÷', '/')
 
 
 
 
 
 
 
 
 
445
  break
446
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447
  # Check for web search needs
448
  # Load keywords and patterns from config
449
  web_search_config = self.agent_config.get("web_search", {})
@@ -609,6 +1150,36 @@ class MeeTARAAgent:
609
  search_q = tool_needs["search_query"] or query
610
  logger.info(f"[AGENT] 🔍 Extracted search query: '{search_q}' (original: '{query[:100]}')")
611
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
612
  # Validate search query before calling API
613
  search_q_lower = search_q.lower().strip()
614
  if (search_q_lower.startswith('and ') or
@@ -623,7 +1194,8 @@ class MeeTARAAgent:
623
  if search_q and 'custom_web_search' in globals() and custom_web_search is not None:
624
  max_results = self.agent_config.get("web_search", {}).get("max_results", 5)
625
  logger.info(f"[AGENT] 🌐 Web search API call: query='{search_q}', max_results={max_results}")
626
- search_result = custom_web_search(search_q, max_results=max_results)
 
627
 
628
  # Check if search actually returned valid results (not "No results found" or error)
629
  has_results = (search_result and
@@ -656,13 +1228,34 @@ class MeeTARAAgent:
656
  # The model already has structured format (🎯, 📊, ⚡, 💡) built-in via meetara_lab_core.py
657
  # Just feed tool results - model will automatically structure the response
658
  if tool_results:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
659
  enhanced_prompt = (
660
  f"{query}\n\n"
661
  f"=== Tool Results ===\n"
662
  + "\n\n".join(tool_results) +
663
- f"\n\nUse the tool results above to provide your response."
664
  )
665
  logger.info(f"[AGENT] 📝 Feeding {len(tool_results)} tool results to model (model will auto-format)")
 
 
666
  logger.debug(f"[AGENT] 📤 Enhanced prompt to model (first 1000 chars):\n{enhanced_prompt[:1000]}...")
667
 
668
  # Log full tool results being fed to model
 
54
  Evaluates a mathematical expression and returns the result.
55
  Use this for any calculations to ensure accuracy.
56
 
57
+ Supports:
58
+ - Basic math: "25 * 48", "1000 - 456 + 234 * 3"
59
+ - Percentage: "15% of 340" (converts to "0.15 * 340")
60
+ - Division text: "999 divided by 3" (converts to "999 / 3")
61
+ - Scientific functions:
62
+ * Powers/roots: "sqrt(144)", "pow(2, 8)", "cbrt(27)"
63
+ * Trigonometry: "sin(pi/2)", "cos(0)", "tan(pi/4)", "asin(1)", "acos(0)", "atan(1)"
64
+ * Hyperbolic: "sinh(2)", "cosh(2)", "tanh(1)"
65
+ * Logarithms: "log(10)", "log10(100)", "log2(8)", "ln(e)", "exp(2)"
66
+ * Advanced: "factorial(5)", "gcd(48, 18)", "lcm(12, 18)", "degrees(pi)", "radians(180)"
67
+ - Natural language: "sine of pi/2", "natural log of e", "square root of 144"
68
+ - Algorithms & Data Structures:
69
+ * Fibonacci: "fibonacci(10)", "fibonacci of 10", "10th fibonacci"
70
+ * Permutations: "npr(5, 3)", "permutations of 5 taken 3", "5P3"
71
+ * Combinations: "ncr(5, 3)", "combinations of 5 choose 3", "5C3"
72
+ * Base conversion: "bin(255)", "hex(255)", "oct(255)", "convert 255 to binary"
73
+
74
  Args:
75
+ expression: A mathematical expression or natural language math query
76
 
77
  Returns:
78
  The calculated result as a string
79
  """
80
  import math
81
+ import re
82
+
83
  try:
84
+ expr_lower = expression.lower()
85
+ original_expr = expression
86
+
87
+ # Handle percentage: "15% of 340" or "15% times 340" or "what's 15% of 340"
88
+ percentage_match = re.search(r'([0-9]+(?:\.[0-9]+)?)\s*%\s*(?:of|times|×)\s*([0-9]+)', expr_lower)
89
+ if percentage_match:
90
+ percent = float(percentage_match.group(1)) / 100
91
+ value = float(percentage_match.group(2))
92
+ expression = f"{percent} * {value}"
93
+ logger.info(f"[AGENT] 🧮 Converted percentage: '{original_expr}' → '{expression}'")
94
+
95
+ # Handle division text: "999 divided by 3" or "999 ÷ 3"
96
+ if not percentage_match:
97
+ division_match = re.search(r'([0-9]+(?:\.[0-9]+)?)\s+(?:divided by|divided|÷)\s+([0-9]+(?:\.[0-9]+)?)', expr_lower)
98
+ if division_match:
99
+ dividend = division_match.group(1)
100
+ divisor = division_match.group(2)
101
+ expression = f"{dividend} / {divisor}"
102
+ logger.info(f"[AGENT] 🧮 Converted division text: '{original_expr}' → '{expression}'")
103
+
104
+ # Handle geometry queries (model will calculate, calculator just provides context)
105
+ geometry_keywords = ["surface area", "area of", "volume of", "perimeter", "circumference", "radius", "diameter"]
106
+ if any(geo in expr_lower for geo in geometry_keywords):
107
+ # Extract numbers for context (model will do the actual geometry calculation)
108
+ numbers = re.findall(r'[0-9]+(?:\.[0-9]+)?', expression)
109
+ if len(numbers) >= 2:
110
+ # Try to extract a simple expression if possible
111
+ logger.info(f"[AGENT] 🧮 Geometry query detected: '{expression}' - model will calculate")
112
+ # Return context - model will handle geometry formulas
113
+ return f"Geometry calculation requested: {expression}. Numbers found: {', '.join(numbers)}. Please calculate using appropriate formulas."
114
+
115
+ # Clean expression: remove extra spaces, normalize operators
116
+ expression = re.sub(r'\s+', ' ', expression.strip())
117
+ expression = expression.replace('×', '*').replace('÷', '/')
118
+
119
+ # Safe evaluation with comprehensive scientific math functions
120
  allowed_names = {
121
+ # Basic functions
122
+ "abs": abs, "round": round, "min": min, "max": max, "sum": sum,
123
+
124
+ # Powers and roots
125
+ "pow": pow, "sqrt": math.sqrt, "cbrt": lambda x: x ** (1/3), # Cube root
126
+
127
+ # Trigonometric functions (radians)
128
  "sin": math.sin, "cos": math.cos, "tan": math.tan,
129
+ "asin": math.asin, "acos": math.acos, "atan": math.atan, "atan2": math.atan2,
130
+
131
+ # Hyperbolic functions
132
+ "sinh": math.sinh, "cosh": math.cosh, "tanh": math.tanh,
133
+ "asinh": math.asinh, "acosh": math.acosh, "atanh": math.atanh,
134
+
135
+ # Logarithmic functions
136
+ "log": math.log, "log10": math.log10, "log2": math.log2, "exp": math.exp,
137
+ "ln": math.log, # Natural logarithm (alias)
138
+
139
+ # Additional functions
140
+ "factorial": math.factorial, "gcd": math.gcd, "lcm": lambda a, b: abs(a * b) // math.gcd(a, b) if a and b else 0,
141
+ "degrees": math.degrees, "radians": math.radians,
142
+ "floor": math.floor, "ceil": math.ceil, "trunc": math.trunc,
143
+
144
+ # Algorithm & Data Structure functions
145
+ "perm": lambda n, r: math.factorial(n) // math.factorial(n - r) if n >= r >= 0 else 0, # Permutations nPr
146
+ "permutations": lambda n, r: math.factorial(n) // math.factorial(n - r) if n >= r >= 0 else 0,
147
+ "npr": lambda n, r: math.factorial(n) // math.factorial(n - r) if n >= r >= 0 else 0,
148
+ "comb": lambda n, r: math.factorial(n) // (math.factorial(r) * math.factorial(n - r)) if n >= r >= 0 else 0, # Combinations nCr
149
+ "combinations": lambda n, r: math.factorial(n) // (math.factorial(r) * math.factorial(n - r)) if n >= r >= 0 else 0,
150
+ "ncr": lambda n, r: math.factorial(n) // (math.factorial(r) * math.factorial(n - r)) if n >= r >= 0 else 0,
151
+ "choose": lambda n, r: math.factorial(n) // (math.factorial(r) * math.factorial(n - r)) if n >= r >= 0 else 0,
152
+
153
+ # Fibonacci function (iterative for efficiency)
154
+ "fibonacci": lambda n: int((((1 + math.sqrt(5)) / 2) ** n - ((1 - math.sqrt(5)) / 2) ** n) / math.sqrt(5)) if n >= 0 else 0,
155
+ "fib": lambda n: int((((1 + math.sqrt(5)) / 2) ** n - ((1 - math.sqrt(5)) / 2) ** n) / math.sqrt(5)) if n >= 0 else 0,
156
+
157
+ # Base conversion helpers (to be used in expressions like "int('1010', 2)")
158
+ "bin_to_dec": lambda b: int(str(b), 2),
159
+ "hex_to_dec": lambda h: int(str(h), 16),
160
+ "oct_to_dec": lambda o: int(str(o), 8),
161
+ "dec_to_bin": lambda n: bin(int(n)),
162
+ "dec_to_hex": lambda n: hex(int(n)),
163
+ "dec_to_oct": lambda n: oct(int(n)),
164
+
165
+ # Constants
166
+ "pi": math.pi, "e": math.e, "tau": math.tau,
167
+ "inf": float('inf'), "nan": float('nan')
168
  }
169
+
170
+ # Handle base conversion expressions separately (they return strings)
171
+ # Check for direct function calls like bin(255), hex(255), oct(255)
172
+ base_conv_pattern = re.search(r'\b(bin|hex|oct|dec_to_bin|dec_to_hex|dec_to_oct)\s*\(\s*([0-9]+)\s*\)', expression, re.IGNORECASE)
173
+ base_conv_result = None
174
+ if base_conv_pattern:
175
+ func_name = base_conv_pattern.group(1).lower()
176
+ num = int(base_conv_pattern.group(2))
177
+ if 'bin' in func_name:
178
+ base_conv_result = bin(num)
179
+ elif 'hex' in func_name:
180
+ base_conv_result = hex(num)
181
+ elif 'oct' in func_name:
182
+ base_conv_result = oct(num)
183
+
184
+ if base_conv_result:
185
+ result_str = f"{original_expr} = {base_conv_result}"
186
+ logger.info(f"[AGENT] 📥 Calculator result (base conversion): {result_str}")
187
+ return result_str
188
+
189
+ # Handle natural language scientific function names (before cleaning)
190
+ scientific_aliases = {
191
+ "sine of": "sin", "cosine of": "cos", "tangent of": "tan",
192
+ "sine": "sin", "cosine": "cos", "tangent": "tan",
193
+ "arcsin of": "asin", "arccos of": "acos", "arctan of": "atan",
194
+ "square root of": "sqrt", "cube root of": "cbrt",
195
+ "natural log of": "ln", "log base 10 of": "log10", "log base 2 of": "log2"
196
+ }
197
+
198
+ # Replace natural language patterns like "sine of 90" -> "sin(90)" or "sine(90)"
199
+ for alias, func_name in scientific_aliases.items():
200
+ if alias in expr_lower:
201
+ # Pattern: "sine of 90" -> "sin(90)"
202
+ pattern1 = re.compile(r'\b' + re.escape(alias) + r'\s+([0-9]+(?:\.[0-9]+)?|pi|e)' , re.IGNORECASE)
203
+ expression = pattern1.sub(func_name + r'(\1)', expression)
204
+ # Pattern: "sine(90)" -> "sin(90)" (already in correct format)
205
+ pattern2 = re.compile(r'\b' + re.escape(alias.split()[0]) + r'\s*\(' , re.IGNORECASE)
206
+ expression = pattern2.sub(func_name + '(', expression)
207
+
208
+ # Handle algorithm & data structure natural language queries
209
+ # Fibonacci: "fibonacci of 10" or "fibonacci(10)" or "10th fibonacci"
210
+ fib_match = re.search(r'(?:fibonacci|fib)\s+(?:of|at|number)?\s*([0-9]+)|([0-9]+)(?:th|st|nd|rd)?\s+fibonacci', expr_lower)
211
+ if fib_match:
212
+ n = int(fib_match.group(1) or fib_match.group(2))
213
+ expression = f"fibonacci({n})"
214
+ logger.info(f"[AGENT] 🧮 Converted fibonacci query: '{original_expr}' → '{expression}'")
215
+
216
+ # Permutations: "permutations of 5 taken 3" or "5P3" or "npr(5, 3)"
217
+ perm_match = re.search(r'(?:permutations?|perm|npr)\s+(?:of|for)?\s*([0-9]+)\s+(?:taken|choose|selected|at a time)?\s*([0-9]+)|([0-9]+)p([0-9]+)', expr_lower)
218
+ if perm_match:
219
+ n = int(perm_match.group(1) or perm_match.group(3))
220
+ r = int(perm_match.group(2) or perm_match.group(4))
221
+ expression = f"npr({n}, {r})"
222
+ logger.info(f"[AGENT] 🧮 Converted permutations query: '{original_expr}' → '{expression}'")
223
+
224
+ # Combinations: "combinations of 5 choose 3" or "5C3" or "ncr(5, 3)"
225
+ comb_match = re.search(r'(?:combinations?|comb|ncr|choose)\s+(?:of|for)?\s*([0-9]+)\s+(?:choose|taken|selected|at a time)?\s*([0-9]+)|([0-9]+)c([0-9]+)', expr_lower)
226
+ if comb_match:
227
+ n = int(comb_match.group(1) or comb_match.group(3))
228
+ r = int(comb_match.group(2) or comb_match.group(4))
229
+ expression = f"ncr({n}, {r})"
230
+ logger.info(f"[AGENT] 🧮 Converted combinations query: '{original_expr}' → '{expression}'")
231
+
232
+ # Base conversion: "convert 255 to binary" or "binary of 255"
233
+ base_conv_match = re.search(r'(?:convert|what is|calculate|compute)\s+([0-9]+)\s+(?:to|in|as)\s+(binary|hex|hexadecimal|decimal|octal|base\s+([0-9]+))|(?:binary|hex|hexadecimal|decimal|octal)\s+(?:of|for)?\s*([0-9]+)', expr_lower)
234
+ if base_conv_match and not percentage_match and not division_match:
235
+ if base_conv_match.group(1): # "convert 255 to binary" pattern
236
+ num = int(base_conv_match.group(1))
237
+ target_base = base_conv_match.group(2).lower() if base_conv_match.group(2) else ""
238
+ if 'binary' in target_base or 'base 2' in target_base or (not target_base and 'binary' in expr_lower):
239
+ expression = f"bin({num})"
240
+ elif 'hex' in target_base or 'base 16' in target_base or (not target_base and ('hex' in expr_lower or 'hexadecimal' in expr_lower)):
241
+ expression = f"hex({num})"
242
+ elif 'octal' in target_base or 'base 8' in target_base or (not target_base and 'octal' in expr_lower):
243
+ expression = f"oct({num})"
244
+ logger.info(f"[AGENT] 🧮 Converted base conversion query: '{original_expr}' → '{expression}'")
245
+ elif base_conv_match.group(4): # "binary of 255" pattern
246
+ num = int(base_conv_match.group(4))
247
+ if 'binary' in expr_lower:
248
+ expression = f"bin({num})"
249
+ elif 'hex' in expr_lower or 'hexadecimal' in expr_lower:
250
+ expression = f"hex({num})"
251
+ elif 'octal' in expr_lower:
252
+ expression = f"oct({num})"
253
+ logger.info(f"[AGENT] 🧮 Converted base conversion query: '{original_expr}' → '{expression}'")
254
+
255
  logger.info(f"[AGENT] 🧮 Calculator API call: expression='{expression}'")
256
+
257
+ # Check again for base conversion after natural language processing
258
+ base_conv_after = re.search(r'\b(bin|hex|oct)\s*\(\s*([0-9]+)\s*\)', expression, re.IGNORECASE)
259
+ if base_conv_after and not base_conv_result:
260
+ func_name = base_conv_after.group(1).lower()
261
+ num = int(base_conv_after.group(2))
262
+ if func_name == 'bin':
263
+ base_conv_result = bin(num)
264
+ elif func_name == 'hex':
265
+ base_conv_result = hex(num)
266
+ elif func_name == 'oct':
267
+ base_conv_result = oct(num)
268
+
269
+ # Evaluate expression
270
+ if base_conv_result:
271
+ result = base_conv_result
272
+ else:
273
+ # Add bin, hex, oct to allowed built-ins for eval
274
+ safe_builtins = {"__builtins__": {}, "bin": bin, "hex": hex, "oct": oct}
275
+ result = eval(expression, safe_builtins, allowed_names)
276
+
277
+ # Format result nicely
278
+ if isinstance(result, str) and (result.startswith('0b') or result.startswith('0x') or result.startswith('0o')):
279
+ # Base conversion result (already formatted)
280
+ result_str = f"{original_expr if original_expr != expression else expression} = {result}"
281
+ elif isinstance(result, float):
282
+ if result.is_integer():
283
+ result = int(result)
284
+ else:
285
+ result = round(result, 10) # Round to 10 decimal places
286
+ result_str = f"{original_expr if original_expr != expression else expression} = {result}"
287
+ else:
288
+ result_str = f"{original_expr if original_expr != expression else expression} = {result}"
289
+
290
  logger.info(f"[AGENT] 📥 Calculator result: {result_str}")
291
  return result_str
292
+
293
  except Exception as e:
294
  error_msg = f"Error calculating '{expression}': {str(e)}"
295
  logger.error(f"[AGENT] ❌ Calculator error: {error_msg}")
 
313
  else:
314
  logger.debug("[AGENT] ℹ️ Google Custom Search API not configured - using DuckDuckGo (free)")
315
 
316
+ def is_safe_search_query(query: str, agent_config: dict = None) -> tuple[bool, str]:
317
+ """
318
+ Check if a search query is safe to execute.
319
+
320
+ Args:
321
+ query: Search query string to validate
322
+ agent_config: Optional agent configuration dict (if None, uses defaults)
323
+
324
+ Returns:
325
+ Tuple of (is_safe: bool, reason: str)
326
+ """
327
+ # Load safety filter config
328
+ if agent_config is None:
329
+ try:
330
+ if CONFIG_AVAILABLE:
331
+ agent_config = get_agent_config()
332
+ else:
333
+ agent_config = {}
334
+ except:
335
+ agent_config = {}
336
+
337
+ web_search_config = agent_config.get("web_search", {})
338
+ safety_config = web_search_config.get("safety_filter", {})
339
+
340
+ # Check if safety filter is enabled (default: True)
341
+ if not safety_config.get("enabled", True):
342
+ return True, "Safety filter disabled"
343
+
344
+ # Load blocked keywords and patterns
345
+ blocked_keywords = safety_config.get("blocked_keywords", [])
346
+ blocked_patterns = safety_config.get("blocked_patterns", [])
347
+
348
+ # Normalize query for checking
349
+ query_lower = query.lower().strip()
350
+
351
+ # Check blocked keywords
352
+ for keyword in blocked_keywords:
353
+ if keyword.lower() in query_lower:
354
+ logger.warning(f"[AGENT] 🚫 Safety filter blocked query: contains blocked keyword '{keyword}'")
355
+ return False, f"Query contains blocked keyword: {keyword}"
356
+
357
+ # Check blocked patterns (regex)
358
+ import re
359
+ for pattern_obj in blocked_patterns:
360
+ pattern = pattern_obj.get("pattern", "") if isinstance(pattern_obj, dict) else pattern_obj
361
+ if not pattern:
362
+ continue
363
+
364
+ try:
365
+ if re.search(pattern, query_lower, re.IGNORECASE):
366
+ desc = pattern_obj.get("description", "harmful pattern") if isinstance(pattern_obj, dict) else "harmful pattern"
367
+ logger.warning(f"[AGENT] 🚫 Safety filter blocked query: matches blocked pattern '{desc}'")
368
+ return False, f"Query matches blocked pattern: {desc}"
369
+ except re.error as e:
370
+ logger.warning(f"[AGENT] ⚠️ Invalid regex pattern in safety config: {pattern}, error: {e}")
371
+ continue
372
+
373
+ # Query passed all safety checks
374
+ return True, "Query is safe"
375
+
376
+
377
  # Define Google Search function first (doesn't require DDGS imports)
378
+ def web_search_google(query: str, max_results: int = 5, agent_config: dict = None) -> str:
379
  """
380
  Search using Google Custom Search API (requires API key - free tier: 100 queries/day)
381
  Returns better, more relevant results than DuckDuckGo
382
+
383
+ Args:
384
+ query: Search query string
385
+ max_results: Maximum number of results (default: 5)
386
+ agent_config: Optional agent configuration dict (if None, uses defaults)
387
  """
388
  if not USE_GOOGLE_SEARCH:
389
  return None
390
 
391
+ # Load config for safety check and news detection
392
+ if agent_config is None:
393
+ try:
394
+ if CONFIG_AVAILABLE:
395
+ agent_config = get_agent_config()
396
+ else:
397
+ agent_config = {}
398
+ except:
399
+ agent_config = {}
400
+
401
+ # Safety check - block harmful queries before searching
402
+ is_safe, safety_reason = is_safe_search_query(query, agent_config)
403
+ if not is_safe:
404
+ web_search_config = agent_config.get("web_search", {})
405
+ safety_config = web_search_config.get("safety_filter", {})
406
+ blocked_message = safety_config.get("blocked_response_message",
407
+ "I cannot search for content that may involve harmful, illegal, or inappropriate material. Please try a different query.")
408
+ logger.warning(f"[AGENT] 🚫 Safety filter blocked Google search: {safety_reason}")
409
+ return f"⚠️ {blocked_message}"
410
+
411
  try:
412
  import requests
413
 
414
+ web_search_config = agent_config.get("web_search", {})
415
+ news_keywords = web_search_config.get("news_query_keywords", ["news", "headlines", "headline", "breaking"])
416
+ meta_filter_phrases = web_search_config.get("meta_page_filter_phrases", [
417
+ "provides", "offers", "visit", "check", "read", "explore", "go to",
418
+ "subscribe", "newsletter", "website", "at cnn", "at fox", "at bbc"
419
+ ])
420
+ safety_config = web_search_config.get("safety_filter", {})
421
+ safe_search_enabled = safety_config.get("safe_search_enabled", True)
422
+
423
  # Google Custom Search API endpoint
424
  url = "https://www.googleapis.com/customsearch/v1"
425
  params = {
 
429
  "num": min(max_results, 10) # Google allows max 10 per request
430
  }
431
 
432
+ # Enable safe search (filters explicit content)
433
+ if safe_search_enabled:
434
+ safe_search_param = safety_config.get("safe_search_param", "safe=active")
435
+ # Google API uses "safe" parameter with values: "active", "off", "high"
436
+ params["safe"] = "active"
437
+
438
  logger.info(f"[AGENT] 🔍 Google Custom Search API call: query='{query}', max_results={max_results}")
439
  response = requests.get(url, params=params, timeout=10)
440
+
441
+ # Handle HTTP errors with detailed diagnostics
442
+ if response.status_code == 429:
443
+ # Quota exceeded (100 queries/day free tier limit reached)
444
+ error_data = response.json() if response.content else {}
445
+ error_message = error_data.get("error", {}).get("message", "Quota exceeded")
446
+ logger.warning(f"[AGENT] ⚠️ Google Search API 429 - Daily quota exceeded (free tier: 100 queries/day)")
447
+ logger.warning(f"[AGENT] Error: {error_message}")
448
+ logger.info(f"[AGENT] → Auto-fallback to DuckDuckGo (free, unlimited)")
449
+ # Return special marker to trigger fallback
450
+ return "QUOTA_EXCEEDED"
451
+
452
+ elif response.status_code == 403:
453
+ # Forbidden - could be quota, API not enabled, or billing issue
454
+ error_data = response.json() if response.content else {}
455
+ error_message = error_data.get("error", {}).get("message", "Forbidden")
456
+ error_code = error_data.get("error", {}).get("code", 0)
457
+
458
+ # Check if it's a quota issue (sometimes 403 is used for quota)
459
+ if "quota" in error_message.lower() or "quotaExceeded" in str(error_data):
460
+ logger.warning(f"[AGENT] ⚠️ Google Search API 403 - Quota exceeded (free tier: 100 queries/day)")
461
+ logger.info(f"[AGENT] → Auto-fallback to DuckDuckGo (free, unlimited)")
462
+ return "QUOTA_EXCEEDED"
463
+ else:
464
+ logger.error(f"[AGENT] ❌ Google Search API 403 Forbidden - Possible causes:")
465
+ logger.error(f"[AGENT] 1. API not enabled in Google Cloud Console")
466
+ logger.error(f"[AGENT] 2. Billing account required (even for free tier)")
467
+ logger.error(f"[AGENT] 3. API key restricted to wrong HTTP referrers (should be unrestricted for server-side)")
468
+ logger.error(f"[AGENT] 4. Daily quota exceeded (free tier: 100 queries/day)")
469
+ logger.error(f"[AGENT] Error: {error_message}")
470
+ logger.info(f"[AGENT] → Falling back to DuckDuckGo (free, unlimited)")
471
+ # Fallback to DuckDuckGo even for other 403 errors
472
+ return None
473
+
474
+ elif response.status_code == 401:
475
+ # Unauthorized - invalid API key
476
+ error_data = response.json() if response.content else {}
477
+ error_message = error_data.get("error", {}).get("message", "Unauthorized")
478
+ logger.error(f"[AGENT] ❌ Google Search API 401 Unauthorized - Invalid API key")
479
+ logger.error(f"[AGENT] Error: {error_message}")
480
+ logger.error(f"[AGENT] → Check GOOGLE_CUSTOM_SEARCH_API_KEY in environment variables")
481
+ logger.info(f"[AGENT] → Falling back to DuckDuckGo")
482
+ return None
483
+
484
+ elif response.status_code == 400:
485
+ # Bad request - invalid parameters
486
+ error_data = response.json() if response.content else {}
487
+ error_message = error_data.get("error", {}).get("message", "Bad request")
488
+ logger.error(f"[AGENT] ❌ Google Search API 400 Bad Request")
489
+ logger.error(f"[AGENT] Error: {error_message}")
490
+ logger.error(f"[AGENT] → Check query parameters and Search Engine ID")
491
+ logger.info(f"[AGENT] → Falling back to DuckDuckGo")
492
+ return None
493
+
494
+ # Raise for other HTTP errors
495
  response.raise_for_status()
496
 
497
+ # Parse successful response
498
  data = response.json()
499
  items = data.get("items", [])
500
 
501
  logger.info(f"[AGENT] 📥 Google Search API returned {len(items)} results")
502
 
503
  if not items:
504
+ logger.info(f"[AGENT] ℹ️ Google Search returned no results - falling back to DuckDuckGo")
505
  return None
506
 
507
+ # Format results for model - prioritize headlines for news queries
508
+ query_lower = query.lower()
509
+ is_news_query = any(kw in query_lower for kw in news_keywords)
510
+
511
+ if is_news_query:
512
+ formatted = f"Today's News Headlines:\n\n"
513
+ else:
514
+ formatted = f"Web search results for '{query}':\n\n"
515
+
516
  for i, item in enumerate(items[:max_results], 1):
517
  title = item.get("title", "No title")
518
  snippet = item.get("snippet", "No description")
519
  link = item.get("link", "")
520
+
521
+ # For news queries, prioritize title (often contains the headline) and filter out meta descriptions
522
+ if is_news_query:
523
+ # Filter out results that are just about news sources (meta pages)
524
+ title_lower = title.lower()
525
+ snippet_lower = snippet.lower()
526
+ is_meta_page = any(phrase in title_lower or phrase in snippet_lower for phrase in meta_filter_phrases)
527
+
528
+ # If it's not a meta page, include it as a headline
529
+ if not is_meta_page and title and title != "No title":
530
+ # Use title as headline, snippet as content if it contains actual news
531
+ if snippet and len(snippet) > 50 and not snippet_lower.startswith("view") and not snippet_lower.startswith("visit"):
532
+ formatted += f"{i}. {title}\n {snippet[:300]}\n Source: {link}\n\n"
533
+ else:
534
+ formatted += f"{i}. {title}\n Source: {link}\n\n"
535
+ elif snippet and len(snippet) > 100 and any(word in snippet_lower for word in ["announced", "reported", "said", "according", "breaking", "developing"]):
536
+ # Snippet contains actual news content - use it
537
+ formatted += f"{i}. {snippet[:300]}\n Source: {link}\n\n"
538
+ else:
539
+ # Regular search results formatting
540
+ formatted += f"{i}. {title}\n {snippet[:200]}...\n Source: {link}\n\n"
541
 
542
  formatted_response = formatted.strip()
543
  logger.info(f"[AGENT] ✅ Google search formatted response: {len(formatted_response)} chars")
544
+ if is_news_query:
545
+ logger.debug(f"[AGENT] 📰 News query - formatted for headline extraction")
546
  return formatted_response
547
 
548
+ except requests.exceptions.RequestException as e:
549
+ # Network/connection errors
550
+ logger.error(f"[AGENT] ❌ Google Search API network error: {e}")
551
+ logger.info(f"[AGENT] → Falling back to DuckDuckGo")
552
+ import traceback
553
+ logger.debug(f"[AGENT] Google search traceback: {traceback.format_exc()}")
554
+ return None
555
  except Exception as e:
556
+ # Other unexpected errors
557
+ error_msg = str(e)
558
+ logger.error(f"[AGENT] ❌ Google Search API unexpected error: {e}")
559
+ logger.info(f"[AGENT] → Falling back to DuckDuckGo")
560
  import traceback
561
+ logger.debug(f"[AGENT] Google search traceback: {traceback.format_exc()}")
562
  return None
563
 
564
  # Initialize DDGS availability flag
 
580
  DDGS_AVAILABLE = False
581
  logger.warning("[AGENT] ⚠️ Neither ddgs nor duckduckgo_search available")
582
 
583
+ def web_search(query: str, max_results: int = 5, agent_config: dict = None) -> str:
584
  """
585
  Search the web for current information.
586
  Uses Google Custom Search API if available (better results), otherwise DuckDuckGo (free).
 
589
  Args:
590
  query: Search query string
591
  max_results: Maximum number of results to return (default: 5)
592
+ agent_config: Optional agent configuration dict (for news detection and filtering)
593
 
594
  Returns:
595
  Formatted search results as a string (or None if no results)
596
  """
597
+ # Load config for safety check and news detection
598
+ if agent_config is None:
599
+ try:
600
+ if CONFIG_AVAILABLE:
601
+ agent_config = get_agent_config()
602
+ else:
603
+ agent_config = {}
604
+ except:
605
+ agent_config = {}
606
+
607
+ # Safety check - block harmful queries before searching
608
+ is_safe, safety_reason = is_safe_search_query(query, agent_config)
609
+ if not is_safe:
610
+ web_search_config = agent_config.get("web_search", {})
611
+ safety_config = web_search_config.get("safety_filter", {})
612
+ blocked_message = safety_config.get("blocked_response_message",
613
+ "I cannot search for content that may involve harmful, illegal, or inappropriate material. Please try a different query.")
614
+ logger.warning(f"[AGENT] 🚫 Safety filter blocked web search: {safety_reason}")
615
+ return f"⚠️ {blocked_message}"
616
+
617
+ web_search_config = agent_config.get("web_search", {})
618
+ news_keywords = web_search_config.get("news_query_keywords", ["news", "headlines", "headline", "breaking"])
619
+ meta_filter_phrases = web_search_config.get("meta_page_filter_phrases", [
620
+ "provides", "offers", "visit", "check", "read", "explore", "go to",
621
+ "subscribe", "newsletter", "website", "at cnn", "at fox", "at bbc"
622
+ ])
623
+
624
  # Try Google Custom Search first (if API key configured - better results)
625
  if USE_GOOGLE_SEARCH:
626
  logger.info("[AGENT] 🔍 Using Google Custom Search API (AI-enhanced results)")
627
+ google_results = web_search_google(query, max_results, agent_config)
628
+
629
+ # Check if quota exceeded (special marker)
630
+ if google_results == "QUOTA_EXCEEDED":
631
+ logger.warning("[AGENT] ⚠️ Google Search quota exceeded (100 queries/day free tier limit)")
632
+ logger.info("[AGENT] → Automatically falling back to DuckDuckGo (free, unlimited)")
633
+ # Continue to DuckDuckGo fallback below
634
+ elif google_results:
635
+ # Success - return Google results
636
  return google_results
637
+ else:
638
+ # No results or error - fallback to DuckDuckGo
639
+ logger.info("[AGENT] ℹ️ Google Search returned no results or error, falling back to DuckDuckGo")
640
 
641
  # Fallback to DuckDuckGo (free, no API key needed)
642
  if not DDGS_AVAILABLE:
 
685
  if ' and ' in query.lower():
686
  logger.info(f"[AGENT] 🔄 Trying simpler query (splitting on 'and')...")
687
  simpler_query = query.split(' and ', 1)[0].strip()
688
+
689
+ # Safety check for retry query as well
690
+ is_safe_retry, safety_reason_retry = is_safe_search_query(simpler_query, agent_config)
691
+ if not is_safe_retry:
692
+ logger.warning(f"[AGENT] 🚫 Safety filter blocked retry query: {safety_reason_retry}")
693
+ return f"⚠️ {blocked_message}"
694
+
695
  logger.info(f"[AGENT] 🔄 Retry with query: '{simpler_query}'")
696
  time.sleep(0.5) # Add delay for retry
697
  results = list(ddgs.text(simpler_query, max_results=max_results))
 
701
  logger.warning(f"[AGENT] ⚠️ No search results found after all attempts for: {query}")
702
  return f"No results found for: {query}"
703
 
704
+ # Format results for model - prioritize headlines for news queries (using config values)
705
+ query_lower = query.lower()
706
+ is_news_query = any(kw in query_lower for kw in news_keywords)
707
+
708
+ if is_news_query:
709
+ formatted = f"Today's News Headlines:\n\n"
710
+ else:
711
+ formatted = f"Web search results for '{query}':\n\n"
712
+
713
  for i, result in enumerate(results, 1):
714
  title = result.get('title', 'No title')
715
  snippet = result.get('body', 'No description')
716
  url = result.get('href', result.get('url', ''))
717
+
718
+ # For news queries, prioritize actual headlines over source descriptions (using config filter phrases)
719
+ if is_news_query:
720
+ title_lower = title.lower()
721
+ snippet_lower = snippet.lower() if snippet else ""
722
+
723
+ # Filter out meta pages about news sources (using config values)
724
+ is_meta_page = any(phrase in title_lower or phrase in snippet_lower for phrase in meta_filter_phrases)
725
+
726
+ # Skip meta pages, focus on actual news content
727
+ if is_meta_page:
728
+ continue # Skip this result
729
+
730
+ # Use title as headline if it contains actual news
731
+ if title and title != "No title" and len(title) > 10:
732
+ if snippet and len(snippet) > 50:
733
+ # Check if snippet contains actual news content (not just "visit website")
734
+ if not snippet_lower.startswith(("visit", "check", "read", "go to", "explore", "view", "subscribe")):
735
+ formatted += f"{i}. {title}\n {snippet[:300]}\n"
736
+ if url:
737
+ formatted += f" Source: {url}\n\n"
738
+ else:
739
+ formatted += "\n"
740
+ else:
741
+ # Title is good, snippet is meta - use just title
742
+ formatted += f"{i}. {title}\n"
743
+ if url:
744
+ formatted += f" Source: {url}\n\n"
745
+ else:
746
+ formatted += "\n"
747
+ else:
748
+ # Only title available
749
+ formatted += f"{i}. {title}\n"
750
+ if url:
751
+ formatted += f" Source: {url}\n\n"
752
+ else:
753
+ formatted += "\n"
754
  else:
755
+ # Regular search results formatting
756
+ if url:
757
+ formatted += f"{i}. {title}\n {snippet[:200] if snippet else 'No description'}...\n Source: {url}\n\n"
758
+ else:
759
+ formatted += f"{i}. {title}\n {snippet[:200] if snippet else 'No description'}...\n\n"
760
 
761
  formatted_response = formatted.strip()
762
  logger.info(f"[AGENT] ✅ Web search formatted response: {len(formatted_response)} chars")
763
+ if is_news_query:
764
+ logger.debug(f"[AGENT] 📰 News query - formatted for headline extraction (filtered meta pages)")
765
  logger.debug(f"[AGENT] 📝 Formatted response preview (first 500 chars):\n{formatted_response[:500]}...")
766
 
767
  return formatted_response
 
910
  needs = {"calculator": False, "web_search": False, "calc_expression": None, "search_query": None}
911
 
912
  # Check for math/calculation needs
913
+ # First check keywords (faster), then patterns (more specific)
914
  query_lower = query.lower()
915
  calculator_config = self.agent_config.get("calculator", {})
916
+ calculator_keywords_config = calculator_config.get("keywords", [])
917
+
918
+ # Check for calculator keywords first
919
+ matched_calc_keywords = [kw for kw in calculator_keywords_config if kw in query_lower]
920
+
921
+ # Load patterns from config
922
  math_patterns_config = calculator_config.get("math_patterns", [])
923
 
924
  # Extract patterns from config (list of dicts with "pattern" key) or use defaults
 
932
  r'\b(sqrt|square root|power|exponent)\s*\(?\s*([0-9]+)\s*\)?',
933
  ]
934
 
935
+ # Try pattern matching first (more specific)
936
  for pattern in math_patterns:
937
  match = re.search(pattern, query, re.IGNORECASE)
938
  if match:
 
940
  # Extract the expression - try all groups
941
  expr = None
942
  for i in range(1, len(match.groups()) + 1):
943
+ if match.group(i) and re.search(r'[0-9+\-*/().×÷%]', match.group(i)):
944
  expr = match.group(i).strip()
945
  break
946
 
 
949
  expr = expr.replace('×', '*').replace('÷', '/')
950
  needs["calc_expression"] = expr
951
  else:
952
+ # For geometry/percentage queries without explicit expression, extract full query as context
953
+ if any(geo in query_lower for geo in ["surface area", "area of", "volume of", "perimeter", "circumference", "% of"]):
954
+ # Geometry or percentage query - extract numbers from query for context
955
+ numbers = re.findall(r'[0-9]+(?:\.[0-9]+)?', query)
956
+ if numbers:
957
+ needs["calc_expression"] = " ".join(numbers) # Pass numbers as context
958
+ else:
959
+ needs["calc_expression"] = query # Pass full query as context
960
+ else:
961
+ # Fallback: extract numbers with operators from the match
962
+ numbers_ops = re.search(r'([0-9+\-*/().\s]+)', match.group(0))
963
+ if numbers_ops:
964
+ needs["calc_expression"] = numbers_ops.group(1).strip().replace('×', '*').replace('÷', '/')
965
  break
966
 
967
+ # If no pattern match but keywords found and numbers present, still flag as calculator
968
+ if not needs["calculator"] and matched_calc_keywords:
969
+ # Check if query contains numbers (likely a math query)
970
+ has_numbers = bool(re.search(r'[0-9]+', query))
971
+ # Check for geometry keywords
972
+ has_geometry = any(geo in query_lower for geo in ["surface area", "area of", "volume of", "perimeter", "circumference"])
973
+ # Check for percentage keywords
974
+ has_percentage = "%" in query or "percent" in query_lower or "percentage" in query_lower
975
+ # Check for division keywords
976
+ has_division = any(div in query_lower for div in ["divided by", "divided", "divide"])
977
+
978
+ if has_numbers and (has_geometry or has_percentage or has_division or any(kw in ["calculate", "compute", "what is", "what's", "find", "find the"] for kw in matched_calc_keywords)):
979
+ needs["calculator"] = True
980
+ logger.info(f"[AGENT] 🔍 Calculator detected via keywords: {matched_calc_keywords}")
981
+ # Extract numbers for context
982
+ numbers = re.findall(r'[0-9]+(?:\.[0-9]+)?', query)
983
+ if numbers:
984
+ needs["calc_expression"] = " ".join(numbers) if len(numbers) <= 3 else query[:100]
985
+ else:
986
+ needs["calc_expression"] = query[:100] # Pass query as context
987
+
988
  # Check for web search needs
989
  # Load keywords and patterns from config
990
  web_search_config = self.agent_config.get("web_search", {})
 
1150
  search_q = tool_needs["search_query"] or query
1151
  logger.info(f"[AGENT] 🔍 Extracted search query: '{search_q}' (original: '{query[:100]}')")
1152
 
1153
+ # Load news detection keywords from config
1154
+ web_search_config = self.agent_config.get("web_search", {})
1155
+ news_keywords = web_search_config.get("news_query_keywords", ["news", "headlines", "headline", "breaking"])
1156
+ news_enhancement_keywords = web_search_config.get("news_enhancement_keywords", ["headlines", "breaking", "latest"])
1157
+
1158
+ # Detect news/headlines queries using config keywords
1159
+ query_lower = query.lower()
1160
+ search_q_lower = search_q.lower()
1161
+ is_news_query = any(keyword in query_lower for keyword in news_keywords)
1162
+
1163
+ # Enhance news queries to get actual headlines, not just source pages
1164
+ if is_news_query:
1165
+ # Remove question marks and question words that might reduce relevance
1166
+ enhanced_search = re.sub(r'[?]', '', search_q)
1167
+ enhanced_search = re.sub(r'^(what are|what is|what\'s|tell me|show me|find me)\s+', '', enhanced_search, flags=re.IGNORECASE)
1168
+ enhanced_search = enhanced_search.strip()
1169
+
1170
+ # For generic news queries, add specific keywords to get actual headlines (using config)
1171
+ if any(gen in search_q_lower for gen in ["today's", "today", "top news", "breaking news", "latest news", "news headlines", "headlines"]):
1172
+ # Already contains news keywords - keep as is but enhance using config
1173
+ if "headlines" not in search_q_lower and "headline" not in search_q_lower:
1174
+ if any(kw in news_enhancement_keywords for kw in ["headlines", "headline"]):
1175
+ enhanced_search = f"{enhanced_search} headlines"
1176
+ if "breaking" not in search_q_lower and "latest" not in search_q_lower:
1177
+ if "latest" in news_enhancement_keywords:
1178
+ enhanced_search = f"latest {enhanced_search}"
1179
+
1180
+ search_q = enhanced_search
1181
+ logger.info(f"[AGENT] 📰 Enhanced news query: '{search_q}' (for better headline results)")
1182
+
1183
  # Validate search query before calling API
1184
  search_q_lower = search_q.lower().strip()
1185
  if (search_q_lower.startswith('and ') or
 
1194
  if search_q and 'custom_web_search' in globals() and custom_web_search is not None:
1195
  max_results = self.agent_config.get("web_search", {}).get("max_results", 5)
1196
  logger.info(f"[AGENT] 🌐 Web search API call: query='{search_q}', max_results={max_results}")
1197
+ # Pass agent_config to web_search for news detection and filtering
1198
+ search_result = custom_web_search(search_q, max_results=max_results, agent_config=self.agent_config)
1199
 
1200
  # Check if search actually returned valid results (not "No results found" or error)
1201
  has_results = (search_result and
 
1228
  # The model already has structured format (🎯, 📊, ⚡, 💡) built-in via meetara_lab_core.py
1229
  # Just feed tool results - model will automatically structure the response
1230
  if tool_results:
1231
+ # Load news query keywords and instruction templates from config
1232
+ web_search_config = self.agent_config.get("web_search", {})
1233
+ news_query_config = web_search_config.get("news_query_instructions", {})
1234
+ news_keywords = web_search_config.get("news_query_keywords", ["news", "headlines", "headline", "breaking"])
1235
+
1236
+ # Detect if this is a news/headlines query using config keywords
1237
+ query_lower = query.lower()
1238
+ is_news_query = any(keyword in query_lower for keyword in news_keywords)
1239
+
1240
+ # Build context-aware instructions from config
1241
+ if is_news_query and news_query_config.get("enabled", True):
1242
+ instruction_text = news_query_config.get("instruction_template",
1243
+ "CRITICAL: Extract and list the ACTUAL NEWS HEADLINES and STORIES from the search results above. "
1244
+ "Do NOT just describe what news sources offer - provide the REAL NEWS HEADLINES and CONTENT."
1245
+ )
1246
+ logger.info(f"[AGENT] 📰 News query detected - using config-based instructions for headline extraction")
1247
+ else:
1248
+ instruction_text = news_query_config.get("regular_instruction", "Use the tool results above to provide your response.")
1249
+
1250
  enhanced_prompt = (
1251
  f"{query}\n\n"
1252
  f"=== Tool Results ===\n"
1253
  + "\n\n".join(tool_results) +
1254
+ f"\n\n{instruction_text}"
1255
  )
1256
  logger.info(f"[AGENT] 📝 Feeding {len(tool_results)} tool results to model (model will auto-format)")
1257
+ if is_news_query:
1258
+ logger.info(f"[AGENT] 📰 News query detected - instructing model to extract actual headlines")
1259
  logger.debug(f"[AGENT] 📤 Enhanced prompt to model (first 1000 chars):\n{enhanced_prompt[:1000]}...")
1260
 
1261
  # Log full tool results being fed to model