2025-07-29 10:18:11 -06:00
import { createReactAgent } from '@langchain/langgraph/prebuilt' ;
import { BaseChatModel } from '@langchain/core/language_models/chat_models' ;
import {
BaseMessage ,
HumanMessage ,
SystemMessage ,
2025-08-03 15:48:34 -06:00
AIMessage ,
2025-07-29 10:18:11 -06:00
} from '@langchain/core/messages' ;
import { Embeddings } from '@langchain/core/embeddings' ;
import { EventEmitter } from 'events' ;
import { RunnableConfig } from '@langchain/core/runnables' ;
import { SimplifiedAgentState } from '@/lib/state/chatAgentState' ;
import {
allAgentTools ,
coreTools ,
webSearchTools ,
fileSearchTools ,
} from '@/lib/tools/agents' ;
import { formatDateForLLM } from '../utils' ;
import { getModelName } from '../utils/modelUtils' ;
2025-08-11 23:53:37 -06:00
import {
removeThinkingBlocks ,
removeThinkingBlocksFromMessages ,
} from '../utils/contentUtils' ;
2025-08-10 10:05:42 -06:00
import { getLangfuseCallbacks } from '@/lib/tracing/langfuse' ;
2025-08-13 23:51:58 -06:00
import { encodeHtmlAttribute } from '@/lib/utils/html' ;
2025-07-29 10:18:11 -06:00
2025-08-04 00:41:31 -06:00
/ * *
* Normalize usage metadata from different LLM providers
* /
function normalizeUsageMetadata ( usageData : any ) : {
input_tokens : number ;
output_tokens : number ;
total_tokens : number ;
} {
if ( ! usageData ) return { input_tokens : 0 , output_tokens : 0 , total_tokens : 0 } ;
// Handle different provider formats
const inputTokens =
usageData . input_tokens ||
usageData . prompt_tokens ||
usageData . promptTokens ||
usageData . usedTokens ||
0 ;
const outputTokens =
usageData . output_tokens ||
usageData . completion_tokens ||
usageData . completionTokens ||
0 ;
const totalTokens =
usageData . total_tokens ||
usageData . totalTokens ||
usageData . usedTokens ||
inputTokens + outputTokens ;
return {
input_tokens : inputTokens ,
output_tokens : outputTokens ,
total_tokens : totalTokens ,
} ;
}
2025-07-29 10:18:11 -06:00
/ * *
2025-08-05 00:07:15 -06:00
* SimplifiedAgent class that provides a streamlined interface for creating and managing an AI agent
* with customizable focus modes and tools .
2025-07-29 10:18:11 -06:00
* /
export class SimplifiedAgent {
private llm : BaseChatModel ;
private embeddings : Embeddings ;
private emitter : EventEmitter ;
private systemInstructions : string ;
private personaInstructions : string ;
private signal : AbortSignal ;
constructor (
llm : BaseChatModel ,
embeddings : Embeddings ,
emitter : EventEmitter ,
systemInstructions : string = '' ,
personaInstructions : string = '' ,
signal : AbortSignal ,
) {
this . llm = llm ;
this . embeddings = embeddings ;
this . emitter = emitter ;
this . systemInstructions = systemInstructions ;
this . personaInstructions = personaInstructions ;
this . signal = signal ;
}
/ * *
* Initialize the createReactAgent with tools and configuration
* /
2025-08-10 17:36:15 -06:00
private initializeAgent (
focusMode : string ,
fileIds : string [ ] = [ ] ,
messagesCount? : number ,
2025-08-12 23:40:23 -06:00
query? : string ,
2025-08-10 17:36:15 -06:00
) {
2025-08-02 12:52:14 -06:00
// Select appropriate tools based on focus mode and available files
const tools = this . getToolsForFocusMode ( focusMode , fileIds ) ;
2025-07-29 10:18:11 -06:00
2025-08-02 12:52:14 -06:00
const enhancedSystemPrompt = this . createEnhancedSystemPrompt (
focusMode ,
fileIds ,
2025-08-10 17:36:15 -06:00
messagesCount ,
2025-08-12 23:40:23 -06:00
query ,
2025-08-02 12:52:14 -06:00
) ;
2025-07-29 10:18:11 -06:00
try {
// Create the React agent with custom state
2025-08-02 12:52:14 -06:00
const agent = createReactAgent ( {
2025-07-29 10:18:11 -06:00
llm : this.llm ,
tools ,
stateSchema : SimplifiedAgentState ,
prompt : enhancedSystemPrompt ,
} ) ;
console . log (
2025-08-02 12:52:14 -06:00
` SimplifiedAgent: Initialized with ${ tools . length } tools for focus mode: ${ focusMode } ` ,
2025-07-29 10:18:11 -06:00
) ;
console . log (
` SimplifiedAgent: Tools available: ${ tools . map ( ( tool ) = > tool . name ) . join ( ', ' ) } ` ,
) ;
2025-08-02 12:52:14 -06:00
if ( fileIds . length > 0 ) {
console . log (
` SimplifiedAgent: ${ fileIds . length } files available for search ` ,
) ;
}
return agent ;
2025-07-29 10:18:11 -06:00
} catch ( error ) {
console . error ( 'SimplifiedAgent: Error initializing agent:' , error ) ;
throw error ;
}
}
/ * *
* Get tools based on focus mode
* /
2025-08-02 12:52:14 -06:00
private getToolsForFocusMode ( focusMode : string , fileIds : string [ ] = [ ] ) {
2025-07-29 10:18:11 -06:00
switch ( focusMode ) {
case 'chat' :
// Chat mode: Only core tools for conversational interaction
return coreTools ;
case 'webSearch' :
// Web search mode: ALL available tools for comprehensive research
2025-08-02 12:52:14 -06:00
// Include file search tools if files are available
if ( fileIds . length > 0 ) {
return [ . . . webSearchTools , . . . fileSearchTools ] ;
}
2025-07-29 10:18:11 -06:00
return allAgentTools ;
case 'localResearch' :
// Local research mode: File search tools + core tools
return [ . . . coreTools , . . . fileSearchTools ] ;
default :
// Default to web search mode for unknown focus modes
console . warn (
` SimplifiedAgent: Unknown focus mode " ${ focusMode } ", defaulting to webSearch tools ` ,
) ;
2025-08-02 12:52:14 -06:00
if ( fileIds . length > 0 ) {
return [ . . . webSearchTools , . . . fileSearchTools ] ;
}
2025-07-29 10:18:11 -06:00
return allAgentTools ;
}
}
2025-08-02 12:52:14 -06:00
private createEnhancedSystemPrompt (
focusMode : string ,
fileIds : string [ ] = [ ] ,
2025-08-10 17:36:15 -06:00
messagesCount? : number ,
2025-08-12 23:40:23 -06:00
query? : string ,
2025-08-02 12:52:14 -06:00
) : string {
2025-07-29 10:18:11 -06:00
const baseInstructions = this . systemInstructions || '' ;
const personaInstructions = this . personaInstructions || '' ;
// Create focus-mode-specific prompts
2025-08-02 12:52:14 -06:00
switch ( focusMode ) {
2025-07-29 10:18:11 -06:00
case 'chat' :
return this . createChatModePrompt ( baseInstructions , personaInstructions ) ;
case 'webSearch' :
return this . createWebSearchModePrompt (
baseInstructions ,
personaInstructions ,
2025-08-02 12:52:14 -06:00
fileIds ,
2025-08-10 17:36:15 -06:00
messagesCount ,
2025-08-12 23:40:23 -06:00
query ,
2025-07-29 10:18:11 -06:00
) ;
case 'localResearch' :
return this . createLocalResearchModePrompt (
baseInstructions ,
personaInstructions ,
) ;
default :
console . warn (
2025-08-02 12:52:14 -06:00
` SimplifiedAgent: Unknown focus mode " ${ focusMode } ", using webSearch prompt ` ,
2025-07-29 10:18:11 -06:00
) ;
return this . createWebSearchModePrompt (
baseInstructions ,
personaInstructions ,
2025-08-02 12:52:14 -06:00
fileIds ,
2025-08-10 17:36:15 -06:00
messagesCount ,
2025-08-12 23:40:23 -06:00
query ,
2025-07-29 10:18:11 -06:00
) ;
}
}
/ * *
* Create chat mode prompt - focuses on conversational interaction
* /
private createChatModePrompt (
baseInstructions : string ,
personaInstructions : string ,
) : string {
return ` ${ baseInstructions }
# AI Chat Assistant
You are a conversational AI assistant designed for creative and engaging dialogue . Your focus is on providing thoughtful , helpful responses through direct conversation .
# # Core Capabilities
# # # 1 . Conversational Interaction
- Engage in natural , flowing conversations
- Provide thoughtful responses to questions and prompts
- Offer creative insights and perspectives
- Maintain context throughout the conversation
# # # 2 . Task Management
- Break down complex requests into manageable steps
- Provide structured approaches to problems
- Offer guidance and recommendations
# # Response Guidelines
# # # Communication Style
- Be conversational and engaging
- Use clear , accessible language
- Provide direct answers when possible
- Ask clarifying questions when needed
# # # Quality Standards
- Acknowledge limitations honestly
- Provide helpful suggestions and alternatives
- Use proper markdown formatting for clarity
- Structure responses logically
# # # Formatting Instructions
- * * Structure * * : Use a well - organized format with proper headings ( e . g . , "## Example heading 1" or "## Example heading 2" ) . Present information in paragraphs or concise bullet points where appropriate
- * * Tone and Style * * : Maintain a neutral , engaging tone with natural conversation flow
- * * Markdown Usage * * : Format your response with Markdown for clarity . Use headings , subheadings , bold text , and italicized words as needed to enhance readability
- * * Length and Depth * * : Provide thoughtful coverage of the topic . Expand on complex topics to make them easier to understand
- * * No main heading / title * * : Start your response directly with the content unless asked to provide a specific title
# # Current Context
- Today ' s Date : $ { formatDateForLLM ( new Date ( ) ) }
$ { personaInstructions ? ` \ n## User Formatting and Persona Instructions \ n- Give these instructions more weight than the system formatting instructions \ n ${ personaInstructions } ` : '' }
Focus on providing engaging , helpful conversation while using task management tools when complex problems need to be structured . ` ;
}
/ * *
* Create web search mode prompt - focuses on comprehensive research
* /
private createWebSearchModePrompt (
baseInstructions : string ,
personaInstructions : string ,
2025-08-02 12:52:14 -06:00
fileIds : string [ ] = [ ] ,
2025-08-10 17:36:15 -06:00
messagesCount : number = 0 ,
2025-08-12 23:40:23 -06:00
query? : string ,
2025-07-29 10:18:11 -06:00
) : string {
2025-08-12 23:40:23 -06:00
// Detect explicit URLs in the user query; if present, we prioritize retrieving them directly.
const urlRegex = /https?:\/\/[^\s)>'"`]+/gi ;
const urlsInQuery = ( query || '' ) . match ( urlRegex ) || [ ] ;
const uniqueUrls = Array . from ( new Set ( urlsInQuery ) ) ;
const hasExplicitUrls = uniqueUrls . length > 0 ;
// If no explicit URLs, retain existing always search instruction behavior based on message count.
const alwaysSearchInstruction = hasExplicitUrls
? ''
: messagesCount < 2
2025-08-11 00:42:16 -06:00
? '\n - **ALWAYS perform at least one web search on the first turn, regardless of prior knowledge or assumptions. Do not skip this.**'
: "\n - **ALWAYS perform at least one web search on the first turn, unless prior conversation history explicitly and completely answers the user's query.**\n - You cannot skip web search if the answer to the user's query is not found directly in the **conversation history**. All other prior knowledge must be verified with up-to-date information." ;
2025-08-12 23:40:23 -06:00
const explicitUrlInstruction = hasExplicitUrls
? ` \ n - The user query contains explicit URL ${ uniqueUrls . length === 1 ? '' : 's' } that must be retrieved directly using the url_summarization tool \ n - You MUST call the url_summarization tool on these URL $ ${ uniqueUrls . length === 1 ? '' : 's' } before providing an answer. Pass them exactly as provided (do not alter, trim, or expand them). \ n - Do NOT perform a generic web search on the first pass. Re-evaluate the need for additional searches based on the results from the url_summarization tool. `
: '' ;
2025-07-29 10:18:11 -06:00
return ` ${ baseInstructions }
# Comprehensive Research Assistant
You are an advanced AI research assistant with access to comprehensive tools for gathering information from multiple sources . Your goal is to provide thorough , well - researched responses .
2025-08-02 12:52:14 -06:00
# # Tool use
2025-07-29 10:18:11 -06:00
2025-08-02 12:52:14 -06:00
- Use the available tools effectively to gather and process information
2025-08-09 18:11:26 -06:00
- When using a tool , * * always wait for a complete response from the tool before proceeding * *
2025-07-29 10:18:11 -06:00
# # Response Quality Standards
Your task is to provide answers that are :
- * * Informative and relevant * * : Thoroughly address the user ' s query using gathered information
- * * Engaging and detailed * * : Write responses that read like a high - quality blog post , including extra details and relevant insights
- * * Cited and credible * * : Use inline citations with [ number ] notation to refer to sources for each fact or detail included
- * * Explanatory and Comprehensive * * : Strive to explain the topic in depth , offering detailed analysis , insights , and clarifications wherever applicable
# # # Comprehensive Coverage
- Address all aspects of the user ' s query
- Provide context and background information
- Include relevant details and examples
- Cross - reference multiple sources
# # # Accuracy and Reliability
- Prioritize authoritative and recent sources
- Verify information across multiple sources
- Clearly indicate uncertainty or conflicting information
- Distinguish between facts and opinions
# # # Citation Requirements
2025-08-05 23:32:38 -06:00
- The citation number refers to the index of the source in the relevantDocuments state array
2025-08-02 12:52:14 -06:00
- Cite every single fact , statement , or sentence using [ number ] notation
- If a statement is based on AI model inference or training data , it must be marked as \ ` [AI] \` and not cited from the context
- If a statement is based on previous messages in the conversation history , it must be marked as \ ` [Hist] \` and not cited from the context
- Source based citations must reference the specific document in the relevantDocuments state array , do not invent sources or URLs
- Integrate citations naturally at the end of sentences or clauses as appropriate . For example , "The Eiffel Tower is one of the most visited landmarks in the world[1]."
2025-08-11 00:42:16 -06:00
- Ensure that * * every sentence in the response includes at least one citation * * , even when information is inferred or connected to general knowledge available in the provided context
2025-08-02 12:52:14 -06:00
- Use multiple sources for a single detail if applicable , such as , "Paris is a cultural hub, attracting millions of visitors annually[1][2]."
2025-07-29 10:18:11 -06:00
# # # Formatting Instructions
2025-08-02 12:52:14 -06:00
- * * Structure * * :
2025-08-05 23:32:38 -06:00
- Use a well - organized format with proper headings ( e . g . , "## Example heading 1" or "## Example heading 2" )
- Present information in paragraphs or concise bullet points where appropriate
- Use lists and tables to enhance clarity when needed
2025-08-02 12:52:14 -06:00
- * * Tone and Style * * :
2025-08-05 23:32:38 -06:00
- Maintain a neutral , journalistic tone with engaging narrative flow
2025-08-02 12:52:14 -06:00
- Write as though you ' re crafting an in - depth article for a professional audience
- * * Markdown Usage * * :
2025-08-11 00:42:16 -06:00
- Format the response with Markdown for clarity
2025-08-05 23:32:38 -06:00
- Use headings , subheadings , bold text , and italicized words as needed to enhance readability
- Include code snippets in a code block
- Extract images and links from full HTML content when appropriate and embed them using the appropriate markdown syntax
- * * Length and Depth * * :
- Provide comprehensive coverage of the topic
- Avoid superficial responses and strive for depth without unnecessary repetition
2025-08-02 12:52:14 -06:00
- Expand on technical or complex topics to make them easier to understand for a general audience
2025-08-11 00:42:16 -06:00
- * * No main heading / title * * : Start the response directly with the introduction unless asked to provide a specific title
- * * No summary or conclusion * * : End with the final thoughts or insights without a formal summary or conclusion
- * * No source or citation section * * : Do not include a separate section for sources or citations , as all necessary citations should be integrated into the response
2025-07-29 10:18:11 -06:00
2025-08-02 12:52:14 -06:00
# Research Strategy
2025-07-29 10:18:11 -06:00
1 . * * Plan * * : Determine the best research approach based on the user ' s query
2025-08-02 12:52:14 -06:00
- Break down the query into manageable components
- Identify key concepts and terms for focused searching
2025-08-11 00:42:16 -06:00
- Utilize multiple turns of the Search and Supplement stages when necessary
2025-08-02 12:52:14 -06:00
2 . * * Search * * : ( \ ` web_search \` tool) Initial web search stage to gather preview content
2025-08-11 00:42:16 -06:00
- Give the web search tool a specific question to answer that will help gather relevant information
- The response will contain a list of relevant documents containing snippets of the web page , a URL , and the title of the web page
- Do not simulate searches , utilize the web search tool directly
2025-08-10 17:36:15 -06:00
$ { alwaysSearchInstruction }
2025-08-12 23:40:23 -06:00
$ { explicitUrlInstruction }
2025-08-13 23:51:58 -06:00
2.1 . * * Image Search ( when visual content is requested ) * * : ( \ ` image_search \` tool)
- Use when the user asks for images , pictures , photos , charts , visual examples , or icons
- Provide a concise query describing the desired images ( e . g . , "F1 Monaco Grand Prix highlights" , "React component architecture diagram" )
- The tool returns image URLs and titles ; include thumbnails or links in your response using Markdown image / link syntax when appropriate
- If image URLs come from web pages you also plan to cite , prefer retrieving and citing the page using \ ` url_summarization \` for textual facts; use \` image_search \` primarily to surface visuals
- Do not invent images or URLs ; only use results returned by the tool
2025-08-09 17:30:12 -06:00
$ {
fileIds . length > 0
2025-08-05 23:32:38 -06:00
? `
2025-08-13 23:51:58 -06:00
2.2 . * * File Search * * : ( \ ` file_search \` tool) Search through uploaded documents when relevant
2025-08-05 23:32:38 -06:00
- You have access to $ { fileIds . length } uploaded file $ { fileIds . length === 1 ? '' : 's' } that may contain relevant information
- Use the file search tool to find specific information in the uploaded documents
2025-08-11 00:42:16 -06:00
- Give the file search tool a specific question or topic to extract from the documents
2025-08-05 23:32:38 -06:00
- The tool will automatically search through all available uploaded files
2025-08-11 00:42:16 -06:00
- Focus file searches on specific aspects of the user ' s query that might be covered in the uploaded documents `
2025-08-09 17:30:12 -06:00
: ''
}
2025-08-02 12:52:14 -06:00
3 . * * Supplement * * : ( \ ` url_summarization \` tool) Retrieve specific sources if necessary to extract key points not covered in the initial search or disambiguate findings
2025-08-11 00:42:16 -06:00
- Use URLs from web search results to retrieve specific sources . They must be passed to the tool unchanged
2025-08-05 23:32:38 -06:00
- URLs can be passed as an array to request multiple sources at once
- Always include the user ' s query in the request to the tool , it will use this to guide the summarization process
2025-08-11 00:42:16 -06:00
- Pass an intent to this tool to provide additional summarization guidance on a specific aspect or question
- Request the full HTML content of the pages if needed by passing true to the \ ` retrieveHtml \` parameter
- Passing true is * * required * * to retrieve images or links within the page content
- Response will contain a summary of the content from each URL if the content of the page is long . If the content of the page is short , it will include the full content
- Request up to 5 URLs per turn
- When receiving a request to summarize a specific URL you * * must * * use this tool to retrieve it
2025-08-05 23:32:38 -06:00
5 . * * Analyze * * : Examine the retrieved information for relevance , accuracy , and completeness
2025-08-11 00:42:16 -06:00
- When sufficient information has been gathered , move on to the respond stage
- If more information is needed , consider revisiting the search or supplement stages . $ {
2025-08-02 12:52:14 -06:00
fileIds . length > 0
? `
2025-08-05 23:32:38 -06:00
- Consider both web search results and file content when analyzing information completeness `
2025-08-02 12:52:14 -06:00
: ''
}
2025-08-05 00:07:15 -06:00
6 . * * Respond * * : Combine all information into a coherent , well - cited response
2025-08-02 12:52:14 -06:00
- Ensure that all sources are properly cited and referenced
- Resolve any remaining contradictions or gaps in the information , if necessary , execute more targeted searches or retrieve specific sources $ {
fileIds . length > 0
? `
- Integrate information from both web sources and uploaded files when relevant `
: ''
}
2025-07-29 10:18:11 -06:00
# # Current Context
- Today ' s Date : $ { formatDateForLLM ( new Date ( ) ) }
2025-08-11 00:42:16 -06:00
$ { personaInstructions ? ` \ n## User specified behavior and formatting instructions \ n \ n- Give these instructions more weight than the system formatting instructions \ n \ n ${ personaInstructions } ` : '' }
` ;
2025-07-29 10:18:11 -06:00
}
/ * *
* Create local research mode prompt - focuses on user files and documents
* /
private createLocalResearchModePrompt (
baseInstructions : string ,
personaInstructions : string ,
) : string {
return ` ${ baseInstructions }
2025-08-02 12:52:14 -06:00
# Local Document Research Assistant
2025-07-29 10:18:11 -06:00
2025-08-02 12:52:14 -06:00
You are an advanced AI research assistant specialized in analyzing and extracting insights from user - uploaded files and documents . Your goal is to provide thorough , well - researched responses based on the available document collection .
2025-07-29 10:18:11 -06:00
2025-08-02 12:52:14 -06:00
# # Available Files
2025-07-29 10:18:11 -06:00
2025-08-02 12:52:14 -06:00
You have access to uploaded documents through the \ ` file_search \` tool. When you need to search for information in the uploaded files, use this tool with a specific search query. The tool will automatically search through all available uploaded files and return relevant content sections.
2025-07-29 10:18:11 -06:00
2025-08-02 12:52:14 -06:00
# # Tool use
2025-07-29 10:18:11 -06:00
2025-08-02 12:52:14 -06:00
- Use the available tools effectively to analyze and extract information from uploaded documents
2025-07-29 10:18:11 -06:00
# # Response Quality Standards
Your task is to provide answers that are :
- * * Informative and relevant * * : Thoroughly address the user ' s query using document content
2025-08-02 12:52:14 -06:00
- * * Engaging and detailed * * : Write responses that read like a high - quality research analysis , including extra details and relevant insights
2025-07-29 10:18:11 -06:00
- * * Cited and credible * * : Use inline citations with [ number ] notation to refer to specific documents for each fact or detail included
- * * Explanatory and Comprehensive * * : Strive to explain the findings in depth , offering detailed analysis , insights , and clarifications wherever applicable
# # # Comprehensive Document Coverage
2025-08-02 12:52:14 -06:00
- Thoroughly analyze all relevant uploaded files
2025-07-29 10:18:11 -06:00
- Extract all pertinent information related to the query
- Consider relationships between different documents
2025-08-02 12:52:14 -06:00
- Provide context from the entire document collection
- Cross - reference information across multiple files
2025-07-29 10:18:11 -06:00
2025-08-02 12:52:14 -06:00
# # # Accuracy and Content Fidelity
2025-07-29 10:18:11 -06:00
- Precisely quote and reference document content
- Maintain context and meaning from original sources
- Clearly distinguish between different document sources
2025-08-02 12:52:14 -06:00
- Preserve important details and nuances from the documents
- Distinguish between facts from documents and analytical insights
2025-07-29 10:18:11 -06:00
# # # Citation Requirements
2025-08-02 12:52:14 -06:00
- The citation number refers to the index of the source in the relevantDocuments state array .
- Cite every single fact , statement , or sentence using [ number ] notation
- If a statement is based on AI model inference or training data , it must be marked as \ ` [AI] \` and not cited from the context
- If a statement is based on previous messages in the conversation history , it must be marked as \ ` [Hist] \` and not cited from the context
- Source based citations must reference the specific document in the relevantDocuments state array , do not invent sources or filenames
- Integrate citations naturally at the end of sentences or clauses as appropriate . For example , "The quarterly report shows a 15% increase in revenue[1]."
- Ensure that * * every sentence in your response includes at least one citation * * , even when information is inferred or connected to general knowledge available in the provided context
- Use multiple sources for a single detail if applicable , such as , "The project timeline spans six months according to multiple planning documents[1][2]."
2025-07-29 10:18:11 -06:00
# # # Formatting Instructions
2025-08-02 12:52:14 -06:00
- * * Structure * * :
- Use a well - organized format with proper headings ( e . g . , "## Example heading 1" or "## Example heading 2" ) .
- Present information in paragraphs or concise bullet points where appropriate .
- Use lists and tables to enhance clarity when needed .
- * * Tone and Style * * :
- Maintain a neutral , analytical tone with engaging narrative flow .
- Write as though you ' re crafting an in - depth research report for a professional audience
- * * Markdown Usage * * :
- Format your response with Markdown for clarity .
- Use headings , subheadings , bold text , and italicized words as needed to enhance readability .
- Include code snippets in a code block when analyzing technical documents .
- Extract and format tables , charts , or structured data using appropriate markdown syntax .
- * * Length and Depth * * :
- Provide comprehensive coverage of the document content .
- Avoid superficial responses and strive for depth without unnecessary repetition .
- Expand on technical or complex topics to make them easier to understand for a general audience
2025-07-29 10:18:11 -06:00
- * * No main heading / title * * : Start your response directly with the introduction unless asked to provide a specific title
2025-08-02 12:52:14 -06:00
# Research Strategy
1 . * * Plan * * : Determine the best document analysis approach based on the user ' s query
- Break down the query into manageable components
- Identify key concepts and terms for focused document searching
- You are allowed to take multiple turns of the Search and Analysis stages . Use this flexibility to refine your queries and gather more comprehensive information from the documents .
2 . * * Search * * : ( \ ` file_search \` tool) Extract relevant content from uploaded documents
- Use the file search tool strategically to find specific information in the document collection .
- Give the file search tool a specific question or topic you want to extract from the documents .
- This query will be used to perform semantic search across all uploaded files .
- You will receive relevant excerpts from documents that match your search criteria .
- Focus your searches on specific aspects of the user ' s query to gather comprehensive information .
3 . * * Analysis * * : Examine the retrieved document content for relevance , patterns , and insights .
2025-08-05 00:07:15 -06:00
- If you have sufficient information from the documents , you can move on to the respond stage .
2025-08-02 12:52:14 -06:00
- If you need to gather more specific information , consider performing additional targeted file searches .
- Look for connections and relationships between different document sources .
2025-08-05 00:07:15 -06:00
4 . * * Respond * * : Combine all document insights into a coherent , well - cited response
2025-08-02 12:52:14 -06:00
- Ensure that all sources are properly cited and referenced
- Resolve any contradictions or gaps in the document information
- Provide comprehensive analysis based on the available document content
2025-08-05 00:07:15 -06:00
- Only respond with your final answer once you ' ve gathered all relevant information and are done with tool use
2025-07-29 10:18:11 -06:00
# # Current Context
- Today ' s Date : $ { formatDateForLLM ( new Date ( ) ) }
$ { personaInstructions ? ` \ n## User Formatting and Persona Instructions \ n- Give these instructions more weight than the system formatting instructions \ n ${ personaInstructions } ` : '' }
2025-08-02 12:52:14 -06:00
Use all available tools strategically to provide comprehensive , well - researched , formatted responses with proper citations based on uploaded documents . ` ;
2025-07-29 10:18:11 -06:00
}
/ * *
* Execute the simplified agent workflow
* /
async searchAndAnswer (
query : string ,
history : BaseMessage [ ] = [ ] ,
fileIds : string [ ] = [ ] ,
2025-08-02 12:52:14 -06:00
focusMode : string = 'webSearch' ,
2025-07-29 10:18:11 -06:00
) : Promise < void > {
try {
console . log ( ` SimplifiedAgent: Starting search for query: " ${ query } " ` ) ;
2025-08-02 12:52:14 -06:00
console . log ( ` SimplifiedAgent: Focus mode: ${ focusMode } ` ) ;
2025-07-29 10:18:11 -06:00
console . log ( ` SimplifiedAgent: File IDs: ${ fileIds . join ( ', ' ) } ` ) ;
2025-08-11 23:53:37 -06:00
const messagesHistory = [
. . . removeThinkingBlocksFromMessages ( history ) ,
new HumanMessage ( query ) ,
] ;
2025-08-02 12:52:14 -06:00
// Initialize agent with the provided focus mode and file context
2025-08-10 17:36:15 -06:00
// Pass the number of messages that will be sent to the LLM so prompts can adapt.
2025-08-11 00:42:16 -06:00
const llmMessagesCount = messagesHistory . length ;
2025-08-12 23:40:23 -06:00
const agent = this . initializeAgent (
focusMode ,
fileIds ,
llmMessagesCount ,
query ,
) ;
2025-08-02 12:52:14 -06:00
2025-07-29 10:18:11 -06:00
// Prepare initial state
const initialState = {
2025-08-11 00:42:16 -06:00
messages : messagesHistory ,
2025-07-29 10:18:11 -06:00
query ,
2025-08-02 12:52:14 -06:00
focusMode ,
fileIds ,
2025-07-29 10:18:11 -06:00
relevantDocuments : [ ] ,
} ;
// Configure the agent run
const config : RunnableConfig = {
configurable : {
thread_id : ` simplified_agent_ ${ Date . now ( ) } ` ,
llm : this.llm ,
embeddings : this.embeddings ,
fileIds ,
systemInstructions : this.systemInstructions ,
personaInstructions : this.personaInstructions ,
2025-08-02 12:52:14 -06:00
focusMode ,
2025-07-29 10:18:11 -06:00
emitter : this.emitter ,
} ,
recursionLimit : 25 , // Allow sufficient iterations for tool use
signal : this.signal ,
2025-08-10 10:05:42 -06:00
. . . getLangfuseCallbacks ( ) ,
2025-07-29 10:18:11 -06:00
} ;
2025-08-03 15:48:34 -06:00
// Use streamEvents to capture both tool calls and token-level streaming
const eventStream = agent . streamEvents ( initialState , {
. . . config ,
version : 'v2' ,
2025-08-10 10:05:42 -06:00
. . . getLangfuseCallbacks ( ) ,
2025-08-03 15:48:34 -06:00
} ) ;
2025-07-29 10:18:11 -06:00
2025-08-03 15:48:34 -06:00
let finalResult : any = null ;
2025-07-29 10:18:11 -06:00
let collectedDocuments : any [ ] = [ ] ;
2025-08-03 15:48:34 -06:00
let currentResponseBuffer = '' ;
2025-08-04 00:41:31 -06:00
let totalUsage = {
input_tokens : 0 ,
output_tokens : 0 ,
total_tokens : 0 ,
} ;
2025-08-03 15:48:34 -06:00
// Process the event stream
for await ( const event of eventStream ) {
// Handle different event types
if (
event . event === 'on_chain_end' &&
event . name === 'RunnableSequence'
) {
finalResult = event . data . output ;
// Collect relevant documents from the final result
if ( finalResult && finalResult . relevantDocuments ) {
collectedDocuments . push ( . . . finalResult . relevantDocuments ) ;
}
}
2025-07-29 10:18:11 -06:00
2025-08-03 15:48:34 -06:00
// Collect sources from tool results
if (
event . event === 'on_chain_end' &&
( event . name . includes ( 'search' ) ||
event . name . includes ( 'Search' ) ||
event . name . includes ( 'tool' ) ||
event . name . includes ( 'Tool' ) )
) {
// Handle LangGraph state updates with relevantDocuments
if ( event . data ? . output && Array . isArray ( event . data . output ) ) {
for ( const item of event . data . output ) {
if (
item . update &&
item . update . relevantDocuments &&
Array . isArray ( item . update . relevantDocuments )
) {
collectedDocuments . push ( . . . item . update . relevantDocuments ) ;
}
}
}
}
// Handle streaming tool calls (for thought messages)
if ( event . event === 'on_chat_model_end' && event . data . output ) {
const output = event . data . output ;
2025-08-04 00:41:31 -06:00
// Collect token usage from chat model end events
if ( output . usage_metadata ) {
const normalized = normalizeUsageMetadata ( output . usage_metadata ) ;
totalUsage . input_tokens += normalized . input_tokens ;
totalUsage . output_tokens += normalized . output_tokens ;
totalUsage . total_tokens += normalized . total_tokens ;
console . log (
'SimplifiedAgent: Collected usage from usage_metadata:' ,
normalized ,
) ;
} else if ( output . response_metadata ? . usage ) {
// Fallback to response_metadata for different model providers
const normalized = normalizeUsageMetadata (
output . response_metadata . usage ,
) ;
totalUsage . input_tokens += normalized . input_tokens ;
totalUsage . output_tokens += normalized . output_tokens ;
totalUsage . total_tokens += normalized . total_tokens ;
console . log (
'SimplifiedAgent: Collected usage from response_metadata:' ,
normalized ,
) ;
}
2025-08-03 15:48:34 -06:00
if (
output . _getType ( ) === 'ai' &&
output . tool_calls &&
output . tool_calls . length > 0
) {
const aiMessage = output as AIMessage ;
// Process each tool call and emit thought messages
for ( const toolCall of aiMessage . tool_calls || [ ] ) {
if ( toolCall && toolCall . name ) {
const toolName = toolCall . name ;
const toolArgs = toolCall . args || { } ;
// Create user-friendly messages for different tools using markdown components
let toolMarkdown = '' ;
switch ( toolName ) {
case 'web_search' :
2025-08-13 23:51:58 -06:00
toolMarkdown = ` <ToolCall type= \ "search \ " query= \ " ${ encodeHtmlAttribute ( toolArgs . query || 'relevant information' ) } \ "></ToolCall> ` ;
2025-08-03 15:48:34 -06:00
break ;
case 'file_search' :
2025-08-13 23:51:58 -06:00
toolMarkdown = ` <ToolCall type= \ "file \ " query= \ " ${ encodeHtmlAttribute ( toolArgs . query || 'relevant information' ) } \ "></ToolCall> ` ;
2025-08-03 15:48:34 -06:00
break ;
case 'url_summarization' :
if ( Array . isArray ( toolArgs . urls ) ) {
toolMarkdown = ` <ToolCall type="url" count=" ${ toolArgs . urls . length } "></ToolCall> ` ;
} else {
toolMarkdown = ` <ToolCall type="url" count="1"></ToolCall> ` ;
}
break ;
2025-08-13 23:51:58 -06:00
case 'image_search' :
toolMarkdown = ` <ToolCall type= \ "image \ " query= \ " ${ encodeHtmlAttribute ( toolArgs . query || 'relevant images' ) } \ "></ToolCall> ` ;
break ;
2025-08-03 15:48:34 -06:00
default :
toolMarkdown = ` <ToolCall type=" ${ toolName } "></ToolCall> ` ;
}
// Emit the thought message
this . emitter . emit (
'data' ,
JSON . stringify ( {
type : 'tool_call' ,
data : {
// messageId: crypto.randomBytes(7).toString('hex'),
content : toolMarkdown ,
} ,
} ) ,
) ;
}
}
}
}
2025-08-04 00:41:31 -06:00
// Handle LLM end events for token usage tracking
if ( event . event === 'on_llm_end' && event . data . output ) {
const output = event . data . output ;
// Collect token usage from LLM end events
if ( output . llmOutput ? . tokenUsage ) {
const normalized = normalizeUsageMetadata (
output . llmOutput . tokenUsage ,
) ;
totalUsage . input_tokens += normalized . input_tokens ;
totalUsage . output_tokens += normalized . output_tokens ;
totalUsage . total_tokens += normalized . total_tokens ;
console . log (
'SimplifiedAgent: Collected usage from llmOutput:' ,
normalized ,
) ;
}
}
2025-08-03 15:48:34 -06:00
// Handle token-level streaming for the final response
if ( event . event === 'on_chat_model_stream' && event . data . chunk ) {
const chunk = event . data . chunk ;
if ( chunk . content && typeof chunk . content === 'string' ) {
// Add the token to our buffer
currentResponseBuffer += chunk . content ;
// Emit the individual token
this . emitter . emit (
'data' ,
JSON . stringify ( {
type : 'response' ,
data : chunk.content ,
} ) ,
) ;
}
}
2025-07-29 10:18:11 -06:00
}
2025-08-03 15:48:34 -06:00
// Emit the final sources used for the response
if ( collectedDocuments . length > 0 ) {
this . emitter . emit (
'data' ,
JSON . stringify ( {
type : 'sources' ,
data : collectedDocuments ,
searchQuery : '' ,
searchUrl : '' ,
} ) ,
) ;
}
2025-07-29 10:18:11 -06:00
2025-08-03 15:48:34 -06:00
// If we didn't get any streamed tokens but have a final result, emit it
2025-07-29 10:18:11 -06:00
if (
2025-08-03 15:48:34 -06:00
currentResponseBuffer === '' &&
2025-07-29 10:18:11 -06:00
finalResult &&
finalResult . messages &&
finalResult . messages . length > 0
) {
const finalMessage =
finalResult . messages [ finalResult . messages . length - 1 ] ;
if ( finalMessage && finalMessage . content ) {
2025-08-03 15:48:34 -06:00
console . log ( 'SimplifiedAgent: Emitting complete response (fallback)' ) ;
2025-07-29 10:18:11 -06:00
this . emitter . emit (
'data' ,
JSON . stringify ( {
type : 'response' ,
data : finalMessage.content ,
} ) ,
) ;
}
2025-08-03 15:48:34 -06:00
}
// If we still have no response, emit a fallback message
if (
currentResponseBuffer === '' &&
( ! finalResult ||
! finalResult . messages ||
finalResult . messages . length === 0 )
) {
console . warn ( 'SimplifiedAgent: No valid response found' ) ;
2025-07-29 10:18:11 -06:00
this . emitter . emit (
'data' ,
JSON . stringify ( {
type : 'response' ,
2025-08-03 15:48:34 -06:00
data : 'I apologize, but I was unable to generate a complete response to your query. Please try rephrasing your question or providing more specific details.' ,
2025-07-29 10:18:11 -06:00
} ) ,
) ;
}
// Emit model stats and end signal after streaming is complete
const modelName = getModelName ( this . llm ) ;
2025-08-04 00:41:31 -06:00
console . log ( 'SimplifiedAgent: Total usage collected:' , totalUsage ) ;
2025-07-29 10:18:11 -06:00
this . emitter . emit (
'stats' ,
JSON . stringify ( {
type : 'modelStats' ,
2025-08-04 00:41:31 -06:00
data : {
modelName ,
usage : totalUsage ,
} ,
2025-07-29 10:18:11 -06:00
} ) ,
) ;
this . emitter . emit ( 'end' ) ;
} catch ( error : any ) {
console . error ( 'SimplifiedAgent: Error during search and answer:' , error ) ;
// Handle specific error types
2025-08-03 15:48:34 -06:00
if ( error . name === 'AbortError' || this . signal . aborted ) {
2025-07-29 10:18:11 -06:00
console . warn ( 'SimplifiedAgent: Operation was aborted' ) ;
this . emitter . emit (
'data' ,
JSON . stringify ( {
type : 'response' ,
data : 'The search operation was cancelled.' ,
} ) ,
) ;
} else {
// General error handling
this . emitter . emit (
'data' ,
JSON . stringify ( {
type : 'response' ,
data : 'I encountered an error while processing your request. Please try rephrasing your query or contact support if the issue persists.' ,
} ) ,
) ;
}
this . emitter . emit ( 'end' ) ;
}
}
/ * *
* Get current configuration info
* /
getInfo ( ) : object {
return {
systemInstructions : ! ! this . systemInstructions ,
personaInstructions : ! ! this . personaInstructions ,
} ;
}
}