feat: support history messages

This commit is contained in:
Han Xiao
2025-02-12 12:42:51 +08:00
parent 57fb65d0c6
commit 16a2f60fec
3 changed files with 31 additions and 18 deletions

View File

@@ -1,5 +1,5 @@
import {z, ZodObject} from 'zod'; import {z, ZodObject} from 'zod';
import {generateObject} from 'ai'; import {CoreAssistantMessage, CoreUserMessage, generateObject} from 'ai';
import {getModel, getMaxTokens, SEARCH_PROVIDER, STEP_SLEEP} from "./config"; import {getModel, getMaxTokens, SEARCH_PROVIDER, STEP_SLEEP} from "./config";
import {readUrl, removeAllLineBreaks} from "./tools/read"; import {readUrl, removeAllLineBreaks} from "./tools/read";
import {handleGenerateObjectError} from './utils/error-handling'; import {handleGenerateObjectError} from './utils/error-handling';
@@ -16,7 +16,7 @@ import {StepAction, AnswerAction} from "./types";
import {TrackerContext} from "./types"; import {TrackerContext} from "./types";
import {search} from "./tools/jina-search"; import {search} from "./tools/jina-search";
// import {grounding} from "./tools/grounding"; // import {grounding} from "./tools/grounding";
import { zodToJsonSchema } from "zod-to-json-schema"; import {zodToJsonSchema} from "zod-to-json-schema";
async function sleep(ms: number) { async function sleep(ms: number) {
const seconds = Math.ceil(ms / 1000); const seconds = Math.ceil(ms / 1000);
@@ -93,7 +93,7 @@ function getPrompt(
// Add header section // Add header section
sections.push(`Current date: ${new Date().toUTCString()} sections.push(`Current date: ${new Date().toUTCString()}
You are an advanced AI research analyst specializing in multi-step reasoning. Using your training data and prior lessons learned, answer the following question with absolute certainty: You are an advanced AI research agent from Jina AI. You are specialized in multistep reasoning. Using your training data and prior lessons learned, answer the following question with absolute certainty:
<question> <question>
${question} ${question}
@@ -280,15 +280,17 @@ function updateContext(step: any) {
} }
function removeHTMLtags(text: string) { function removeHTMLtags(text: string) {
return text.replace(/<[^>]*>?/gm, ''); return text.replace(/<[^>]*>?/gm, '');
} }
export async function getResponse(question: string, tokenBudget: number = 1_000_000, export async function getResponse(question: string,
tokenBudget: number = 1_000_000,
maxBadAttempts: number = 3, maxBadAttempts: number = 3,
existingContext?: Partial<TrackerContext>): Promise<{ result: StepAction; context: TrackerContext }> { existingContext?: Partial<TrackerContext>,
historyMessages?: Array<CoreAssistantMessage | CoreUserMessage>
): Promise<{ result: StepAction; context: TrackerContext }> {
const context: TrackerContext = { const context: TrackerContext = {
tokenTracker: existingContext?.tokenTracker || new TokenTracker(tokenBudget), tokenTracker: existingContext?.tokenTracker || new TokenTracker(tokenBudget),
actionTracker: existingContext?.actionTracker || new ActionTracker() actionTracker: existingContext?.actionTracker || new ActionTracker()
@@ -301,6 +303,20 @@ export async function getResponse(question: string, tokenBudget: number = 1_000_
const allQuestions = [question]; const allQuestions = [question];
const allKeywords = []; const allKeywords = [];
const allKnowledge = []; // knowledge are intermedidate questions that are answered const allKnowledge = []; // knowledge are intermedidate questions that are answered
// iterate over historyMessages
// if role is user and content is question, add to allQuestions, the next assistant content should be the answer
// put this pair to the allKnowledge
historyMessages?.forEach((message, i) => {
if (message.role === 'user' && message.content && historyMessages[i + 1]?.role === 'assistant') {
allQuestions.push(message.content as string)
allKnowledge.push({
question: message.content,
answer: historyMessages[i + 1]?.content || '',
type: 'history-qa'
});
}
})
const badContext = []; const badContext = [];
let diaryContext = []; let diaryContext = [];
let allowAnswer = true; let allowAnswer = true;
@@ -448,9 +464,9 @@ ${evaluation.think}
}); });
if (errorAnalysis.questionsToAnswer) { if (errorAnalysis.questionsToAnswer) {
gaps.push(...errorAnalysis.questionsToAnswer.slice(0, 2)); gaps.push(...errorAnalysis.questionsToAnswer.slice(0, 2));
allQuestions.push(...errorAnalysis.questionsToAnswer.slice(0, 2)); allQuestions.push(...errorAnalysis.questionsToAnswer.slice(0, 2));
gaps.push(question); // always keep the original question in the gaps gaps.push(question); // always keep the original question in the gaps
} }
badAttempts++; badAttempts++;

View File

@@ -104,12 +104,12 @@ function getTokenBudgetAndMaxAttempts(
switch (reasoningEffort) { switch (reasoningEffort) {
case 'low': case 'low':
return {tokenBudget: 500000, maxBadAttempts: 2}; return {tokenBudget: 100000, maxBadAttempts: 1};
case 'high': case 'high':
return {tokenBudget: 2000000, maxBadAttempts: 4}; return {tokenBudget: 1000000, maxBadAttempts: 3};
case 'medium': case 'medium':
default: default:
return {tokenBudget: 1000000, maxBadAttempts: 3}; return {tokenBudget: 500000, maxBadAttempts: 2};
} }
} }
@@ -357,7 +357,7 @@ app.post('/v1/chat/completions', (async (req: Request, res: Response) => {
} }
try { try {
const {result} = await getResponse(lastMessage.content, tokenBudget, maxBadAttempts, context) const {result} = await getResponse(lastMessage.content as string, tokenBudget, maxBadAttempts, context, body.messages)
const usage = context.tokenTracker.getTotalUsageSnakeCase(); const usage = context.tokenTracker.getTotalUsageSnakeCase();
if (body.stream) { if (body.stream) {

View File

@@ -1,5 +1,5 @@
// Action Types // Action Types
import {LanguageModelUsage} from "ai"; import {CoreAssistantMessage, CoreUserMessage, LanguageModelUsage} from "ai";
type BaseAction = { type BaseAction = {
action: "search" | "answer" | "reflect" | "visit"; action: "search" | "answer" | "reflect" | "visit";
@@ -156,10 +156,7 @@ export interface Model {
export interface ChatCompletionRequest { export interface ChatCompletionRequest {
model: string; model: string;
messages: Array<{ messages: Array<CoreUserMessage | CoreAssistantMessage>;
role: string;
content: string;
}>;
stream?: boolean; stream?: boolean;
reasoning_effort?: 'low' | 'medium' | 'high' | null; reasoning_effort?: 'low' | 'medium' | 'high' | null;
max_completion_tokens?: number | null; max_completion_tokens?: number | null;