(question: string,
tokenBudget: number = 1_000_000,
maxBadAttempts: number = 3,
existingContext?: Partial<TrackerContext>,
historyMessages?: Array<CoreAssistantMessage | CoreUserMessage>
)
| 286 | |
| 287 | |
| 288 | export async function getResponse(question: string, |
| 289 | tokenBudget: number = 1_000_000, |
| 290 | maxBadAttempts: number = 3, |
| 291 | existingContext?: Partial<TrackerContext>, |
| 292 | historyMessages?: Array<CoreAssistantMessage | CoreUserMessage> |
| 293 | ): Promise<{ result: StepAction; context: TrackerContext }> { |
| 294 | const context: TrackerContext = { |
| 295 | tokenTracker: existingContext?.tokenTracker || new TokenTracker(tokenBudget), |
| 296 | actionTracker: existingContext?.actionTracker || new ActionTracker() |
| 297 | }; |
| 298 | let step = 0; |
| 299 | let totalStep = 0; |
| 300 | let badAttempts = 0; |
| 301 | let schema: ZodObject<any> = getSchema(true, true, true, true) |
| 302 | const gaps: string[] = [question.trim()]; // All questions to be answered including the orginal question |
| 303 | const allQuestions = [question.trim()]; |
| 304 | const allKeywords = []; |
| 305 | const allKnowledge = []; // knowledge are intermedidate questions that are answered |
| 306 | // iterate over historyMessages |
| 307 | // if role is user and content is question, add to allQuestions, the next assistant content should be the answer |
| 308 | // put this pair to the allKnowledge |
| 309 | historyMessages?.forEach((message, i) => { |
| 310 | if (message.role === 'user' && message.content && historyMessages[i + 1]?.role === 'assistant') { |
| 311 | allQuestions.push(message.content as string) |
| 312 | allKnowledge.push({ |
| 313 | question: message.content, |
| 314 | answer: historyMessages[i + 1]?.content || '', |
| 315 | type: 'history-qa' |
| 316 | }); |
| 317 | } |
| 318 | }) |
| 319 | |
| 320 | const badContext = []; |
| 321 | let diaryContext = []; |
| 322 | let allowAnswer = true; |
| 323 | let allowSearch = true; |
| 324 | let allowRead = true; |
| 325 | let allowReflect = true; |
| 326 | let prompt = ''; |
| 327 | let thisStep: StepAction = {action: 'answer', answer: '', references: [], think: ''}; |
| 328 | let isAnswered = false; |
| 329 | |
| 330 | const allURLs: Record<string, string> = {}; |
| 331 | const visitedURLs: string[] = []; |
| 332 | const evaluationMetrics: Record<string, any[]> = {}; |
| 333 | while (context.tokenTracker.getTotalUsage().totalTokens < tokenBudget && badAttempts <= maxBadAttempts) { |
| 334 | // add 1s delay to avoid rate limiting |
| 335 | await sleep(STEP_SLEEP); |
| 336 | step++; |
| 337 | totalStep++; |
| 338 | const budgetPercentage = (context.tokenTracker.getTotalUsage().totalTokens / tokenBudget * 100).toFixed(2); |
| 339 | console.log(`Step ${totalStep} / Budget used ${budgetPercentage}%`); |
| 340 | console.log('Gaps:', gaps); |
| 341 | allowReflect = allowReflect && (gaps.length <= 1); |
| 342 | const currentQuestion = gaps.length > 0 ? gaps.shift()! : question.trim(); |
| 343 | if (!evaluationMetrics[currentQuestion]) { |
| 344 | evaluationMetrics[currentQuestion] = await evaluateQuestion(currentQuestion, context.tokenTracker) |
| 345 | } |
no test coverage detected