mulmocast 0.0.15 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/assets/templates/text_and_image.json +6 -0
  2. package/assets/templates/text_only.json +6 -0
  3. package/lib/actions/audio.d.ts +4 -2
  4. package/lib/actions/audio.js +89 -48
  5. package/lib/actions/captions.d.ts +1 -1
  6. package/lib/actions/captions.js +17 -14
  7. package/lib/actions/images.d.ts +6 -3
  8. package/lib/actions/images.js +64 -39
  9. package/lib/actions/movie.js +19 -19
  10. package/lib/actions/pdf.js +3 -4
  11. package/lib/actions/translate.js +11 -11
  12. package/lib/agents/add_bgm_agent.js +3 -3
  13. package/lib/agents/combine_audio_files_agent.js +88 -42
  14. package/lib/agents/index.d.ts +2 -1
  15. package/lib/agents/index.js +2 -1
  16. package/lib/agents/tavily_agent.d.ts +15 -0
  17. package/lib/agents/tavily_agent.js +130 -0
  18. package/lib/cli/commands/audio/builder.d.ts +2 -0
  19. package/lib/cli/commands/image/builder.d.ts +2 -0
  20. package/lib/cli/commands/movie/builder.d.ts +2 -0
  21. package/lib/cli/commands/movie/handler.js +1 -6
  22. package/lib/cli/commands/pdf/builder.d.ts +2 -0
  23. package/lib/cli/commands/translate/builder.d.ts +2 -0
  24. package/lib/cli/common.d.ts +2 -0
  25. package/lib/cli/common.js +6 -0
  26. package/lib/cli/helpers.d.ts +7 -1
  27. package/lib/cli/helpers.js +30 -3
  28. package/lib/methods/index.d.ts +1 -1
  29. package/lib/methods/index.js +1 -1
  30. package/lib/methods/mulmo_presentation_style.d.ts +14 -0
  31. package/lib/methods/mulmo_presentation_style.js +70 -0
  32. package/lib/methods/mulmo_studio_context.d.ts +17 -0
  33. package/lib/methods/mulmo_studio_context.js +30 -2
  34. package/lib/tools/deep_research.d.ts +2 -0
  35. package/lib/tools/deep_research.js +265 -0
  36. package/lib/types/index.d.ts +0 -1
  37. package/lib/types/index.js +0 -1
  38. package/lib/types/schema.d.ts +101 -55
  39. package/lib/types/schema.js +3 -3
  40. package/lib/types/type.d.ts +5 -1
  41. package/lib/utils/ffmpeg_utils.d.ts +1 -0
  42. package/lib/utils/ffmpeg_utils.js +10 -0
  43. package/lib/utils/file.d.ts +7 -4
  44. package/lib/utils/file.js +24 -12
  45. package/lib/utils/preprocess.d.ts +0 -9
  46. package/lib/utils/preprocess.js +4 -10
  47. package/lib/utils/prompt.d.ts +3 -0
  48. package/lib/utils/prompt.js +52 -0
  49. package/package.json +11 -10
  50. package/assets/music/StarsBeyondEx.mp3 +0 -0
@@ -1,4 +1,4 @@
1
- import type { MulmoScript, MulmoStudioContext } from "../types/type.js";
1
+ import type { MulmoScript, MulmoStudioContext, MulmoPresentationStyle, MulmoStudioMultiLingual } from "../types/type.js";
2
2
  import type { CliArgs } from "../types/cli_types.js";
3
3
  export declare const setGraphAILogger: (verbose: boolean | undefined, logValues?: Record<string, unknown>) => void;
