Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 10 additions & 24 deletions apps/masterbots.ai/app/actions/ai-executers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,33 +127,19 @@ export async function getWebSearchTool({
throw new Error('Web Search could not be completed.')
}

if (!response.outputs['web search']?.output) {
if (
!response.outputs['web search']?.output &&
!response.outputs['web search']?.logs
) {
throw new Error('No output given. Web search could not be completed')
}

return `${response.outputs['web search'].output}

## EXAMPLE:

**Resume:**
Brewers: 9
Dodgers: 2

**Summary**
Yelich, Perkins power Brewers to 9-2 victory over Dodgers and avoid being swept in weekend series. — Christian Yelich and Blake Perkins both homered, had three hits and drove in three runs as the Milwaukee Brewers beat the Los Angeles Dodgers 9-2 Sunday to snap a seven-game losing streak at Dodger Stadium.

**Homeruns:**
Yelich

**Winning Pitcher:**
J. Junis

**Sources**:

1. [https://website1.com/](https://website1.com/)
2. [https://website2.com/](https://website2.com/)`
return `## INPUT:

${response.outputs['web search']?.output ?? response.outputs['web search']?.logs}`
} catch (error) {
console.error('Error fetching app data: ', error)
throw error
return `Something went wrong with web search that failed to provide results. Please try again later.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Consider throwing or logging the original error for better debugging

While returning a user-friendly message is good, completely swallowing the original error makes debugging harder. Consider logging the full error details or maintaining the throw with a wrapped error that includes both messages.

    console.error('Web search error:', error);
    throw new Error(`Something went wrong with web search that failed to provide results. Please try again later. Original error: ${error.message}`);


[ERROR LOG]: ${JSON.stringify(error, null, 2)}`
}
}
6 changes: 3 additions & 3 deletions apps/masterbots.ai/app/actions/ai-main-call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export async function createResponseStream(
temperature: 0.4,
tools,
maxRetries: 2,
maxToolRoundtrips: 1
maxToolRoundtrips: 2
})
responseStream = response.toDataStreamResponse().body as ReadableStream
break
Expand All @@ -242,7 +242,7 @@ export async function createResponseStream(
maxTokens: 300,
tools,
maxRetries: 2,
maxToolRoundtrips: 1
maxToolRoundtrips: 2
})
responseStream = response.toDataStreamResponse().body as ReadableStream
break
Expand All @@ -262,7 +262,7 @@ export async function createResponseStream(
maxTokens: 1000,
tools,
maxRetries: 2,
maxToolRoundtrips: 1
maxToolRoundtrips: 2
})
responseStream = response.toDataStreamResponse().body as ReadableStream
break
Expand Down
148 changes: 117 additions & 31 deletions apps/masterbots.ai/components/routes/chat/chat-clickable-text.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,137 @@
import {
cleanClickableText,
extractTextFromReactNode,
parseClickableText
} from '@/lib/utils'

interface ClickableTextProps {
children: React.ReactNode
isListItem: boolean
sendMessageFromResponse?: (message: string) => void
}
extractTextFromReactNodeNormal,
extractTextFromReactNodeWeb,
parseClickableText,
transformLink,
} from '@/lib/clickable-results'
import { useThread } from '@/lib/hooks/use-thread'
import { cn } from '@/lib/utils'
import type { ClickableTextProps } from '@/types/types'
import React from 'react'

/**
* ClickableText component
* Renders phrases as clickable links, triggering a message when clicked.
*/
export function ClickableText({
children,
isListItem,
sendMessageFromResponse
sendMessageFromResponse,
webSearchResults = [],
onReferenceFound,
}: ClickableTextProps) {
const fullText = extractTextFromReactNode(children)
const { clickableText, restText } = parseClickableText(fullText)
const { webSearch } = useThread()

const extractedContent = webSearch
? extractTextFromReactNodeWeb(children)
: extractTextFromReactNodeNormal(children)

const handleClick = () => {
if (sendMessageFromResponse && clickableText.trim()) {
const cleanedText = cleanClickableText(clickableText)
const createClickHandler = (text: string) => () => {
if (sendMessageFromResponse && text.trim()) {
const cleanedText = cleanClickableText(text)
sendMessageFromResponse(`Tell me more about ${cleanedText}`)
}
}

if (!clickableText.trim()) {
return <>{fullText}</>
const processLink = (linkElement: React.ReactElement) => {
const href = linkElement.props.href
// Buscar la referencia correspondiente
const reference = webSearchResults.find((result) => result.url === href)

if (reference && onReferenceFound) {
onReferenceFound(reference)
return null // Remover el link inline
}

return linkElement // Mantener links que no son de búsqueda web
}

return (
const renderClickableContent = (clickableText: string, restText: string) => (
<>
{/* biome-ignore lint/a11y/useKeyWithClickEvents: Click handler is supplementary */}
<span
className="cursor-pointer text-link hover:underline"
onClick={handleClick}
// biome-ignore lint/a11y/useSemanticElements: <explanation>
role="button"
tabIndex={0}
<button
className={cn('cursor-pointer hover:underline bg-transparent border-none p-0 m-0', isListItem ? 'text-blue-500' : 'text-link')}
onClick={createClickHandler(clickableText)}
type="button"
>
{clickableText}
</span>
</button>
{restText}
</>
)
}

if (Array.isArray(extractedContent)) {
return extractedContent.map((content, index) => {
if (React.isValidElement(content)) {
if (content.type === 'a' && webSearch) {
return processLink(content)
}
// Manejo de elementos strong
if (content.type === 'strong') {
const strongContent = extractTextFromReactNodeNormal(
(content.props as { children: React.ReactNode }).children,
)
const { clickableText, restText } = parseClickableText(strongContent + ':')

if (clickableText.trim()) {
return (
<button
key={`clickable-${index}`}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace array indices with stable unique keys.

Using array indices as React keys can lead to rendering issues when items are reordered or removed.

-key={`clickable-${index}`}
+key={`clickable-${clickableText}-${index}`}

Also applies to: 119-119

🧰 Tools
🪛 Biome (1.9.4)

[error] 75-75: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation.

(lint/suspicious/noArrayIndexKey)

className={cn(
'cursor-pointer hover:underline',
isListItem ? 'text-blue-500' : 'text-link',
)}
onClick={createClickHandler(clickableText)}
type="button"
tabIndex={0}
>
{strongContent}
</button>
)
}
return content
}

// Manejo de links cuando webSearch está activo
if (content.type === 'a' && webSearch) {
const parentContext = extractedContent
.filter(
(item) =>
typeof item === 'string' || (React.isValidElement(item) && item.type === 'strong'),
)
.map((item) =>
typeof item === 'string'
? item
: extractTextFromReactNodeNormal(
(item.props as { children: React.ReactNode }).children,
),
)
.join(' ')
return transformLink(content, parentContext)
}

return content
}

const { clickableText, restText } = parseClickableText(String(content))

if (!clickableText.trim()) {
return content
}

return (
<React.Fragment key={`clickable-${index}`}>
{renderClickableContent(clickableText, restText)}
</React.Fragment>
)
})
}

if (React.isValidElement(extractedContent)) {
return extractedContent
}

const { clickableText, restText } = parseClickableText(String(extractedContent))

if (!clickableText.trim()) {
return <>{extractedContent}</>
}

return renderClickableContent(clickableText, restText)
}
Loading