@librechat/agents 3.1.54 → 3.1.55
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.
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/tools.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_outputs.cjs.map +1 -1
- package/dist/cjs/llm/fake.cjs.map +1 -1
- package/dist/cjs/llm/google/index.cjs.map +1 -1
- package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/llm/providers.cjs.map +1 -1
- package/dist/cjs/llm/text.cjs.map +1 -1
- package/dist/cjs/llm/vertexai/index.cjs +68 -4
- package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +28 -28
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/content.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/ids.cjs.map +1 -1
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/messages/tools.cjs.map +1 -1
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/splitStream.cjs.map +1 -1
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/Calculator.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/tools/schema.cjs.map +1 -1
- package/dist/cjs/tools/search/content.cjs.map +1 -1
- package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
- package/dist/cjs/tools/search/format.cjs.map +1 -1
- package/dist/cjs/tools/search/highlights.cjs.map +1 -1
- package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
- package/dist/cjs/tools/search/schema.cjs.map +1 -1
- package/dist/cjs/tools/search/search.cjs +1 -0
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/serper-scraper.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/search/utils.cjs.map +1 -1
- package/dist/cjs/utils/events.cjs.map +1 -1
- package/dist/cjs/utils/graph.cjs.map +1 -1
- package/dist/cjs/utils/handlers.cjs.map +1 -1
- package/dist/cjs/utils/llm.cjs.map +1 -1
- package/dist/cjs/utils/misc.cjs.map +1 -1
- package/dist/cjs/utils/run.cjs.map +1 -1
- package/dist/cjs/utils/schema.cjs.map +1 -1
- package/dist/cjs/utils/title.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/anthropic/types.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/tools.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_outputs.mjs.map +1 -1
- package/dist/esm/llm/fake.mjs.map +1 -1
- package/dist/esm/llm/google/index.mjs.map +1 -1
- package/dist/esm/llm/google/utils/common.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/llm/providers.mjs.map +1 -1
- package/dist/esm/llm/text.mjs.map +1 -1
- package/dist/esm/llm/vertexai/index.mjs +68 -4
- package/dist/esm/llm/vertexai/index.mjs.map +1 -1
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/content.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +1 -1
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +2 -2
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/ids.mjs.map +1 -1
- package/dist/esm/messages/prune.mjs +1 -1
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/messages/tools.mjs.map +1 -1
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/splitStream.mjs.map +1 -1
- package/dist/esm/stream.mjs +1 -1
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/Calculator.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +1 -1
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/tools/schema.mjs.map +1 -1
- package/dist/esm/tools/search/content.mjs.map +1 -1
- package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
- package/dist/esm/tools/search/format.mjs.map +1 -1
- package/dist/esm/tools/search/highlights.mjs.map +1 -1
- package/dist/esm/tools/search/rerankers.mjs.map +1 -1
- package/dist/esm/tools/search/schema.mjs.map +1 -1
- package/dist/esm/tools/search/search.mjs +1 -0
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/serper-scraper.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/search/utils.mjs.map +1 -1
- package/dist/esm/utils/events.mjs.map +1 -1
- package/dist/esm/utils/graph.mjs.map +1 -1
- package/dist/esm/utils/handlers.mjs.map +1 -1
- package/dist/esm/utils/llm.mjs.map +1 -1
- package/dist/esm/utils/misc.mjs.map +1 -1
- package/dist/esm/utils/run.mjs.map +1 -1
- package/dist/esm/utils/schema.mjs.map +1 -1
- package/dist/esm/utils/title.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/types/llm/vertexai/index.d.ts +1 -1
- package/package.json +6 -3
- package/src/llm/vertexai/index.ts +99 -6
- package/src/llm/vertexai/llm.spec.ts +114 -0
- package/src/scripts/thinking-vertexai.ts +168 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"misc.mjs","sources":["../../../src/utils/misc.ts"],"sourcesContent":["export function isPresent(value: string | null | undefined): value is string {\n return value != null && value !== '';\n}\n\n/**\n * Unescapes a c-escaped string\n * @param str The string to unescape\n * @returns The unescaped string\n */\nconst unescapeString = (string: string): string =>\n string.replace(/\\\\(.)/g, (_, char) => {\n switch (char) {\n case 'n':\n return '\\n';\n case 't':\n return '\\t';\n case 'r':\n return '\\r';\n case '\"':\n return '\"';\n case '\\'':\n return '\\'';\n case '\\\\':\n return '\\\\';\n default:\n return char;\n }\n });\n\n/**\n * Recursively unescapes all string values in an object\n * @param obj The object to unescape\n * @returns The unescaped object\n */\nexport function unescapeObject(obj: unknown, key?: string): unknown {\n if (typeof obj === 'string') {\n let unescaped = unescapeString(obj);\n if (key === 'filePath' && unescaped.match(/^\"(.+)\"$/)) {\n unescaped = unescaped.substring(1, unescaped.length - 1);\n }\n return unescaped;\n }\n if (Array.isArray(obj)) {\n return obj.map((value) =>\n unescapeObject(value, key === 'contextPaths' ? 'filePath' : '')\n );\n }\n if (typeof obj === 'object' && obj !== null) {\n return Object.fromEntries(\n Object.entries(obj).map(([key, value]) => [\n key,\n unescapeObject(value, key),\n ])\n );\n }\n return obj;\n}\n"],"names":[],"mappings":"AAAM,SAAU,SAAS,CAAC,KAAgC,EAAA;AACxD,IAAA,OAAO,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,EAAE;AACtC;AAEA;;;;AAIG;AACH,MAAM,cAAc,GAAG,CAAC,MAAc,KACpC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,KAAI;IACnC,QAAQ,IAAI;AACZ,QAAA,KAAK,GAAG;AACN,YAAA,OAAO,IAAI;AACb,QAAA,KAAK,GAAG;AACN,YAAA,OAAO,IAAI;AACb,QAAA,KAAK,GAAG;AACN,YAAA,OAAO,IAAI;AACb,QAAA,KAAK,GAAG;AACN,YAAA,OAAO,GAAG;AACZ,QAAA,KAAK,IAAI;AACP,YAAA,OAAO,IAAI;AACb,QAAA,KAAK,IAAI;AACP,YAAA,OAAO,IAAI;AACb,QAAA;AACE,YAAA,OAAO,IAAI;;AAEf,CAAC,CAAC;AAEJ;;;;AAIG;
|
|
1
|
+
{"version":3,"file":"misc.mjs","sources":["../../../src/utils/misc.ts"],"sourcesContent":["export function isPresent(value: string | null | undefined): value is string {\n return value != null && value !== '';\n}\n\n/**\n * Unescapes a c-escaped string\n * @param str The string to unescape\n * @returns The unescaped string\n */\nconst unescapeString = (string: string): string =>\n string.replace(/\\\\(.)/g, (_, char) => {\n switch (char) {\n case 'n':\n return '\\n';\n case 't':\n return '\\t';\n case 'r':\n return '\\r';\n case '\"':\n return '\"';\n case '\\'':\n return '\\'';\n case '\\\\':\n return '\\\\';\n default:\n return char;\n }\n });\n\n/**\n * Recursively unescapes all string values in an object\n * @param obj The object to unescape\n * @returns The unescaped object\n */\nexport function unescapeObject(obj: unknown, key?: string): unknown {\n if (typeof obj === 'string') {\n let unescaped = unescapeString(obj);\n if (key === 'filePath' && unescaped.match(/^\"(.+)\"$/)) {\n unescaped = unescaped.substring(1, unescaped.length - 1);\n }\n return unescaped;\n }\n if (Array.isArray(obj)) {\n return obj.map((value) =>\n unescapeObject(value, key === 'contextPaths' ? 'filePath' : '')\n );\n }\n if (typeof obj === 'object' && obj !== null) {\n return Object.fromEntries(\n Object.entries(obj).map(([key, value]) => [\n key,\n unescapeObject(value, key),\n ])\n );\n }\n return obj;\n}\n"],"names":[],"mappings":"AAAM,SAAU,SAAS,CAAC,KAAgC,EAAA;AACxD,IAAA,OAAO,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,EAAE;AACtC;AAEA;;;;AAIG;AACH,MAAM,cAAc,GAAG,CAAC,MAAc,KACpC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,KAAI;IACnC,QAAQ,IAAI;AACZ,QAAA,KAAK,GAAG;AACN,YAAA,OAAO,IAAI;AACb,QAAA,KAAK,GAAG;AACN,YAAA,OAAO,IAAI;AACb,QAAA,KAAK,GAAG;AACN,YAAA,OAAO,IAAI;AACb,QAAA,KAAK,GAAG;AACN,YAAA,OAAO,GAAG;AACZ,QAAA,KAAK,IAAI;AACP,YAAA,OAAO,IAAI;AACb,QAAA,KAAK,IAAI;AACP,YAAA,OAAO,IAAI;AACb,QAAA;AACE,YAAA,OAAO,IAAI;;AAEf,CAAC,CAAC;AAEJ;;;;AAIG;AACG,SAAU,cAAc,CAAC,GAAY,EAAE,GAAY,EAAA;AACvD,IAAA,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE;AAC3B,QAAA,IAAI,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC;QACnC,IAAI,GAAG,KAAK,UAAU,IAAI,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE;AACrD,YAAA,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;QAC1D;AACA,QAAA,OAAO,SAAS;IAClB;AACA,IAAA,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;QACtB,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,KACnB,cAAc,CAAC,KAAK,EAAE,GAAG,KAAK,cAAc,GAAG,UAAU,GAAG,EAAE,CAAC,CAChE;IACH;IACA,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE;QAC3C,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK;YACxC,GAAG;AACH,YAAA,cAAc,CAAC,KAAK,EAAE,GAAG,CAAC;AAC3B,SAAA,CAAC,CACH;IACH;AACA,IAAA,OAAO,GAAG;AACZ;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.mjs","sources":["../../../src/utils/run.ts"],"sourcesContent":["import { CallbackManagerForChainRun } from '@langchain/core/callbacks/manager';\nimport {\n mergeConfigs,\n patchConfig,\n Runnable,\n RunnableConfig,\n} from '@langchain/core/runnables';\nimport { AsyncLocalStorageProviderSingleton } from '@langchain/core/singletons';\n\n/**\n * Delays the execution for a specified number of milliseconds.\n *\n * @param {number} ms - The number of milliseconds to delay.\n * @return {Promise<void>} A promise that resolves after the specified delay.\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface RunnableCallableArgs extends Partial<any> {\n name?: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n func: (...args: any[]) => any;\n tags?: string[];\n trace?: boolean;\n recurse?: boolean;\n}\n\nexport class RunnableCallable<I = unknown, O = unknown> extends Runnable<I, O> {\n lc_namespace: string[] = ['langgraph'];\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n func: (...args: any[]) => any;\n\n tags?: string[];\n\n config?: RunnableConfig;\n\n trace: boolean = true;\n\n recurse: boolean = true;\n\n constructor(fields: RunnableCallableArgs) {\n super();\n this.name = fields.name ?? fields.func.name;\n this.func = fields.func;\n this.config = fields.tags ? { tags: fields.tags } : undefined;\n this.trace = fields.trace ?? this.trace;\n this.recurse = fields.recurse ?? this.recurse;\n }\n\n protected async _tracedInvoke(\n input: I,\n config?: Partial<RunnableConfig>,\n runManager?: CallbackManagerForChainRun\n ): Promise<O> {\n return new Promise<O>((resolve, reject) => {\n let childConfig: Partial<RunnableConfig> | null = patchConfig(config, {\n callbacks: runManager?.getChild(),\n });\n void AsyncLocalStorageProviderSingleton.runWithConfig(\n childConfig,\n async () => {\n try {\n const output = await this.func(input, childConfig);\n childConfig = null;\n resolve(output);\n } catch (e) {\n childConfig = null;\n reject(e);\n }\n }\n );\n });\n }\n\n async invoke(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n input: any,\n options?: Partial<RunnableConfig> | undefined\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ): Promise<any> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let returnValue: any;\n\n if (this.trace) {\n returnValue = await this._callWithConfig(\n this._tracedInvoke,\n input,\n mergeConfigs(this.config, options)\n );\n } else {\n returnValue = await this.func(input, mergeConfigs(this.config, options));\n }\n\n if (Runnable.isRunnable(returnValue) && this.recurse) {\n return await returnValue.invoke(input, options);\n }\n\n return returnValue;\n }\n}\n"],"names":[],"mappings":";;;AASA;;;;;AAKG;AACG,SAAU,KAAK,CAAC,EAAU,EAAA;AAC9B,IAAA,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AAC1D;AAYM,MAAO,gBAA2C,SAAQ,QAAc,CAAA;AAC5E,IAAA,YAAY,GAAa,CAAC,WAAW,CAAC;;AAGtC,IAAA,IAAI;AAEJ,IAAA,IAAI;AAEJ,IAAA,MAAM;IAEN,KAAK,GAAY,IAAI;IAErB,OAAO,GAAY,IAAI;AAEvB,IAAA,WAAA,CAAY,MAA4B,EAAA;AACtC,QAAA,KAAK,EAAE;AACP,QAAA,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI;AAC3C,QAAA,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,SAAS;QAC7D,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK;QACvC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO
|
|
1
|
+
{"version":3,"file":"run.mjs","sources":["../../../src/utils/run.ts"],"sourcesContent":["import { CallbackManagerForChainRun } from '@langchain/core/callbacks/manager';\nimport {\n mergeConfigs,\n patchConfig,\n Runnable,\n RunnableConfig,\n} from '@langchain/core/runnables';\nimport { AsyncLocalStorageProviderSingleton } from '@langchain/core/singletons';\n\n/**\n * Delays the execution for a specified number of milliseconds.\n *\n * @param {number} ms - The number of milliseconds to delay.\n * @return {Promise<void>} A promise that resolves after the specified delay.\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface RunnableCallableArgs extends Partial<any> {\n name?: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n func: (...args: any[]) => any;\n tags?: string[];\n trace?: boolean;\n recurse?: boolean;\n}\n\nexport class RunnableCallable<I = unknown, O = unknown> extends Runnable<I, O> {\n lc_namespace: string[] = ['langgraph'];\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n func: (...args: any[]) => any;\n\n tags?: string[];\n\n config?: RunnableConfig;\n\n trace: boolean = true;\n\n recurse: boolean = true;\n\n constructor(fields: RunnableCallableArgs) {\n super();\n this.name = fields.name ?? fields.func.name;\n this.func = fields.func;\n this.config = fields.tags ? { tags: fields.tags } : undefined;\n this.trace = fields.trace ?? this.trace;\n this.recurse = fields.recurse ?? this.recurse;\n }\n\n protected async _tracedInvoke(\n input: I,\n config?: Partial<RunnableConfig>,\n runManager?: CallbackManagerForChainRun\n ): Promise<O> {\n return new Promise<O>((resolve, reject) => {\n let childConfig: Partial<RunnableConfig> | null = patchConfig(config, {\n callbacks: runManager?.getChild(),\n });\n void AsyncLocalStorageProviderSingleton.runWithConfig(\n childConfig,\n async () => {\n try {\n const output = await this.func(input, childConfig);\n childConfig = null;\n resolve(output);\n } catch (e) {\n childConfig = null;\n reject(e);\n }\n }\n );\n });\n }\n\n async invoke(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n input: any,\n options?: Partial<RunnableConfig> | undefined\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ): Promise<any> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let returnValue: any;\n\n if (this.trace) {\n returnValue = await this._callWithConfig(\n this._tracedInvoke,\n input,\n mergeConfigs(this.config, options)\n );\n } else {\n returnValue = await this.func(input, mergeConfigs(this.config, options));\n }\n\n if (Runnable.isRunnable(returnValue) && this.recurse) {\n return await returnValue.invoke(input, options);\n }\n\n return returnValue;\n }\n}\n"],"names":[],"mappings":";;;AASA;;;;;AAKG;AACG,SAAU,KAAK,CAAC,EAAU,EAAA;AAC9B,IAAA,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AAC1D;AAYM,MAAO,gBAA2C,SAAQ,QAAc,CAAA;AAC5E,IAAA,YAAY,GAAa,CAAC,WAAW,CAAC;;AAGtC,IAAA,IAAI;AAEJ,IAAA,IAAI;AAEJ,IAAA,MAAM;IAEN,KAAK,GAAY,IAAI;IAErB,OAAO,GAAY,IAAI;AAEvB,IAAA,WAAA,CAAY,MAA4B,EAAA;AACtC,QAAA,KAAK,EAAE;AACP,QAAA,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI;AAC3C,QAAA,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,SAAS;QAC7D,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK;QACvC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO;IAC/C;AAEU,IAAA,MAAM,aAAa,CAC3B,KAAQ,EACR,MAAgC,EAChC,UAAuC,EAAA;QAEvC,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,KAAI;AACxC,YAAA,IAAI,WAAW,GAAmC,WAAW,CAAC,MAAM,EAAE;AACpE,gBAAA,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;AAClC,aAAA,CAAC;YACF,KAAK,kCAAkC,CAAC,aAAa,CACnD,WAAW,EACX,YAAW;AACT,gBAAA,IAAI;oBACF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,CAAC;oBAClD,WAAW,GAAG,IAAI;oBAClB,OAAO,CAAC,MAAM,CAAC;gBACjB;gBAAE,OAAO,CAAC,EAAE;oBACV,WAAW,GAAG,IAAI;oBAClB,MAAM,CAAC,CAAC,CAAC;gBACX;AACF,YAAA,CAAC,CACF;AACH,QAAA,CAAC,CAAC;IACJ;AAEA,IAAA,MAAM,MAAM;;AAEV,IAAA,KAAU,EACV;;;;AAIA,QAAA,IAAI,WAAgB;AAEpB,QAAA,IAAI,IAAI,CAAC,KAAK,EAAE;YACd,WAAW,GAAG,MAAM,IAAI,CAAC,eAAe,CACtC,IAAI,CAAC,aAAa,EAClB,KAAK,EACL,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CACnC;QACH;aAAO;AACL,YAAA,WAAW,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC1E;QAEA,IAAI,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE;YACpD,OAAO,MAAM,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC;QACjD;AAEA,QAAA,OAAO,WAAW;IACpB;AACD;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.mjs","sources":["../../../src/utils/schema.ts"],"sourcesContent":["// src/utils/schema.ts\nimport { zodToJsonSchema } from 'zod-to-json-schema';\nimport type { ZodTypeAny } from 'zod';\n\n/** Checks if a schema is a Zod schema by looking for the _def property */\nexport function isZodSchema(schema: unknown): schema is ZodTypeAny {\n return (\n schema != null && typeof schema === 'object' && '_def' in (schema as object)\n );\n}\n\n/**\n * Converts a schema to JSON schema format.\n * Handles both Zod schemas (converts) and JSON schemas (passthrough).\n */\nexport function toJsonSchema(\n schema: unknown,\n name?: string,\n description?: string\n): Record<string, unknown> {\n if (isZodSchema(schema)) {\n const zodSchema = schema as ZodTypeAny & {\n describe: (desc: string) => ZodTypeAny;\n };\n const described =\n description != null && description !== ''\n ? zodSchema.describe(description)\n : schema;\n return zodToJsonSchema(\n described as Parameters<typeof zodToJsonSchema>[0],\n name ?? ''\n );\n }\n return schema as Record<string, unknown>;\n}\n"],"names":[],"mappings":";;AAAA;AAIA;AACM,SAAU,WAAW,CAAC,MAAe,EAAA;AACzC,IAAA,QACE,MAAM,IAAI,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,IAAK,MAAiB;AAEhF;AAEA;;;AAGG;SACa,YAAY,CAC1B,MAAe,EACf,IAAa,EACb,WAAoB,EAAA;AAEpB,IAAA,IAAI,WAAW,CAAC,MAAM,CAAC,EAAE;QACvB,MAAM,SAAS,GAAG,MAEjB;QACD,MAAM,SAAS,GACb,WAAW,IAAI,IAAI,IAAI,WAAW,KAAK;AACrC,cAAE,SAAS,CAAC,QAAQ,CAAC,WAAW;cAC9B,MAAM;QACZ,OAAO,eAAe,CACpB,SAAkD,EAClD,IAAI,IAAI,EAAE,CACX
|
|
1
|
+
{"version":3,"file":"schema.mjs","sources":["../../../src/utils/schema.ts"],"sourcesContent":["// src/utils/schema.ts\nimport { zodToJsonSchema } from 'zod-to-json-schema';\nimport type { ZodTypeAny } from 'zod';\n\n/** Checks if a schema is a Zod schema by looking for the _def property */\nexport function isZodSchema(schema: unknown): schema is ZodTypeAny {\n return (\n schema != null && typeof schema === 'object' && '_def' in (schema as object)\n );\n}\n\n/**\n * Converts a schema to JSON schema format.\n * Handles both Zod schemas (converts) and JSON schemas (passthrough).\n */\nexport function toJsonSchema(\n schema: unknown,\n name?: string,\n description?: string\n): Record<string, unknown> {\n if (isZodSchema(schema)) {\n const zodSchema = schema as ZodTypeAny & {\n describe: (desc: string) => ZodTypeAny;\n };\n const described =\n description != null && description !== ''\n ? zodSchema.describe(description)\n : schema;\n return zodToJsonSchema(\n described as Parameters<typeof zodToJsonSchema>[0],\n name ?? ''\n );\n }\n return schema as Record<string, unknown>;\n}\n"],"names":[],"mappings":";;AAAA;AAIA;AACM,SAAU,WAAW,CAAC,MAAe,EAAA;AACzC,IAAA,QACE,MAAM,IAAI,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,IAAK,MAAiB;AAEhF;AAEA;;;AAGG;SACa,YAAY,CAC1B,MAAe,EACf,IAAa,EACb,WAAoB,EAAA;AAEpB,IAAA,IAAI,WAAW,CAAC,MAAM,CAAC,EAAE;QACvB,MAAM,SAAS,GAAG,MAEjB;QACD,MAAM,SAAS,GACb,WAAW,IAAI,IAAI,IAAI,WAAW,KAAK;AACrC,cAAE,SAAS,CAAC,QAAQ,CAAC,WAAW;cAC9B,MAAM;QACZ,OAAO,eAAe,CACpB,SAAkD,EAClD,IAAI,IAAI,EAAE,CACX;IACH;AACA,IAAA,OAAO,MAAiC;AAC1C;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"title.mjs","sources":["../../../src/utils/title.ts"],"sourcesContent":["import { ChatPromptTemplate } from '@langchain/core/prompts';\nimport { RunnableLambda, RunnableSequence } from '@langchain/core/runnables';\nimport type { Runnable, RunnableConfig } from '@langchain/core/runnables';\nimport type { AIMessage } from '@langchain/core/messages';\nimport type * as t from '@/types';\nimport { ContentTypes } from '@/common';\n\nconst defaultTitlePrompt = `Analyze this conversation and provide:\n1. The detected language of the conversation\n2. A concise title in the detected language (5 words or less, no punctuation or quotation)\n\n{convo}`;\n\nconst titleSchema = {\n type: 'object',\n properties: {\n title: {\n type: 'string',\n description:\n 'A concise title for the conversation in 5 words or less, without punctuation or quotation',\n },\n },\n required: ['title'],\n} as const;\n\nconst combinedSchema = {\n type: 'object',\n properties: {\n language: {\n type: 'string',\n description: 'The detected language of the conversation',\n },\n title: {\n type: 'string',\n description:\n 'A concise title for the conversation in 5 words or less, without punctuation or quotation',\n },\n },\n required: ['language', 'title'],\n} as const;\n\nexport const createTitleRunnable = async (\n model: t.ChatModelInstance,\n _titlePrompt?: string\n): Promise<Runnable> => {\n // Disabled since this works fine\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /* @ts-ignore */\n const titleLLM = model.withStructuredOutput(titleSchema);\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /* @ts-ignore */\n const combinedLLM = model.withStructuredOutput(combinedSchema);\n\n const titlePrompt = ChatPromptTemplate.fromTemplate(\n _titlePrompt ?? defaultTitlePrompt\n ).withConfig({ runName: 'TitlePrompt' });\n\n const titleOnlyInnerChain = RunnableSequence.from([titlePrompt, titleLLM]);\n const combinedInnerChain = RunnableSequence.from([titlePrompt, combinedLLM]);\n\n /** Wrap titleOnlyChain in RunnableLambda to create parent span */\n const titleOnlyChain = new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ title: string }> => {\n const result = await titleOnlyInnerChain.invoke(input, config);\n return result as { title: string };\n },\n }).withConfig({ runName: 'TitleOnlyChain' });\n\n /** Wrap combinedChain in RunnableLambda to create parent span */\n const combinedChain = new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ language: string; title: string }> => {\n const result = await combinedInnerChain.invoke(input, config);\n return result as { language: string; title: string };\n },\n }).withConfig({ runName: 'TitleLanguageChain' });\n\n /** Runnable to add default values if needed */\n const addDefaults = new RunnableLambda({\n func: (\n result: { language: string; title: string } | undefined\n ): { language: string; title: string } => ({\n language: result?.language ?? 'English',\n title: result?.title ?? '',\n }),\n }).withConfig({ runName: 'AddDefaults' });\n\n const combinedChainInner = RunnableSequence.from([\n combinedChain,\n addDefaults,\n ]);\n\n /** Wrap combinedChainWithDefaults in RunnableLambda to create parent span */\n const combinedChainWithDefaults = new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ language: string; title: string }> => {\n return await combinedChainInner.invoke(input, config);\n },\n }).withConfig({ runName: 'CombinedChainWithDefaults' });\n\n return new RunnableLambda({\n func: async (\n input: {\n convo: string;\n inputText: string;\n skipLanguage: boolean;\n },\n config?: Partial<RunnableConfig>\n ): Promise<{ language: string; title: string } | { title: string }> => {\n const invokeInput = { convo: input.convo };\n\n if (input.skipLanguage) {\n return (await titleOnlyChain.invoke(invokeInput, config)) as {\n title: string;\n };\n }\n\n return await combinedChainWithDefaults.invoke(invokeInput, config);\n },\n }).withConfig({ runName: 'TitleGenerator' });\n};\n\nconst defaultCompletionPrompt = `Provide a concise, 5-word-or-less title for the conversation, using title case conventions. Only return the title itself.\n\nConversation:\n{convo}`;\n\nexport const createCompletionTitleRunnable = async (\n model: t.ChatModelInstance,\n titlePrompt?: string\n): Promise<Runnable> => {\n const completionPrompt = ChatPromptTemplate.fromTemplate(\n titlePrompt ?? defaultCompletionPrompt\n ).withConfig({ runName: 'CompletionTitlePrompt' });\n\n /** Runnable to extract content from model response */\n const extractContent = new RunnableLambda({\n func: (response: AIMessage): { title: string } => {\n let content = '';\n if (typeof response.content === 'string') {\n content = response.content;\n } else if (Array.isArray(response.content)) {\n content = response.content\n .filter(\n (part): part is { type: ContentTypes.TEXT; text: string } =>\n part.type === ContentTypes.TEXT\n )\n .map((part) => part.text)\n .join('');\n }\n return { title: content.trim() };\n },\n }).withConfig({ runName: 'ExtractTitle' });\n\n const innerChain = RunnableSequence.from([\n completionPrompt,\n model,\n extractContent,\n ]);\n\n /** Wrap in RunnableLambda to create a parent span for LangFuse */\n return new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ title: string }> => {\n return await innerChain.invoke(input, config);\n },\n }).withConfig({ runName: 'CompletionTitleChain' });\n};\n"],"names":[],"mappings":";;;;AAOA,MAAM,kBAAkB,GAAG,CAAA;;;;QAInB;AAER,MAAM,WAAW,GAAG;AAClB,IAAA,IAAI,EAAE,QAAQ;AACd,IAAA,UAAU,EAAE;AACV,QAAA,KAAK,EAAE;AACL,YAAA,IAAI,EAAE,QAAQ;AACd,YAAA,WAAW,EACT,2FAA2F;AAC9F,SAAA;AACF,KAAA;IACD,QAAQ,EAAE,CAAC,OAAO,CAAC;CACX;AAEV,MAAM,cAAc,GAAG;AACrB,IAAA,IAAI,EAAE,QAAQ;AACd,IAAA,UAAU,EAAE;AACV,QAAA,QAAQ,EAAE;AACR,YAAA,IAAI,EAAE,QAAQ;AACd,YAAA,WAAW,EAAE,2CAA2C;AACzD,SAAA;AACD,QAAA,KAAK,EAAE;AACL,YAAA,IAAI,EAAE,QAAQ;AACd,YAAA,WAAW,EACT,2FAA2F;AAC9F,SAAA;AACF,KAAA;AACD,IAAA,QAAQ,EAAE,CAAC,UAAU,EAAE,OAAO,CAAC;CACvB;
|
|
1
|
+
{"version":3,"file":"title.mjs","sources":["../../../src/utils/title.ts"],"sourcesContent":["import { ChatPromptTemplate } from '@langchain/core/prompts';\nimport { RunnableLambda, RunnableSequence } from '@langchain/core/runnables';\nimport type { Runnable, RunnableConfig } from '@langchain/core/runnables';\nimport type { AIMessage } from '@langchain/core/messages';\nimport type * as t from '@/types';\nimport { ContentTypes } from '@/common';\n\nconst defaultTitlePrompt = `Analyze this conversation and provide:\n1. The detected language of the conversation\n2. A concise title in the detected language (5 words or less, no punctuation or quotation)\n\n{convo}`;\n\nconst titleSchema = {\n type: 'object',\n properties: {\n title: {\n type: 'string',\n description:\n 'A concise title for the conversation in 5 words or less, without punctuation or quotation',\n },\n },\n required: ['title'],\n} as const;\n\nconst combinedSchema = {\n type: 'object',\n properties: {\n language: {\n type: 'string',\n description: 'The detected language of the conversation',\n },\n title: {\n type: 'string',\n description:\n 'A concise title for the conversation in 5 words or less, without punctuation or quotation',\n },\n },\n required: ['language', 'title'],\n} as const;\n\nexport const createTitleRunnable = async (\n model: t.ChatModelInstance,\n _titlePrompt?: string\n): Promise<Runnable> => {\n // Disabled since this works fine\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /* @ts-ignore */\n const titleLLM = model.withStructuredOutput(titleSchema);\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /* @ts-ignore */\n const combinedLLM = model.withStructuredOutput(combinedSchema);\n\n const titlePrompt = ChatPromptTemplate.fromTemplate(\n _titlePrompt ?? defaultTitlePrompt\n ).withConfig({ runName: 'TitlePrompt' });\n\n const titleOnlyInnerChain = RunnableSequence.from([titlePrompt, titleLLM]);\n const combinedInnerChain = RunnableSequence.from([titlePrompt, combinedLLM]);\n\n /** Wrap titleOnlyChain in RunnableLambda to create parent span */\n const titleOnlyChain = new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ title: string }> => {\n const result = await titleOnlyInnerChain.invoke(input, config);\n return result as { title: string };\n },\n }).withConfig({ runName: 'TitleOnlyChain' });\n\n /** Wrap combinedChain in RunnableLambda to create parent span */\n const combinedChain = new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ language: string; title: string }> => {\n const result = await combinedInnerChain.invoke(input, config);\n return result as { language: string; title: string };\n },\n }).withConfig({ runName: 'TitleLanguageChain' });\n\n /** Runnable to add default values if needed */\n const addDefaults = new RunnableLambda({\n func: (\n result: { language: string; title: string } | undefined\n ): { language: string; title: string } => ({\n language: result?.language ?? 'English',\n title: result?.title ?? '',\n }),\n }).withConfig({ runName: 'AddDefaults' });\n\n const combinedChainInner = RunnableSequence.from([\n combinedChain,\n addDefaults,\n ]);\n\n /** Wrap combinedChainWithDefaults in RunnableLambda to create parent span */\n const combinedChainWithDefaults = new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ language: string; title: string }> => {\n return await combinedChainInner.invoke(input, config);\n },\n }).withConfig({ runName: 'CombinedChainWithDefaults' });\n\n return new RunnableLambda({\n func: async (\n input: {\n convo: string;\n inputText: string;\n skipLanguage: boolean;\n },\n config?: Partial<RunnableConfig>\n ): Promise<{ language: string; title: string } | { title: string }> => {\n const invokeInput = { convo: input.convo };\n\n if (input.skipLanguage) {\n return (await titleOnlyChain.invoke(invokeInput, config)) as {\n title: string;\n };\n }\n\n return await combinedChainWithDefaults.invoke(invokeInput, config);\n },\n }).withConfig({ runName: 'TitleGenerator' });\n};\n\nconst defaultCompletionPrompt = `Provide a concise, 5-word-or-less title for the conversation, using title case conventions. Only return the title itself.\n\nConversation:\n{convo}`;\n\nexport const createCompletionTitleRunnable = async (\n model: t.ChatModelInstance,\n titlePrompt?: string\n): Promise<Runnable> => {\n const completionPrompt = ChatPromptTemplate.fromTemplate(\n titlePrompt ?? defaultCompletionPrompt\n ).withConfig({ runName: 'CompletionTitlePrompt' });\n\n /** Runnable to extract content from model response */\n const extractContent = new RunnableLambda({\n func: (response: AIMessage): { title: string } => {\n let content = '';\n if (typeof response.content === 'string') {\n content = response.content;\n } else if (Array.isArray(response.content)) {\n content = response.content\n .filter(\n (part): part is { type: ContentTypes.TEXT; text: string } =>\n part.type === ContentTypes.TEXT\n )\n .map((part) => part.text)\n .join('');\n }\n return { title: content.trim() };\n },\n }).withConfig({ runName: 'ExtractTitle' });\n\n const innerChain = RunnableSequence.from([\n completionPrompt,\n model,\n extractContent,\n ]);\n\n /** Wrap in RunnableLambda to create a parent span for LangFuse */\n return new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ title: string }> => {\n return await innerChain.invoke(input, config);\n },\n }).withConfig({ runName: 'CompletionTitleChain' });\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.mjs","sources":["../../../src/utils/tokens.ts"],"sourcesContent":["import { Tiktoken } from 'js-tiktoken/lite';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport { ContentTypes } from '@/common/enum';\n\nexport function getTokenCountForMessage(\n message: BaseMessage,\n getTokenCount: (text: string) => number\n): number {\n const tokensPerMessage = 3;\n\n const processValue = (value: unknown): void => {\n if (Array.isArray(value)) {\n for (const item of value) {\n if (\n !item ||\n !item.type ||\n item.type === ContentTypes.ERROR ||\n item.type === ContentTypes.IMAGE_URL\n ) {\n continue;\n }\n\n if (item.type === ContentTypes.TOOL_CALL && item.tool_call != null) {\n const toolName = item.tool_call?.name || '';\n if (toolName != null && toolName && typeof toolName === 'string') {\n numTokens += getTokenCount(toolName);\n }\n\n const args = item.tool_call?.args || '';\n if (args != null && args && typeof args === 'string') {\n numTokens += getTokenCount(args);\n }\n\n const output = item.tool_call?.output || '';\n if (output != null && output && typeof output === 'string') {\n numTokens += getTokenCount(output);\n }\n continue;\n }\n\n const nestedValue = item[item.type];\n\n if (!nestedValue) {\n continue;\n }\n\n processValue(nestedValue);\n }\n } else if (typeof value === 'string') {\n numTokens += getTokenCount(value);\n } else if (typeof value === 'number') {\n numTokens += getTokenCount(value.toString());\n } else if (typeof value === 'boolean') {\n numTokens += getTokenCount(value.toString());\n }\n };\n\n let numTokens = tokensPerMessage;\n processValue(message.content);\n return numTokens;\n}\n\nlet encoderPromise: Promise<Tiktoken> | undefined;\nlet tokenCounterPromise: Promise<(message: BaseMessage) => number> | undefined;\n\nasync function getSharedEncoder(): Promise<Tiktoken> {\n if (encoderPromise) {\n return encoderPromise;\n }\n encoderPromise = (async (): Promise<Tiktoken> => {\n const res = await fetch('https://tiktoken.pages.dev/js/o200k_base.json');\n const o200k_base = await res.json();\n return new Tiktoken(o200k_base);\n })();\n return encoderPromise;\n}\n\n/**\n * Creates a singleton token counter function that reuses the same encoder instance.\n * This avoids creating multiple function closures and prevents potential memory issues.\n */\nexport const createTokenCounter = async (): Promise<\n (message: BaseMessage) => number\n> => {\n if (tokenCounterPromise) {\n return tokenCounterPromise;\n }\n\n tokenCounterPromise = (async (): Promise<\n (message: BaseMessage) => number\n > => {\n const enc = await getSharedEncoder();\n const countTokens = (text: string): number => enc.encode(text).length;\n return (message: BaseMessage): number =>\n getTokenCountForMessage(message, countTokens);\n })();\n\n return tokenCounterPromise;\n};\n\n/**\n * Utility to manage the token encoder lifecycle explicitly.\n * Useful for applications that need fine-grained control over resource management.\n */\nexport const TokenEncoderManager = {\n /**\n * Pre-initializes the encoder. This can be called during app startup\n * to avoid lazy loading delays later.\n */\n async initialize(): Promise<void> {\n await getSharedEncoder();\n },\n\n /**\n * Clears the cached encoder and token counter.\n * Useful for testing or when you need to force a fresh reload.\n */\n reset(): void {\n encoderPromise = undefined;\n tokenCounterPromise = undefined;\n },\n\n /**\n * Checks if the encoder has been initialized.\n */\n isInitialized(): boolean {\n return encoderPromise !== undefined;\n },\n};\n"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"tokens.mjs","sources":["../../../src/utils/tokens.ts"],"sourcesContent":["import { Tiktoken } from 'js-tiktoken/lite';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport { ContentTypes } from '@/common/enum';\n\nexport function getTokenCountForMessage(\n message: BaseMessage,\n getTokenCount: (text: string) => number\n): number {\n const tokensPerMessage = 3;\n\n const processValue = (value: unknown): void => {\n if (Array.isArray(value)) {\n for (const item of value) {\n if (\n !item ||\n !item.type ||\n item.type === ContentTypes.ERROR ||\n item.type === ContentTypes.IMAGE_URL\n ) {\n continue;\n }\n\n if (item.type === ContentTypes.TOOL_CALL && item.tool_call != null) {\n const toolName = item.tool_call?.name || '';\n if (toolName != null && toolName && typeof toolName === 'string') {\n numTokens += getTokenCount(toolName);\n }\n\n const args = item.tool_call?.args || '';\n if (args != null && args && typeof args === 'string') {\n numTokens += getTokenCount(args);\n }\n\n const output = item.tool_call?.output || '';\n if (output != null && output && typeof output === 'string') {\n numTokens += getTokenCount(output);\n }\n continue;\n }\n\n const nestedValue = item[item.type];\n\n if (!nestedValue) {\n continue;\n }\n\n processValue(nestedValue);\n }\n } else if (typeof value === 'string') {\n numTokens += getTokenCount(value);\n } else if (typeof value === 'number') {\n numTokens += getTokenCount(value.toString());\n } else if (typeof value === 'boolean') {\n numTokens += getTokenCount(value.toString());\n }\n };\n\n let numTokens = tokensPerMessage;\n processValue(message.content);\n return numTokens;\n}\n\nlet encoderPromise: Promise<Tiktoken> | undefined;\nlet tokenCounterPromise: Promise<(message: BaseMessage) => number> | undefined;\n\nasync function getSharedEncoder(): Promise<Tiktoken> {\n if (encoderPromise) {\n return encoderPromise;\n }\n encoderPromise = (async (): Promise<Tiktoken> => {\n const res = await fetch('https://tiktoken.pages.dev/js/o200k_base.json');\n const o200k_base = await res.json();\n return new Tiktoken(o200k_base);\n })();\n return encoderPromise;\n}\n\n/**\n * Creates a singleton token counter function that reuses the same encoder instance.\n * This avoids creating multiple function closures and prevents potential memory issues.\n */\nexport const createTokenCounter = async (): Promise<\n (message: BaseMessage) => number\n> => {\n if (tokenCounterPromise) {\n return tokenCounterPromise;\n }\n\n tokenCounterPromise = (async (): Promise<\n (message: BaseMessage) => number\n > => {\n const enc = await getSharedEncoder();\n const countTokens = (text: string): number => enc.encode(text).length;\n return (message: BaseMessage): number =>\n getTokenCountForMessage(message, countTokens);\n })();\n\n return tokenCounterPromise;\n};\n\n/**\n * Utility to manage the token encoder lifecycle explicitly.\n * Useful for applications that need fine-grained control over resource management.\n */\nexport const TokenEncoderManager = {\n /**\n * Pre-initializes the encoder. This can be called during app startup\n * to avoid lazy loading delays later.\n */\n async initialize(): Promise<void> {\n await getSharedEncoder();\n },\n\n /**\n * Clears the cached encoder and token counter.\n * Useful for testing or when you need to force a fresh reload.\n */\n reset(): void {\n encoderPromise = undefined;\n tokenCounterPromise = undefined;\n },\n\n /**\n * Checks if the encoder has been initialized.\n */\n isInitialized(): boolean {\n return encoderPromise !== undefined;\n },\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -290,5 +290,5 @@ export declare class ChatVertexAI extends ChatGoogle {
|
|
|
290
290
|
static lc_name(): 'LibreChatVertexAI';
|
|
291
291
|
constructor(fields?: VertexAIClientOptions);
|
|
292
292
|
invocationParams(options?: this['ParsedCallOptions'] | undefined): GoogleAIModelRequestParams;
|
|
293
|
-
buildConnection(fields: VertexAIClientOptions, client: GoogleAbstractedClient): void;
|
|
293
|
+
buildConnection(fields: VertexAIClientOptions | undefined, client: GoogleAbstractedClient): void;
|
|
294
294
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@librechat/agents",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.55",
|
|
4
4
|
"main": "./dist/cjs/main.cjs",
|
|
5
5
|
"module": "./dist/esm/main.mjs",
|
|
6
6
|
"types": "./dist/types/index.d.ts",
|
|
@@ -112,7 +112,9 @@
|
|
|
112
112
|
"@browserbasehq/stagehand": {
|
|
113
113
|
"openai": "$openai"
|
|
114
114
|
},
|
|
115
|
-
"fast-xml-parser": "5.3.
|
|
115
|
+
"fast-xml-parser": "5.3.8",
|
|
116
|
+
"ajv": "6.14.0",
|
|
117
|
+
"minimatch": "3.1.4"
|
|
116
118
|
},
|
|
117
119
|
"dependencies": {
|
|
118
120
|
"@anthropic-ai/sdk": "^0.73.0",
|
|
@@ -132,6 +134,7 @@
|
|
|
132
134
|
"@langfuse/otel": "^4.3.0",
|
|
133
135
|
"@langfuse/tracing": "^4.3.0",
|
|
134
136
|
"@opentelemetry/sdk-node": "^0.207.0",
|
|
137
|
+
"@scarf/scarf": "^1.4.0",
|
|
135
138
|
"axios": "^1.13.5",
|
|
136
139
|
"cheerio": "^1.0.0",
|
|
137
140
|
"dotenv": "^16.4.7",
|
|
@@ -168,7 +171,7 @@
|
|
|
168
171
|
"jest-util": "^30.2.0",
|
|
169
172
|
"lint-staged": "^15.2.7",
|
|
170
173
|
"prettier": "^3.6.2",
|
|
171
|
-
"rollup": "^4.
|
|
174
|
+
"rollup": "^4.59.0",
|
|
172
175
|
"rollup-plugin-cleandir": "^2.0.0",
|
|
173
176
|
"ts-jest": "^29.4.5",
|
|
174
177
|
"ts-node": "^10.9.2",
|
|
@@ -1,13 +1,82 @@
|
|
|
1
1
|
import { ChatGoogle } from '@langchain/google-gauth';
|
|
2
2
|
import { ChatConnection } from '@langchain/google-common';
|
|
3
3
|
import type {
|
|
4
|
+
GeminiContent,
|
|
4
5
|
GeminiRequest,
|
|
5
6
|
GoogleAIModelRequestParams,
|
|
6
7
|
GoogleAbstractedClient,
|
|
7
8
|
} from '@langchain/google-common';
|
|
8
9
|
import type { BaseMessage } from '@langchain/core/messages';
|
|
10
|
+
import { isAIMessage } from '@langchain/core/messages';
|
|
9
11
|
import type { GoogleThinkingConfig, VertexAIClientOptions } from '@/types';
|
|
10
12
|
|
|
13
|
+
type AdditionalKwargs =
|
|
14
|
+
| undefined
|
|
15
|
+
| (BaseMessage['additional_kwargs'] & {
|
|
16
|
+
signatures?: Array<string | undefined>;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Fixes thought signatures on functionCall parts in the formatted Gemini request.
|
|
21
|
+
*
|
|
22
|
+
* `@langchain/google-common` stores signatures as a flat array in
|
|
23
|
+
* `additional_kwargs.signatures` (one per response part) and re-attaches them
|
|
24
|
+
* by index only when `signatures.length === parts.length`. This fails when:
|
|
25
|
+
* - The API omits a signature (length mismatch)
|
|
26
|
+
* - Streaming chunks merge with different part counts
|
|
27
|
+
* - The signature for a functionCall part is an empty string
|
|
28
|
+
*
|
|
29
|
+
* This function correlates each "model" content block in the formatted request
|
|
30
|
+
* back to its originating AI message, then re-attaches non-empty signatures
|
|
31
|
+
* that the library failed to apply.
|
|
32
|
+
*/
|
|
33
|
+
function fixThoughtSignatures(
|
|
34
|
+
contents: GeminiContent[],
|
|
35
|
+
input: BaseMessage[]
|
|
36
|
+
): void {
|
|
37
|
+
// Collect AI messages that have signatures, in order
|
|
38
|
+
const aiMessages = input.filter(
|
|
39
|
+
(msg) =>
|
|
40
|
+
isAIMessage(msg) &&
|
|
41
|
+
Array.isArray((msg.additional_kwargs as AdditionalKwargs)?.signatures) &&
|
|
42
|
+
(msg.additional_kwargs.signatures as string[]).length > 0
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Collect "model" content blocks from the formatted request, in order
|
|
46
|
+
const modelContents = contents.filter((c) => c.role === 'model');
|
|
47
|
+
|
|
48
|
+
// They should correspond 1:1 in order (both derived from the same input sequence)
|
|
49
|
+
const count = Math.min(aiMessages.length, modelContents.length);
|
|
50
|
+
for (let i = 0; i < count; i++) {
|
|
51
|
+
const msg = aiMessages[i];
|
|
52
|
+
const content = modelContents[i];
|
|
53
|
+
const signatures = (msg.additional_kwargs as AdditionalKwargs)?.signatures;
|
|
54
|
+
|
|
55
|
+
// Collect non-empty signatures that aren't already attached to any part
|
|
56
|
+
const attachedSignatures = new Set(
|
|
57
|
+
content.parts
|
|
58
|
+
.map((p) => p.thoughtSignature)
|
|
59
|
+
.filter((s): s is string => s != null && s !== '')
|
|
60
|
+
);
|
|
61
|
+
const availableSignatures = signatures?.filter(
|
|
62
|
+
(s) => s != null && s !== '' && !attachedSignatures.has(s)
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// Assign available signatures to functionCall parts missing one, in order
|
|
66
|
+
let sigIdx = 0;
|
|
67
|
+
for (const part of content.parts) {
|
|
68
|
+
if (
|
|
69
|
+
'functionCall' in part &&
|
|
70
|
+
(part.thoughtSignature == null || part.thoughtSignature === '') &&
|
|
71
|
+
sigIdx < (availableSignatures?.length ?? 0)
|
|
72
|
+
) {
|
|
73
|
+
part.thoughtSignature = availableSignatures?.[sigIdx];
|
|
74
|
+
sigIdx++;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
11
80
|
class CustomChatConnection extends ChatConnection<VertexAIClientOptions> {
|
|
12
81
|
thinkingConfig?: GoogleThinkingConfig;
|
|
13
82
|
|
|
@@ -28,15 +97,36 @@ class CustomChatConnection extends ChatConnection<VertexAIClientOptions> {
|
|
|
28
97
|
}
|
|
29
98
|
delete formattedData.generationConfig.thinkingConfig.thinkingBudget;
|
|
30
99
|
}
|
|
31
|
-
if (
|
|
100
|
+
if (
|
|
101
|
+
this.thinkingConfig?.thinkingLevel != null &&
|
|
102
|
+
this.thinkingConfig.thinkingLevel !== ''
|
|
103
|
+
) {
|
|
32
104
|
formattedData.generationConfig ??= {};
|
|
105
|
+
// thinkingLevel and thinkingBudget cannot coexist — the API rejects the request.
|
|
106
|
+
// Remove thinkingBudget when thinkingLevel is set.
|
|
107
|
+
const { thinkingBudget: _, ...existingThinkingConfig } =
|
|
108
|
+
(formattedData.generationConfig.thinkingConfig as
|
|
109
|
+
| Record<string, unknown>
|
|
110
|
+
| undefined) ?? {};
|
|
33
111
|
(
|
|
34
112
|
formattedData.generationConfig as Record<string, unknown>
|
|
35
113
|
).thinkingConfig = {
|
|
36
|
-
...
|
|
114
|
+
...existingThinkingConfig,
|
|
37
115
|
thinkingLevel: this.thinkingConfig.thinkingLevel,
|
|
116
|
+
...(this.thinkingConfig.includeThoughts != null && {
|
|
117
|
+
includeThoughts: this.thinkingConfig.includeThoughts,
|
|
118
|
+
}),
|
|
38
119
|
};
|
|
39
120
|
}
|
|
121
|
+
if (formattedData.contents) {
|
|
122
|
+
fixThoughtSignatures(formattedData.contents, input);
|
|
123
|
+
// gemini-3.1+ models reject role="function"; convert to role="user"
|
|
124
|
+
for (const content of formattedData.contents) {
|
|
125
|
+
if (content.role === 'function') {
|
|
126
|
+
(content as { role: string }).role = 'user';
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
40
130
|
return formattedData;
|
|
41
131
|
}
|
|
42
132
|
}
|
|
@@ -350,18 +440,21 @@ export class ChatVertexAI extends ChatGoogle {
|
|
|
350
440
|
}
|
|
351
441
|
return params;
|
|
352
442
|
}
|
|
353
|
-
|
|
354
443
|
buildConnection(
|
|
355
|
-
fields: VertexAIClientOptions,
|
|
444
|
+
fields: VertexAIClientOptions | undefined,
|
|
356
445
|
client: GoogleAbstractedClient
|
|
357
446
|
): void {
|
|
447
|
+
// Note: buildConnection is called from super() BEFORE this.thinkingConfig is set,
|
|
448
|
+
// so we must read thinkingConfig from `fields` directly.
|
|
449
|
+
const thinkingConfig = fields?.thinkingConfig ?? this.thinkingConfig;
|
|
450
|
+
|
|
358
451
|
const connection = new CustomChatConnection(
|
|
359
452
|
{ ...fields, ...this },
|
|
360
453
|
this.caller,
|
|
361
454
|
client,
|
|
362
455
|
false
|
|
363
456
|
);
|
|
364
|
-
connection.thinkingConfig =
|
|
457
|
+
connection.thinkingConfig = thinkingConfig;
|
|
365
458
|
this.connection = connection;
|
|
366
459
|
|
|
367
460
|
const streamedConnection = new CustomChatConnection(
|
|
@@ -370,7 +463,7 @@ export class ChatVertexAI extends ChatGoogle {
|
|
|
370
463
|
client,
|
|
371
464
|
true
|
|
372
465
|
);
|
|
373
|
-
streamedConnection.thinkingConfig =
|
|
466
|
+
streamedConnection.thinkingConfig = thinkingConfig;
|
|
374
467
|
this.streamedConnection = streamedConnection;
|
|
375
468
|
}
|
|
376
469
|
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { config } from 'dotenv';
|
|
2
|
+
config();
|
|
3
|
+
import { test, describe, jest } from '@jest/globals';
|
|
4
|
+
|
|
5
|
+
jest.setTimeout(90000);
|
|
6
|
+
import {
|
|
7
|
+
AIMessageChunk,
|
|
8
|
+
HumanMessage,
|
|
9
|
+
ToolMessage,
|
|
10
|
+
} from '@langchain/core/messages';
|
|
11
|
+
import { tool } from '@langchain/core/tools';
|
|
12
|
+
import { z } from 'zod/v3';
|
|
13
|
+
import { ChatVertexAI } from './index';
|
|
14
|
+
|
|
15
|
+
const gemini3Models = [
|
|
16
|
+
'gemini-3-pro-preview',
|
|
17
|
+
'gemini-3-flash-preview',
|
|
18
|
+
'gemini-3.1-flash-lite-preview',
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const weatherTool = tool(async () => 'The weather is 80 degrees and sunny', {
|
|
22
|
+
name: 'weather',
|
|
23
|
+
description: 'Gets the current weather in a given location',
|
|
24
|
+
schema: z.object({
|
|
25
|
+
location: z.string().describe('The city to get the weather for'),
|
|
26
|
+
}),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe.each(gemini3Models)(
|
|
30
|
+
'Vertex AI reasoning with thinkingLevel (%s)',
|
|
31
|
+
(modelName) => {
|
|
32
|
+
const model = new ChatVertexAI({
|
|
33
|
+
model: modelName,
|
|
34
|
+
location: 'global',
|
|
35
|
+
maxRetries: 2,
|
|
36
|
+
thinkingConfig: {
|
|
37
|
+
thinkingLevel: 'HIGH',
|
|
38
|
+
includeThoughts: true,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('invoke with thinkingLevel produces a response with reasoning tokens', async () => {
|
|
43
|
+
const result = await model.invoke('What is 2+2? Think step by step.');
|
|
44
|
+
expect(result.content).toBeDefined();
|
|
45
|
+
const reasoningTokens = (result.usage_metadata as Record<string, unknown>)
|
|
46
|
+
?.output_token_details;
|
|
47
|
+
expect(reasoningTokens).toBeDefined();
|
|
48
|
+
expect(
|
|
49
|
+
(reasoningTokens as Record<string, number>)?.reasoning
|
|
50
|
+
).toBeGreaterThan(0);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
describe.each(gemini3Models)(
|
|
56
|
+
'Vertex AI tool calling with thought signatures (%s)',
|
|
57
|
+
(modelName) => {
|
|
58
|
+
const model = new ChatVertexAI({
|
|
59
|
+
model: modelName,
|
|
60
|
+
location: 'global',
|
|
61
|
+
maxRetries: 2,
|
|
62
|
+
});
|
|
63
|
+
const modelWithTools = model.bindTools([weatherTool]);
|
|
64
|
+
|
|
65
|
+
test('invoke: tool call completes round-trip with thought signature', async () => {
|
|
66
|
+
const result = await modelWithTools.invoke(
|
|
67
|
+
'What is the current weather in San Francisco?'
|
|
68
|
+
);
|
|
69
|
+
expect(result.tool_calls).toBeDefined();
|
|
70
|
+
expect(result.tool_calls!.length).toBeGreaterThanOrEqual(1);
|
|
71
|
+
expect(result.tool_calls![0].id).toBeDefined();
|
|
72
|
+
|
|
73
|
+
const toolMessage = new ToolMessage({
|
|
74
|
+
content: 'The weather is 80 degrees and sunny',
|
|
75
|
+
tool_call_id: result.tool_calls![0].id ?? '',
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Critical round-trip: sending the function call + tool result back to the API.
|
|
79
|
+
// Without proper thought_signature handling, this fails with
|
|
80
|
+
// "function call X is missing a thought_signature"
|
|
81
|
+
const finalResult = await model.invoke([
|
|
82
|
+
new HumanMessage('What is the current weather in San Francisco?'),
|
|
83
|
+
result,
|
|
84
|
+
toolMessage,
|
|
85
|
+
]);
|
|
86
|
+
expect(finalResult.content).toBeDefined();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('stream: tool call completes round-trip with thought signature', async () => {
|
|
90
|
+
let finalChunk: AIMessageChunk | undefined;
|
|
91
|
+
for await (const chunk of await modelWithTools.stream(
|
|
92
|
+
'What is the current weather in San Francisco?'
|
|
93
|
+
)) {
|
|
94
|
+
finalChunk = finalChunk ? finalChunk.concat(chunk) : chunk;
|
|
95
|
+
}
|
|
96
|
+
expect(finalChunk).toBeDefined();
|
|
97
|
+
expect(finalChunk?.tool_calls).toBeDefined();
|
|
98
|
+
expect(finalChunk?.tool_calls!.length).toBeGreaterThanOrEqual(1);
|
|
99
|
+
|
|
100
|
+
const toolMessage = new ToolMessage({
|
|
101
|
+
content: 'The weather is 80 degrees and sunny',
|
|
102
|
+
tool_call_id: finalChunk?.tool_calls![0].id ?? '',
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Round-trip: send tool result back — verifies thought_signature handling
|
|
106
|
+
const finalResult = await model.invoke([
|
|
107
|
+
new HumanMessage('What is the current weather in San Francisco?'),
|
|
108
|
+
finalChunk!,
|
|
109
|
+
toolMessage,
|
|
110
|
+
]);
|
|
111
|
+
expect(finalResult.content).toBeDefined();
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
);
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// src/scripts/thinking-vertexai.ts
|
|
2
|
+
import { config } from 'dotenv';
|
|
3
|
+
config();
|
|
4
|
+
import { HumanMessage, BaseMessage } from '@langchain/core/messages';
|
|
5
|
+
import type { UsageMetadata } from '@langchain/core/messages';
|
|
6
|
+
import * as t from '@/types';
|
|
7
|
+
import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
|
|
8
|
+
import { ToolEndHandler, ModelEndHandler } from '@/events';
|
|
9
|
+
import { GraphEvents, Providers } from '@/common';
|
|
10
|
+
import { getLLMConfig } from '@/utils/llmConfig';
|
|
11
|
+
import { getArgs } from '@/scripts/args';
|
|
12
|
+
import { Run } from '@/run';
|
|
13
|
+
|
|
14
|
+
const conversationHistory: BaseMessage[] = [];
|
|
15
|
+
let _contentParts: t.MessageContentComplex[] = [];
|
|
16
|
+
const collectedUsage: UsageMetadata[] = [];
|
|
17
|
+
|
|
18
|
+
async function testVertexAIThinking(): Promise<void> {
|
|
19
|
+
const { userName } = await getArgs();
|
|
20
|
+
const instructions = `You are a helpful AI assistant for ${userName}. When answering questions, be thorough in your reasoning.`;
|
|
21
|
+
const { contentParts, aggregateContent } = createContentAggregator();
|
|
22
|
+
_contentParts = contentParts as t.MessageContentComplex[];
|
|
23
|
+
|
|
24
|
+
// Set up event handlers
|
|
25
|
+
const customHandlers = {
|
|
26
|
+
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
27
|
+
[GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage),
|
|
28
|
+
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
29
|
+
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
30
|
+
handle: (
|
|
31
|
+
event: GraphEvents.ON_RUN_STEP_COMPLETED,
|
|
32
|
+
data: t.StreamEventData
|
|
33
|
+
): void => {
|
|
34
|
+
console.log('====== ON_RUN_STEP_COMPLETED ======');
|
|
35
|
+
aggregateContent({
|
|
36
|
+
event,
|
|
37
|
+
data: data as unknown as { result: t.ToolEndEvent },
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
[GraphEvents.ON_RUN_STEP]: {
|
|
42
|
+
handle: (event: GraphEvents.ON_RUN_STEP, data: t.RunStep) => {
|
|
43
|
+
aggregateContent({ event, data });
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
47
|
+
handle: (
|
|
48
|
+
event: GraphEvents.ON_RUN_STEP_DELTA,
|
|
49
|
+
data: t.RunStepDeltaEvent
|
|
50
|
+
) => {
|
|
51
|
+
aggregateContent({ event, data });
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
55
|
+
handle: (
|
|
56
|
+
event: GraphEvents.ON_MESSAGE_DELTA,
|
|
57
|
+
data: t.MessageDeltaEvent
|
|
58
|
+
) => {
|
|
59
|
+
aggregateContent({ event, data });
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
[GraphEvents.ON_REASONING_DELTA]: {
|
|
63
|
+
handle: (
|
|
64
|
+
event: GraphEvents.ON_REASONING_DELTA,
|
|
65
|
+
data: t.ReasoningDeltaEvent
|
|
66
|
+
) => {
|
|
67
|
+
console.log(
|
|
68
|
+
'[ON_REASONING_DELTA]',
|
|
69
|
+
JSON.stringify(data.delta.content?.[0]).slice(0, 100)
|
|
70
|
+
);
|
|
71
|
+
aggregateContent({ event, data });
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const baseLlmConfig = getLLMConfig(Providers.VERTEXAI);
|
|
77
|
+
|
|
78
|
+
const llmConfig = {
|
|
79
|
+
...baseLlmConfig,
|
|
80
|
+
model: 'gemini-3-flash-preview',
|
|
81
|
+
location: 'global',
|
|
82
|
+
streaming: true,
|
|
83
|
+
streamUsage: true,
|
|
84
|
+
thinkingConfig: {
|
|
85
|
+
thinkingLevel: 'HIGH',
|
|
86
|
+
includeThoughts: true,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const run = await Run.create<t.IState>({
|
|
91
|
+
runId: 'test-vertexai-thinking-id',
|
|
92
|
+
graphConfig: {
|
|
93
|
+
instructions,
|
|
94
|
+
type: 'standard',
|
|
95
|
+
llmConfig,
|
|
96
|
+
},
|
|
97
|
+
returnContent: true,
|
|
98
|
+
skipCleanup: true,
|
|
99
|
+
customHandlers: customHandlers as t.RunConfig['customHandlers'],
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const streamConfig = {
|
|
103
|
+
configurable: {
|
|
104
|
+
thread_id: 'vertexai-thinking-test-thread',
|
|
105
|
+
},
|
|
106
|
+
streamMode: 'values',
|
|
107
|
+
version: 'v2' as const,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Test 1: Regular thinking mode
|
|
111
|
+
console.log('\n\nTest 1: Vertex AI thinking mode with thinkingLevel=HIGH');
|
|
112
|
+
const userMessage1 =
|
|
113
|
+
'How many r\'s are in the word "strawberry"? Think carefully.';
|
|
114
|
+
conversationHistory.push(new HumanMessage(userMessage1));
|
|
115
|
+
|
|
116
|
+
console.log('Running first query with Vertex AI thinking enabled...');
|
|
117
|
+
const firstInputs = { messages: [...conversationHistory] };
|
|
118
|
+
await run.processStream(firstInputs, streamConfig);
|
|
119
|
+
|
|
120
|
+
// Extract and display results
|
|
121
|
+
const finalMessages = run.getRunMessages();
|
|
122
|
+
console.log('\n\nFinal messages after Test 1:');
|
|
123
|
+
console.dir(finalMessages, { depth: null });
|
|
124
|
+
|
|
125
|
+
// Test 2: Multi-turn conversation
|
|
126
|
+
console.log(
|
|
127
|
+
'\n\nTest 2: Multi-turn conversation with Vertex AI thinking enabled'
|
|
128
|
+
);
|
|
129
|
+
const userMessage2 =
|
|
130
|
+
'Now count the number of letters in "Mississippi". Explain step by step.';
|
|
131
|
+
conversationHistory.push(new HumanMessage(userMessage2));
|
|
132
|
+
|
|
133
|
+
console.log('Running second query with Vertex AI thinking enabled...');
|
|
134
|
+
const secondInputs = { messages: [...conversationHistory] };
|
|
135
|
+
await run.processStream(secondInputs, streamConfig);
|
|
136
|
+
|
|
137
|
+
const finalMessages2 = run.getRunMessages();
|
|
138
|
+
console.log('\n\nVertex AI thinking feature test completed!');
|
|
139
|
+
console.dir(finalMessages2, { depth: null });
|
|
140
|
+
|
|
141
|
+
console.log('\n\nContent parts:');
|
|
142
|
+
console.dir(_contentParts, { depth: null });
|
|
143
|
+
|
|
144
|
+
console.log('\n\nCollected usage:');
|
|
145
|
+
console.dir(collectedUsage, { depth: null });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
149
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
150
|
+
console.log('Conversation history:');
|
|
151
|
+
console.dir(conversationHistory, { depth: null });
|
|
152
|
+
console.log('Content parts:');
|
|
153
|
+
console.dir(_contentParts, { depth: null });
|
|
154
|
+
process.exit(1);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
process.on('uncaughtException', (err) => {
|
|
158
|
+
console.error('Uncaught Exception:', err);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
testVertexAIThinking().catch((err) => {
|
|
162
|
+
console.error(err);
|
|
163
|
+
console.log('Conversation history:');
|
|
164
|
+
console.dir(conversationHistory, { depth: null });
|
|
165
|
+
console.log('Content parts:');
|
|
166
|
+
console.dir(_contentParts, { depth: null });
|
|
167
|
+
process.exit(1);
|
|
168
|
+
});
|