4
4
  export interface FileObject {
@@ -11,6 +11,8 @@ export interface FileObject {
11
11
  isHttpPath: boolean;
12
12
  fileOrUrl: string;
13
13
  outputStudioFilePath: string;
14
+ outputMultilingualFilePath: string;
15
+ presentationStylePath: string | undefined;
14
16
  fileName: string;
15
17
  }
16
18
  export declare const getFileObject: (args: {
@@ -18,9 +20,12 @@ export declare const getFileObject: (args: {
18
20
  outdir?: string;
19
21
  imagedir?: string;
20
22
  audiodir?: string;
23
+ presentationStyle?: string;
21
24
  file: string;
22
25
  }) => FileObject;
23
26
  export declare const fetchScript: (isHttpPath: boolean, mulmoFilePath: string, fileOrUrl: string) => Promise<MulmoScript | null>;
27
+ export declare const getMultiLingual: (multilingualFilePath: string, beatsLength: number) => MulmoStudioMultiLingual;
28
+ export declare const getPresentationStyle: (presentationStylePath: string | undefined) => MulmoPresentationStyle | null;
24
29
  type InitOptions = {
25
30
  b?: string;
26
31
  o?: string;
@@ -29,6 +34,7 @@ type InitOptions = {
29
34
  file?: string;
30
35
  l?: string;
31
36
  c?: string;
37
+ p?: string;
32
38
  };
33
39
  export declare const initializeContext: (argv: CliArgs<InitOptions>) => Promise<MulmoStudioContext | null>;
34
40
  export declare const runTranslateIfNeeded: (context: MulmoStudioContext, argv: {
@@ -2,11 +2,12 @@ import { GraphAILogger } from "graphai";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
4
  import clipboardy from "clipboardy";
5
- import { getBaseDirPath, getFullPath, readMulmoScriptFile, fetchMulmoScriptFile, getOutputStudioFilePath, resolveDirPath, mkdir } from "../utils/file.js";
5
+ import { getBaseDirPath, getFullPath, readMulmoScriptFile, fetchMulmoScriptFile, getOutputStudioFilePath, resolveDirPath, mkdir, getOutputMultilingualFilePath, } from "../utils/file.js";
6
6
  import { isHttp } from "../utils/utils.js";
7
7
  import { createOrUpdateStudioData } from "../utils/preprocess.js";
8
8
  import { outDirName, imageDirName, audioDirName } from "../utils/const.js";
9
9
  import { translate } from "../actions/translate.js";
10
+ import { mulmoPresentationStyleSchema, mulmoStudioMultiLingualSchema } from "../types/schema.js";
10
11
  export const setGraphAILogger = (verbose, logValues) => {
11
12
  if (verbose) {
12
13
  if (logValues) {
@@ -22,7 +23,7 @@ export const setGraphAILogger = (verbose, logValues) => {
22
23
  }
23
24
  };
24
25
  export const getFileObject = (args) => {
25
- const { basedir, outdir, imagedir, audiodir, file } = args;
26
+ const { basedir, outdir, imagedir, audiodir, file, presentationStyle } = args;
26
27
  const baseDirPath = getBaseDirPath(basedir);
27
28
  const outDirPath = getFullPath(baseDirPath, outdir ?? outDirName);
28
29
  const { fileOrUrl, fileName } = (() => {
@@ -47,6 +48,8 @@ export const getFileObject = (args) => {
47
48
  const imageDirPath = getFullPath(outDirPath, imagedir ?? imageDirName);
48
49
  const audioDirPath = getFullPath(outDirPath, audiodir ?? audioDirName);
49
50
  const outputStudioFilePath = getOutputStudioFilePath(outDirPath, fileName);
51
+ const outputMultilingualFilePath = getOutputMultilingualFilePath(outDirPath, fileName);
52
+ const presentationStylePath = presentationStyle ? getFullPath(baseDirPath, presentationStyle) : undefined;
50
53
  return {
51
54
  baseDirPath,
52
55
  mulmoFilePath,
@@ -57,6 +60,8 @@ export const getFileObject = (args) => {
57
60
  isHttpPath,
58
61
  fileOrUrl,
59
62
  outputStudioFilePath,
63
+ outputMultilingualFilePath,
64
+ presentationStylePath,
60
65
  fileName,
61
66
  };
62
67
  };
@@ -75,15 +80,33 @@ export const fetchScript = async (isHttpPath, mulmoFilePath, fileOrUrl) => {
75
80
  }
76
81
  return readMulmoScriptFile(mulmoFilePath, "ERROR: File does not exist " + mulmoFilePath)?.mulmoData ?? null;
77
82
  };
83
+ export const getMultiLingual = (multilingualFilePath, beatsLength) => {
84
+ if (fs.existsSync(multilingualFilePath)) {
85
+ const jsonData = readMulmoScriptFile(multilingualFilePath, "ERROR: File does not exist " + multilingualFilePath)?.mulmoData ?? null;
86
+ return mulmoStudioMultiLingualSchema.parse(jsonData);
87
+ }
88
+ return [...Array(beatsLength)].map(() => ({ multiLingualTexts: {} }));
89
+ };
90
+ export const getPresentationStyle = (presentationStylePath) => {
91
+ if (presentationStylePath) {
92
+ if (!fs.existsSync(presentationStylePath)) {
93
+ throw new Error(`ERROR: File not exists ${presentationStylePath}`);
94
+ }
95
+ const jsonData = readMulmoScriptFile(presentationStylePath, "ERROR: File does not exist " + presentationStylePath)?.mulmoData ?? null;
96
+ return mulmoPresentationStyleSchema.parse(jsonData);
97
+ }
98
+ return null;
99
+ };
78
100
  export const initializeContext = async (argv) => {
79
101
  const files = getFileObject({
80
102
  basedir: argv.b,
81
103
  outdir: argv.o,
82
104
  imagedir: argv.i,
83
105
  audiodir: argv.a,
106
+ presentationStyle: argv.p,
84
107
  file: argv.file ?? "",
85
108
  });
86
- const { fileName, isHttpPath, fileOrUrl, mulmoFilePath, outputStudioFilePath } = files;
109
+ const { fileName, isHttpPath, fileOrUrl, mulmoFilePath, outputStudioFilePath, presentationStylePath, outputMultilingualFilePath } = files;
87
110
  setGraphAILogger(argv.v, {
88
111
  files,
89
112
  });
@@ -91,6 +114,8 @@ export const initializeContext = async (argv) => {
91
114
  if (!mulmoScript) {
92
115
  return null;
93
116
  }
117
+ const presentationStyle = getPresentationStyle(presentationStylePath);
118
+ const multiLingual = getMultiLingual(outputMultilingualFilePath, mulmoScript.beats.length);
94
119
  // Create or update MulmoStudio file with MulmoScript
95
120
  const currentStudio = readMulmoScriptFile(outputStudioFilePath);
96
121
  try {
@@ -120,6 +145,8 @@ export const initializeContext = async (argv) => {
120
145
  caption: {},
121
146
  },
122
147
  },
148
+ presentationStyle: presentationStyle ?? studio.script,
149
+ multiLingual,
123
150
  };
124
151
  }
125
152
  catch (error) {
@@ -1,4 +1,4 @@
1
- export * from "./mulmo_script.js";
1
+ export * from "./mulmo_presentation_style.js";
2
2
  export * from "./mulmo_script_template.js";
3
3
  export * from "./mulmo_studio_context.js";
4
4
  export * from "./mulmo_media_source.js";
@@ -1,4 +1,4 @@
1
- export * from "./mulmo_script.js";
1
+ export * from "./mulmo_presentation_style.js";
2
2
  export * from "./mulmo_script_template.js";
3
3
  export * from "./mulmo_studio_context.js";
4
4
  export * from "./mulmo_media_source.js";
@@ -0,0 +1,14 @@
1
+ import "dotenv/config";
2
+ import { MulmoCanvasDimension, MulmoBeat, SpeechOptions, Text2SpeechProvider, Text2ImageAgentInfo, BeatMediaType, MulmoPresentationStyle, SpeakerData } from "../types/index.js";
3
+ export declare const MulmoPresentationStyleMethods: {
4
+ getCanvasSize(presentationStyle: MulmoPresentationStyle): MulmoCanvasDimension;
5
+ getSpeechProvider(presentationStyle: MulmoPresentationStyle): Text2SpeechProvider;
6
+ getAllSpeechProviders(presentationStyle: MulmoPresentationStyle): Set<Text2SpeechProvider>;
7
+ getTextSlideStyle(presentationStyle: MulmoPresentationStyle, beat: MulmoBeat): string;
8
+ getSpeechOptions(presentationStyle: MulmoPresentationStyle, beat: MulmoBeat): SpeechOptions | undefined;
9
+ getSpeaker(presentationStyle: MulmoPresentationStyle, beat: MulmoBeat): SpeakerData;
10
+ getProvider(presentationStyle: MulmoPresentationStyle, beat: MulmoBeat): Text2SpeechProvider;
11
+ getVoiceId(presentationStyle: MulmoPresentationStyle, beat: MulmoBeat): string;
12
+ getImageAgentInfo(presentationStyle: MulmoPresentationStyle, dryRun?: boolean): Text2ImageAgentInfo;
13
+ getImageType(_: MulmoPresentationStyle, beat: MulmoBeat): BeatMediaType;
14
+ };
@@ -0,0 +1,70 @@
1
+ import "dotenv/config";
2
+ import { text2ImageProviderSchema, text2SpeechProviderSchema, mulmoCanvasDimensionSchema } from "../types/schema.js";
3
+ const defaultTextSlideStyles = [
4
+ '*,*::before,*::after{box-sizing:border-box}body,h1,h2,h3,h4,p,figure,blockquote,dl,dd{margin:0}ul[role="list"],ol[role="list"]{list-style:none}html:focus-within{scroll-behavior:smooth}body{min-height:100vh;text-rendering:optimizeSpeed;line-height:1.5}a:not([class]){text-decoration-skip-ink:auto}img,picture{max-width:100%;display:block}input,button,textarea,select{font:inherit}@media(prefers-reduced-motion:reduce){html:focus-within{scroll-behavior:auto}*,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important;scroll-behavior:auto !important}}',
5
+ "body { margin: 60px; margin-top: 40px; color:#333; font-size: 30px; font-family: Arial, sans-serif; box-sizing: border-box; height: 100vh }",
6
+ "h1 { font-size: 56px; margin-bottom: 20px; text-align: center }",
7
+ "h2 { font-size: 48px; text-align: center }",
8
+ "h3 { font-size: 36px }",
9
+ "ul { margin-left: 40px } ",
10
+ "pre { background: #eeeecc; font-size: 16px; padding:4px }",
11
+ "p { margin-left: 40px }",
12
+ "table { font-size: 36px; margin: auto; border: 1px solid gray; border-collapse: collapse }",
13
+ "th { border-bottom: 1px solid gray }",
14
+ "td, th { padding: 8px }",
15
+ "tr:nth-child(even) { background-color: #eee }",
16
+ ];
17
+ export const MulmoPresentationStyleMethods = {
18
+ getCanvasSize(presentationStyle) {
19
+ return mulmoCanvasDimensionSchema.parse(presentationStyle.canvasSize);
20
+ },
21
+ getSpeechProvider(presentationStyle) {
22
+ return text2SpeechProviderSchema.parse(presentationStyle.speechParams?.provider);
23
+ },
24
+ getAllSpeechProviders(presentationStyle) {
25
+ const providers = new Set();
26
+ const defaultProvider = this.getSpeechProvider(presentationStyle);
27
+ Object.values(presentationStyle.speechParams.speakers).forEach((speaker) => {
28
+ const provider = speaker.provider ?? defaultProvider;
29
+ providers.add(provider);
30
+ });
31
+ return providers;
32
+ },
33
+ getTextSlideStyle(presentationStyle, beat) {
34
+ const styles = presentationStyle.textSlideParams?.cssStyles ?? [];
35
+ // NOTES: Taking advantage of CSS override rule (you can redefine it to override)
36
+ const extraStyles = beat.textSlideParams?.cssStyles ?? [];
37
+ // This code allows us to support both string and array of strings for cssStyles
38
+ return [...defaultTextSlideStyles, ...[styles], ...[extraStyles]].flat().join("\n");
39
+ },
40
+ getSpeechOptions(presentationStyle, beat) {
41
+ return { ...presentationStyle.speechParams.speakers[beat.speaker].speechOptions, ...beat.speechOptions };
42
+ },
43
+ getSpeaker(presentationStyle, beat) {
44
+ return presentationStyle.speechParams.speakers[beat.speaker];
45
+ },
46
+ getProvider(presentationStyle, beat) {
47
+ const speaker = MulmoPresentationStyleMethods.getSpeaker(presentationStyle, beat);
48
+ return speaker.provider ?? presentationStyle.speechParams.provider;
49
+ },
50
+ getVoiceId(presentationStyle, beat) {
51
+ const speaker = MulmoPresentationStyleMethods.getSpeaker(presentationStyle, beat);
52
+ return speaker.voiceId;
53
+ },
54
+ getImageAgentInfo(presentationStyle, dryRun = false) {
55
+ // Notice that we copy imageParams from presentationStyle and update
56
+ // provider and model appropriately.
57
+ const provider = text2ImageProviderSchema.parse(presentationStyle.imageParams?.provider);
58
+ const defaultImageParams = {
59
+ model: provider === "openai" ? process.env.DEFAULT_OPENAI_IMAGE_MODEL : undefined,
60
+ };
61
+ return {
62
+ provider,
63
+ agent: dryRun ? "mediaMockAgent" : provider === "google" ? "imageGoogleAgent" : "imageOpenaiAgent",
64
+ imageParams: { ...defaultImageParams, ...presentationStyle.imageParams },
65
+ };
66
+ },
67
+ getImageType(_, beat) {
68
+ return beat.image?.type == "movie" ? "movie" : "image";
69
+ },
70
+ };
@@ -1,8 +1,25 @@
1
1
  import { MulmoStudioContext } from "../types/index.js";
2
2
  type SessionType = "audio" | "image" | "video" | "multiLingual" | "caption" | "pdf";
3
3
  type BeatSessionType = "audio" | "image" | "multiLingual" | "caption" | "movie";
4
+ type SessionProgressEvent = {
5
+ kind: "session";
6
+ sessionType: SessionType;
7
+ inSession: boolean;
8
+ } | {
9
+ kind: "beat";
10
+ sessionType: BeatSessionType;
11
+ index: number;
12
+ inSession: boolean;
13
+ };
14
+ type SessionProgressCallback = (change: SessionProgressEvent) => void;
15
+ export declare const addSessionProgressCallback: (cb: SessionProgressCallback) => void;
16
+ export declare const removeSessionProgressCallback: (cb: SessionProgressCallback) => void;
4
17
  export declare const MulmoStudioContextMethods: {
5
18
  resolveAssetPath(context: MulmoStudioContext, relativePath: string): string;
19
+ getAudioDirPath(context: MulmoStudioContext): string;
20
+ getImageDirPath(context: MulmoStudioContext): string;
21
+ getImageProjectDirPath(context: MulmoStudioContext): string;
22
+ getOutDirPath(context: MulmoStudioContext): string;
6
23
  setSessionState(context: MulmoStudioContext, sessionType: SessionType, value: boolean): void;
7
24
  setBeatSessionState(context: MulmoStudioContext, sessionType: BeatSessionType, index: number, value: boolean): void;
8
25
  };
@@ -1,17 +1,45 @@
1
1
  import path from "path";
2
2
  import { GraphAILogger } from "graphai";
3
+ const sessionProgressCallbacks = new Set();
4
+ export const addSessionProgressCallback = (cb) => {
5
+ sessionProgressCallbacks.add(cb);
6
+ };
7
+ export const removeSessionProgressCallback = (cb) => {
8
+ sessionProgressCallbacks.delete(cb);
9
+ };
3
10
  const notifyStateChange = (context, sessionType) => {
4
- const prefix = context.sessionState.inSession[sessionType] ? "<" : " >";
11
+ const inSession = context.sessionState.inSession[sessionType] ?? false;
12
+ const prefix = inSession ? "<" : " >";
5
13
  GraphAILogger.info(`${prefix} ${sessionType}`);
14
+ for (const callback of sessionProgressCallbacks) {
15
+ callback({ kind: "session", sessionType, inSession });
16
+ }
6
17
  };
7
18
  const notifyBeatStateChange = (context, sessionType, index) => {
8
- const prefix = context.sessionState.inBeatSession[sessionType][index] ? "{" : " }";
19
+ const inSession = context.sessionState.inBeatSession[sessionType][index] ?? false;
20
+ const prefix = inSession ? "{" : " }";
9
21
  GraphAILogger.info(`${prefix} ${sessionType} ${index}`);
22
+ for (const callback of sessionProgressCallbacks) {
23
+ callback({ kind: "beat", sessionType, index, inSession });
24
+ }
10
25
  };
11
26
  export const MulmoStudioContextMethods = {
12
27
  resolveAssetPath(context, relativePath) {
13
28
  return path.resolve(context.fileDirs.mulmoFileDirPath, relativePath);
14
29
  },
30
+ getAudioDirPath(context) {
31
+ return context.fileDirs.audioDirPath;
32
+ },
33
+ getImageDirPath(context) {
34
+ return context.fileDirs.imageDirPath;
35
+ },
36
+ getImageProjectDirPath(context) {
37
+ const imageDirPath = MulmoStudioContextMethods.getImageDirPath(context);
38
+ return `${imageDirPath}/${context.studio.filename}`;
39
+ },
40
+ getOutDirPath(context) {
41
+ return context.fileDirs.outDirPath;
42
+ },
15
43
  setSessionState(context, sessionType, value) {
16
44
  context.sessionState.inSession[sessionType] = value;
17
45
  notifyStateChange(context, sessionType);
@@ -0,0 +1,2 @@
1
+ import "dotenv/config";
2
+ export declare const deepResearch: () => Promise<void>;
@@ -0,0 +1,265 @@
1
+ import "dotenv/config";
2
+ import { GraphAILogger, GraphAI } from "graphai";
3
+ import { textInputAgent } from "@graphai/input_agents";
4
+ import { consoleStreamDataAgentFilter } from "@graphai/stream_agent_filter/node";
5
+ import { openAIAgent } from "@graphai/openai_agent";
6
+ import * as agents from "@graphai/vanilla";
7
+ import tavilySearchAgent from "../agents/tavily_agent.js";
8
+ import { cliLoadingPlugin } from "../utils/plugins.js";
9
+ import { searchQueryPrompt, reflectionPrompt, finalAnswerPrompt } from "../utils/prompt.js";
10
+ const vanillaAgents = agents.default ?? agents;
11
+ const agentHeader = "\x1b[34m● \x1b[0m\x1b[1mAgent\x1b[0m:\x1b[0m";
12
+ const graphData = {
13
+ version: 0.5,
14
+ nodes: {
15
+ maxRetries: {
16
+ value: 0,
17
+ },
18
+ userInput: {
19
+ agent: "textInputAgent",
20
+ params: {
21
+ message: "You:",
22
+ required: true,
23
+ },
24
+ },
25
+ startMessage: {
26
+ agent: "consoleAgent",
27
+ inputs: {
28
+ text: `\n${agentHeader} It takes a few minutes to gather resources, analyze data, and create a report.`,
29
+ userInput: ":userInput.text",
30
+ },
31
+ },
32
+ deepResearch: {
33
+ agent: "nestedAgent",
34
+ inputs: {
35
+ userInput: ":userInput.text",
36
+ maxRetries: ":maxRetries",
37
+ startMessage: ":startMessage",
38
+ },
39
+ graph: {
40
+ loop: {
41
+ while: ":continue",
42
+ },
43
+ nodes: {
44
+ searchResults: {
45
+ value: [],
46
+ update: ":reducer.array",
47
+ },
48
+ followUpQueries: {
49
+ value: [],
50
+ update: ":reflectionAgent.follow_up_queries",
51
+ },
52
+ counter: {
53
+ value: 0,
54
+ update: ":counter.add(1)",
55
+ },
56
+ searchQueryAgent: {
57
+ agent: "openAIAgent",
58
+ inputs: {
59
+ model: "gpt-4o-mini",
60
+ system: "You are a professional research assistant. Based on the user's inquiry, return the search query to be used for the search engine.",
61
+ prompt: searchQueryPrompt("${:userInput}", "${:followUpQueries.join(,)}"),
62
+ },
63
+ params: {
64
+ tool_choice: "auto",
65
+ tools: [
66
+ {
67
+ type: "function",
68
+ function: {
69
+ name: "search_query",
70
+ description: "Return the search queries to be used for the search engine.",
71
+ parameters: {
72
+ type: "object",
73
+ properties: {
74
+ queries: {
75
+ type: "array",
76
+ items: {
77
+ type: "string",
78
+ description: "A search query to be used for the search engine.",
79
+ },
80
+ description: "An array of search queries to be used for the search engine.",
81
+ },
82
+ research_topic: {
83
+ type: "string",
84
+ description: "The topic of the research. This is used to filter the search results.",
85
+ },
86
+ analysis_plan: {
87
+ type: "string",
88
+ description: "A detailed plan for analyzing the research topic, including main areas to investigate, key factors, and specific aspects that need deeper investigation",
89
+ },
90
+ },
91
+ required: ["queries", "research_topic", "analysis_plan"],
92
+ },
93
+ },
94
+ },
95
+ ],
96
+ },
97
+ output: {
98
+ queries: ".tool.arguments.queries",
99
+ research_topic: ".tool.arguments.research_topic",
100
+ analysis_plan: ".tool.arguments.analysis_plan",
101
+ },
102
+ },
103
+ logSearchQuery: {
104
+ agent: "consoleAgent",
105
+ inputs: {
106
+ text: "\n" + agentHeader + " ${:searchQueryAgent.analysis_plan}",
107
+ },
108
+ },
109
+ mapSearchAgent: {
110
+ agent: "mapAgent",
111
+ inputs: {
112
+ rows: ":searchQueryAgent.queries",
113
+ },
114
+ params: {
115
+ compositeResult: true,
116
+ },
117
+ graph: {
118
+ nodes: {
119
+ tavilySearchAgent: {
120
+ agent: "tavilySearchAgent",
121
+ inputs: {
122
+ query: ":row",
123
+ },
124
+ params: {
125
+ max_results: 3,
126
+ },
127
+ },
128
+ result: {
129
+ agent: "copyAgent",
130
+ inputs: {
131
+ results: ":tavilySearchAgent.results",
132
+ },
133
+ params: {
134
+ namedKey: "results",
135
+ },
136
+ isResult: true,
137
+ },
138
+ logSearchStatus: {
139
+ agent: ({ result }) => {
140
+ GraphAILogger.info(result.map((r) => `- [${r.title}](${r.url})`).join("\n"));
141
+ },
142
+ inputs: {
143
+ result: ":result",
144
+ },
145
+ },
146
+ },
147
+ },
148
+ },
149
+ extractResults: {
150
+ agent: "copyAgent",
151
+ inputs: {
152
+ results: ":mapSearchAgent.result.flat()",
153
+ },
154
+ params: {
155
+ namedKey: "results",
156
+ },
157
+ },
158
+ reflectionAgent: {
159
+ agent: "openAIAgent",
160
+ inputs: {
161
+ model: "gpt-4o-mini",
162
+ system: "You are a professional research assistant. Based on the user's inquiry and the search results, return the sufficiency of information, knowledge gaps, and follow-up queries as a function call.",
163
+ prompt: reflectionPrompt("${:searchQueryAgent.research_topic}", "${:reducer.array.toJSON()}"),
164
+ },
165
+ params: {
166
+ tool_choice: "auto",
167
+ tools: [
168
+ {
169
+ type: "function",
170
+ function: {
171
+ name: "research_sufficiency",
172
+ description: "Return whether the information is sufficient, any knowledge gaps, and follow-up queries for the user's inquiry.",
173
+ parameters: {
174
+ type: "object",
175
+ properties: {
176
+ is_sufficient: {
177
+ type: "boolean",
178
+ description: "Whether the information is sufficient",
179
+ },
180
+ knowledge_gap: {
181
+ type: "string",
182
+ description: "Summary of missing knowledge or information",
183
+ },
184
+ follow_up_queries: {
185
+ type: "array",
186
+ items: {
187
+ type: "string",
188
+ description: "Additional questions to investigate (up to 3 maximum)",
189
+ },
190
+ },
191
+ },
192
+ required: ["is_sufficient", "knowledge_gap", "follow_up_queries"],
193
+ },
194
+ },
195
+ },
196
+ ],
197
+ },
198
+ output: {
199
+ is_sufficient: ".tool.arguments.is_sufficient",
200
+ knowledge_gap: ".tool.arguments.knowledge_gap",
201
+ follow_up_queries: ".tool.arguments.follow_up_queries",
202
+ },
203
+ },
204
+ reducer: {
205
+ agent: "pushAgent",
206
+ inputs: {
207
+ array: ":searchResults",
208
+ items: ":extractResults",
209
+ },
210
+ },
211
+ continue: {
212
+ agent: ({ is_sufficient, knowledge_gap, counter, maxRetries, }) => {
213
+ if (is_sufficient || counter >= maxRetries - 1) {
214
+ GraphAILogger.info(`\n${agentHeader} All necessary information has been gathered. Preparing comprehensive report.`);
215
+ return false;
216
+ }
217
+ GraphAILogger.info(`\n${agentHeader} ${knowledge_gap}`);
218
+ return true;
219
+ },
220
+ inputs: {
221
+ is_sufficient: ":reflectionAgent.is_sufficient",
222
+ knowledge_gap: ":reflectionAgent.knowledge_gap",
223
+ counter: ":counter",
224
+ maxRetries: ":maxRetries",
225
+ },
226
+ },
227
+ finalAnswer: {
228
+ agent: "openAIAgent",
229
+ unless: ":continue",
230
+ inputs: {
231
+ model: "gpt-4o-mini",
232
+ system: "You are a professional research assistant. Based on the user's inquiry and the search results, return the final answer.",
233
+ prompt: finalAnswerPrompt("${:userInput}", "${:searchResults.toJSON()}", "${:searchQueryAgent.research_topic}"),
234
+ },
235
+ isResult: true,
236
+ },
237
+ },
238
+ },
239
+ },
240
+ writeResult: {
241
+ agent: "consoleAgent",
242
+ inputs: {
243
+ text: "\n------Answer------\n\n${:deepResearch.finalAnswer.text}\n",
244
+ },
245
+ },
246
+ },
247
+ };
248
+ export const deepResearch = async () => {
249
+ const agentFilters = [
250
+ {
251
+ name: "consoleStreamDataAgentFilter",
252
+ agent: consoleStreamDataAgentFilter,
253
+ nodeIds: ["chatAgent"],
254
+ },
255
+ ];
256
+ const graph = new GraphAI(graphData, { ...vanillaAgents, openAIAgent, textInputAgent, tavilySearchAgent }, { agentFilters });
257
+ graph.injectValue("maxRetries", 3);
258
+ graph.registerCallback(cliLoadingPlugin({ nodeId: "searchQueryAgent", message: "Generating search queries..." }));
259
+ graph.registerCallback(cliLoadingPlugin({ nodeId: "reflectionAgent", message: "Analyzing search results..." }));
260
+ graph.registerCallback(cliLoadingPlugin({ nodeId: "tavilySearchAgent", message: "Searching..." }));
261
+ graph.registerCallback(cliLoadingPlugin({ nodeId: "finalAnswer", message: "Generating final answer..." }));
262
+ GraphAILogger.info(`${agentHeader} What would you like to know?\n`);
263
+ await graph.run();
264
+ };
265
+ deepResearch();
@@ -1,3 +1,2 @@
1
1
  export * from "./type.js";
2
2
  export * from "./schema.js";
3
- export * from "./cli_types.js";
@@ -1,3 +1,2 @@
1
1
  export * from "./type.js";
2
2
  export * from "./schema.js";
3
- export * from "./cli_types.js";