node-llama-cpp 3.5.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/README.md +2 -2
  2. package/dist/ChatWrapper.d.ts +3 -5
  3. package/dist/ChatWrapper.js +57 -5
  4. package/dist/ChatWrapper.js.map +1 -1
  5. package/dist/bindings/AddonTypes.d.ts +1 -1
  6. package/dist/bindings/Llama.js +2 -0
  7. package/dist/bindings/Llama.js.map +1 -1
  8. package/dist/bindings/utils/compileLLamaCpp.js +2 -0
  9. package/dist/bindings/utils/compileLLamaCpp.js.map +1 -1
  10. package/dist/chatWrappers/AlpacaChatWrapper.js.map +1 -1
  11. package/dist/chatWrappers/DeepSeekChatWrapper.d.ts +37 -0
  12. package/dist/chatWrappers/DeepSeekChatWrapper.js +294 -0
  13. package/dist/chatWrappers/DeepSeekChatWrapper.js.map +1 -0
  14. package/dist/chatWrappers/FalconChatWrapper.js.map +1 -1
  15. package/dist/chatWrappers/FunctionaryChatWrapper.js +40 -14
  16. package/dist/chatWrappers/FunctionaryChatWrapper.js.map +1 -1
  17. package/dist/chatWrappers/GeneralChatWrapper.js.map +1 -1
  18. package/dist/chatWrappers/Llama2ChatWrapper.js.map +1 -1
  19. package/dist/chatWrappers/Llama3_1ChatWrapper.d.ts +0 -3
  20. package/dist/chatWrappers/Llama3_1ChatWrapper.js +24 -13
  21. package/dist/chatWrappers/Llama3_1ChatWrapper.js.map +1 -1
  22. package/dist/chatWrappers/Llama3_2LightweightChatWrapper.js +22 -11
  23. package/dist/chatWrappers/Llama3_2LightweightChatWrapper.js.map +1 -1
  24. package/dist/chatWrappers/MistralChatWrapper.d.ts +2 -1
  25. package/dist/chatWrappers/MistralChatWrapper.js +39 -28
  26. package/dist/chatWrappers/MistralChatWrapper.js.map +1 -1
  27. package/dist/chatWrappers/QwenChatWrapper.d.ts +21 -0
  28. package/dist/chatWrappers/QwenChatWrapper.js +162 -0
  29. package/dist/chatWrappers/QwenChatWrapper.js.map +1 -0
  30. package/dist/chatWrappers/generic/JinjaTemplateChatWrapper.d.ts +41 -3
  31. package/dist/chatWrappers/generic/JinjaTemplateChatWrapper.js +343 -126
  32. package/dist/chatWrappers/generic/JinjaTemplateChatWrapper.js.map +1 -1
  33. package/dist/chatWrappers/generic/TemplateChatWrapper.d.ts +17 -1
  34. package/dist/chatWrappers/generic/TemplateChatWrapper.js +10 -2
  35. package/dist/chatWrappers/generic/TemplateChatWrapper.js.map +1 -1
  36. package/dist/chatWrappers/generic/utils/UniqueIdGenerator.d.ts +7 -0
  37. package/dist/chatWrappers/generic/utils/UniqueIdGenerator.js +30 -0
  38. package/dist/chatWrappers/generic/utils/UniqueIdGenerator.js.map +1 -0
  39. package/dist/chatWrappers/generic/utils/chatHistoryFunctionCallMessageTemplate.d.ts +5 -4
  40. package/dist/chatWrappers/generic/utils/extractFunctionCallSettingsFromJinjaTemplate.d.ts +19 -0
  41. package/dist/chatWrappers/generic/utils/extractFunctionCallSettingsFromJinjaTemplate.js +446 -0
  42. package/dist/chatWrappers/generic/utils/extractFunctionCallSettingsFromJinjaTemplate.js.map +1 -0
  43. package/dist/chatWrappers/generic/utils/extractSegmentSettingsFromTokenizerAndChatTemplate.d.ts +2 -0
  44. package/dist/chatWrappers/generic/utils/extractSegmentSettingsFromTokenizerAndChatTemplate.js +38 -0
  45. package/dist/chatWrappers/generic/utils/extractSegmentSettingsFromTokenizerAndChatTemplate.js.map +1 -0
  46. package/dist/chatWrappers/generic/utils/getFirstValidResult.d.ts +6 -0
  47. package/dist/chatWrappers/generic/utils/getFirstValidResult.js +19 -0
  48. package/dist/chatWrappers/generic/utils/getFirstValidResult.js.map +1 -0
  49. package/dist/chatWrappers/generic/utils/squashChatHistoryItems.d.ts +2 -0
  50. package/dist/chatWrappers/generic/utils/squashChatHistoryItems.js +35 -0
  51. package/dist/chatWrappers/generic/utils/squashChatHistoryItems.js.map +1 -0
  52. package/dist/chatWrappers/generic/utils/templateSegmentOptionsToChatWrapperSettings.d.ts +22 -0
  53. package/dist/chatWrappers/generic/utils/templateSegmentOptionsToChatWrapperSettings.js +28 -0
  54. package/dist/chatWrappers/generic/utils/templateSegmentOptionsToChatWrapperSettings.js.map +1 -0
  55. package/dist/chatWrappers/utils/ChatModelFunctionsDocumentationGenerator.d.ts +3 -0
  56. package/dist/chatWrappers/utils/ChatModelFunctionsDocumentationGenerator.js +25 -0
  57. package/dist/chatWrappers/utils/ChatModelFunctionsDocumentationGenerator.js.map +1 -1
  58. package/dist/chatWrappers/utils/isJinjaTemplateEquivalentToSpecializedChatWrapper.js +197 -30
  59. package/dist/chatWrappers/utils/isJinjaTemplateEquivalentToSpecializedChatWrapper.js.map +1 -1
  60. package/dist/chatWrappers/utils/resolveChatWrapper.d.ts +48 -3
  61. package/dist/chatWrappers/utils/resolveChatWrapper.js +15 -5
  62. package/dist/chatWrappers/utils/resolveChatWrapper.js.map +1 -1
  63. package/dist/cli/commands/ChatCommand.js +38 -7
  64. package/dist/cli/commands/ChatCommand.js.map +1 -1
  65. package/dist/cli/recommendedModels.js +93 -10
  66. package/dist/cli/recommendedModels.js.map +1 -1
  67. package/dist/cli/utils/resolveModelRecommendationFileOptions.d.ts +1 -1
  68. package/dist/config.d.ts +1 -1
  69. package/dist/config.js +1 -1
  70. package/dist/config.js.map +1 -1
  71. package/dist/evaluator/LlamaChat/LlamaChat.d.ts +87 -5
  72. package/dist/evaluator/LlamaChat/LlamaChat.js +781 -196
  73. package/dist/evaluator/LlamaChat/LlamaChat.js.map +1 -1
  74. package/dist/evaluator/LlamaChat/utils/contextShiftStrategies/eraseFirstResponseAndKeepFirstSystemChatContextShiftStrategy.js +55 -1
  75. package/dist/evaluator/LlamaChat/utils/contextShiftStrategies/eraseFirstResponseAndKeepFirstSystemChatContextShiftStrategy.js.map +1 -1
  76. package/dist/evaluator/LlamaChatSession/LlamaChatSession.d.ts +22 -7
  77. package/dist/evaluator/LlamaChatSession/LlamaChatSession.js +28 -8
  78. package/dist/evaluator/LlamaChatSession/LlamaChatSession.js.map +1 -1
  79. package/dist/evaluator/LlamaChatSession/utils/LlamaChatSessionPromptCompletionEngine.js +1 -1
  80. package/dist/evaluator/LlamaChatSession/utils/LlamaChatSessionPromptCompletionEngine.js.map +1 -1
  81. package/dist/evaluator/LlamaChatSession/utils/defineChatSessionFunction.d.ts +1 -1
  82. package/dist/evaluator/LlamaChatSession/utils/defineChatSessionFunction.js.map +1 -1
  83. package/dist/evaluator/LlamaCompletion.js +61 -48
  84. package/dist/evaluator/LlamaCompletion.js.map +1 -1
  85. package/dist/evaluator/LlamaGrammar.d.ts +2 -2
  86. package/dist/evaluator/LlamaGrammar.js +5 -3
  87. package/dist/evaluator/LlamaGrammar.js.map +1 -1
  88. package/dist/evaluator/LlamaModel/LlamaModel.d.ts +3 -1
  89. package/dist/evaluator/LlamaModel/LlamaModel.js +4 -1
  90. package/dist/evaluator/LlamaModel/LlamaModel.js.map +1 -1
  91. package/dist/evaluator/LlamaRankingContext.js +1 -1
  92. package/dist/evaluator/LlamaRankingContext.js.map +1 -1
  93. package/dist/gguf/types/GgufMetadataTypes.d.ts +1 -1
  94. package/dist/gguf/types/GgufMetadataTypes.js.map +1 -1
  95. package/dist/index.d.ts +8 -5
  96. package/dist/index.js +4 -2
  97. package/dist/index.js.map +1 -1
  98. package/dist/tsconfig.tsbuildinfo +1 -1
  99. package/dist/types.d.ts +40 -2
  100. package/dist/types.js +7 -1
  101. package/dist/types.js.map +1 -1
  102. package/dist/utils/LlamaText.js +8 -9
  103. package/dist/utils/LlamaText.js.map +1 -1
  104. package/dist/utils/OpenAIFormat.d.ts +177 -0
  105. package/dist/utils/OpenAIFormat.js +488 -0
  106. package/dist/utils/OpenAIFormat.js.map +1 -0
  107. package/dist/utils/TokenStreamRegulator.d.ts +2 -0
  108. package/dist/utils/TokenStreamRegulator.js +12 -0
  109. package/dist/utils/TokenStreamRegulator.js.map +1 -1
  110. package/dist/utils/getChatWrapperSegmentDefinition.d.ts +2 -0
  111. package/dist/utils/getChatWrapperSegmentDefinition.js +7 -0
  112. package/dist/utils/getChatWrapperSegmentDefinition.js.map +1 -0
  113. package/dist/utils/optionsMatrix.d.ts +58 -0
  114. package/dist/utils/optionsMatrix.js +97 -0
  115. package/dist/utils/optionsMatrix.js.map +1 -0
  116. package/dist/utils/parseModelUri.js +1 -1
  117. package/dist/utils/parseModelUri.js.map +1 -1
  118. package/dist/utils/resolveModelFile.js +2 -0
  119. package/dist/utils/resolveModelFile.js.map +1 -1
  120. package/llama/addon/AddonContext.cpp +11 -9
  121. package/llama/binariesGithubRelease.json +1 -1
  122. package/llama/gitRelease.bundle +0 -0
  123. package/llama/grammars/README.md +4 -4
  124. package/llama/llama.cpp.info.json +2 -2
  125. package/package.json +48 -45
  126. package/templates/packed/electron-typescript-react.json +1 -1
  127. package/templates/packed/node-typescript.json +1 -1
