diff --git a/modal/_tools.py b/modal/_tools.py index 9d3a82ef..a6996be0 100644 --- a/modal/_tools.py +++ b/modal/_tools.py @@ -365,9 +365,9 @@ class TavilySearchBackend: 'api_key': self.api_key, 'query': query, 'max_results': max(1, min(int(top_k), 8)), - 'include_answer': False, + 'include_answer': True, 'include_raw_content': False, - 'search_depth': 'basic', + 'search_depth': 'advanced', }, timeout=self.timeout, ) @@ -375,15 +375,23 @@ class TavilySearchBackend: data = r.json() except Exception: return [] - return [ - SearchHit( + direct_answer = (data.get('answer') or '').strip() + hits: list[SearchHit] = [] + # Surface Tavily's synthesized answer as the first hit so a 1.4B model + # can parrot a clean, grounded sentence instead of fighting with noisy snippets. + if direct_answer: + hits.append(SearchHit( + url='https://tavily.com/answer', + title='Tavily direct answer', + snippet=direct_answer, + )) + for h in data.get('results', [])[: max(0, top_k - (1 if direct_answer else 0))]: + hits.append(SearchHit( url=h.get('url', ''), title=h.get('title', ''), snippet=h.get('content', ''), - ) - for h in data.get('results', [])[:top_k] - ] - + )) + return hits class CloudflareBrowserRenderingClient: diff --git a/nanochat/tools.py b/nanochat/tools.py index 9d3a82ef..b3de89e6 100644 --- a/nanochat/tools.py +++ b/nanochat/tools.py @@ -365,9 +365,9 @@ class TavilySearchBackend: 'api_key': self.api_key, 'query': query, 'max_results': max(1, min(int(top_k), 8)), - 'include_answer': False, + 'include_answer': True, 'include_raw_content': False, - 'search_depth': 'basic', + 'search_depth': 'advanced', }, timeout=self.timeout, ) @@ -375,14 +375,23 @@ class TavilySearchBackend: data = r.json() except Exception: return [] - return [ - SearchHit( + direct_answer = (data.get('answer') or '').strip() + hits: list[SearchHit] = [] + # Surface Tavily's synthesized answer as the first hit so a 1.4B model + # can parrot a clean, grounded sentence instead of fighting with noisy snippets. + if direct_answer: + hits.append(SearchHit( + url='https://tavily.com/answer', + title='Tavily direct answer', + snippet=direct_answer, + )) + for h in data.get('results', [])[: max(0, top_k - (1 if direct_answer else 0))]: + hits.append(SearchHit( url=h.get('url', ''), title=h.get('title', ''), snippet=h.get('content', ''), - ) - for h in data.get('results', [])[:top_k] - ] + )) + return hits diff --git a/services/frontend/components/chat/MessageBubble.tsx b/services/frontend/components/chat/MessageBubble.tsx index 017e141e..0d7c1314 100644 --- a/services/frontend/components/chat/MessageBubble.tsx +++ b/services/frontend/components/chat/MessageBubble.tsx @@ -78,10 +78,10 @@ function ToolCallBlock({ content, closed }: { content: string; closed: boolean } const icon = toolName === 'web_search' ? : toolName === 'calculator' ? : ; const query = parsed?.arguments ? JSON.stringify(parsed.arguments) : content; return ( -
+
{icon} - Calling {toolName}{closed ? '' : '…'} + Calling {toolName}{closed ? '' : '…'}
{query}
@@ -93,21 +93,22 @@ function ToolResultBlock({ content, closed }: { content: string; closed: boolean let summary = content; try { const j = JSON.parse(content); - if (j?.output?.results?.[0]?.snippet) summary = String(j.output.results[0].snippet).slice(0, 160); + if (j?.output?.answer) summary = String(j.output.answer).slice(0, 220); + else if (j?.output?.results?.[0]?.snippet) summary = String(j.output.results[0].snippet).slice(0, 160); else if (j?.output?.value !== undefined) summary = `= ${j.output.value}`; else if (j?.error) summary = `error: ${j.error}`; } catch { /* partial */ } return ( -
- {open && ( -
{content}
+
{content}
)}
);