mirror of
https://github.com/eliasstepanik/core.git
synced 2026-01-11 09:18:26 +00:00
* Feat: add deep search api * Feat: deep search agent * fix: stream utils for deep search * fix: deep search --------- Co-authored-by: Manoj <saimanoj58@gmail.com>
86 lines
2.6 KiB
TypeScript
86 lines
2.6 KiB
TypeScript
import { metadata, task } from "@trigger.dev/sdk";
|
|
import { type CoreMessage } from "ai";
|
|
import { logger } from "@trigger.dev/sdk/v3";
|
|
import { nanoid } from "nanoid";
|
|
import {
|
|
deletePersonalAccessToken,
|
|
getOrCreatePersonalAccessToken,
|
|
} from "../utils/utils";
|
|
import { getReActPrompt } from "./prompt";
|
|
import { type DeepSearchPayload, type DeepSearchResponse } from "./types";
|
|
import { createSearchMemoryTool } from "./utils";
|
|
import { run } from "./deep-search-utils";
|
|
import { AgentMessageType } from "../chat/types";
|
|
|
|
export const deepSearch = task({
|
|
id: "deep-search",
|
|
maxDuration: 3000,
|
|
run: async (payload: DeepSearchPayload): Promise<DeepSearchResponse> => {
|
|
const { content, userId, stream, metadata: meta, intentOverride } = payload;
|
|
|
|
const randomKeyName = `deepSearch_${nanoid(10)}`;
|
|
|
|
// Get or create token for search API calls
|
|
const pat = await getOrCreatePersonalAccessToken({
|
|
name: randomKeyName,
|
|
userId: userId as string,
|
|
});
|
|
|
|
if (!pat?.token) {
|
|
throw new Error("Failed to create personal access token");
|
|
}
|
|
|
|
try {
|
|
// Create search tool that agent will use
|
|
const searchTool = createSearchMemoryTool(pat.token);
|
|
|
|
// Build initial messages with ReAct prompt
|
|
const initialMessages: CoreMessage[] = [
|
|
{
|
|
role: "system",
|
|
content: getReActPrompt(meta, intentOverride),
|
|
},
|
|
{
|
|
role: "user",
|
|
content: `CONTENT TO ANALYZE:\n${content}\n\nPlease search my memory for relevant context and synthesize what you find.`,
|
|
},
|
|
];
|
|
|
|
// Run the ReAct loop generator
|
|
const llmResponse = run(initialMessages, searchTool);
|
|
|
|
// Streaming mode: stream via metadata.stream like chat.ts does
|
|
// This makes all message types available to clients in real-time
|
|
const messageStream = await metadata.stream("messages", llmResponse);
|
|
|
|
let synthesis = "";
|
|
|
|
for await (const step of messageStream) {
|
|
// MESSAGE_CHUNK: Final synthesis - accumulate and stream
|
|
if (step.type === AgentMessageType.MESSAGE_CHUNK) {
|
|
synthesis += step.message;
|
|
}
|
|
|
|
// STREAM_END: Loop completed
|
|
if (step.type === AgentMessageType.STREAM_END) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
await deletePersonalAccessToken(pat?.id);
|
|
|
|
// Clean up any remaining tags
|
|
synthesis = synthesis
|
|
.replace(/<final_response>/gi, "")
|
|
.replace(/<\/final_response>/gi, "")
|
|
.trim();
|
|
|
|
return { synthesis };
|
|
} catch (error) {
|
|
await deletePersonalAccessToken(pat?.id);
|
|
logger.error(`Deep search error: ${error}`);
|
|
throw error;
|
|
}
|
|
},
|
|
});
|