@@ -2,7 +2,16 @@ import { Template } from "@huggingface/jinja";
2
2
  import { splitText } from "lifecycle-utils";
3
3
  import { SpecialToken, LlamaText, SpecialTokensText } from "../../utils/LlamaText.js";
4
4
  import { ChatWrapper } from "../../ChatWrapper.js";
5
+ import { fromChatHistoryToIntermediateOpenAiMessages, fromIntermediateToCompleteOpenAiMessages } from "../../utils/OpenAIFormat.js";
6
+ import { removeUndefinedFields } from "../../utils/removeNullFields.js";
7
+ import { jsonDumps } from "../utils/jsonDumps.js";
8
+ import { tryMatrix } from "../../utils/optionsMatrix.js";
5
9
  import { parseFunctionCallMessageTemplate } from "./utils/chatHistoryFunctionCallMessageTemplate.js";
10
+ import { templateSegmentOptionsToChatWrapperSettings } from "./utils/templateSegmentOptionsToChatWrapperSettings.js";
11
+ import { UniqueIdGenerator } from "./utils/UniqueIdGenerator.js";
12
+ import { extractFunctionCallSettingsFromJinjaTemplate } from "./utils/extractFunctionCallSettingsFromJinjaTemplate.js";
13
+ import { squashChatHistoryItems } from "./utils/squashChatHistoryItems.js";
14
+ import { extractSegmentSettingsFromTokenizerAndChatTemplate } from "./utils/extractSegmentSettingsFromTokenizerAndChatTemplate.js";
6
15
  const defaultConvertUnsupportedSystemMessagesToUserMessagesFormat = {
7
16
  format: "### System message\n\n{{message}}\n\n----"
8
17
  };
