@librechat/agents 3.1.54 → 3.1.56
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 +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 +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 +29 -28
- package/dist/cjs/main.cjs.map +1 -1
- 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 +118 -32
- 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 +5 -2
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/splitStream.cjs.map +1 -1
- package/dist/cjs/stream.cjs +9 -0
- 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 +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 +33 -45
- 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 +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 +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/main.mjs +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 +120 -34
- 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 +5 -2
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/splitStream.mjs.map +1 -1
- package/dist/esm/stream.mjs +10 -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 +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 +33 -46
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/types/llm/vertexai/index.d.ts +1 -1
- package/dist/types/types/graph.d.ts +2 -0
- package/dist/types/types/stream.d.ts +2 -0
- package/dist/types/utils/tokens.d.ts +6 -18
- package/package.json +7 -3
- package/src/llm/vertexai/index.ts +99 -6
- package/src/llm/vertexai/llm.spec.ts +114 -0
- package/src/messages/ensureThinkingBlock.test.ts +502 -27
- package/src/messages/format.ts +155 -44
- package/src/run.ts +6 -2
- package/src/scripts/bedrock-cache-debug.ts +15 -15
- package/src/scripts/code_exec_multi_session.ts +8 -13
- package/src/scripts/image.ts +2 -1
- package/src/scripts/multi-agent-parallel-start.ts +3 -4
- package/src/scripts/multi-agent-sequence.ts +3 -4
- package/src/scripts/single-agent-metadata-test.ts +3 -6
- package/src/scripts/test-tool-before-handoff-role-order.ts +2 -3
- package/src/scripts/test-tools-before-handoff.ts +2 -3
- package/src/scripts/thinking-vertexai.ts +168 -0
- package/src/scripts/tools.ts +1 -7
- package/src/specs/token-memoization.test.ts +35 -34
- package/src/specs/tokens.test.ts +64 -0
- package/src/stream.ts +12 -0
- package/src/types/graph.ts +2 -0
- package/src/types/stream.ts +2 -0
- package/src/utils/tokens.ts +43 -54
|
@@ -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,6 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Tokenizer } from 'ai-tokenizer';
|
|
2
2
|
import { ContentTypes } from '../common/enum.mjs';
|
|
3
3
|
|
|
4
|
+
const tokenizers = {};
|
|
5
|
+
async function getTokenizer(encoding = 'o200k_base') {
|
|
6
|
+
const cached = tokenizers[encoding];
|
|
7
|
+
if (cached) {
|
|
8
|
+
return cached;
|
|
9
|
+
}
|
|
10
|
+
const data = encoding === 'claude'
|
|
11
|
+
? await import('ai-tokenizer/encoding/claude')
|
|
12
|
+
: await import('ai-tokenizer/encoding/o200k_base');
|
|
13
|
+
const instance = new Tokenizer(data);
|
|
14
|
+
tokenizers[encoding] = instance;
|
|
15
|
+
return instance;
|
|
16
|
+
}
|
|
17
|
+
function encodingForModel(model) {
|
|
18
|
+
if (model.toLowerCase().includes('claude')) {
|
|
19
|
+
return 'claude';
|
|
20
|
+
}
|
|
21
|
+
return 'o200k_base';
|
|
22
|
+
}
|
|
4
23
|
function getTokenCountForMessage(message, getTokenCount) {
|
|
5
24
|
const tokensPerMessage = 3;
|
|
6
25
|
const processValue = (value) => {
|
|
@@ -48,61 +67,29 @@ function getTokenCountForMessage(message, getTokenCount) {
|
|
|
48
67
|
processValue(message.content);
|
|
49
68
|
return numTokens;
|
|
50
69
|
}
|
|
51
|
-
let encoderPromise;
|
|
52
|
-
let tokenCounterPromise;
|
|
53
|
-
async function getSharedEncoder() {
|
|
54
|
-
if (encoderPromise) {
|
|
55
|
-
return encoderPromise;
|
|
56
|
-
}
|
|
57
|
-
encoderPromise = (async () => {
|
|
58
|
-
const res = await fetch('https://tiktoken.pages.dev/js/o200k_base.json');
|
|
59
|
-
const o200k_base = await res.json();
|
|
60
|
-
return new Tiktoken(o200k_base);
|
|
61
|
-
})();
|
|
62
|
-
return encoderPromise;
|
|
63
|
-
}
|
|
64
70
|
/**
|
|
65
|
-
* Creates a
|
|
66
|
-
*
|
|
71
|
+
* Creates a token counter function using the specified encoding.
|
|
72
|
+
* Lazily loads the encoding data on first use via dynamic import.
|
|
67
73
|
*/
|
|
68
|
-
const createTokenCounter = async () => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
tokenCounterPromise = (async () => {
|
|
73
|
-
const enc = await getSharedEncoder();
|
|
74
|
-
const countTokens = (text) => enc.encode(text).length;
|
|
75
|
-
return (message) => getTokenCountForMessage(message, countTokens);
|
|
76
|
-
})();
|
|
77
|
-
return tokenCounterPromise;
|
|
74
|
+
const createTokenCounter = async (encoding = 'o200k_base') => {
|
|
75
|
+
const tok = await getTokenizer(encoding);
|
|
76
|
+
const countTokens = (text) => tok.count(text);
|
|
77
|
+
return (message) => getTokenCountForMessage(message, countTokens);
|
|
78
78
|
};
|
|
79
|
-
/**
|
|
80
|
-
* Utility to manage the token encoder lifecycle explicitly.
|
|
81
|
-
* Useful for applications that need fine-grained control over resource management.
|
|
82
|
-
*/
|
|
79
|
+
/** Utility to manage the token encoder lifecycle explicitly. */
|
|
83
80
|
const TokenEncoderManager = {
|
|
84
|
-
/**
|
|
85
|
-
* Pre-initializes the encoder. This can be called during app startup
|
|
86
|
-
* to avoid lazy loading delays later.
|
|
87
|
-
*/
|
|
88
81
|
async initialize() {
|
|
89
|
-
|
|
82
|
+
// No-op: ai-tokenizer is synchronously initialized from bundled data.
|
|
90
83
|
},
|
|
91
|
-
/**
|
|
92
|
-
* Clears the cached encoder and token counter.
|
|
93
|
-
* Useful for testing or when you need to force a fresh reload.
|
|
94
|
-
*/
|
|
95
84
|
reset() {
|
|
96
|
-
|
|
97
|
-
|
|
85
|
+
for (const key of Object.keys(tokenizers)) {
|
|
86
|
+
delete tokenizers[key];
|
|
87
|
+
}
|
|
98
88
|
},
|
|
99
|
-
/**
|
|
100
|
-
* Checks if the encoder has been initialized.
|
|
101
|
-
*/
|
|
102
89
|
isInitialized() {
|
|
103
|
-
return
|
|
90
|
+
return Object.keys(tokenizers).length > 0;
|
|
104
91
|
},
|
|
105
92
|
};
|
|
106
93
|
|
|
107
|
-
export { TokenEncoderManager, createTokenCounter, getTokenCountForMessage };
|
|
94
|
+
export { TokenEncoderManager, createTokenCounter, encodingForModel, getTokenCountForMessage };
|
|
108
95
|
//# sourceMappingURL=tokens.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.mjs","sources":["../../../src/utils/tokens.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"tokens.mjs","sources":["../../../src/utils/tokens.ts"],"sourcesContent":["import { Tokenizer } from 'ai-tokenizer';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport { ContentTypes } from '@/common/enum';\n\nexport type EncodingName = 'o200k_base' | 'claude';\n\nconst tokenizers: Partial<Record<EncodingName, Tokenizer>> = {};\n\nasync function getTokenizer(\n encoding: EncodingName = 'o200k_base'\n): Promise<Tokenizer> {\n const cached = tokenizers[encoding];\n if (cached) {\n return cached;\n }\n const data =\n encoding === 'claude'\n ? await import('ai-tokenizer/encoding/claude')\n : await import('ai-tokenizer/encoding/o200k_base');\n const instance = new Tokenizer(data);\n tokenizers[encoding] = instance;\n return instance;\n}\n\nexport function encodingForModel(model: string): EncodingName {\n if (model.toLowerCase().includes('claude')) {\n return 'claude';\n }\n return 'o200k_base';\n}\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\n/**\n * Creates a token counter function using the specified encoding.\n * Lazily loads the encoding data on first use via dynamic import.\n */\nexport const createTokenCounter = async (\n encoding: EncodingName = 'o200k_base'\n): Promise<(message: BaseMessage) => number> => {\n const tok = await getTokenizer(encoding);\n const countTokens = (text: string): number => tok.count(text);\n return (message: BaseMessage): number =>\n getTokenCountForMessage(message, countTokens);\n};\n\n/** Utility to manage the token encoder lifecycle explicitly. */\nexport const TokenEncoderManager = {\n async initialize(): Promise<void> {\n // No-op: ai-tokenizer is synchronously initialized from bundled data.\n },\n\n reset(): void {\n for (const key of Object.keys(tokenizers)) {\n delete tokenizers[key as EncodingName];\n }\n },\n\n isInitialized(): boolean {\n return Object.keys(tokenizers).length > 0;\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
|
}
|
|
@@ -1,27 +1,15 @@
|
|
|
1
1
|
import type { BaseMessage } from '@langchain/core/messages';
|
|
2
|
+
export type EncodingName = 'o200k_base' | 'claude';
|
|
3
|
+
export declare function encodingForModel(model: string): EncodingName;
|
|
2
4
|
export declare function getTokenCountForMessage(message: BaseMessage, getTokenCount: (text: string) => number): number;
|
|
3
5
|
/**
|
|
4
|
-
* Creates a
|
|
5
|
-
*
|
|
6
|
-
*/
|
|
7
|
-
export declare const createTokenCounter: () => Promise<(message: BaseMessage) => number>;
|
|
8
|
-
/**
|
|
9
|
-
* Utility to manage the token encoder lifecycle explicitly.
|
|
10
|
-
* Useful for applications that need fine-grained control over resource management.
|
|
6
|
+
* Creates a token counter function using the specified encoding.
|
|
7
|
+
* Lazily loads the encoding data on first use via dynamic import.
|
|
11
8
|
*/
|
|
9
|
+
export declare const createTokenCounter: (encoding?: EncodingName) => Promise<(message: BaseMessage) => number>;
|
|
10
|
+
/** Utility to manage the token encoder lifecycle explicitly. */
|
|
12
11
|
export declare const TokenEncoderManager: {
|
|
13
|
-
/**
|
|
14
|
-
* Pre-initializes the encoder. This can be called during app startup
|
|
15
|
-
* to avoid lazy loading delays later.
|
|
16
|
-
*/
|
|
17
12
|
initialize(): Promise<void>;
|
|
18
|
-
/**
|
|
19
|
-
* Clears the cached encoder and token counter.
|
|
20
|
-
* Useful for testing or when you need to force a fresh reload.
|
|
21
|
-
*/
|
|
22
13
|
reset(): void;
|
|
23
|
-
/**
|
|
24
|
-
* Checks if the encoder has been initialized.
|
|
25
|
-
*/
|
|
26
14
|
isInitialized(): boolean;
|
|
27
15
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@librechat/agents",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.56",
|
|
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,8 @@
|
|
|
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",
|
|
138
|
+
"ai-tokenizer": "^1.0.6",
|
|
135
139
|
"axios": "^1.13.5",
|
|
136
140
|
"cheerio": "^1.0.0",
|
|
137
141
|
"dotenv": "^16.4.7",
|
|
@@ -168,7 +172,7 @@
|
|
|
168
172
|
"jest-util": "^30.2.0",
|
|
169
173
|
"lint-staged": "^15.2.7",
|
|
170
174
|
"prettier": "^3.6.2",
|
|
171
|
-
"rollup": "^4.
|
|
175
|
+
"rollup": "^4.59.0",
|
|
172
176
|
"rollup-plugin-cleandir": "^2.0.0",
|
|
173
177
|
"ts-jest": "^29.4.5",
|
|
174
178
|
"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
|
+
);
|