@@ -25,6 +34,10 @@ const defaultConvertUnsupportedSystemMessagesToUserMessagesFormat = {
25
34
  * // functionCallMessageTemplate: { // optional
26
35
  * // call: "[[call: {{functionName}}({{functionParams}})]]",
27
36
  * // result: " [[result: {{functionCallResult}}]]"
37
+ * // },
38
+ * // segments: {
39
+ * // thoughtTemplate: "<think>{{content}}</think>",
40
+ * // reopenThoughtAfterFunctionCalls: true
28
41
  * // }
29
42
  * });
30
43
  * ```
@@ -43,11 +56,17 @@ export class JinjaTemplateChatWrapper extends ChatWrapper {
43
56
  trimLeadingWhitespaceInResponses;
44
57
  additionalRenderParameters;
45
58
  /** @internal */ _jinjaTemplate;
59
+ /** @internal */ _usingJinjaFunctionCallTemplate = false;
60
+ /** @internal */ _stringifyFunctionParams = false;
61
+ /** @internal */ _stringifyFunctionResult = false;
62
+ /** @internal */ _combineJinjaModelMessageAndToolCalls = true;
63
+ /** @internal */ _endJinjaMessagesWithUserMessage = false;
46
64
  /**
47
65
  * @param options
48
66
  */
49
- constructor({ template, modelRoleName = "assistant", userRoleName = "user", systemRoleName = "system", convertUnsupportedSystemMessagesToUserMessages = defaultConvertUnsupportedSystemMessagesToUserMessagesFormat, functionCallMessageTemplate, joinAdjacentMessagesOfTheSameType = true, trimLeadingWhitespaceInResponses = true, additionalRenderParameters }) {
67
+ constructor(options) {
50
68
  super();
69
+ const { template, modelRoleName = "assistant", userRoleName = "user", systemRoleName = "system", convertUnsupportedSystemMessagesToUserMessages = defaultConvertUnsupportedSystemMessagesToUserMessagesFormat, functionCallMessageTemplate = "auto", joinAdjacentMessagesOfTheSameType = true, trimLeadingWhitespaceInResponses = true, additionalRenderParameters, segments, tokenizer, _requireFunctionCallSettingsExtraction = false } = options;
51
70
  if (template == null)
52
71
  throw new Error("template cannot be null");
53
72
  this.template = template;
@@ -59,50 +78,219 @@ export class JinjaTemplateChatWrapper extends ChatWrapper {
59
78
  this.joinAdjacentMessagesOfTheSameType = joinAdjacentMessagesOfTheSameType;
60
79
  this.trimLeadingWhitespaceInResponses = trimLeadingWhitespaceInResponses;
61
80
  this.additionalRenderParameters = additionalRenderParameters;
62
- this.settings = {
63
- ...ChatWrapper.defaultSettings,
64
- functions: parseFunctionCallMessageTemplate(functionCallMessageTemplate) ?? ChatWrapper.defaultSettings.functions
65
- };
66
81
  if (this.convertUnsupportedSystemMessagesToUserMessages != null && !this.convertUnsupportedSystemMessagesToUserMessages.format.includes("{{message}}"))
67
82
  throw new Error('convertUnsupportedSystemMessagesToUserMessages format must include "{{message}}"');
68
83
  this._jinjaTemplate = new Template(this.template);
69
- const { supportsSystemMessages } = this._runSanityTest();
84
+ this.settings = {
85
+ ...ChatWrapper.defaultSettings,
86
+ segments: templateSegmentOptionsToChatWrapperSettings(segments)
87
+ };
88
+ const { supportsSystemMessages, needsToEndJinjaMessagesWithUserMessage } = this._runSanityTest();
70
89
  this.settings = {
71
90
  ...this.settings,
72
- supportsSystemMessages
91
+ supportsSystemMessages,
92
+ segments: {
93
+ ...this.settings.segments,
94
+ ...extractSegmentSettingsFromTokenizerAndChatTemplate(this.template, tokenizer)
95
+ }
73
96
  };
97
+ if (needsToEndJinjaMessagesWithUserMessage)
98
+ this._endJinjaMessagesWithUserMessage = true;
99
+ let functionCallSettings = parseFunctionCallMessageTemplate((functionCallMessageTemplate === "auto" || functionCallMessageTemplate === "noJinja")
100
+ ? undefined
101
+ : functionCallMessageTemplate);
102
+ if (functionCallSettings == null && functionCallMessageTemplate !== "noJinja") {
103
+ try {
104
+ const idsGenerator = new UniqueIdGenerator(this.template + this.modelRoleName + this.userRoleName + this.systemRoleName +
105
+ (this.convertUnsupportedSystemMessagesToUserMessages?.format ?? ""));
106
+ const extractedSettings = extractFunctionCallSettingsFromJinjaTemplate({
107
+ idsGenerator,
108
+ renderTemplate: ({ chatHistory, functions, additionalParams, stringifyFunctionParams, stringifyFunctionResults, combineModelMessageAndToolCalls, squashModelTextResponses = true }) => {
109
+ const render = (convertSystemMessagesToUserMessagesFormat, wipeFunctionCallIds) => {
110
+ const { messages: intermediateMessages, tools } = fromChatHistoryToIntermediateOpenAiMessages({
111
+ chatHistory: this._transformChatHistory(chatHistory, {
112
+ convertSystemMessagesToUserMessagesFormat,
113
+ joinAdjacentMessagesOfTheSameType: !squashModelTextResponses
114
+ ? false
115
+ : undefined
116
+ }).transformedHistory,
117
+ chatWrapperSettings: this.settings,
118
+ useRawValues: false,
119
+ functions,
120
+ stringifyFunctionParams,
121
+ stringifyFunctionResults,
122
+ combineModelMessageAndToolCalls,
123
+ squashModelTextResponses
124
+ });
125
+ const messages = fromIntermediateToCompleteOpenAiMessages(intermediateMessages)
126
+ .map((item) => {
127
+ if (!wipeFunctionCallIds)
128
+ return item;
129
+ if (item.role === "assistant" && item["tool_calls"] != null && item["tool_calls"].length > 0) {
130
+ for (const toolCall of item["tool_calls"]) {
131
+ if (wipeFunctionCallIds === "align")
132
+ toolCall.id = "fc_1_0001";
133
+ else
134
+ delete toolCall.id;
135
+ }
136
+ }
137
+ else if (item.role === "tool") {
138
+ if (wipeFunctionCallIds === "align")
139
+ item["tool_call_id"] = "fc_1_0001";
140
+ else
141
+ delete item["tool_call_id"];
142
+ }
143
+ return item;
144
+ });
145
+ const lastJinjaItem = messages.at(-1);
146
+ let eraseRenderedJinjaFromId;
147
+ if (this._endJinjaMessagesWithUserMessage && lastJinjaItem?.role === this.modelRoleName &&
148
+ typeof lastJinjaItem.content === "string" &&
149
+ lastJinjaItem.content.length > 0 &&
150
+ (lastJinjaItem["tool_calls"] == null ||
151
+ lastJinjaItem["tool_calls"]?.length === 0)) {
152
+ eraseRenderedJinjaFromId = lastJinjaItem.content;
153
+ messages.push({
154
+ role: this.userRoleName,
155
+ content: idsGenerator.generateId()
156
+ });
157
+ }
158
+ let res = this._jinjaTemplate.render({
159
+ ...(this.additionalRenderParameters == null
160
+ ? {}
161
+ : structuredClone(this.additionalRenderParameters)),
162
+ ...additionalParams,
163
+ messages,
164
+ ...removeUndefinedFields({ tools })
165
+ });
166
+ if (eraseRenderedJinjaFromId != null) {
167
+ const eraseIndex = res.lastIndexOf(eraseRenderedJinjaFromId);
168
+ if (eraseIndex >= 0)
169
+ res = res.slice(0, eraseIndex + eraseRenderedJinjaFromId.length);
170
+ }
171
+ // attempt to remove the ID pattern from the output
172
+ if (wipeFunctionCallIds === "align")
173
+ res = res
174
+ .replaceAll(/,\s*"(tool_call_id|call_id|id)":\s*"fc_1_0001"/g, "")
175
+ .replaceAll(/"(tool_call_id|call_id|id)":\s*"fc_1_0001"\s*,/g, "");
176
+ return res;
177
+ };
178
+ return tryMatrix({
179
+ convertSystemMessagesToUserMessagesFormat: getConvertUnsupportedSystemMessagesToUserMessagesTryOptions(this.convertUnsupportedSystemMessagesToUserMessages),
180
+ wipeFunctionCallIds: [true, "align", false]
181
+ }, ({ convertSystemMessagesToUserMessagesFormat, wipeFunctionCallIds }) => {
182
+ return render(convertSystemMessagesToUserMessagesFormat, wipeFunctionCallIds);
183
+ });
184
+ }
185
+ });
186
+ functionCallSettings = extractedSettings.settings;
187
+ if (functionCallSettings != null) {
188
+ this._usingJinjaFunctionCallTemplate = true;
189
+ this._stringifyFunctionParams = extractedSettings.stringifyParams;
190
+ this._stringifyFunctionResult = extractedSettings.stringifyResult;
191
+ }
192
+ }
193
+ catch (err) {
194
+ // do nothing
195
+ }
196
+ if (functionCallSettings == null && _requireFunctionCallSettingsExtraction)
197
+ throw new Error("failed to extract function call settings from the Jinja template");
198
+ }
199
+ this.settings = {
200
+ ...this.settings,
201
+ functions: functionCallSettings ?? ChatWrapper.defaultSettings.functions
202
+ };
203
+ }
204
+ /**
205
+ * Whether the function call syntax settings were extracted from the given Jinja template.
206
+ *
207
+ * The function call syntax settings can be accessed using the `.settings.functions` property.
208
+ */
209
+ get usingJinjaFunctionCallTemplate() {
210
+ return this._usingJinjaFunctionCallTemplate;
74
211
  }
75
212
  generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
76
- const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(chatHistory, availableFunctions, {
77
- documentParams: documentFunctionParams
213
+ const { contextText, stopGenerationTriggers, ignoreStartText, functionCall, transformedSystemMessagesToUserMessages } = this._generateContextState({
214
+ chatHistory, availableFunctions, documentFunctionParams,
215
+ endJinjaMessagesWithUserMessage: this._endJinjaMessagesWithUserMessage
78
216
  });
79
- if (this.convertUnsupportedSystemMessagesToUserMessages == null) {
80
- return this._generateContextText(historyWithFunctions, {
81
- convertSystemMessagesToUserMessagesFormat: undefined
82
- });
83
- }
84
- else if (this.convertUnsupportedSystemMessagesToUserMessages.use === "always") {
85
- return this._generateContextText(historyWithFunctions, {
86
- convertSystemMessagesToUserMessagesFormat: this.convertUnsupportedSystemMessagesToUserMessages.format
87
- });
88
- }
89
- try {
90
- return this._generateContextText(historyWithFunctions, {
91
- convertSystemMessagesToUserMessagesFormat: undefined
217
+ return { contextText, stopGenerationTriggers, ignoreStartText, functionCall, transformedSystemMessagesToUserMessages };
218
+ }
219
+ addAvailableFunctionsSystemMessageToHistory(history, availableFunctions, options = {}) {
220
+ if (this._usingJinjaFunctionCallTemplate)
221
+ return history;
222
+ return super.addAvailableFunctionsSystemMessageToHistory(history, availableFunctions, options);
223
+ }
224
+ generateFunctionCall(name, params) {
225
+ if (!this._stringifyFunctionParams)
226
+ return super.generateFunctionCall(name, params);
227
+ const emptyCallParamsPlaceholder = this.settings.functions.call.emptyCallParamsPlaceholder;
228
+ return LlamaText([
229
+ this.settings.functions.call.prefix,
230
+ name,
231
+ this.settings.functions.call.paramsPrefix,
232
+ (params === undefined
233
+ ? (emptyCallParamsPlaceholder === undefined || emptyCallParamsPlaceholder === "")
234
+ ? ""
235
+ : JSON.stringify(jsonDumps(emptyCallParamsPlaceholder))
236
+ : JSON.stringify(jsonDumps(params))),
237
+ this.settings.functions.call.suffix
238
+ ]);
239
+ }
240
+ generateFunctionCallResult(functionName, functionParams, result) {
241
+ const resolveParameters = (text) => {
242
+ return LlamaText(text)
243
+ .mapValues((value) => {
244
+ if (typeof value !== "string")
245
+ return value;
246
+ const funcParamsText = functionParams === undefined
247
+ ? ""
248
+ : jsonDumps(functionParams);
249
+ return value
250
+ .replaceAll("{{functionName}}", functionName)
251
+ .replaceAll("{{functionParams}}", (this._stringifyFunctionParams && funcParamsText !== "")
252
+ ? JSON.stringify(funcParamsText)
253
+ : funcParamsText);
92
254
  });
93
- }
94
- catch (error) {
95
- return this._generateContextText(historyWithFunctions, {
96
- convertSystemMessagesToUserMessagesFormat: this.convertUnsupportedSystemMessagesToUserMessages.format
255
+ };
256
+ const resultText = result === undefined
257
+ ? "void"
258
+ : jsonDumps(result);
259
+ return LlamaText([
260
+ resolveParameters(this.settings.functions.result.prefix),
261
+ ((this._stringifyFunctionResult && result !== undefined)
262
+ ? JSON.stringify(resultText)
263
+ : resultText),
264
+ resolveParameters(this.settings.functions.result.suffix)
265
+ ]);
266
+ }
267
+ /** @internal */
268
+ _generateContextState({ chatHistory, availableFunctions, documentFunctionParams, endJinjaMessagesWithUserMessage }) {
269
+ return tryMatrix({
270
+ convertSystemMessagesToUserMessagesFormat: getConvertUnsupportedSystemMessagesToUserMessagesTryOptions(this.convertUnsupportedSystemMessagesToUserMessages),
271
+ endJinjaMessagesWithUserMessage: endJinjaMessagesWithUserMessage == null
272
+ ? [false, true]
273
+ : [endJinjaMessagesWithUserMessage],
274
+ useMessagesWithEmbeddedTools: this._usingJinjaFunctionCallTemplate
275
+ ? [undefined, true]
276
+ : [undefined]
277
+ }, ({ useMessagesWithEmbeddedTools, endJinjaMessagesWithUserMessage, convertSystemMessagesToUserMessagesFormat }) => {
278
+ return this._generateContextText(chatHistory, {
279
+ convertSystemMessagesToUserMessagesFormat, availableFunctions, documentFunctionParams,
280
+ endJinjaMessagesWithUserMessage,
281
+ useMessagesWithEmbeddedTools
97
282
  });
98
- }
283
+ });
99
284
  }
100
285
  /** @internal */
101
- _generateContextText(history, { convertSystemMessagesToUserMessagesFormat }) {
286
+ _transformChatHistory(history, { convertSystemMessagesToUserMessagesFormat, availableFunctions, documentFunctionParams = true, joinAdjacentMessagesOfTheSameType = this.joinAdjacentMessagesOfTheSameType }) {
287
+ const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(history, availableFunctions, {
288
+ documentParams: documentFunctionParams
289
+ });
102
290
  let transformedSystemMessagesToUserMessages = false;
103
291
  const transformedHistory = convertSystemMessagesToUserMessagesFormat == null
104
- ? history
105
- : history.map((item) => {
292
+ ? historyWithFunctions
293
+ : historyWithFunctions.map((item) => {
106
294
  if (item.type === "system") {
107
295
  transformedSystemMessagesToUserMessages = true;
108
296
  return {
@@ -112,58 +300,89 @@ export class JinjaTemplateChatWrapper extends ChatWrapper {
112
300
  }
113
301
  return item;
114
302
  });
115
- const resultItems = [];
116
- const currentTexts = [];
117
- let currentAggregateFocus = null;
118
- function flush() {
119
- if (currentTexts.length > 0 && currentAggregateFocus != null)
120
- resultItems.push({ role: currentAggregateFocus, content: LlamaText.joinValues("\n\n", currentTexts) });
121
- currentTexts.length = 0;
122
- }
123
- for (const item of transformedHistory) {
124
- if (item.type === "system") {
125
- if (!this.joinAdjacentMessagesOfTheSameType || currentAggregateFocus !== "system")
126
- flush();
127
- currentAggregateFocus = "system";
128
- currentTexts.push(LlamaText.fromJSON(item.text));
129
- }
130
- else if (item.type === "user") {
131
- if (!this.joinAdjacentMessagesOfTheSameType || currentAggregateFocus !== "user")
132
- flush();
133
- currentAggregateFocus = "user";
134
- currentTexts.push(LlamaText(item.text));
135
- }
136
- else if (item.type === "model") {
137
- if (!this.joinAdjacentMessagesOfTheSameType || currentAggregateFocus !== "model")
138
- flush();
139
- currentAggregateFocus = "model";
140
- currentTexts.push(this.generateModelResponseText(item.response));
141
- }
142
- else
303
+ return {
304
+ transformedHistory: joinAdjacentMessagesOfTheSameType
305
+ ? squashChatHistoryItems(transformedHistory)
306
+ : transformedHistory,
307
+ transformedSystemMessagesToUserMessages
308
+ };
309
+ }
310
+ /** @internal */
311
+ _generateContextText(history, { convertSystemMessagesToUserMessagesFormat, availableFunctions, documentFunctionParams = true, endJinjaMessagesWithUserMessage, useMessagesWithEmbeddedTools = false }) {
312
+ const { transformedSystemMessagesToUserMessages, transformedHistory } = this._transformChatHistory(history, { convertSystemMessagesToUserMessagesFormat, availableFunctions, documentFunctionParams });
313
+ const generateMessagesWithEmbeddedTools = (chatHistory) => ({
314
+ messages: chatHistory.map((item) => {
315
+ if (item.type === "system")
316
+ return {
317
+ role: "system",
318
+ content: LlamaText.fromJSON(item.text)
319
+ };
320
+ else if (item.type === "user")
321
+ return {
322
+ role: "user",
323
+ content: LlamaText(item.text)
324
+ };
325
+ else if (item.type === "model")
326
+ return {
327
+ role: "assistant",
328
+ content: this.generateModelResponseText(item.response)
329
+ };
143
330
  void item;
144
- }
145
- const lastItemIsModelMessage = currentAggregateFocus === "model";
146
- flush();
147
- const idsGenerator = new UniqueTemplateId(this.template + this.modelRoleName + this.userRoleName + this.systemRoleName +
148
- (convertSystemMessagesToUserMessagesFormat ?? "") + resultItems.map(({ content }) => content.toString()).join("\n\n"));
331
+ return { role: "user", content: LlamaText("") };
332
+ }),
333
+ tools: undefined
334
+ });
335
+ const generateMessagesWithTools = (chatHistory) => (fromChatHistoryToIntermediateOpenAiMessages({
336
+ chatHistory,
337
+ chatWrapperSettings: this.settings,
338
+ useRawValues: false,
339
+ functions: (availableFunctions != null && !documentFunctionParams)
340
+ ? Object.fromEntries(Object.entries(availableFunctions)
341
+ .map(([funcName, { description, ...func }]) => [funcName, func]))
342
+ : availableFunctions,
343
+ stringifyFunctionParams: this._stringifyFunctionParams,
344
+ stringifyFunctionResults: this._stringifyFunctionResult,
345
+ combineModelMessageAndToolCalls: this._combineJinjaModelMessageAndToolCalls
346
+ }));
347
+ const lastItemIsModelMessage = transformedHistory.at(-1)?.type === "model";
348
+ const { messages: intermediateMessages, tools } = this._usingJinjaFunctionCallTemplate
349
+ ? useMessagesWithEmbeddedTools
350
+ ? {
351
+ messages: generateMessagesWithEmbeddedTools(transformedHistory).messages,
352
+ tools: generateMessagesWithTools(transformedHistory).tools
353
+ }
354
+ : generateMessagesWithTools(transformedHistory)
355
+ : generateMessagesWithEmbeddedTools(transformedHistory);
356
+ const idsGenerator = new UniqueIdGenerator(this.template + this.modelRoleName + this.userRoleName + this.systemRoleName +
357
+ (convertSystemMessagesToUserMessagesFormat ?? "") +
358
+ intermediateMessages.map(({ content }) => (content?.toString() ?? "")).join("\n\n"));
149
359
  const jinjaItems = [];
150
360
  const jinjaRoleMap = {
151
361
  system: this.systemRoleName,
152
362
  user: this.userRoleName,
153
- model: this.modelRoleName
363
+ assistant: this.modelRoleName,
364
+ tool: "tool"
154
365
  };
155
366
  const idToContent = new Map();
156
367
  const modelMessageIds = new Set();
157
368
  const messageIds = new Set();
158
- for (const resultItem of resultItems) {
159
- const id = idsGenerator.generateId();
369
+ for (const intermediateMessage of intermediateMessages) {
370
+ if (intermediateMessage.content == null) {
371
+ jinjaItems.push({
372
+ ...intermediateMessage,
373
+ role: jinjaRoleMap[intermediateMessage.role] ?? intermediateMessage.role
374
+ });
375
+ continue;
376
+ }
377
+ const id = idsGenerator.generateId(intermediateMessage.role === "tool");
160
378
  messageIds.add(id);
161
- idToContent.set(id, resultItem.content);
379
+ idToContent.set(id, LlamaText(intermediateMessage.content));
162
380
  jinjaItems.push({
163
- role: jinjaRoleMap[resultItem.role],
381
+ ...intermediateMessage,
382
+ role: jinjaRoleMap[intermediateMessage.role] ?? intermediateMessage.role,
164
383
  content: id
165
384
  });
166
- if (resultItem.role === "model")
385
+ if (intermediateMessage.role === "assistant" || intermediateMessage.role === "tool")
167
386
  modelMessageIds.add(id);
168
387
  }
169
388
  const bosTokenId = idsGenerator.generateId();
@@ -172,41 +391,39 @@ export class JinjaTemplateChatWrapper extends ChatWrapper {
172
391
  idToContent.set(bosTokenId, new SpecialToken("BOS"));
173
392
  idToContent.set(eosTokenId, new SpecialToken("EOS"));
174
393
  idToContent.set(eotTokenId, new SpecialToken("EOT"));
175
- function tryOptions(options) {
176
- for (let i = 0; i < options.length; i++) {
177
- if (i === options.length - 1)
178
- return options[i]();
179
- try {
180
- return options[i]();
181
- }
182
- catch (err) {
183
- // do nothing
184
- }
185
- }
186
- throw new Error("All options failed");
394
+ const lastJinjaItem = jinjaItems.at(-1);
395
+ let eraseRenderedJinjaFromId;
396
+ if (endJinjaMessagesWithUserMessage && lastJinjaItem?.role === this.modelRoleName &&
397
+ typeof lastJinjaItem.content === "string" &&
398
+ lastJinjaItem.content.length > 0 &&
399
+ (lastJinjaItem["tool_calls"] == null ||
400
+ lastJinjaItem["tool_calls"]?.length === 0)) {
401
+ eraseRenderedJinjaFromId = lastJinjaItem.content;
402
+ jinjaItems.push({
403
+ role: this.userRoleName,
404
+ content: idsGenerator.generateId()
405
+ });
187
406
  }
188
407
  const renderJinjaText = () => {
189
- return tryOptions([
190
- () => this._jinjaTemplate.render({
191
- ...(this.additionalRenderParameters == null
192
- ? {}
193
- : structuredClone(this.additionalRenderParameters)),
194
- messages: jinjaItems,
195
- "bos_token": bosTokenId,
196
- "eos_token": eosTokenId,
197
- "eot_token": eotTokenId
198
- }),
199
- () => this._jinjaTemplate.render({
200
- ...(this.additionalRenderParameters == null
201
- ? {}
202
- : structuredClone(this.additionalRenderParameters)),
203
- messages: jinjaItems,
204
- "bos_token": bosTokenId,
205
- "eos_token": eosTokenId,
206
- "eot_token": eotTokenId,
207
- "add_generation_prompt": true
208
- })
209
- ]);
408
+ let res = tryMatrix({
409
+ options: [{}, { "add_generation_prompt": true }]
410
+ }, ({ options }) => (this._jinjaTemplate.render({
411
+ ...(this.additionalRenderParameters == null
412
+ ? {}
413
+ : structuredClone(this.additionalRenderParameters)),
414
+ messages: jinjaItems,
415
+ ...removeUndefinedFields({ tools }),
416
+ "bos_token": bosTokenId,
417
+ "eos_token": eosTokenId,
418
+ "eot_token": eotTokenId,
419
+ ...options
420
+ })));
421
+ if (eraseRenderedJinjaFromId != null) {
422
+ const eraseIndex = res.lastIndexOf(eraseRenderedJinjaFromId);
423
+ if (eraseIndex >= 0)
424
+ res = res.slice(0, eraseIndex + eraseRenderedJinjaFromId.length);
425
+ }
426
+ return res;
210
427
  };
211
428
  const validateThatAllMessageIdsAreUsed = (parts) => {
212
429
  const messageIdsLeft = new Set(messageIds);
@@ -290,48 +507,41 @@ export class JinjaTemplateChatWrapper extends ChatWrapper {
290
507
  }))
291
508
  ])
292
509
  ],
293
- transformedSystemMessagesToUserMessages
510
+ transformedSystemMessagesToUserMessages,
511
+ endJinjaMessagesWithUserMessage
294
512
  };
295
513
  }
296
514
  /**
297
515
  * Validate that this Jinja template can be rendered
298
516
  * @internal
299
517
  */
300
- _runSanityTest() {
518
+ _runSanityTest(needsToEndJinjaMessagesWithUserMessage = false) {
301
519
  try {
302
520
  let supportsSystemMessages = true;
303
521
  for (const chatHistory of chatHistoriesForSanityTest) {
304
- const { transformedSystemMessagesToUserMessages } = this.generateContextState({ chatHistory });
522
+ const { transformedSystemMessagesToUserMessages, endJinjaMessagesWithUserMessage: endedJinjaMessagesWithUserMessage } = this._generateContextState({
523
+ chatHistory,
524
+ endJinjaMessagesWithUserMessage: needsToEndJinjaMessagesWithUserMessage
525
+ ? true
526
+ : undefined
527
+ });
305
528
  if (transformedSystemMessagesToUserMessages)
306
529
  supportsSystemMessages = false;
530
+ if (!needsToEndJinjaMessagesWithUserMessage && endedJinjaMessagesWithUserMessage) {
531
+ if (chatHistory !== chatHistoriesForSanityTest[0])
532
+ // validate tha this doesn't break the template
533
+ return this._runSanityTest(true);
534
+ else
535
+ needsToEndJinjaMessagesWithUserMessage = true;
536
+ }
307
537
  }
308
- return { supportsSystemMessages };
538
+ return { supportsSystemMessages, needsToEndJinjaMessagesWithUserMessage };
309
539
  }
310
540
  catch (err) {
311
541
  throw new Error("The provided Jinja template failed the sanity test: " + String(err) + ". Inspect the Jinja template to find out what went wrong");
312
542
  }
313
543
  }
314
544
  }
315
- class UniqueTemplateId {
316
- antiText;
317
- _ids = new Set();
318
- constructor(antiText) {
319
- this.antiText = antiText;
320
- }
321
- generateId() {
322
- let id;
323
- do {
324
- id = "W" + (Math.random()
325
- .toString(36)
326
- .slice(2)) + "W";
327
- } while (this._ids.has(id) || this.antiText.includes(id));
328
- this._ids.add(id);
329
- return id;
330
- }
331
- removeId(id) {
332
- this._ids.delete(id);
333
- }
334
- }
335
545
  function resolveConvertUnsupportedSystemMessagesToUserMessagesOption(convertUnsupportedSystemMessagesToUserMessages) {
336
546
  if (convertUnsupportedSystemMessagesToUserMessages === false)
337
547
  return undefined;
@@ -352,6 +562,13 @@ function resolveConvertUnsupportedSystemMessagesToUserMessagesOption(convertUnsu
352
562
  };
353
563
  return { ...defaultConvertUnsupportedSystemMessagesToUserMessagesFormat, use: "ifNeeded" };
354
564
  }
565
+ function getConvertUnsupportedSystemMessagesToUserMessagesTryOptions(convertUnsupportedSystemMessagesToUserMessages) {
566
+ if (convertUnsupportedSystemMessagesToUserMessages == null)
567
+ return [undefined];
568
+ else if (convertUnsupportedSystemMessagesToUserMessages.use === "always")
569
+ return [convertUnsupportedSystemMessagesToUserMessages.format];
570
+ return [undefined, convertUnsupportedSystemMessagesToUserMessages.format];
571
+ }
355
572
  const chatHistoriesForSanityTest = [
356
573
  [{
357
574
  type: "system",