First upload version 0.0.1

This commit is contained in:
Neyra
2026-02-05 15:27:49 +08:00
commit 8e9b7201ed
4182 changed files with 593136 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
import { GeneralChatWrapper } from "./GeneralChatWrapper.js";
/**
* This chat wrapper is not safe against chat syntax injection attacks
* ([learn more](https://node-llama-cpp.withcat.ai/guide/llama-text#input-safety-in-node-llama-cpp)).
*/
export declare class AlpacaChatWrapper extends GeneralChatWrapper {
readonly wrapperName: string;
constructor({ userMessageTitle, modelResponseTitle, middleSystemMessageTitle, allowSpecialTokensInTitles }?: {
userMessageTitle?: string;
modelResponseTitle?: string;
middleSystemMessageTitle?: string;
allowSpecialTokensInTitles?: boolean;
});
get userMessageTitle(): string;
get modelResponseTitle(): string;
get middleSystemMessageTitle(): string;
}

View File

@@ -0,0 +1,33 @@
import { GeneralChatWrapper } from "./GeneralChatWrapper.js";
/**
* This chat wrapper is not safe against chat syntax injection attacks
* ([learn more](https://node-llama-cpp.withcat.ai/guide/llama-text#input-safety-in-node-llama-cpp)).
*/
export class AlpacaChatWrapper extends GeneralChatWrapper {
wrapperName = "AlpacaChat";
constructor({ userMessageTitle = "Instruction", modelResponseTitle = "Response", middleSystemMessageTitle = "System", allowSpecialTokensInTitles = false } = {}) {
super({
userMessageTitle: userMessageTitle + ":",
modelResponseTitle: modelResponseTitle + ":",
middleSystemMessageTitle: middleSystemMessageTitle + ":",
allowSpecialTokensInTitles
});
}
get userMessageTitle() {
return super.userMessageTitle.slice(0, -1);
}
get modelResponseTitle() {
return super.modelResponseTitle.slice(0, -1);
}
get middleSystemMessageTitle() {
return super.middleSystemMessageTitle.slice(0, -1);
}
/** @internal */
static _getOptionConfigurationsToTestIfCanSupersedeJinjaTemplate() {
return [
{},
{ allowSpecialTokensInTitles: true }
];
}
}
//# sourceMappingURL=AlpacaChatWrapper.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"AlpacaChatWrapper.js","sourceRoot":"","sources":["../../src/chatWrappers/AlpacaChatWrapper.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,kBAAkB,EAAC,MAAM,yBAAyB,CAAC;AAE3D;;;GAGG;AACH,MAAM,OAAO,iBAAkB,SAAQ,kBAAkB;IAC5B,WAAW,GAAW,YAAY,CAAC;IAE5D,YAAmB,EACf,gBAAgB,GAAG,aAAa,EAAE,kBAAkB,GAAG,UAAU,EAAE,wBAAwB,GAAG,QAAQ,EACtG,0BAA0B,GAAG,KAAK,KAGlC,EAAE;QACF,KAAK,CAAC;YACF,gBAAgB,EAAE,gBAAgB,GAAG,GAAG;YACxC,kBAAkB,EAAE,kBAAkB,GAAG,GAAG;YAC5C,wBAAwB,EAAE,wBAAwB,GAAG,GAAG;YACxD,0BAA0B;SAC7B,CAAC,CAAC;IACP,CAAC;IAED,IAAoB,gBAAgB;QAChC,OAAO,KAAK,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;IAED,IAAoB,kBAAkB;QAClC,OAAO,KAAK,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,IAAoB,wBAAwB;QACxC,OAAO,KAAK,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;IAED,gBAAgB;IACT,MAAM,CAAU,yDAAyD;QAC5E,OAAO;YACH,EAAE;YACF,EAAC,0BAA0B,EAAE,IAAI,EAAC;SACrC,CAAC;IACN,CAAC;CACJ"}

View File

@@ -0,0 +1,6 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState } from "../types.js";
export declare class ChatMLChatWrapper extends ChatWrapper {
readonly wrapperName: string;
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState;
}

View File

@@ -0,0 +1,85 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { SpecialToken, LlamaText, SpecialTokensText } from "../utils/LlamaText.js";
// source: https://github.com/openai/openai-python/blob/120d225b91a8453e15240a49fb1c6794d8119326/chatml.md
export class ChatMLChatWrapper extends ChatWrapper {
wrapperName = "ChatML";
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(chatHistory, availableFunctions, {
documentParams: documentFunctionParams
});
const resultItems = [];
let systemTexts = [];
let userTexts = [];
let modelTexts = [];
let currentAggregateFocus = null;
function flush() {
if (systemTexts.length > 0 || userTexts.length > 0 || modelTexts.length > 0)
resultItems.push({
system: LlamaText.joinValues("\n\n", systemTexts),
user: LlamaText.joinValues("\n\n", userTexts),
model: LlamaText.joinValues("\n\n", modelTexts)
});
systemTexts = [];
userTexts = [];
modelTexts = [];
}
for (const item of historyWithFunctions) {
if (item.type === "system") {
if (currentAggregateFocus !== "system")
flush();
currentAggregateFocus = "system";
systemTexts.push(LlamaText.fromJSON(item.text));
}
else if (item.type === "user") {
flush();
currentAggregateFocus = null;
userTexts.push(LlamaText(item.text));
}
else if (item.type === "model") {
flush();
currentAggregateFocus = null;
modelTexts.push(this.generateModelResponseText(item.response));
}
else
void item;
}
flush();
const contextText = LlamaText(new SpecialToken("BOS"), resultItems.map(({ system, user, model }, index) => {
const isLastItem = index === resultItems.length - 1;
return LlamaText([
(system.values.length === 0)
? LlamaText([])
: LlamaText([
new SpecialTokensText("<|im_start|>system\n"),
system,
new SpecialTokensText("<|im_end|>\n")
]),
(user.values.length === 0)
? LlamaText([])
: LlamaText([
new SpecialTokensText("<|im_start|>user\n"),
user,
new SpecialTokensText("<|im_end|>\n")
]),
(model.values.length === 0 && !isLastItem)
? LlamaText([])
: LlamaText([
new SpecialTokensText("<|im_start|>assistant\n"),
model,
isLastItem
? LlamaText([])
: new SpecialTokensText("<|im_end|>\n")
])
]);
}));
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialTokensText("<|im_end|>")),
LlamaText("<|im_end|>")
]
};
}
}
//# sourceMappingURL=ChatMLChatWrapper.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ChatMLChatWrapper.js","sourceRoot":"","sources":["../../src/chatWrappers/ChatMLChatWrapper.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,37 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { ChatModelFunctions, ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState, ChatWrapperSettings } from "../types.js";
export declare class DeepSeekChatWrapper extends ChatWrapper {
readonly wrapperName: string;
readonly keepOnlyLastThought: boolean;
readonly functionCallingSyntax: "r1-workaround" | "simplified" | "original";
readonly parallelFunctionCalling: boolean;
readonly settings: ChatWrapperSettings;
constructor(options?: {
/**
* Whether to keep only the chain of thought from the last model response.
*
* Setting this to `false` will keep all the chain of thoughts from the model responses in the context state.
*
* Defaults to `true`.
*/
keepOnlyLastThought?: boolean;
/**
* Use a different variation function calling syntax to improve syntax compliance.
*
* Defaults to `"r1-workaround"`.
*/
functionCallingSyntax?: "r1-workaround" | "simplified" | "original";
/**
* Support parallel function calling.
*
* May not work well with all distill model variations, as some distillation models make unnecessary additional calls in parallel.
*
* Defaults to `false`.
*/
parallelFunctionCalling?: boolean;
});
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState;
generateAvailableFunctionsSystemText(availableFunctions: ChatModelFunctions, { documentParams }: {
documentParams?: boolean;
}): import("../utils/LlamaText.js")._LlamaText;
}

View File

@@ -0,0 +1,294 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { isChatModelResponseSegment } from "../types.js";
import { SpecialToken, LlamaText, SpecialTokensText } from "../utils/LlamaText.js";
import { ChatModelFunctionsDocumentationGenerator } from "./utils/ChatModelFunctionsDocumentationGenerator.js";
import { jsonDumps } from "./utils/jsonDumps.js";
export class DeepSeekChatWrapper extends ChatWrapper {
wrapperName = "DeepSeek";
keepOnlyLastThought;
functionCallingSyntax;
parallelFunctionCalling;
settings;
constructor(options = {}) {
super();
const { keepOnlyLastThought = true, functionCallingSyntax = "r1-workaround", parallelFunctionCalling = false } = options;
this.keepOnlyLastThought = keepOnlyLastThought;
this.functionCallingSyntax = functionCallingSyntax;
this.parallelFunctionCalling = parallelFunctionCalling;
const getFunctionsSettings = () => {
if (functionCallingSyntax === "original") {
if (parallelFunctionCalling)
return {
call: {
optionalPrefixSpace: true,
prefix: LlamaText(new SpecialTokensText("<tool▁call▁begin>function<tool▁sep>")),
paramsPrefix: LlamaText(new SpecialTokensText("\n```json\n")),
suffix: LlamaText([new SpecialTokensText("\n```<tool▁call▁end>")])
},
result: {
prefix: LlamaText(new SpecialTokensText("<tool▁output▁begin>")),
suffix: LlamaText(new SpecialTokensText("<tool▁output▁end>"))
},
parallelism: {
call: {
sectionPrefix: LlamaText(new SpecialTokensText("<tool▁calls▁begin>")),
betweenCalls: LlamaText(new SpecialTokensText("\n")),
sectionSuffix: LlamaText(new SpecialTokensText("<tool▁calls▁end><end▁of▁sentence>"))
},
result: {
sectionPrefix: LlamaText(new SpecialTokensText("<tool▁outputs▁begin>")),
betweenResults: LlamaText(new SpecialTokensText("\n")),
sectionSuffix: LlamaText(new SpecialTokensText("<tool▁outputs▁end><Assistant>"))
}
}
};
else
return {
call: {
optionalPrefixSpace: true,
prefix: LlamaText(new SpecialTokensText("<tool▁calls▁begin><tool▁call▁begin>function<tool▁sep>")),
paramsPrefix: LlamaText(new SpecialTokensText("\n```json\n")),
suffix: LlamaText([new SpecialTokensText("\n```<tool▁call▁end><tool▁calls▁end><end▁of▁sentence>")])
},
result: {
prefix: LlamaText(new SpecialTokensText("<tool▁outputs▁begin><tool▁output▁begin>")),
suffix: LlamaText(new SpecialTokensText("<tool▁output▁end><tool▁outputs▁end><Assistant>"))
}
};
}
else if (functionCallingSyntax === "simplified") {
if (parallelFunctionCalling)
return {
call: {
optionalPrefixSpace: true,
prefix: LlamaText(new SpecialTokensText("<tool▁call▁begin>")),
paramsPrefix: LlamaText(new SpecialTokensText("<tool▁sep>\n```json\n")),
suffix: LlamaText([new SpecialTokensText("\n```<tool▁call▁end>")])
},
result: {
prefix: LlamaText(new SpecialTokensText("<tool▁output▁begin>")),
suffix: LlamaText(new SpecialTokensText("<tool▁output▁end>"))
},
parallelism: {
call: {
sectionPrefix: LlamaText(new SpecialTokensText("<tool▁calls▁begin>")),
betweenCalls: LlamaText(new SpecialTokensText("\n")),
sectionSuffix: LlamaText(new SpecialTokensText("<tool▁calls▁end><end▁of▁sentence>"))
},
result: {
sectionPrefix: LlamaText(new SpecialTokensText("<tool▁outputs▁begin>")),
betweenResults: LlamaText(new SpecialTokensText("\n")),
sectionSuffix: LlamaText(new SpecialTokensText("<tool▁outputs▁end>"))
}
}
};
else
return {
call: {
optionalPrefixSpace: true,
prefix: LlamaText(new SpecialTokensText("<tool▁call▁begin>")),
paramsPrefix: LlamaText(new SpecialTokensText("<tool▁sep>\n```json\n")),
suffix: LlamaText([new SpecialTokensText("\n```<tool▁call▁end><end▁of▁sentence>")])
},
result: {
prefix: LlamaText(new SpecialTokensText("<tool▁output▁begin>")),
suffix: LlamaText(new SpecialTokensText("<tool▁output▁end>"))
}
};
}
void functionCallingSyntax;
if (parallelFunctionCalling)
throw new Error(`parallel function calling is not supported with "${"r1-workaround"}" syntax`);
return {
call: {
optionalPrefixSpace: true,
prefix: LlamaText(new SpecialTokensText("<function=")),
paramsPrefix: LlamaText(new SpecialTokensText(">")),
suffix: LlamaText(new SpecialTokensText("</function>"))
},
result: {
prefix: LlamaText(new SpecialTokensText("<tool▁output▁begin>")),
suffix: LlamaText(new SpecialTokensText("<tool▁output▁end>\n"))
}
};
};
this.settings = {
supportsSystemMessages: true,
functions: getFunctionsSettings(),
segments: {
reiterateStackAfterFunctionCalls: true,
thought: {
prefix: LlamaText(new SpecialTokensText("<think>")),
suffix: LlamaText(new SpecialTokensText("</think>")),
reopenAfterFunctionCalls: functionCallingSyntax === "simplified"
}
}
};
}
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(chatHistory, availableFunctions, {
documentParams: documentFunctionParams
});
const systemTexts = historyWithFunctions
.filter((item) => item.type === "system")
.map((item) => LlamaText.fromJSON(item.text));
const contextText = LlamaText(new SpecialToken("BOS"), LlamaText.joinValues("\n\n", systemTexts), historyWithFunctions.map((item, index) => {
const isLastItem = index === historyWithFunctions.length - 1;
// system messages are already prepended at the beginning
if (item.type === "system")
return LlamaText([]);
if (item.type === "user")
return LlamaText([
new SpecialTokensText("<User>"),
item.text
]);
else if (item.type === "model")
return LlamaText([
new SpecialTokensText("<Assistant>"),
this.generateModelResponseText((this.keepOnlyLastThought && !isLastItem)
? item.response.filter((response) => (!isChatModelResponseSegment(response) || response.segmentType !== "thought"))
: item.response),
isLastItem
? LlamaText([])
: new SpecialTokensText("<end▁of▁sentence>")
]);
void item;
return LlamaText([]);
}));
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialToken("EOT")),
LlamaText(new SpecialTokensText("<end▁of▁sentence>")),
LlamaText(new SpecialTokensText("<User>"))
]
};
}
generateAvailableFunctionsSystemText(availableFunctions, { documentParams = true }) {
const functionsDocumentationGenerator = new ChatModelFunctionsDocumentationGenerator(availableFunctions);
if (!functionsDocumentationGenerator.hasAnyFunctions)
return LlamaText([]);
if (this.functionCallingSyntax === "r1-workaround") {
return LlamaText.joinValues("\n", [
"The assistant calls the provided functions as needed to retrieve information instead of relying on existing knowledge.",
"To fulfill a request, the assistant calls relevant functions in advance when needed before responding to the request, and does not tell the user prior to calling a function.",
"If the result of function calls from previous turns might be stale, the assistant will call the functions again if needed.",
// "The assistant NEVER relies on existing knowledge when there's a function that can be used instead.",
"Provided functions:",
functionsDocumentationGenerator.getLlama3_2LightweightFunctionSignatures({ documentParams }),
"",
"Calling any of the provided functions can be done like this:",
LlamaText.joinValues("", [
this.settings.functions.call.prefix,
"getSomeInfo",
this.settings.functions.call.paramsPrefix,
jsonDumps({ someKey: "someValue" }),
this.settings.functions.call.suffix
]),
"",
LlamaText(["Note that the verbatim ", this.settings.functions.call.prefix, " prefix is mandatory."]),
"",
"The assistant never assumes the results of function calls, and instead uses the raw results directly for processing.",
"The assistant does not inform the user about using functions and does not explain anything before calling a function.",
"After calling a function, the raw result appears afterwards and is not part of the conversation.",
"To make information be part of the conversation, the assistant paraphrases and repeats the information without the function syntax.",
"The assistant never repeats itself unless necessary."
]);
}
return LlamaText.joinValues("\n", [
"You have access to the following functions:",
functionsDocumentationGenerator.getLlama3_2LightweightFunctionSignatures({ documentParams }),
"",
this.parallelFunctionCalling
? LlamaText.joinValues("\n", [
"If you choose to call a function, use the following format:",
LlamaText([
this.settings.functions.parallelism.call.sectionPrefix,
this.settings.functions.call.prefix,
"funcName",
this.settings.functions.call.paramsPrefix,
"parameters",
this.settings.functions.call.suffix,
this.settings.functions.parallelism.call.sectionSuffix
]),
"where",
"",
"funcName => the function name to call",
"parameters => a JSON dict with the function arguments",
// "",
// "Note that the following syntax is mandatory to precede the function name:",
// LlamaText([
// this.settings.functions.parallelism!.call.sectionPrefix,
// this.settings.functions.call.prefix
// ]),
"",
"You can call multiple functions in parallel using the following format:",
LlamaText([
this.settings.functions.parallelism.call.sectionPrefix,
this.settings.functions.call.prefix,
"funcName1",
this.settings.functions.call.paramsPrefix,
"parameters1",
this.settings.functions.call.suffix,
this.settings.functions.parallelism.call.betweenCalls ?? "",
this.settings.functions.call.prefix,
"funcName2",
this.settings.functions.call.paramsPrefix,
"parameters2",
this.settings.functions.call.suffix,
this.settings.functions.parallelism.call.sectionSuffix
])
])
: LlamaText.joinValues("\n", [
"If you choose to call a function, use the following format:",
LlamaText.joinValues("", [
this.settings.functions.call.prefix,
"funcName",
this.settings.functions.call.paramsPrefix,
"parameters",
this.settings.functions.call.suffix
]),
"where",
"",
"funcName => the function name to call",
"parameters => a JSON dict with the function arguments"
]),
"",
"Reminder:",
"- Function calls MUST follow the specified format verbatim",
this.parallelFunctionCalling
? LlamaText.joinValues("\n", [
"- You can call a single or multiple functions at a time, their responses will appear afterwards in the order they were called",
"- After calling functions, the results will appear afterwards and are visible only to you"
])
: LlamaText.joinValues("\n", [
"- Only call one function at a time",
"- After calling a function, the result will appear afterwards and is visible only to you"
]),
"- Do not inform the user about using functions and do not explain anything before calling a function",
"- After calling a function, the raw result appears afterwards and is not part of the conversation.",
"- To make information be part of the conversation, paraphrase and repeat the information without the function syntax.",
"- To make information visible to the user, you MUST include it in your response",
"- Call functions when needed and avoid redundant calls",
"- NEVER speak about functions, just use them",
"- NEVER tell the user about the functions you are using",
"- NEVER repeat yourself unless necessary",
LlamaText([
"- After calling a function, use ", new SpecialTokensText("</think>"), " to finish thinking and respond to the user"
])
]);
}
/** @internal */
static _getOptionConfigurationsToTestIfCanSupersedeJinjaTemplate() {
return [
[undefined, {}, { functionCallMessageTemplate: "noJinja" }],
[undefined, { keepOnlyLastThought: true }, { functionCallMessageTemplate: "noJinja" }],
[undefined, { functionCallingSyntax: "simplified" }, { functionCallMessageTemplate: "noJinja" }],
[undefined, { functionCallingSyntax: "simplified", keepOnlyLastThought: true }, { functionCallMessageTemplate: "noJinja" }],
[undefined, { functionCallingSyntax: "original" }, { functionCallMessageTemplate: "noJinja" }],
[undefined, { functionCallingSyntax: "original", keepOnlyLastThought: true }, { functionCallMessageTemplate: "noJinja" }]
];
}
}
//# sourceMappingURL=DeepSeekChatWrapper.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
import { ChatWrapper } from "../ChatWrapper.js";
export declare class EmptyChatWrapper extends ChatWrapper {
readonly wrapperName: string;
}

View File

@@ -0,0 +1,5 @@
import { ChatWrapper } from "../ChatWrapper.js";
export class EmptyChatWrapper extends ChatWrapper {
wrapperName = "Empty";
}
//# sourceMappingURL=EmptyChatWrapper.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"EmptyChatWrapper.js","sourceRoot":"","sources":["../../src/chatWrappers/EmptyChatWrapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAE9C,MAAM,OAAO,gBAAiB,SAAQ,WAAW;IAC7B,WAAW,GAAW,OAAO,CAAC;CACjD"}

View File

@@ -0,0 +1,19 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState } from "../types.js";
/**
* This chat wrapper is not safe against chat syntax injection attacks
* ([learn more](https://node-llama-cpp.withcat.ai/guide/llama-text#input-safety-in-node-llama-cpp)).
*/
export declare class FalconChatWrapper extends ChatWrapper {
readonly wrapperName: string;
constructor({ userMessageTitle, modelResponseTitle, middleSystemMessageTitle, allowSpecialTokensInTitles }?: {
userMessageTitle?: string;
modelResponseTitle?: string;
middleSystemMessageTitle?: string;
allowSpecialTokensInTitles?: boolean;
});
get userMessageTitle(): string;
get modelResponseTitle(): string;
get middleSystemMessageTitle(): string;
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState;
}

View File

@@ -0,0 +1,126 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { LlamaText, SpecialToken, SpecialTokensText } from "../utils/LlamaText.js";
/**
* This chat wrapper is not safe against chat syntax injection attacks
* ([learn more](https://node-llama-cpp.withcat.ai/guide/llama-text#input-safety-in-node-llama-cpp)).
*/
export class FalconChatWrapper extends ChatWrapper {
wrapperName = "Falcon";
/** @internal */ _userMessageTitle;
/** @internal */ _modelResponseTitle;
/** @internal */ _middleSystemMessageTitle;
/** @internal */ _allowSpecialTokensInTitles;
constructor({ userMessageTitle = "User", modelResponseTitle = "Assistant", middleSystemMessageTitle = "System", allowSpecialTokensInTitles = false } = {}) {
super();
this._userMessageTitle = userMessageTitle;
this._modelResponseTitle = modelResponseTitle;
this._middleSystemMessageTitle = middleSystemMessageTitle;
this._allowSpecialTokensInTitles = allowSpecialTokensInTitles;
}
get userMessageTitle() {
return this._userMessageTitle;
}
get modelResponseTitle() {
return this._modelResponseTitle;
}
get middleSystemMessageTitle() {
return this._middleSystemMessageTitle;
}
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(chatHistory, availableFunctions, {
documentParams: documentFunctionParams
});
const resultItems = [];
let systemTexts = [];
let userTexts = [];
let modelTexts = [];
let currentAggregateFocus = null;
function flush() {
if (systemTexts.length > 0 || userTexts.length > 0 || modelTexts.length > 0)
resultItems.push({
system: LlamaText.joinValues("\n\n", systemTexts),
user: LlamaText.joinValues("\n\n", userTexts),
model: LlamaText.joinValues("\n\n", modelTexts)
});
systemTexts = [];
userTexts = [];
modelTexts = [];
}
for (const item of historyWithFunctions) {
if (item.type === "system") {
if (currentAggregateFocus !== "system")
flush();
currentAggregateFocus = "system";
systemTexts.push(LlamaText.fromJSON(item.text));
}
else if (item.type === "user") {
flush();
currentAggregateFocus = null;
userTexts.push(LlamaText(item.text));
}
else if (item.type === "model") {
flush();
currentAggregateFocus = null;
modelTexts.push(this.generateModelResponseText(item.response));
}
else
void item;
}
flush();
const contextText = LlamaText(new SpecialToken("BOS"), resultItems.map(({ system, user, model }, index) => {
const isFirstItem = index === 0;
const isLastItem = index === resultItems.length - 1;
return LlamaText([
(system.values.length === 0)
? LlamaText([])
: LlamaText([
isFirstItem
? LlamaText([])
: SpecialTokensText.wrapIf(this._allowSpecialTokensInTitles, `${this._middleSystemMessageTitle}: `),
system,
SpecialTokensText.wrapIf(this._allowSpecialTokensInTitles, "\n\n")
]),
(user.values.length === 0)
? LlamaText([])
: LlamaText([
SpecialTokensText.wrapIf(this._allowSpecialTokensInTitles, `${this._userMessageTitle}: `),
user,
SpecialTokensText.wrapIf(this._allowSpecialTokensInTitles, "\n\n")
]),
(model.values.length === 0 && !isLastItem)
? LlamaText([])
: LlamaText([
SpecialTokensText.wrapIf(this._allowSpecialTokensInTitles, `${this._modelResponseTitle}: `),
model,
isLastItem
? LlamaText([])
: SpecialTokensText.wrapIf(this._allowSpecialTokensInTitles, "\n\n")
])
]);
}));
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(`\n${this._userMessageTitle}:`),
LlamaText(`\n${this._modelResponseTitle}:`),
LlamaText(`\n${this._middleSystemMessageTitle}:`),
...(!this._allowSpecialTokensInTitles
? []
: [
LlamaText(new SpecialTokensText(`\n${this._userMessageTitle}:`)),
LlamaText(new SpecialTokensText(`\n${this._modelResponseTitle}:`)),
LlamaText(new SpecialTokensText(`\n${this._middleSystemMessageTitle}:`))
])
]
};
}
/** @internal */
static _getOptionConfigurationsToTestIfCanSupersedeJinjaTemplate() {
return [
{},
{ allowSpecialTokensInTitles: true }
];
}
}
//# sourceMappingURL=FalconChatWrapper.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FalconChatWrapper.js","sourceRoot":"","sources":["../../src/chatWrappers/FalconChatWrapper.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,17 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { ChatHistoryItem, ChatModelFunctions, ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState, ChatWrapperSettings } from "../types.js";
export declare class FunctionaryChatWrapper extends ChatWrapper {
readonly wrapperName: string;
readonly variation: "v3" | "v2" | "v2.llama3";
readonly settings: ChatWrapperSettings;
constructor({ variation }?: {
variation?: "v3" | "v2" | "v2.llama3";
});
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState;
generateAvailableFunctionsSystemText(availableFunctions: ChatModelFunctions, { documentParams }: {
documentParams?: boolean;
}): import("../utils/LlamaText.js")._LlamaText;
addAvailableFunctionsSystemMessageToHistory(history: readonly ChatHistoryItem[], availableFunctions?: ChatModelFunctions, { documentParams }?: {
documentParams?: boolean;
}): readonly ChatHistoryItem[];
}

View File

@@ -0,0 +1,622 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { isChatModelResponseFunctionCall, isChatModelResponseSegment } from "../types.js";
import { LlamaText, SpecialToken, SpecialTokensText } from "../utils/LlamaText.js";
import { ChatModelFunctionsDocumentationGenerator } from "./utils/ChatModelFunctionsDocumentationGenerator.js";
import { jsonDumps } from "./utils/jsonDumps.js";
// source: https://github.com/MeetKai/functionary/blob/main/tests/prompt_test_v2.txt
export class FunctionaryChatWrapper extends ChatWrapper {
wrapperName = "Functionary";
variation;
settings;
constructor({ variation = "v3" } = {}) {
super();
this.variation = variation;
if (variation === "v3")
this.settings = {
...ChatWrapper.defaultSettings,
supportsSystemMessages: true,
functions: {
call: {
optionalPrefixSpace: true,
prefix: LlamaText(new SpecialTokensText(">>>")),
paramsPrefix: LlamaText(new SpecialTokensText("\n")),
suffix: ""
},
result: {
prefix: LlamaText([
new SpecialTokensText("<|start_header_id|>tool<|end_header_id|>\n\n")
]),
suffix: LlamaText(new SpecialTokensText("<|eot_id|>"))
},
parallelism: {
call: {
sectionPrefix: "",
betweenCalls: "",
sectionSuffix: LlamaText(new SpecialTokensText("<|eot_id|>"))
},
result: {
sectionPrefix: "",
betweenResults: "",
sectionSuffix: ""
}
}
}
};
else if (variation === "v2.llama3")
this.settings = {
...ChatWrapper.defaultSettings,
supportsSystemMessages: true,
functions: {
call: {
optionalPrefixSpace: true,
prefix: LlamaText(new SpecialTokensText("<|reserved_special_token_249|>")),
paramsPrefix: LlamaText(new SpecialTokensText("\n")),
suffix: ""
},
result: {
prefix: LlamaText([
new SpecialTokensText("<|start_header_id|>tool<|end_header_id|>\n\nname="),
"{{functionName}}",
new SpecialTokensText("\n")
]),
suffix: LlamaText(new SpecialTokensText("<|eot_id|>"))
},
parallelism: {
call: {
sectionPrefix: "",
betweenCalls: "",
sectionSuffix: LlamaText(new SpecialTokensText("<|eot_id|>"))
},
result: {
sectionPrefix: "",
betweenResults: "",
sectionSuffix: ""
}
}
}
};
else
this.settings = {
...ChatWrapper.defaultSettings,
supportsSystemMessages: true,
functions: {
call: {
optionalPrefixSpace: true,
prefix: LlamaText(new SpecialTokensText("\n<|from|>assistant\n<|recipient|>")),
paramsPrefix: LlamaText(new SpecialTokensText("\n<|content|>")),
suffix: ""
},
result: {
prefix: LlamaText([
new SpecialTokensText("\n<|from|>"),
"{{functionName}}",
new SpecialTokensText("\n<|recipient|>all\n<|content|>")
]),
suffix: ""
},
parallelism: {
call: {
sectionPrefix: "",
betweenCalls: "\n",
sectionSuffix: LlamaText(new SpecialTokensText("<|stop|>"))
},
result: {
sectionPrefix: "",
betweenResults: "",
sectionSuffix: ""
}
}
}
};
}
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
if (this.variation === "v3")
return this._generateContextStateV3({ chatHistory, availableFunctions, documentFunctionParams });
else if (this.variation === "v2.llama3")
return this._generateContextStateV2Llama3({ chatHistory, availableFunctions, documentFunctionParams });
return this._generateContextStateV2({ chatHistory, availableFunctions, documentFunctionParams });
}
/** @internal */
_generateContextStateV3({ chatHistory, availableFunctions, documentFunctionParams }) {
const hasFunctions = Object.keys(availableFunctions ?? {}).length > 0;
const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(chatHistory, availableFunctions, {
documentParams: documentFunctionParams
});
const contextText = LlamaText(historyWithFunctions.map((item, index) => {
const isLastItem = index === historyWithFunctions.length - 1;
if (item.type === "system") {
if (item.text.length === 0)
return "";
return LlamaText([
new SpecialTokensText("<|start_header_id|>system<|end_header_id|>\n\n"),
LlamaText.fromJSON(item.text),
new SpecialTokensText("<|eot_id|>")
]);
}
else if (item.type === "user") {
return LlamaText([
new SpecialTokensText("<|start_header_id|>user<|end_header_id|>\n\n"),
item.text,
new SpecialTokensText("<|eot_id|>")
]);
}
else if (item.type === "model") {
if (isLastItem && item.response.length === 0)
return LlamaText([
new SpecialTokensText("<|start_header_id|>assistant<|end_header_id|>\n\n")
]);
const res = [];
const pendingFunctionCalls = [];
const pendingFunctionResults = [];
const addPendingFunctions = () => {
if (pendingFunctionResults.length === 0)
return;
res.push(LlamaText(pendingFunctionCalls));
res.push(LlamaText(new SpecialTokensText("<|eot_id|>")));
res.push(LlamaText(pendingFunctionResults));
pendingFunctionResults.length = 0;
};
const simplifiedResponse = convertModelResponseToLamaTextAndFunctionCalls(item.response, this);
for (let index = 0; index < simplifiedResponse.length; index++) {
const response = simplifiedResponse[index];
const isLastResponse = index === simplifiedResponse.length - 1;
if (response == null)
continue;
if (LlamaText.isLlamaText(response)) {
addPendingFunctions();
res.push(LlamaText([
new SpecialTokensText("<|start_header_id|>assistant<|end_header_id|>\n\n"),
(isLastResponse && response.values.length === 0)
? hasFunctions
? LlamaText(new SpecialTokensText(">>>"))
: LlamaText(new SpecialTokensText(">>>all\n"))
: LlamaText([
new SpecialTokensText(">>>all\n"),
response,
(!isLastResponse || isLastItem)
? LlamaText([])
: new SpecialTokensText("<|eot_id|>")
])
]));
}
else if (isChatModelResponseFunctionCall(response)) {
if (response.startsNewChunk)
addPendingFunctions();
pendingFunctionCalls.push(response.rawCall != null
? LlamaText.fromJSON(response.rawCall)
: LlamaText([
new SpecialTokensText(">>>"),
response.name,
new SpecialTokensText("\n"),
response.params === undefined
? ""
: jsonDumps(response.params)
]));
pendingFunctionResults.push(LlamaText([
new SpecialTokensText("<|start_header_id|>tool<|end_header_id|>\n\n"),
response.result === undefined
? "" // "void"
: jsonDumps(response.result),
new SpecialTokensText("<|eot_id|>")
]));
}
else
void response;
}
addPendingFunctions();
if (isLastItem && (res.length === 0 || typeof item.response[item.response.length - 1] !== "string"))
res.push(hasFunctions
? LlamaText(new SpecialTokensText("<|start_header_id|>assistant<|end_header_id|>\n\n"))
: LlamaText(new SpecialTokensText("<|start_header_id|>assistant<|end_header_id|>\n\n>>>all\n")));
return LlamaText(res);
}
void item;
return "";
}));
const lastItem = historyWithFunctions.at(-1);
if (!hasFunctions || (lastItem?.type === "model" &&
lastItem.response.length > 0 &&
typeof lastItem.response.at(-1) === "string" &&
lastItem.response.at(-1) !== "")) {
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialToken("EOT")),
LlamaText(new SpecialTokensText("<|eot_id|>")),
LlamaText(new SpecialTokensText("<|end_of_text|>")),
LlamaText("<|eot_id|>"),
LlamaText("<|end_of_text|>")
]
};
}
const textResponseStart = [
LlamaText(new SpecialTokensText(">>>all\n")),
LlamaText(">>>all\n")
];
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialToken("EOT")),
LlamaText(new SpecialTokensText("<|eot_id|>")),
LlamaText(new SpecialTokensText("<|end_of_text|>")),
LlamaText("<|eot_id|>"),
LlamaText("<|end_of_text|>")
],
ignoreStartText: textResponseStart,
functionCall: {
initiallyEngaged: true,
disengageInitiallyEngaged: textResponseStart
}
};
}
/** @internal */
_generateContextStateV2Llama3({ chatHistory, availableFunctions, documentFunctionParams }) {
const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(chatHistory, availableFunctions, {
documentParams: documentFunctionParams
});
const contextText = LlamaText(new SpecialToken("BOS"), historyWithFunctions.map((item, index) => {
const isLastItem = index === historyWithFunctions.length - 1;
if (item.type === "system") {
if (item.text.length === 0)
return "";
return LlamaText([
new SpecialTokensText("<|start_header_id|>system<|end_header_id|>\n\n"),
LlamaText.fromJSON(item.text),
new SpecialTokensText("<|eot_id|>")
]);
}
else if (item.type === "user") {
return LlamaText([
new SpecialTokensText("<|start_header_id|>user<|end_header_id|>\n\n"),
item.text,
new SpecialTokensText("<|eot_id|>")
]);
}
else if (item.type === "model") {
if (isLastItem && item.response.length === 0)
return LlamaText([
new SpecialTokensText("<|start_header_id|>assistant<|end_header_id|>\n\n")
]);
const res = [];
const pendingFunctionCalls = [];
const pendingFunctionResults = [];
const addPendingFunctions = () => {
if (pendingFunctionResults.length === 0)
return;
res.push(LlamaText(pendingFunctionCalls));
res.push(LlamaText(new SpecialTokensText("<|eot_id|>")));
res.push(LlamaText(pendingFunctionResults));
pendingFunctionResults.length = 0;
};
const simplifiedResponse = convertModelResponseToLamaTextAndFunctionCalls(item.response, this);
for (let index = 0; index < simplifiedResponse.length; index++) {
const response = simplifiedResponse[index];
const isLastResponse = index === simplifiedResponse.length - 1;
if (response == null)
continue;
if (LlamaText.isLlamaText(response)) {
addPendingFunctions();
res.push(LlamaText([
new SpecialTokensText("<|start_header_id|>assistant<|end_header_id|>\n\n"),
response,
(isLastItem && isLastResponse)
? LlamaText([])
: new SpecialTokensText("<|eot_id|>")
]));
}
else if (isChatModelResponseFunctionCall(response)) {
if (response.startsNewChunk)
addPendingFunctions();
pendingFunctionCalls.push(response.rawCall != null
? LlamaText.fromJSON(response.rawCall)
: LlamaText([
new SpecialTokensText("<|reserved_special_token_249|>"),
response.name,
new SpecialTokensText("\n"),
response.params === undefined
? ""
: jsonDumps(response.params)
]));
pendingFunctionResults.push(LlamaText([
new SpecialTokensText("<|start_header_id|>tool<|end_header_id|>\n\nname="),
response.name,
new SpecialTokensText("\n"),
response.result === undefined
? "" // "void"
: jsonDumps(response.result),
new SpecialTokensText("<|eot_id|>")
]));
}
else
void response;
}
addPendingFunctions();
if (isLastItem && (res.length === 0 || typeof item.response[item.response.length - 1] !== "string"))
res.push(LlamaText([
new SpecialTokensText("<|start_header_id|>assistant<|end_header_id|>\n\n")
]));
return LlamaText(res);
}
void item;
return "";
}));
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialToken("EOT")),
LlamaText(new SpecialTokensText("<|eot_id|>")),
LlamaText(new SpecialTokensText("<|end_of_text|>")),
LlamaText("<|eot_id|>"),
LlamaText("<|end_of_text|>")
]
};
}
/** @internal */
_generateContextStateV2({ chatHistory, availableFunctions, documentFunctionParams }) {
const hasFunctions = Object.keys(availableFunctions ?? {}).length > 0;
const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(chatHistory, availableFunctions, {
documentParams: documentFunctionParams
});
const contextText = LlamaText(new SpecialToken("BOS"), historyWithFunctions.map((item, index) => {
const isFirstItem = index === 0;
const isLastItem = index === historyWithFunctions.length - 1;
if (item.type === "system") {
if (item.text.length === 0)
return "";
return LlamaText([
isFirstItem
? LlamaText([])
: new SpecialTokensText("\n"),
new SpecialTokensText("<|from|>system\n"),
new SpecialTokensText("<|recipient|>all\n"),
new SpecialTokensText("<|content|>"),
LlamaText.fromJSON(item.text)
]);
}
else if (item.type === "user") {
return LlamaText([
isFirstItem
? LlamaText([])
: new SpecialTokensText("\n"),
new SpecialTokensText("<|from|>user\n"),
new SpecialTokensText("<|recipient|>all\n"),
new SpecialTokensText("<|content|>"),
item.text
]);
}
else if (item.type === "model") {
if (isLastItem && item.response.length === 0 && !hasFunctions)
return LlamaText([
isFirstItem
? LlamaText([])
: new SpecialTokensText("\n"),
new SpecialTokensText("<|from|>assistant\n"),
new SpecialTokensText("<|recipient|>all\n"),
new SpecialTokensText("<|content|>")
]);
const res = [];
const pendingFunctionCalls = [];
const pendingFunctionResults = [];
const addPendingFunctions = () => {
if (pendingFunctionResults.length === 0)
return;
res.push(LlamaText(pendingFunctionCalls));
res.push(LlamaText(new SpecialTokensText("<|stop|>")));
res.push(LlamaText(pendingFunctionResults));
pendingFunctionResults.length = 0;
};
const simplifiedResponse = convertModelResponseToLamaTextAndFunctionCalls(item.response, this);
for (let index = 0; index < simplifiedResponse.length; index++) {
const response = simplifiedResponse[index];
const isFirstResponse = index === 0;
if (response == null)
continue;
if (LlamaText.isLlamaText(response)) {
addPendingFunctions();
res.push(LlamaText([
(isFirstItem && isFirstResponse)
? LlamaText([])
: new SpecialTokensText("\n"),
new SpecialTokensText("<|from|>assistant\n"),
new SpecialTokensText("<|recipient|>all\n"),
new SpecialTokensText("<|content|>"),
response
]));
}
else if (isChatModelResponseFunctionCall(response)) {
pendingFunctionCalls.push(response.rawCall != null
? LlamaText.fromJSON(response.rawCall)
: LlamaText([
(isFirstItem && isFirstResponse)
? LlamaText([])
: new SpecialTokensText("\n"),
new SpecialTokensText("<|from|>assistant\n"),
new SpecialTokensText("<|recipient|>"), response.name, new SpecialTokensText("\n"),
new SpecialTokensText("<|content|>"),
response.params === undefined
? ""
: jsonDumps(response.params)
]));
pendingFunctionResults.push(LlamaText([
new SpecialTokensText("\n"),
new SpecialTokensText("<|from|>"), response.name, new SpecialTokensText("\n"),
new SpecialTokensText("<|recipient|>all\n"),
new SpecialTokensText("<|content|>"),
response.result === undefined
? "" // "void"
: jsonDumps(response.result)
]));
}
else
void response;
}
addPendingFunctions();
if (res.length === 0) {
if (isLastItem) {
if (!hasFunctions)
res.push(LlamaText([
isFirstItem
? LlamaText([])
: new SpecialTokensText("\n"),
new SpecialTokensText("<|from|>assistant\n"),
new SpecialTokensText("<|recipient|>all\n"),
new SpecialTokensText("<|content|>")
]));
}
else
res.push(LlamaText([
isFirstItem
? LlamaText([])
: new SpecialTokensText("\n"),
new SpecialTokensText("<|from|>assistant\n"),
new SpecialTokensText("<|recipient|>all\n"),
new SpecialTokensText("<|content|>")
]));
}
else if (isLastItem && typeof item.response[item.response.length - 1] !== "string") {
if (!hasFunctions)
res.push(LlamaText([
isFirstItem
? LlamaText([])
: new SpecialTokensText("\n"),
new SpecialTokensText("<|from|>assistant\n"),
new SpecialTokensText("<|recipient|>all\n"),
new SpecialTokensText("<|content|>")
]));
}
if (!isLastItem)
res.push(LlamaText(new SpecialTokensText("<|stop|>")));
return LlamaText(res);
}
void item;
return "";
}));
if (!hasFunctions) {
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialTokensText("<|stop|>")),
LlamaText(" <|stop|>"),
LlamaText("<|stop|>"),
LlamaText("\n<|from|>user"),
LlamaText("\n<|from|>assistant"),
LlamaText("\n<|from|>system"),
LlamaText(new SpecialTokensText(" <|stop|>")),
LlamaText(new SpecialTokensText("<|stop|>")),
LlamaText(new SpecialTokensText("\n<|from|>user")),
LlamaText(new SpecialTokensText("\n<|from|>assistant")),
LlamaText(new SpecialTokensText("\n<|from|>system"))
]
};
}
const textResponseStart = [
"\n",
"\n\n",
" \n",
" \n\n"
].flatMap((prefix) => [
LlamaText(new SpecialTokensText(prefix + "<|from|>assistant\n<|recipient|>all\n<|content|>")),
LlamaText(prefix + "<|from|>assistant\n<|recipient|>all\n<|content|>")
]);
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialTokensText("<|stop|>")),
LlamaText(" <|stop|>"),
LlamaText("<|stop|>"),
LlamaText("\n<|from|>user"),
LlamaText(new SpecialTokensText(" <|stop|>")),
LlamaText(new SpecialTokensText("<|stop|>")),
LlamaText(new SpecialTokensText("\n<|from|>user"))
],
ignoreStartText: textResponseStart,
functionCall: {
initiallyEngaged: true,
disengageInitiallyEngaged: textResponseStart
}
};
}
generateAvailableFunctionsSystemText(availableFunctions, { documentParams = true }) {
const functionsDocumentationGenerator = new ChatModelFunctionsDocumentationGenerator(availableFunctions);
if (!functionsDocumentationGenerator.hasAnyFunctions)
return LlamaText([]);
const availableFunctionNames = Object.keys(availableFunctions ?? {});
if (availableFunctionNames.length === 0)
return LlamaText([]);
if (this.variation === "v3") {
return LlamaText.joinValues("\n", [
"You are capable of executing available function(s) if required.",
"Only execute function(s) when absolutely necessary.",
"Ask for the required input to:recipient==all",
"Use JSON for function arguments.",
"Respond in this format:",
">>>${recipient}",
"${content}",
"Available functions:",
"// Supported function definitions that should be called when necessary.",
"namespace functions {",
"",
functionsDocumentationGenerator.getTypeScriptFunctionTypes({ documentParams, reservedFunctionNames: ["all"] }),
"",
"} // namespace functions"
]);
}
return LlamaText.joinValues("\n", [
"// Supported function definitions that should be called when necessary.",
"namespace functions {",
"",
functionsDocumentationGenerator.getTypeScriptFunctionTypes({ documentParams, reservedFunctionNames: ["all"] }),
"",
"} // namespace functions"
]);
}
addAvailableFunctionsSystemMessageToHistory(history, availableFunctions, { documentParams = true } = {}) {
const availableFunctionNames = Object.keys(availableFunctions ?? {});
if (availableFunctions == null || availableFunctionNames.length === 0)
return history;
const res = history.slice();
const firstSystemMessageIndex = res.findIndex((item) => item.type === "system");
res.splice(Math.max(0, firstSystemMessageIndex), 0, {
type: "system",
text: this.generateAvailableFunctionsSystemText(availableFunctions, { documentParams }).toJSON()
}, {
type: "system",
text: "The assistant calls functions with appropriate input when necessary. The assistant writes <|stop|> when finished answering."
});
return res;
}
/** @internal */
static _getOptionConfigurationsToTestIfCanSupersedeJinjaTemplate() {
return [
{ variation: "v3" },
{ variation: "v2.llama3" },
{ variation: "v2" }
];
}
}
function convertModelResponseToLamaTextAndFunctionCalls(modelResponse, chatWrapper) {
const pendingItems = [];
const res = [];
function pushPendingItems() {
if (pendingItems.length === 0)
return;
res.push(chatWrapper.generateModelResponseText(pendingItems));
pendingItems.length = 0;
}
for (const item of modelResponse) {
if (typeof item === "string" || isChatModelResponseSegment(item))
pendingItems.push(item);
else {
pushPendingItems();
res.push(item);
}
}
pushPendingItems();
return res;
}
//# sourceMappingURL=FunctionaryChatWrapper.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,7 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState, ChatWrapperSettings } from "../types.js";
export declare class GemmaChatWrapper extends ChatWrapper {
readonly wrapperName: string;
readonly settings: ChatWrapperSettings;
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState;
}

View File

@@ -0,0 +1,96 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { SpecialToken, LlamaText, SpecialTokensText } from "../utils/LlamaText.js";
// source: https://ai.google.dev/gemma/docs/formatting
// source: https://www.promptingguide.ai/models/gemma
export class GemmaChatWrapper extends ChatWrapper {
wrapperName = "Gemma";
settings = {
...ChatWrapper.defaultSettings,
supportsSystemMessages: false
};
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(chatHistory, availableFunctions, {
documentParams: documentFunctionParams
});
const resultItems = [];
let systemTexts = [];
let userTexts = [];
let modelTexts = [];
let currentAggregateFocus = null;
function flush() {
if (systemTexts.length > 0 || userTexts.length > 0 || modelTexts.length > 0) {
const systemText = LlamaText.joinValues("\n\n", systemTexts);
let userText = LlamaText.joinValues("\n\n", userTexts);
// there's no system prompt support in Gemma, so we'll prepend the system text to the user message
if (systemText.values.length > 0) {
if (userText.values.length === 0)
userText = systemText;
else
userText = LlamaText([
systemText,
"\n\n---\n\n",
userText
]);
}
resultItems.push({
user: userText,
model: LlamaText.joinValues("\n\n", modelTexts)
});
}
systemTexts = [];
userTexts = [];
modelTexts = [];
}
for (const item of historyWithFunctions) {
if (item.type === "system") {
if (currentAggregateFocus !== "system")
flush();
currentAggregateFocus = "system";
systemTexts.push(LlamaText.fromJSON(item.text));
}
else if (item.type === "user") {
if (currentAggregateFocus !== "system" && currentAggregateFocus !== "user")
flush();
currentAggregateFocus = "user";
userTexts.push(LlamaText(item.text));
}
else if (item.type === "model") {
currentAggregateFocus = "model";
modelTexts.push(this.generateModelResponseText(item.response));
}
else
void item;
}
flush();
const contextText = LlamaText(new SpecialToken("BOS"), resultItems.map(({ user, model }, index) => {
const isLastItem = index === resultItems.length - 1;
return LlamaText([
(user.values.length === 0)
? LlamaText([])
: LlamaText([
new SpecialTokensText("<start_of_turn>user\n"),
user,
new SpecialTokensText("<end_of_turn>\n")
]),
(model.values.length === 0 && !isLastItem)
? LlamaText([])
: LlamaText([
new SpecialTokensText("<start_of_turn>model\n"),
model,
isLastItem
? LlamaText([])
: new SpecialTokensText("<end_of_turn>\n")
])
]);
}));
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialTokensText("<end_of_turn>\n")),
LlamaText("<end_of_turn>")
]
};
}
}
//# sourceMappingURL=GemmaChatWrapper.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"GemmaChatWrapper.js","sourceRoot":"","sources":["../../src/chatWrappers/GemmaChatWrapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAE9C,OAAO,EAAC,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAC,MAAM,uBAAuB,CAAC;AAEjF,sDAAsD;AACtD,qDAAqD;AACrD,MAAM,OAAO,gBAAiB,SAAQ,WAAW;IAC7B,WAAW,GAAW,OAAO,CAAC;IAErB,QAAQ,GAAwB;QACrD,GAAG,WAAW,CAAC,eAAe;QAC9B,sBAAsB,EAAE,KAAK;KAChC,CAAC;IAEc,oBAAoB,CAAC,EACjC,WAAW,EAAE,kBAAkB,EAAE,sBAAsB,EAClB;QACrC,MAAM,oBAAoB,GAAG,IAAI,CAAC,2CAA2C,CAAC,WAAW,EAAE,kBAAkB,EAAE;YAC3G,cAAc,EAAE,sBAAsB;SACzC,CAAC,CAAC;QAEH,MAAM,WAAW,GAGZ,EAAE,CAAC;QAER,IAAI,WAAW,GAAgB,EAAE,CAAC;QAClC,IAAI,SAAS,GAAgB,EAAE,CAAC;QAChC,IAAI,UAAU,GAAgB,EAAE,CAAC;QACjC,IAAI,qBAAqB,GAAuC,IAAI,CAAC;QAErE,SAAS,KAAK;YACV,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC1E,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;gBAC7D,IAAI,QAAQ,GAAG,SAAS,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;gBAEvD,kGAAkG;gBAClG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC/B,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;wBAC5B,QAAQ,GAAG,UAAU,CAAC;;wBAEtB,QAAQ,GAAG,SAAS,CAAC;4BACjB,UAAU;4BACV,aAAa;4BACb,QAAQ;yBACX,CAAC,CAAC;gBACX,CAAC;gBAED,WAAW,CAAC,IAAI,CAAC;oBACb,IAAI,EAAE,QAAQ;oBACd,KAAK,EAAE,SAAS,CAAC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC;iBAClD,CAAC,CAAC;YACP,CAAC;YAED,WAAW,GAAG,EAAE,CAAC;YACjB,SAAS,GAAG,EAAE,CAAC;YACf,UAAU,GAAG,EAAE,CAAC;QACpB,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,oBAAoB,EAAE,CAAC;YACtC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACzB,IAAI,qBAAqB,KAAK,QAAQ;oBAClC,KAAK,EAAE,CAAC;gBAEZ,qBAAqB,GAAG,QAAQ,CAAC;gBACjC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACpD,CAAC;iBAAM,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC9B,IAAI,qBAAqB,KAAK,QAAQ,IAAI,qBAAqB,KAAK,MAAM;oBACtE,KAAK,EAAE,CAAC;gBAEZ,qBAAqB,GAAG,MAAM,CAAC;gBAC/B,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACzC,CAAC;iBAAM,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC/B,qBAAqB,GAAG,OAAO,CAAC;gBAChC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YACnE,CAAC;;gBACG,KAAM,IAAqB,CAAC;QACpC,CAAC;QAED,KAAK,EAAE,CAAC;QAER,MAAM,WAAW,GAAG,SAAS,CACzB,IAAI,YAAY,CAAC,KAAK,CAAC,EACvB,WAAW,CAAC,GAAG,CAAC,CAAC,EAAC,IAAI,EAAE,KAAK,EAAC,EAAE,KAAK,EAAE,EAAE;YACrC,MAAM,UAAU,GAAG,KAAK,KAAK,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC;YAEpD,OAAO,SAAS,CAAC;gBACb,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC;oBACtB,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;oBACf,CAAC,CAAC,SAAS,CAAC;wBACR,IAAI,iBAAiB,CAAC,uBAAuB,CAAC;wBAC9C,IAAI;wBACJ,IAAI,iBAAiB,CAAC,iBAAiB,CAAC;qBAC3C,CAAC;gBAEN,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;oBACtC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;oBACf,CAAC,CAAC,SAAS,CAAC;wBACR,IAAI,iBAAiB,CAAC,wBAAwB,CAAC;wBAC/C,KAAK;wBAEL,UAAU;4BACN,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;4BACf,CAAC,CAAC,IAAI,iBAAiB,CAAC,iBAAiB,CAAC;qBACjD,CAAC;aACT,CAAC,CAAC;QACP,CAAC,CAAC,CACL,CAAC;QAEF,OAAO;YACH,WAAW;YACX,sBAAsB,EAAE;gBACpB,SAAS,CAAC,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC;gBAClC,SAAS,CAAC,IAAI,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;gBACnD,SAAS,CAAC,eAAe,CAAC;aAC7B;SACJ,CAAC;IACN,CAAC;CACJ"}

View File

@@ -0,0 +1,19 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState } from "../types.js";
/**
* This chat wrapper is not safe against chat syntax injection attacks
* ([learn more](https://node-llama-cpp.withcat.ai/guide/llama-text#input-safety-in-node-llama-cpp)).
*/
export declare class GeneralChatWrapper extends ChatWrapper {
readonly wrapperName: string;
constructor({ userMessageTitle, modelResponseTitle, middleSystemMessageTitle, allowSpecialTokensInTitles }?: {
userMessageTitle?: string;
modelResponseTitle?: string;
middleSystemMessageTitle?: string;
allowSpecialTokensInTitles?: boolean;
});
get userMessageTitle(): string;
get modelResponseTitle(): string;
get middleSystemMessageTitle(): string;
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState;
}

View File

@@ -0,0 +1,140 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { SpecialToken, LlamaText, SpecialTokensText } from "../utils/LlamaText.js";
/**
* This chat wrapper is not safe against chat syntax injection attacks
* ([learn more](https://node-llama-cpp.withcat.ai/guide/llama-text#input-safety-in-node-llama-cpp)).
*/
export class GeneralChatWrapper extends ChatWrapper {
wrapperName = "General";
/** @internal */ _userMessageTitle;
/** @internal */ _modelResponseTitle;
/** @internal */ _middleSystemMessageTitle;
/** @internal */ _allowSpecialTokensInTitles;
constructor({ userMessageTitle = "Human", modelResponseTitle = "Assistant", middleSystemMessageTitle = "System", allowSpecialTokensInTitles = false } = {}) {
super();
this._userMessageTitle = userMessageTitle;
this._modelResponseTitle = modelResponseTitle;
this._middleSystemMessageTitle = middleSystemMessageTitle;
this._allowSpecialTokensInTitles = allowSpecialTokensInTitles;
}
get userMessageTitle() {
return this._userMessageTitle;
}
get modelResponseTitle() {
return this._modelResponseTitle;
}
get middleSystemMessageTitle() {
return this._middleSystemMessageTitle;
}
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(chatHistory, availableFunctions, {
documentParams: documentFunctionParams
});
const resultItems = [];
let systemTexts = [];
let userTexts = [];
let modelTexts = [];
let currentAggregateFocus = null;
function flush() {
if (systemTexts.length > 0 || userTexts.length > 0 || modelTexts.length > 0)
resultItems.push({
system: LlamaText.joinValues("\n\n", systemTexts),
user: LlamaText.joinValues("\n\n", userTexts),
model: LlamaText.joinValues("\n\n", modelTexts)
});
systemTexts = [];
userTexts = [];
modelTexts = [];
}
for (const item of historyWithFunctions) {
if (item.type === "system") {
if (currentAggregateFocus !== "system")
flush();
currentAggregateFocus = "system";
systemTexts.push(LlamaText.fromJSON(item.text));
}
else if (item.type === "user") {
flush();
currentAggregateFocus = null;
userTexts.push(LlamaText(item.text));
}
else if (item.type === "model") {
flush();
currentAggregateFocus = null;
modelTexts.push(this.generateModelResponseText(item.response));
}
else
void item;
}
flush();
const contextText = LlamaText(new SpecialToken("BOS"), resultItems.map(({ system, user, model }, index) => {
const isFirstItem = index === 0;
const isLastItem = index === resultItems.length - 1;
return LlamaText([
(system.values.length === 0)
? LlamaText([])
: LlamaText([
isFirstItem
? LlamaText([])
: SpecialTokensText.wrapIf(this._allowSpecialTokensInTitles, `### ${this._middleSystemMessageTitle}\n`),
system,
SpecialTokensText.wrapIf(this._allowSpecialTokensInTitles, "\n\n")
]),
(user.values.length === 0)
? LlamaText([])
: LlamaText([
SpecialTokensText.wrapIf(this._allowSpecialTokensInTitles, `### ${this._userMessageTitle}\n`),
user,
SpecialTokensText.wrapIf(this._allowSpecialTokensInTitles, "\n\n")
]),
(model.values.length === 0 && !isLastItem)
? LlamaText([])
: LlamaText([
SpecialTokensText.wrapIf(this._allowSpecialTokensInTitles, `### ${this._modelResponseTitle}\n`),
model,
isLastItem
? LlamaText([])
: SpecialTokensText.wrapIf(this._allowSpecialTokensInTitles, "\n\n")
])
]);
}));
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialTokensText("<end>")),
LlamaText("<end>"),
LlamaText(`### ${this._userMessageTitle}`),
LlamaText(`\n### ${this._userMessageTitle}`),
LlamaText(`\n\n### ${this._userMessageTitle}`),
LlamaText(`### ${this._modelResponseTitle}`),
LlamaText(`\n### ${this._modelResponseTitle}`),
LlamaText(`\n\n### ${this._modelResponseTitle}`),
LlamaText(`### ${this._middleSystemMessageTitle}`),
LlamaText(`\n### ${this._middleSystemMessageTitle}`),
LlamaText(`\n\n### ${this._middleSystemMessageTitle}`),
...(!this._allowSpecialTokensInTitles
? []
: [
LlamaText(new SpecialTokensText(`### ${this._userMessageTitle}`)),
LlamaText(new SpecialTokensText(`\n### ${this._userMessageTitle}`)),
LlamaText(new SpecialTokensText(`\n\n### ${this._userMessageTitle}`)),
LlamaText(new SpecialTokensText(`### ${this._modelResponseTitle}`)),
LlamaText(new SpecialTokensText(`\n### ${this._modelResponseTitle}`)),
LlamaText(new SpecialTokensText(`\n\n### ${this._modelResponseTitle}`)),
LlamaText(new SpecialTokensText(`### ${this._middleSystemMessageTitle}`)),
LlamaText(new SpecialTokensText(`\n### ${this._middleSystemMessageTitle}`)),
LlamaText(new SpecialTokensText(`\n\n### ${this._middleSystemMessageTitle}`))
])
]
};
}
/** @internal */
static _getOptionConfigurationsToTestIfCanSupersedeJinjaTemplate() {
return [
{},
{ allowSpecialTokensInTitles: true }
];
}
}
//# sourceMappingURL=GeneralChatWrapper.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,78 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { ChatModelFunctions, ChatModelResponse, ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState, ChatWrapperSettings } from "../types.js";
import { LlamaText } from "../utils/LlamaText.js";
export declare class HarmonyChatWrapper extends ChatWrapper {
readonly wrapperName: string;
readonly modelIdentity: string | null;
readonly cuttingKnowledgeDate?: Date | (() => Date) | null;
readonly todayDate: Date | (() => Date) | null;
readonly reasoningEffort: "high" | "medium" | "low" | null;
readonly requiredChannels: {
analysis: boolean;
commentary: boolean;
final: boolean;
};
readonly keepOnlyLastThought: boolean;
readonly settings: ChatWrapperSettings;
constructor(options?: {
/**
* The model identity to use in the internal system message.
*
* Set to `null` to disable.
*
* Defaults to `"You are ChatGPT, a large language model trained by OpenAI."`
*/
modelIdentity?: string | null;
/**
* Set to `null` to disable
*
* Defaults to `new Date("2024-06-01T00:00:00Z")`
*/
cuttingKnowledgeDate?: Date | (() => Date) | number | string | null;
/**
* Set to `null` to disable
*
* Defaults to the current date
*/
todayDate?: Date | (() => Date) | number | string | null;
/**
* The amount of reasoning to instruct the model to use.
*
* Not enforced, it's up to the model to follow this instruction.
*
* Set to `null` to omit the instruction.
*
* Defaults to `"medium"`.
*/
reasoningEffort?: "high" | "medium" | "low" | null;
requiredChannels?: {
/**
* Defaults to `true`
*/
analysis?: boolean;
/**
* Defaults to `true`
*/
commentary?: boolean;
/**
* Defaults to `true`
*/
final?: boolean;
};
/**
* Whether to keep only the chain of thought from the last model response.
*
* Setting this to `false` will keep all the chain of thoughts from the model responses in the context state.
*
* Defaults to `true`.
*/
keepOnlyLastThought?: boolean;
});
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState;
generateFunctionCall(name: string, params: any): LlamaText;
generateFunctionCallResult(functionName: string, functionParams: any, result: any): LlamaText;
generateModelResponseText(modelResponse: ChatModelResponse["response"], useRawValues?: boolean): LlamaText;
generateAvailableFunctionsSystemText(availableFunctions: ChatModelFunctions, { documentParams }: {
documentParams?: boolean;
}): import("../utils/LlamaText.js")._LlamaText;
}

View File

@@ -0,0 +1,539 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { SpecialToken, LlamaText, SpecialTokensText } from "../utils/LlamaText.js";
import { ChatModelFunctionsDocumentationGenerator } from "./utils/ChatModelFunctionsDocumentationGenerator.js";
import { jsonDumps } from "./utils/jsonDumps.js";
const defaultModelIdentity = "You are ChatGPT, a large language model trained by OpenAI.";
const defaultCuttingKnowledgeDate = new Date("2024-06-01T00:00:00Z");
const defaultReasoningEffort = "medium";
// source: https://github.com/openai/harmony, https://cookbook.openai.com/articles/openai-harmony,
// https://github.com/openai/openai-cookbook/blob/main/articles/openai-harmony.md
export class HarmonyChatWrapper extends ChatWrapper {
wrapperName = "Harmony";
modelIdentity;
cuttingKnowledgeDate;
todayDate;
reasoningEffort;
requiredChannels;
keepOnlyLastThought;
/** @internal */ _jinjaFlags;
settings = {
supportsSystemMessages: true,
functions: {
call: {
optionalPrefixSpace: true,
prefix: LlamaText(new SpecialTokensText(" to="), "functions."),
paramsPrefix: LlamaText(new SpecialTokensText("<|constrain|>json<|message|>")),
suffix: LlamaText(new SpecialTokensText("<|call|>")),
emptyCallParamsPlaceholder: {}
},
result: {
prefix: LlamaText(new SpecialTokensText("<|start|>"), "functions.{{functionName}}", new SpecialTokensText(" to=assistant<|channel|>commentary<|message|>")),
suffix: LlamaText(new SpecialTokensText("<|end|>"))
}
},
segments: {
thought: {
prefix: LlamaText(new SpecialTokensText("<|channel|>analysis<|message|>")),
suffix: LlamaText(new SpecialTokensText("<|end|>"))
},
comment: {
prefix: LlamaText(new SpecialTokensText("<|channel|>commentary<|message|>")),
suffix: LlamaText(new SpecialTokensText("<|end|>"))
}
}
};
constructor(options = {}) {
super();
const { modelIdentity = defaultModelIdentity, cuttingKnowledgeDate = defaultCuttingKnowledgeDate, todayDate = () => new Date(), reasoningEffort = defaultReasoningEffort, requiredChannels = {}, keepOnlyLastThought = true, _jinjaFlags = {} } = options;
this.modelIdentity = modelIdentity;
this.cuttingKnowledgeDate = cuttingKnowledgeDate == null
? null
: cuttingKnowledgeDate instanceof Function
? cuttingKnowledgeDate
: new Date(cuttingKnowledgeDate);
this.todayDate = todayDate == null
? null
: todayDate instanceof Function
? todayDate
: new Date(todayDate);
this.reasoningEffort = reasoningEffort;
this.requiredChannels = {
analysis: requiredChannels.analysis ?? true,
commentary: requiredChannels.commentary ?? true,
final: requiredChannels.final ?? true
};
this.keepOnlyLastThought = keepOnlyLastThought;
this._jinjaFlags = {
emptyLastModelResponseIsFinalMessage: false,
useSpecialTokensForFullSystemMessage: false,
disableNonFinalFinalMessages: false,
useNonFinalFinalMessage: false,
noFinalMessages: false,
..._jinjaFlags
};
}
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
const hasFunctions = Object.keys(availableFunctions ?? {}).length > 0;
const modifiedChatHistory = chatHistory.slice();
let systemMessage = LlamaText();
if (modifiedChatHistory[0]?.type === "system") {
systemMessage = LlamaText.fromJSON(modifiedChatHistory[0].text);
modifiedChatHistory.shift();
}
const contextContent = [
this._getPreamble(hasFunctions)
];
if (systemMessage.values.length > 0 || hasFunctions)
contextContent.push(LlamaText([
new SpecialTokensText("<|start|>developer<|message|>"),
this._getFirstDeveloperMessage(systemMessage, availableFunctions, { documentParams: documentFunctionParams }),
new SpecialTokensText("<|end|>")
]));
let needsTriggers = true;
for (let i = 0; i < modifiedChatHistory.length; i++) {
const isLastItem = i === modifiedChatHistory.length - 1;
const item = modifiedChatHistory[i];
if (item == null)
continue;
if (item.type === "system") {
contextContent.push(LlamaText([
new SpecialTokensText("<|start|>developer<|message|>"),
LlamaText.fromJSON(item.text),
isLastItem
? LlamaText([])
: new SpecialTokensText("<|end|>")
]));
if (isLastItem)
needsTriggers = false;
}
else if (item.type === "user") {
contextContent.push(LlamaText([
new SpecialTokensText("<|start|>user<|message|>"),
item.text,
isLastItem
? LlamaText([])
: new SpecialTokensText("<|end|>")
]));
if (isLastItem)
needsTriggers = false;
}
else if (item.type === "model") {
const { res, needsTriggers: modelNeedsTriggers } = this._getModelResponse(item.response, true, isLastItem, this.keepOnlyLastThought);
if (isLastItem)
needsTriggers = modelNeedsTriggers;
contextContent.push(res);
}
else
void item;
}
const contextText = LlamaText(contextContent);
if (!needsTriggers)
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialTokensText("<|return|>")),
LlamaText("<|return|>")
],
detectFunctionCalls: false,
rerender: {
triggers: [
LlamaText(new SpecialTokensText("<|end|>"))
],
action: "closeResponseItem"
}
};
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialTokensText("<|return|>")),
LlamaText("<|return|>")
],
prefixTriggers: [
{
type: "segment",
segmentType: "thought",
triggers: [
LlamaText(new SpecialTokensText("<|channel|>analysis<|message|>"))
]
}, {
type: "segment",
segmentType: "comment",
triggers: [
// the trigger here includes the `<|message|>` part
// to not conflict with the `<|channel|>commentary to=` prefix used for function calls
LlamaText(new SpecialTokensText("<|channel|>commentary<|message|>"))
]
}, {
type: "response",
triggers: [
LlamaText(new SpecialTokensText("<|channel|>final"))
],
inject: LlamaText(new SpecialTokensText("<|message|>"))
},
{
type: "functionCall",
triggers: [
LlamaText(new SpecialTokensText("<|channel|>commentary to="))
],
replaceTrigger: true,
inject: LlamaText(new SpecialTokensText("<|channel|>commentary"))
}, {
type: "functionCall",
triggers: [
LlamaText(new SpecialTokensText("<|channel|>analysis to="))
],
replaceTrigger: true,
inject: LlamaText(new SpecialTokensText("<|channel|>analysis"))
}
],
noPrefixTrigger: {
type: "response",
inject: LlamaText(new SpecialTokensText("<|channel|>final<|message|>"))
},
detectFunctionCalls: false,
rerender: {
triggers: [
LlamaText(new SpecialTokensText("<|end|>"))
],
action: "closeResponseItem"
}
};
}
generateFunctionCall(name, params) {
const emptyCallParamsPlaceholder = this.settings.functions.call.emptyCallParamsPlaceholder;
return LlamaText([
new SpecialTokensText("<|start|>assistant<|channel|>commentary to="),
"functions.", name,
this.settings.functions.call.paramsPrefix,
params === undefined
? (emptyCallParamsPlaceholder === undefined || emptyCallParamsPlaceholder === "")
? ""
: jsonDumps(emptyCallParamsPlaceholder)
: jsonDumps(params),
this.settings.functions.call.suffix
]);
}
generateFunctionCallResult(functionName, functionParams, result) {
return LlamaText([
new SpecialTokensText("<|start|>"),
"functions.", functionName,
new SpecialTokensText(" to=assistant<|channel|>commentary<|message|>"),
(result === undefined
? ""
: jsonDumps(result)),
new SpecialTokensText("<|end|>")
]);
}
generateModelResponseText(modelResponse, useRawValues = true) {
const { res } = this._getModelResponse(modelResponse, useRawValues, false, false);
const [start, ...rest] = res.values;
let newStart = start;
let newEnd = rest.pop();
if (newStart instanceof SpecialTokensText && newStart.value.startsWith("<|start|>assistant"))
newStart = new SpecialTokensText(newStart.value.slice("<|start|>assistant".length));
if (newEnd instanceof SpecialTokensText && newEnd.value.startsWith("<|end|>"))
newEnd = new SpecialTokensText(newEnd.value.slice("<|end|>".length));
else if (newEnd instanceof SpecialTokensText && newEnd.value.startsWith("<|return|>"))
newEnd = new SpecialTokensText(newEnd.value.slice("<|return|>".length));
return LlamaText([
newStart ?? [],
...rest,
newEnd ?? []
]);
}
generateAvailableFunctionsSystemText(availableFunctions, { documentParams = true }) {
const functionsDocumentationGenerator = new ChatModelFunctionsDocumentationGenerator(availableFunctions);
if (!functionsDocumentationGenerator.hasAnyFunctions)
return LlamaText([]);
return LlamaText.joinValues("\n", [
"# Tools",
"",
"## functions",
"",
"namespace functions {",
"",
functionsDocumentationGenerator
.getTypeScriptFunctionTypes({ documentParams })
.split("\n")
.map((line) => line.trim())
.join("\n"),
"",
"} // namespace functions"
]);
}
/** @internal */
_getFirstDeveloperMessage(systemPrompt, availableFunctions, { documentParams = true } = {}) {
const functionsDocumentationGenerator = new ChatModelFunctionsDocumentationGenerator(availableFunctions);
if (!functionsDocumentationGenerator.hasAnyFunctions && systemPrompt.values.length === 0)
return LlamaText([]);
if (!functionsDocumentationGenerator.hasAnyFunctions)
return LlamaText([
this._jinjaFlags.useSpecialTokensForFullSystemMessage
? new SpecialTokensText("# Instructions\n\n")
: "# Instruction\n\n",
systemPrompt
]);
return LlamaText([
this._jinjaFlags.useSpecialTokensForFullSystemMessage
? new SpecialTokensText("# Instructions\n\n")
: "# Instructions\n\n",
systemPrompt.values.length > 0
? [systemPrompt, "\n\n"]
: [],
this.generateAvailableFunctionsSystemText(availableFunctions ?? {}, { documentParams })
]);
}
/** @internal */
_getModelResponse(modelResponse, useRawValues, isLastItem, keepOnlyLastThought) {
const res = [];
let canEnableTriggers = true;
for (let index = 0; index < modelResponse.length; index++) {
const isLastResponse = index === modelResponse.length - 1;
const response = modelResponse[index];
if (response == null)
continue;
else if (response === "" && (!isLastResponse || !isLastItem))
continue;
if (typeof response === "string") {
if (isLastItem && isLastResponse) {
if (response === "" && !this._jinjaFlags.emptyLastModelResponseIsFinalMessage)
canEnableTriggers = true;
else if (!this._jinjaFlags.useNonFinalFinalMessage) {
res.push(LlamaText([
new SpecialTokensText("<|start|>assistant<|channel|>final<|message|>"),
response
]));
canEnableTriggers = false;
}
else {
res.push(LlamaText([
new SpecialTokensText("<|start|>assistant<|message|>"),
response
]));
canEnableTriggers = false;
}
}
else if (!this._jinjaFlags.noFinalMessages && (isLastResponse || !this._jinjaFlags.disableNonFinalFinalMessages))
res.push(LlamaText([
new SpecialTokensText("<|start|>assistant<|channel|>final<|message|>"),
response,
new SpecialTokensText("<|end|>")
]));
else
res.push(LlamaText([
new SpecialTokensText("<|start|>assistant<|message|>"),
response,
new SpecialTokensText("<|end|>")
]));
}
else if (response.type === "segment") {
if (response.ended && response.raw != null && useRawValues)
res.push(LlamaText([
new SpecialTokensText("<|start|>assistant"),
LlamaText.fromJSON(response.raw)
]));
else if (response.segmentType === "thought") {
if (keepOnlyLastThought && !isLastItem)
continue;
res.push(LlamaText([
new SpecialTokensText("<|start|>assistant<|channel|>analysis<|message|>"),
response.text,
(isLastItem && !response.ended)
? LlamaText([])
: new SpecialTokensText("<|end|>")
]));
if (isLastItem && isLastResponse && !response.ended)
canEnableTriggers = false;
}
else if (response.segmentType === "comment") {
res.push(LlamaText([
new SpecialTokensText("<|start|>assistant<|channel|>commentary<|message|>"),
response.text,
(isLastItem && !response.ended)
? LlamaText([])
: new SpecialTokensText("<|end|>")
]));
if (isLastItem && isLastResponse && !response.ended)
canEnableTriggers = false;
}
else
void response.segmentType;
}
else if (response.type === "functionCall") {
res.push(LlamaText([
(response.rawCall != null && useRawValues)
? LlamaText.fromJSON(response.rawCall)
: this.generateFunctionCall(response.name, response.params),
this.generateFunctionCallResult(response.name, response.params, response.result)
]));
}
else
void response;
}
const needsTriggers = canEnableTriggers && isLastItem;
if (needsTriggers)
res.push(LlamaText([
new SpecialTokensText("<|start|>assistant")
]));
return {
res: LlamaText(res),
needsTriggers
};
}
/** @internal */
_getPreamble(hasFunctions) {
const formatCutoff = (date, timezone) => {
const month = date.toLocaleDateString("en-US", { month: "numeric", timeZone: timezone }).padStart(2, "0");
const year = date.toLocaleDateString("en-US", { year: "numeric", timeZone: timezone }).padStart(4, "0");
return `${year}-${month}`;
};
const lines = [];
if (this.modelIdentity != null && this.modelIdentity !== "")
lines.push(this.modelIdentity);
if (this.cuttingKnowledgeDate != null) {
const date = this.cuttingKnowledgeDate instanceof Function
? this.cuttingKnowledgeDate()
: this.cuttingKnowledgeDate;
lines.push(`Knowledge cutoff: ${formatCutoff(date, "UTC")}`);
if (this._jinjaFlags.formatting === 1)
lines.push([lines.shift(), lines.shift()].filter(Boolean).join(""));
}
if (this.todayDate != null) {
const date = this.todayDate instanceof Function
? this.todayDate()
: this.todayDate;
lines.push(`Current date: ${formatDate(date, undefined)}`);
}
if (this.reasoningEffort != null) {
if (lines.length > 0)
lines.push("");
if (this._jinjaFlags.formatting === 1)
lines.push(`reasoning: ${this.reasoningEffort}`);
else
lines.push(`Reasoning: ${this.reasoningEffort}`);
}
if (this.requiredChannels.analysis || this.requiredChannels.commentary || this.requiredChannels.final) {
const channels = [
...(this.requiredChannels.analysis ? ["analysis"] : []),
...(this.requiredChannels.commentary ? ["commentary"] : []),
...(this.requiredChannels.final ? ["final"] : [])
];
if (lines.length > 0)
lines.push("");
lines.push(`# Valid channels: ${channels.join(", ")}. Channel must be included for every message.`);
if ((this.requiredChannels.commentary && hasFunctions) || this._jinjaFlags.formatting === 1)
lines.push("Calls to these tools must go to the commentary channel: 'functions'.");
}
return LlamaText([
new SpecialTokensText("<|start|>system<|message|>"),
this._jinjaFlags.useSpecialTokensForFullSystemMessage
? new SpecialTokensText(lines.join("\n"))
: lines.join("\n"),
new SpecialTokensText("<|end|>")
]);
}
/** @internal */
static _getOptionConfigurationsToTestIfCanSupersedeJinjaTemplate() {
const jinjaParameters = {
"model_identity": defaultModelIdentity,
"reasoning_effort": defaultReasoningEffort
};
return [
[{}, {}],
[{ _jinjaFlags: { emptyLastModelResponseIsFinalMessage: true } }, {}],
[{}, {}, { additionalRenderParameters: jinjaParameters }],
[{ _jinjaFlags: { emptyLastModelResponseIsFinalMessage: true } }, {}, { additionalRenderParameters: jinjaParameters }],
[{ _jinjaFlags: { useSpecialTokensForFullSystemMessage: true } }, {}, { additionalRenderParameters: jinjaParameters }],
[
{ _jinjaFlags: { emptyLastModelResponseIsFinalMessage: true, useSpecialTokensForFullSystemMessage: true } },
{},
{ additionalRenderParameters: jinjaParameters }
],
[
{
_jinjaFlags: {
emptyLastModelResponseIsFinalMessage: true,
useSpecialTokensForFullSystemMessage: true,
disableNonFinalFinalMessages: true
}
},
{},
{ additionalRenderParameters: jinjaParameters }
],
[
{
_jinjaFlags: {
emptyLastModelResponseIsFinalMessage: true,
useSpecialTokensForFullSystemMessage: true,
disableNonFinalFinalMessages: true,
useNonFinalFinalMessage: true
}
},
{},
{ additionalRenderParameters: jinjaParameters }
],
[
{
_jinjaFlags: {
emptyLastModelResponseIsFinalMessage: true,
useSpecialTokensForFullSystemMessage: true,
useNonFinalFinalMessage: true
}
},
{},
{ additionalRenderParameters: jinjaParameters }
],
[
{
_jinjaFlags: {
emptyLastModelResponseIsFinalMessage: true,
useSpecialTokensForFullSystemMessage: true,
useNonFinalFinalMessage: true,
noFinalMessages: true
}
},
{},
{ additionalRenderParameters: jinjaParameters }
],
[
{
_jinjaFlags: {
emptyLastModelResponseIsFinalMessage: true,
useSpecialTokensForFullSystemMessage: true,
useNonFinalFinalMessage: false,
noFinalMessages: false
}
},
{},
{ additionalRenderParameters: jinjaParameters }
],
[
{
_jinjaFlags: {
emptyLastModelResponseIsFinalMessage: true,
useSpecialTokensForFullSystemMessage: true,
useNonFinalFinalMessage: true,
noFinalMessages: true,
formatting: 1
}
},
{},
{ additionalRenderParameters: jinjaParameters }
],
[{ todayDate: null }, {}, {}],
[{ cuttingKnowledgeDate: null }, {}, {}],
[{ reasoningEffort: null }, {}, {}],
[{ todayDate: null, cuttingKnowledgeDate: null }, {}, {}],
[{ todayDate: null, cuttingKnowledgeDate: null, reasoningEffort: null }, {}, {}]
];
}
}
function formatDate(date, timezone) {
const day = date.toLocaleDateString("en-US", { day: "numeric", timeZone: timezone }).padStart(2, "0");
const month = date.toLocaleDateString("en-US", { month: "numeric", timeZone: timezone }).padStart(2, "0");
const year = date.toLocaleDateString("en-US", { year: "numeric", timeZone: timezone }).padStart(4, "0");
return `${year}-${month}-${day}`;
}
//# sourceMappingURL=HarmonyChatWrapper.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,12 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState } from "../types.js";
export declare class Llama2ChatWrapper extends ChatWrapper {
readonly wrapperName: string;
constructor({ addSpaceBeforeEos }?: {
/**
* Default to `true`
*/
addSpaceBeforeEos?: boolean;
});
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState;
}

View File

@@ -0,0 +1,95 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { SpecialToken, LlamaText, SpecialTokensText } from "../utils/LlamaText.js";
// source: https://huggingface.co/blog/llama2#how-to-prompt-llama-2
export class Llama2ChatWrapper extends ChatWrapper {
wrapperName = "Llama2Chat";
/** @internal */ _addSpaceBeforeEos;
constructor({ addSpaceBeforeEos = false } = {}) {
super();
this._addSpaceBeforeEos = addSpaceBeforeEos;
}
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(chatHistory, availableFunctions, {
documentParams: documentFunctionParams
});
const resultItems = [];
let systemTexts = [];
let userTexts = [];
let modelTexts = [];
let currentAggregateFocus = null;
function flush() {
if (systemTexts.length > 0 || userTexts.length > 0 || modelTexts.length > 0)
resultItems.push({
system: LlamaText.joinValues("\n\n", systemTexts),
user: LlamaText.joinValues("\n\n", userTexts),
model: LlamaText.joinValues("\n\n", modelTexts)
});
systemTexts = [];
userTexts = [];
modelTexts = [];
}
for (const item of historyWithFunctions) {
if (item.type === "system") {
if (currentAggregateFocus !== "system")
flush();
currentAggregateFocus = "system";
systemTexts.push(LlamaText.fromJSON(item.text));
}
else if (item.type === "user") {
if (currentAggregateFocus !== "system" && currentAggregateFocus !== "user")
flush();
currentAggregateFocus = "user";
userTexts.push(LlamaText(item.text));
}
else if (item.type === "model") {
currentAggregateFocus = "model";
modelTexts.push(this.generateModelResponseText(item.response));
}
else
void item;
}
flush();
const contextText = LlamaText(resultItems.map(({ system, user, model }, index) => {
const isLastItem = index === resultItems.length - 1;
return LlamaText([
new SpecialToken("BOS"),
(system.values.length === 0 && user.values.length === 0)
? LlamaText([])
: LlamaText([
new SpecialTokensText("[INST] "),
system.values.length === 0
? LlamaText([])
: LlamaText([
new SpecialTokensText("<<SYS>>\n"),
system,
new SpecialTokensText("\n<</SYS>>\n\n")
]),
user,
new SpecialTokensText(" [/INST] ")
]),
model,
this._addSpaceBeforeEos
? " "
: "",
isLastItem
? LlamaText([])
: new SpecialToken("EOS")
]);
}));
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText("</s>")
]
};
}
/** @internal */
static _getOptionConfigurationsToTestIfCanSupersedeJinjaTemplate() {
return [
{ addSpaceBeforeEos: false },
{ addSpaceBeforeEos: true }
];
}
}
//# sourceMappingURL=Llama2ChatWrapper.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Llama2ChatWrapper.js","sourceRoot":"","sources":["../../src/chatWrappers/Llama2ChatWrapper.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,16 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { ChatModelFunctions, ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState, ChatWrapperSettings } from "../types.js";
export declare class Llama3ChatWrapper extends ChatWrapper {
readonly wrapperName: string;
readonly settings: ChatWrapperSettings;
constructor({ parallelFunctionCalling }?: {
/**
* Defaults to `true`
*/
parallelFunctionCalling?: boolean;
});
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState;
generateAvailableFunctionsSystemText(availableFunctions: ChatModelFunctions, { documentParams }: {
documentParams?: boolean;
}): import("../utils/LlamaText.js")._LlamaText;
}

View File

@@ -0,0 +1,173 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { SpecialToken, LlamaText, SpecialTokensText } from "../utils/LlamaText.js";
import { ChatModelFunctionsDocumentationGenerator } from "./utils/ChatModelFunctionsDocumentationGenerator.js";
// source: https://github.com/meta-llama/llama-recipes/blob/79aa70442e97c3127e53c2d22c54438c32adcf5e/README.md
// source: https://llama.meta.com/docs/model-cards-and-prompt-formats/meta-llama-3/
export class Llama3ChatWrapper extends ChatWrapper {
wrapperName = "Llama 3";
settings;
constructor({ parallelFunctionCalling = true } = {}) {
super();
if (parallelFunctionCalling)
this.settings = {
supportsSystemMessages: true,
functions: {
call: {
optionalPrefixSpace: true,
prefix: "||call: ",
paramsPrefix: LlamaText(new SpecialTokensText("(")),
suffix: LlamaText(new SpecialTokensText(")"))
},
result: {
prefix: LlamaText(new SpecialTokensText("<|start_header_id|>function_call_result<|end_header_id|>\n\n")),
suffix: LlamaText(new SpecialTokensText("<|eot_id|>"))
},
parallelism: {
call: {
sectionPrefix: "",
betweenCalls: "\n",
sectionSuffix: LlamaText(new SpecialTokensText("<|eot_id|>"))
},
result: {
sectionPrefix: "",
betweenResults: "",
sectionSuffix: LlamaText(new SpecialTokensText("<|start_header_id|>assistant<|end_header_id|>\n\n"))
}
}
}
};
else
this.settings = {
supportsSystemMessages: true,
functions: {
call: {
optionalPrefixSpace: true,
prefix: "||call: ",
paramsPrefix: LlamaText(new SpecialTokensText("(")),
suffix: LlamaText(new SpecialTokensText(")"))
},
result: {
prefix: LlamaText([
LlamaText(new SpecialTokensText("<|eot_id|>")),
new SpecialTokensText("<|start_header_id|>function_call_result<|end_header_id|>\n\n")
]),
suffix: LlamaText([
new SpecialTokensText("<|eot_id|>"),
new SpecialTokensText("<|start_header_id|>assistant<|end_header_id|>\n\n")
])
}
}
};
}
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(chatHistory, availableFunctions, {
documentParams: documentFunctionParams
});
const resultItems = [];
let systemTexts = [];
let userTexts = [];
let modelTexts = [];
let currentAggregateFocus = null;
function flush() {
if (systemTexts.length > 0 || userTexts.length > 0 || modelTexts.length > 0)
resultItems.push({
system: systemTexts.length === 0
? null
: LlamaText.joinValues("\n\n", systemTexts),
user: userTexts.length === 0
? null
: LlamaText.joinValues("\n\n", userTexts),
model: modelTexts.length === 0
? null
: LlamaText.joinValues("\n\n", modelTexts)
});
systemTexts = [];
userTexts = [];
modelTexts = [];
}
for (const item of historyWithFunctions) {
if (item.type === "system") {
if (currentAggregateFocus !== "system")
flush();
currentAggregateFocus = "system";
systemTexts.push(LlamaText.fromJSON(item.text));
}
else if (item.type === "user") {
if (currentAggregateFocus !== "user")
flush();
currentAggregateFocus = "user";
userTexts.push(LlamaText(item.text));
}
else if (item.type === "model") {
if (currentAggregateFocus !== "model")
flush();
currentAggregateFocus = "model";
modelTexts.push(this.generateModelResponseText(item.response));
}
else
void item;
}
flush();
const contextText = LlamaText(new SpecialToken("BOS"), resultItems.map((item, index) => {
const isLastItem = index === resultItems.length - 1;
const res = [];
if (item.system != null) {
res.push(LlamaText([
new SpecialTokensText("<|start_header_id|>system<|end_header_id|>\n\n"),
item.system,
new SpecialTokensText("<|eot_id|>")
]));
}
if (item.user != null) {
res.push(LlamaText([
new SpecialTokensText("<|start_header_id|>user<|end_header_id|>\n\n"),
item.user,
new SpecialTokensText("<|eot_id|>")
]));
}
if (item.model != null) {
res.push(LlamaText([
new SpecialTokensText("<|start_header_id|>assistant<|end_header_id|>\n\n"),
item.model,
isLastItem
? LlamaText([])
: new SpecialTokensText("<|eot_id|>")
]));
}
return LlamaText(res);
}));
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialToken("EOT")),
LlamaText(new SpecialTokensText("<|eot_id|>")),
LlamaText(new SpecialTokensText("<|end_of_text|>")),
LlamaText("<|eot_id|>"),
LlamaText("<|end_of_text|>")
]
};
}
generateAvailableFunctionsSystemText(availableFunctions, { documentParams = true }) {
const functionsDocumentationGenerator = new ChatModelFunctionsDocumentationGenerator(availableFunctions);
if (!functionsDocumentationGenerator.hasAnyFunctions)
return LlamaText([]);
return LlamaText.joinValues("\n", [
"The assistant calls the provided functions as needed to retrieve information instead of relying on existing knowledge.",
"To fulfill a request, the assistant calls relevant functions in advance when needed before responding to the request, and does not tell the user prior to calling a function.",
"Provided functions:",
"```typescript",
functionsDocumentationGenerator.getTypeScriptFunctionSignatures({ documentParams }),
"```",
"",
"Calling any of the provided functions can be done like this:",
this.generateFunctionCall("getSomeInfo", { someKey: "someValue" }),
"",
"Note that the || prefix is mandatory.",
"The assistant does not inform the user about using functions and does not explain anything before calling a function.",
"After calling a function, the raw result appears afterwards and is not part of the conversation.",
"To make information be part of the conversation, the assistant paraphrases and repeats the information without the function syntax."
]);
}
}
//# sourceMappingURL=Llama3ChatWrapper.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,32 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { ChatHistoryItem, ChatModelFunctions, ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState, ChatWrapperSettings } from "../types.js";
export declare class Llama3_1ChatWrapper extends ChatWrapper {
readonly wrapperName: string;
readonly cuttingKnowledgeDate?: Date | (() => Date) | null;
readonly todayDate: Date | (() => Date) | null;
readonly noToolInstructions: boolean;
readonly settings: ChatWrapperSettings;
constructor(options?: {
/**
* Set to `null` to disable
*
* Defaults to December 2023
*/
cuttingKnowledgeDate?: Date | (() => Date) | number | string | null;
/**
* Set to `null` to disable
*
* Defaults to current date
*/
todayDate?: Date | (() => Date) | number | string | null;
noToolInstructions?: boolean;
});
addAvailableFunctionsSystemMessageToHistory(history: readonly ChatHistoryItem[], availableFunctions?: ChatModelFunctions, { documentParams }?: {
documentParams?: boolean;
}): readonly ChatHistoryItem[];
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState;
generateAvailableFunctionsSystemText(availableFunctions: ChatModelFunctions, { documentParams }: {
documentParams?: boolean;
}): import("../utils/LlamaText.js")._LlamaText;
prependPreambleToChatHistory(chatHistory: readonly ChatHistoryItem[]): readonly ChatHistoryItem[];
}

View File

@@ -0,0 +1,290 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { SpecialToken, LlamaText, SpecialTokensText } from "../utils/LlamaText.js";
import { ChatModelFunctionsDocumentationGenerator } from "./utils/ChatModelFunctionsDocumentationGenerator.js";
import { jsonDumps } from "./utils/jsonDumps.js";
import { isLlama3_2LightweightModel } from "./utils/isLlama3_2LightweightModel.js";
// source: https://llama.meta.com/docs/model-cards-and-prompt-formats/llama3_1
export class Llama3_1ChatWrapper extends ChatWrapper {
wrapperName = "Llama 3.1";
cuttingKnowledgeDate;
todayDate;
noToolInstructions;
/** @internal */ _specialTokensTextForPreamble;
settings = {
supportsSystemMessages: true,
functions: {
call: {
optionalPrefixSpace: true,
prefix: LlamaText(new SpecialTokensText("<function=")),
paramsPrefix: LlamaText(new SpecialTokensText(">")),
suffix: LlamaText(new SpecialTokensText("</function><|eom_id|>"))
},
result: {
prefix: LlamaText(new SpecialTokensText("\n<|start_header_id|>ipython<|end_header_id|>\n\n")),
suffix: LlamaText(new SpecialTokensText("<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n"))
}
}
};
constructor(options = {}) {
super();
const { cuttingKnowledgeDate = new Date("2023-12-01T00:00:00Z"), todayDate = () => new Date(), noToolInstructions = false, _specialTokensTextForPreamble = false } = options;
this.cuttingKnowledgeDate = cuttingKnowledgeDate == null
? null
: cuttingKnowledgeDate instanceof Function
? cuttingKnowledgeDate
: new Date(cuttingKnowledgeDate);
this.todayDate = todayDate == null
? null
: todayDate instanceof Function
? todayDate
: new Date(todayDate);
this.noToolInstructions = noToolInstructions;
this._specialTokensTextForPreamble = _specialTokensTextForPreamble;
}
addAvailableFunctionsSystemMessageToHistory(history, availableFunctions, { documentParams = true } = {}) {
const availableFunctionNames = Object.keys(availableFunctions ?? {});
if (availableFunctions == null || availableFunctionNames.length === 0)
return history;
const res = history.slice();
const functionsSystemMessage = {
type: "system",
text: this.generateAvailableFunctionsSystemText(availableFunctions, { documentParams }).toJSON()
};
if (res.length >= 2 && res[0].type === "system" && res[1].type === "system")
res.splice(1, 0, functionsSystemMessage);
else
res.unshift({
type: "system",
text: this.generateAvailableFunctionsSystemText(availableFunctions, { documentParams }).toJSON()
});
return res;
}
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
const chatHistoryWithPreamble = this.prependPreambleToChatHistory(chatHistory);
const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(chatHistoryWithPreamble, availableFunctions, {
documentParams: documentFunctionParams
});
const resultItems = [];
let systemTexts = [];
let userTexts = [];
let modelTexts = [];
let currentAggregateFocus = null;
const flush = () => {
if (systemTexts.length > 0 || userTexts.length > 0 || modelTexts.length > 0)
resultItems.push({
system: systemTexts.length === 0
? null
: LlamaText.joinValues(resultItems.length === 0 && this._specialTokensTextForPreamble
? LlamaText(new SpecialTokensText("\n\n"))
: "\n\n", systemTexts),
user: userTexts.length === 0
? null
: LlamaText.joinValues("\n\n", userTexts),
model: modelTexts.length === 0
? null
: LlamaText.joinValues("\n\n", modelTexts)
});
systemTexts = [];
userTexts = [];
modelTexts = [];
};
for (const item of historyWithFunctions) {
if (item.type === "system") {
if (currentAggregateFocus !== "system")
flush();
currentAggregateFocus = "system";
systemTexts.push(LlamaText.fromJSON(item.text));
}
else if (item.type === "user") {
if (currentAggregateFocus !== "user")
flush();
currentAggregateFocus = "user";
userTexts.push(LlamaText(item.text));
}
else if (item.type === "model") {
if (currentAggregateFocus !== "model")
flush();
currentAggregateFocus = "model";
modelTexts.push(this.generateModelResponseText(item.response));
}
else
void item;
}
flush();
const contextText = LlamaText(new SpecialToken("BOS"), resultItems.map((item, index) => {
const isLastItem = index === resultItems.length - 1;
const res = [];
if (item.system != null) {
res.push(LlamaText([
new SpecialTokensText("<|start_header_id|>system<|end_header_id|>\n\n"),
item.system,
new SpecialTokensText("<|eot_id|>")
]));
}
if (item.user != null) {
res.push(LlamaText([
new SpecialTokensText("<|start_header_id|>user<|end_header_id|>\n\n"),
item.user,
new SpecialTokensText("<|eot_id|>")
]));
}
if (item.model != null) {
res.push(LlamaText([
new SpecialTokensText("<|start_header_id|>assistant<|end_header_id|>\n\n"),
item.model,
isLastItem
? LlamaText([])
: new SpecialTokensText("<|eot_id|>")
]));
}
return LlamaText(res);
}));
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialToken("EOT")),
LlamaText(new SpecialTokensText("<|eot_id|>")),
LlamaText(new SpecialTokensText("<|end_of_text|>")),
LlamaText("<|eot_id|>"),
LlamaText("<|end_of_text|>")
]
};
}
generateAvailableFunctionsSystemText(availableFunctions, { documentParams = true }) {
const functionsDocumentationGenerator = new ChatModelFunctionsDocumentationGenerator(availableFunctions);
if (!functionsDocumentationGenerator.hasAnyFunctions)
return LlamaText([]);
return LlamaText.joinValues("\n", [
"You have access to the following functions:",
"",
functionsDocumentationGenerator.getLlama3_1FunctionSignatures({ documentParams }),
"",
"",
"If you choose to call a function ONLY reply in the following format:",
"<{start_tag}={function_name}>{parameters}{end_tag}",
"where",
"",
"start_tag => `<function`",
"parameters => a JSON dict with the function argument name as key and function argument value as value.",
"end_tag => `</function>`",
"",
"Here is an example,",
LlamaText([
new SpecialTokensText("<function="),
"example_function_name",
new SpecialTokensText(">"),
jsonDumps({ "example_name": "example_value" }),
new SpecialTokensText("</function>")
]),
"",
"Reminder:",
"- Function calls MUST follow the specified format",
"- Only call one function at a time",
"- Put the entire function call reply on one line",
"- Always add your sources when using search results to answer the user query",
"- After calling a function, the result will appear afterwards and is only visible to you",
"- To make information visible to the user, you must include it in your response",
"- Do not tell the user about the functions you are using",
"- Only call functions when needed"
]);
}
prependPreambleToChatHistory(chatHistory) {
const res = chatHistory.slice();
const formatMonthDate = (date, timezone) => {
const today = this.todayDate instanceof Function
? this.todayDate()
: (this.todayDate ?? new Date());
if (today.getUTCMonth() === date.getUTCMonth() && today.getUTCFullYear() === date.getUTCFullYear())
return formatDate(date, timezone);
const month = date.toLocaleDateString("en-US", { month: "long", timeZone: timezone });
const year = date.toLocaleDateString("en-US", { year: "numeric", timeZone: timezone });
return `${month} ${year}`;
};
const lines = [];
if (this.cuttingKnowledgeDate != null) {
const date = this.cuttingKnowledgeDate instanceof Function
? this.cuttingKnowledgeDate()
: this.cuttingKnowledgeDate;
lines.push(`Cutting Knowledge Date: ${formatMonthDate(date, "UTC")}`);
}
if (this.todayDate != null) {
const date = this.todayDate instanceof Function
? this.todayDate()
: this.todayDate;
lines.push(`Today Date: ${formatDate(date, undefined)}`);
}
if (!this.noToolInstructions) {
if (lines.length > 0)
lines.push("");
lines.push("# Tool Instructions");
lines.push("- When looking for real time information use relevant functions if available");
lines.push("");
lines.push("");
}
if (lines.length > 0)
res.unshift({
type: "system",
text: this._specialTokensTextForPreamble
? LlamaText(new SpecialTokensText(lines.join("\n"))).toJSON()
: LlamaText.joinValues("\n", lines).toJSON()
});
return res;
}
/** @internal */
static _checkModelCompatibility(options) {
if (options.tokenizer != null) {
const tokens = options.tokenizer("<|eom_id|>", true, "trimLeadingSpace");
return tokens.length === 1 && options.tokenizer.isSpecialToken(tokens[0]) && !isLlama3_2LightweightModel(options);
}
return !isLlama3_2LightweightModel(options);
}
/** @internal */
static _getOptionConfigurationsToTestIfCanSupersedeJinjaTemplate() {
return [
[{}, undefined, { functionCallMessageTemplate: "noJinja" }],
[{ todayDate: null }, {}, { functionCallMessageTemplate: "noJinja" }],
[{ cuttingKnowledgeDate: null }, {}, { functionCallMessageTemplate: "noJinja" }],
[{ noToolInstructions: true }, {}, { functionCallMessageTemplate: "noJinja" }],
[{ todayDate: null, cuttingKnowledgeDate: null }, {}, { functionCallMessageTemplate: "noJinja" }],
[{ todayDate: null, cuttingKnowledgeDate: null, noToolInstructions: true }, {}, { functionCallMessageTemplate: "noJinja" }],
[
{ todayDate: new Date("2024-07-26T00:00:00"), cuttingKnowledgeDate: null, noToolInstructions: true },
{},
{ functionCallMessageTemplate: "noJinja" }
],
[
{
todayDate: new Date("2024-07-26T00:00:00"),
cuttingKnowledgeDate: new Date("2023-12-01T00:00:00Z"),
noToolInstructions: true
},
{ cuttingKnowledgeDate: new Date("2023-12-01T00:00:00Z") },
{
additionalRenderParameters: { "date_string": formatDate(new Date("2024-07-26T00:00:00"), undefined) },
functionCallMessageTemplate: "noJinja"
}
],
[
{
todayDate: new Date("2024-07-26T00:00:00"),
cuttingKnowledgeDate: new Date("2023-12-01T00:00:00Z"),
noToolInstructions: true,
_specialTokensTextForPreamble: true
},
{ cuttingKnowledgeDate: new Date("2023-12-01T00:00:00Z") },
{
additionalRenderParameters: { "date_string": formatDate(new Date("2024-07-26T00:00:00"), undefined) },
functionCallMessageTemplate: "noJinja"
}
]
];
}
}
function formatDate(date, timezone) {
const day = date.toLocaleDateString("en-US", { day: "numeric", timeZone: timezone });
const month = date.toLocaleDateString("en-US", { month: "short", timeZone: timezone });
const year = date.toLocaleDateString("en-US", { year: "numeric", timeZone: timezone });
return `${day} ${month} ${year}`;
}
//# sourceMappingURL=Llama3_1ChatWrapper.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,35 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { ChatHistoryItem, ChatModelFunctions, ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState, ChatWrapperSettings } from "../types.js";
export declare class Llama3_2LightweightChatWrapper extends ChatWrapper {
readonly wrapperName: string;
readonly cuttingKnowledgeDate?: Date | (() => Date) | null;
readonly todayDate: Date | (() => Date) | null;
readonly noToolInstructions: boolean;
readonly settings: ChatWrapperSettings;
/**
* @param options
*/
constructor(options?: {
/**
* Set to `null` to disable
*
* Defaults to December 2023
*/
cuttingKnowledgeDate?: Date | (() => Date) | number | string | null;
/**
* Set to `null` to disable
*
* Defaults to current date
*/
todayDate?: Date | (() => Date) | number | string | null;
noToolInstructions?: boolean;
});
addAvailableFunctionsSystemMessageToHistory(history: readonly ChatHistoryItem[], availableFunctions?: ChatModelFunctions, { documentParams }?: {
documentParams?: boolean;
}): readonly ChatHistoryItem[];
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState;
generateAvailableFunctionsSystemText(availableFunctions: ChatModelFunctions, { documentParams }: {
documentParams?: boolean;
}): import("../utils/LlamaText.js")._LlamaText;
prependPreambleToChatHistory(chatHistory: readonly ChatHistoryItem[]): readonly ChatHistoryItem[];
}

View File

@@ -0,0 +1,264 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { SpecialToken, LlamaText, SpecialTokensText } from "../utils/LlamaText.js";
import { ChatModelFunctionsDocumentationGenerator } from "./utils/ChatModelFunctionsDocumentationGenerator.js";
import { isLlama3_2LightweightModel } from "./utils/isLlama3_2LightweightModel.js";
// source: https://llama.meta.com/docs/model-cards-and-prompt-formats/llama3_2/
export class Llama3_2LightweightChatWrapper extends ChatWrapper {
wrapperName = "Llama 3.2 lightweight";
cuttingKnowledgeDate;
todayDate;
noToolInstructions;
/** @internal */ _specialTokensTextForPreamble;
settings = {
supportsSystemMessages: true,
functions: {
call: {
optionalPrefixSpace: true,
prefix: '{"name": "',
paramsPrefix: '", "parameters": ',
suffix: LlamaText("}", new SpecialTokensText("<|eot_id|>")),
emptyCallParamsPlaceholder: {}
},
result: {
prefix: LlamaText(new SpecialTokensText("<|eot_id|><|start_header_id|>ipython<|end_header_id|>\n\n")),
suffix: LlamaText(new SpecialTokensText("<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n"))
}
}
};
/**
* @param options
*/
constructor(options = {}) {
super();
const { cuttingKnowledgeDate = new Date("2023-12-01T00:00:00Z"), todayDate = () => new Date(), noToolInstructions = false, _specialTokensTextForPreamble = false } = options;
this.cuttingKnowledgeDate = cuttingKnowledgeDate == null
? null
: cuttingKnowledgeDate instanceof Function
? cuttingKnowledgeDate
: new Date(cuttingKnowledgeDate);
this.todayDate = todayDate == null
? null
: todayDate instanceof Function
? todayDate
: new Date(todayDate);
this.noToolInstructions = noToolInstructions;
this._specialTokensTextForPreamble = _specialTokensTextForPreamble;
}
addAvailableFunctionsSystemMessageToHistory(history, availableFunctions, { documentParams = true } = {}) {
const availableFunctionNames = Object.keys(availableFunctions ?? {});
if (availableFunctions == null || availableFunctionNames.length === 0)
return history;
const res = history.slice();
const functionsSystemMessage = {
type: "system",
text: this.generateAvailableFunctionsSystemText(availableFunctions, { documentParams }).toJSON()
};
if (res.length >= 2 && res[0].type === "system" && res[1].type === "system")
res.splice(1, 0, functionsSystemMessage);
else
res.unshift({
type: "system",
text: this.generateAvailableFunctionsSystemText(availableFunctions, { documentParams }).toJSON()
});
return res;
}
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
const chatHistoryWithPreamble = this.prependPreambleToChatHistory(chatHistory);
const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(chatHistoryWithPreamble, availableFunctions, {
documentParams: documentFunctionParams
});
const resultItems = [];
let systemTexts = [];
let userTexts = [];
let modelTexts = [];
let currentAggregateFocus = null;
const flush = () => {
if (systemTexts.length > 0 || userTexts.length > 0 || modelTexts.length > 0)
resultItems.push({
system: systemTexts.length === 0
? null
: LlamaText.joinValues(resultItems.length === 0 && this._specialTokensTextForPreamble
? LlamaText(new SpecialTokensText("\n\n"))
: "\n\n", systemTexts),
user: userTexts.length === 0
? null
: LlamaText.joinValues("\n\n", userTexts),
model: modelTexts.length === 0
? null
: LlamaText.joinValues("\n\n", modelTexts)
});
systemTexts = [];
userTexts = [];
modelTexts = [];
};
for (const item of historyWithFunctions) {
if (item.type === "system") {
if (currentAggregateFocus !== "system")
flush();
currentAggregateFocus = "system";
systemTexts.push(LlamaText.fromJSON(item.text));
}
else if (item.type === "user") {
if (currentAggregateFocus !== "user")
flush();
currentAggregateFocus = "user";
userTexts.push(LlamaText(item.text));
}
else if (item.type === "model") {
if (currentAggregateFocus !== "model")
flush();
currentAggregateFocus = "model";
modelTexts.push(this.generateModelResponseText(item.response));
}
else
void item;
}
flush();
const contextText = LlamaText(new SpecialToken("BOS"), resultItems.map((item, index) => {
const isLastItem = index === resultItems.length - 1;
const res = [];
if (item.system != null) {
res.push(LlamaText([
new SpecialTokensText("<|start_header_id|>system<|end_header_id|>\n\n"),
item.system,
new SpecialTokensText("<|eot_id|>")
]));
}
if (item.user != null) {
res.push(LlamaText([
new SpecialTokensText("<|start_header_id|>user<|end_header_id|>\n\n"),
item.user,
new SpecialTokensText("<|eot_id|>")
]));
}
if (item.model != null) {
res.push(LlamaText([
new SpecialTokensText("<|start_header_id|>assistant<|end_header_id|>\n\n"),
item.model,
isLastItem
? LlamaText([])
: new SpecialTokensText("<|eot_id|>")
]));
}
return LlamaText(res);
}));
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialToken("EOT")),
LlamaText(new SpecialTokensText("<|eot_id|>")),
LlamaText(new SpecialTokensText("<|end_of_text|>")),
LlamaText("<|eot_id|>"),
LlamaText("<|end_of_text|>")
]
};
}
generateAvailableFunctionsSystemText(availableFunctions, { documentParams = true }) {
const functionsDocumentationGenerator = new ChatModelFunctionsDocumentationGenerator(availableFunctions);
if (!functionsDocumentationGenerator.hasAnyFunctions)
return LlamaText([]);
return LlamaText.joinValues("\n", [
"You have access to the following functions. To call a function, please respond with JSON for a function call.",
'Respond in the format {"name": function name, "parameters": function call parameters}.',
"Do not use variables.",
"",
functionsDocumentationGenerator.getLlama3_2LightweightFunctionSignatures({ documentParams }),
"",
"After calling a function, the result will appear afterwards and is only visible to you.",
"To make information visible to the user, you must include it in your response.",
"Do not tell the user about the functions you are using.",
"Only call functions when needed."
]);
}
prependPreambleToChatHistory(chatHistory) {
const res = chatHistory.slice();
const formatMonthDate = (date, timezone) => {
const today = this.todayDate instanceof Function
? this.todayDate()
: (this.todayDate ?? new Date());
if (today.getUTCMonth() === date.getUTCMonth() && today.getUTCFullYear() === date.getUTCFullYear())
return formatDate(date, timezone);
const month = date.toLocaleDateString("en-US", { month: "long", timeZone: timezone });
const year = date.toLocaleDateString("en-US", { year: "numeric", timeZone: timezone });
return `${month} ${year}`;
};
const lines = [];
if (this.cuttingKnowledgeDate != null) {
const date = this.cuttingKnowledgeDate instanceof Function
? this.cuttingKnowledgeDate()
: this.cuttingKnowledgeDate;
lines.push(`Cutting Knowledge Date: ${formatMonthDate(date, "UTC")}`);
}
if (this.todayDate != null) {
const date = this.todayDate instanceof Function
? this.todayDate()
: this.todayDate;
lines.push(`Today Date: ${formatDate(date, undefined)}`);
}
if (lines.length > 0)
res.unshift({
type: "system",
text: this._specialTokensTextForPreamble
? LlamaText(new SpecialTokensText(lines.join("\n"))).toJSON()
: LlamaText.joinValues("\n", lines).toJSON()
});
return res;
}
/** @internal */
static _checkModelCompatibility(options) {
if (options.tokenizer != null) {
const tokens = options.tokenizer("<|eom_id|>", true, "trimLeadingSpace");
return tokens.length === 1 && options.tokenizer.isSpecialToken(tokens[0]) && isLlama3_2LightweightModel(options);
}
return isLlama3_2LightweightModel(options);
}
/** @internal */
static _getOptionConfigurationsToTestIfCanSupersedeJinjaTemplate() {
return [
[{}, undefined, { functionCallMessageTemplate: "noJinja" }],
[{ todayDate: null }, {}, { functionCallMessageTemplate: "noJinja" }],
[{ cuttingKnowledgeDate: null }, {}, { functionCallMessageTemplate: "noJinja" }],
[{ noToolInstructions: true }, {}, { functionCallMessageTemplate: "noJinja" }],
[{ todayDate: null, cuttingKnowledgeDate: null }, {}, { functionCallMessageTemplate: "noJinja" }],
[{ todayDate: null, cuttingKnowledgeDate: null, noToolInstructions: true }, {}, { functionCallMessageTemplate: "noJinja" }],
[
{ todayDate: new Date("2024-07-26T00:00:00"), cuttingKnowledgeDate: null, noToolInstructions: true },
{},
{ functionCallMessageTemplate: "noJinja" }
],
[
{
todayDate: new Date("2024-07-26T00:00:00"),
cuttingKnowledgeDate: new Date("2023-12-01T00:00:00Z"),
noToolInstructions: true
},
{ cuttingKnowledgeDate: new Date("2023-12-01T00:00:00Z") },
{
additionalRenderParameters: { "date_string": formatDate(new Date("2024-07-26T00:00:00"), undefined) },
functionCallMessageTemplate: "noJinja"
}
],
[
{
todayDate: new Date("2024-07-26T00:00:00"),
cuttingKnowledgeDate: new Date("2023-12-01T00:00:00Z"),
noToolInstructions: true,
_specialTokensTextForPreamble: true
},
{ cuttingKnowledgeDate: new Date("2023-12-01T00:00:00Z") },
{
additionalRenderParameters: { "date_string": formatDate(new Date("2024-07-26T00:00:00"), undefined) },
functionCallMessageTemplate: "noJinja"
}
]
];
}
}
function formatDate(date, timezone) {
const day = date.toLocaleDateString("en-US", { day: "numeric", timeZone: timezone });
const month = date.toLocaleDateString("en-US", { month: "short", timeZone: timezone });
const year = date.toLocaleDateString("en-US", { year: "numeric", timeZone: timezone });
return `${day} ${month} ${year}`;
}
//# sourceMappingURL=Llama3_2LightweightChatWrapper.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,16 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { ChatHistoryItem, ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState, ChatWrapperGenerateInitialHistoryOptions, ChatWrapperSettings } from "../types.js";
export declare class MistralChatWrapper extends ChatWrapper {
readonly wrapperName: string;
readonly settings: ChatWrapperSettings;
constructor(options?: {
/**
* Default to `true`
*/
addSpaceBeforeEos?: boolean;
});
addAvailableFunctionsSystemMessageToHistory(history: readonly ChatHistoryItem[]): readonly ChatHistoryItem[];
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState;
generateInitialChatHistory({ systemPrompt }?: ChatWrapperGenerateInitialHistoryOptions): ChatHistoryItem[];
generateFunctionCallResult(functionName: string, functionParams: any, result: any): import("../utils/LlamaText.js")._LlamaText;
}

View File

@@ -0,0 +1,180 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { SpecialToken, LlamaText, SpecialTokensText } from "../utils/LlamaText.js";
import { jsonDumps } from "./utils/jsonDumps.js";
import { chunkChatItems } from "./utils/chunkChatItems.js";
// source:
// https://github.com/mistralai/platform-docs-public/blob/02c3f50e427ce5cf96bba9710501598f621babea/docs/guides/tokenization.mdx#v3-tokenizer
//
// source: https://docs.mistral.ai/guides/tokenization/#v3-tokenizer
export class MistralChatWrapper extends ChatWrapper {
wrapperName = "Mistral";
settings;
/** @internal */ _addSpaceBeforeEos;
/** @internal */ _stringifyFunctionCallResult;
constructor(options = {}) {
super();
const { addSpaceBeforeEos = false, _noFunctionNameInResult = false, _stringifyFunctionCallResult = false } = options;
this._addSpaceBeforeEos = addSpaceBeforeEos;
this._stringifyFunctionCallResult = _stringifyFunctionCallResult;
this.settings = {
supportsSystemMessages: true,
functions: {
call: {
optionalPrefixSpace: true,
prefix: '{"name": "',
paramsPrefix: '", "arguments": ',
suffix: "}",
emptyCallParamsPlaceholder: {}
},
result: {
prefix: _noFunctionNameInResult
? LlamaText(new SpecialTokensText("[TOOL_RESULTS]"), '{"content": ')
: LlamaText(new SpecialTokensText("[TOOL_RESULTS]"), '{"name": "{{functionName}}", "content": '),
suffix: LlamaText("}", new SpecialTokensText("[/TOOL_RESULTS]"))
},
parallelism: {
call: {
sectionPrefix: LlamaText(new SpecialTokensText("[TOOL_CALLS]"), "["),
betweenCalls: ", ",
sectionSuffix: LlamaText("]", new SpecialToken("EOS"))
}
}
}
};
}
addAvailableFunctionsSystemMessageToHistory(history) {
return history;
}
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
const toolsText = this._generateAvailableToolsText({ availableFunctions, documentFunctionParams });
const { systemMessage, chatHistory: chatHistoryWithoutSystemMessage } = this._splitSystemMessageFromChatHistory(chatHistory);
const { lastInteraction, chatHistory: cleanChatHistory } = this._splitLastInteractionFromChatHistory(chatHistoryWithoutSystemMessage);
const chunkedChatHistory = chunkChatItems(cleanChatHistory, {
generateModelResponseText: this.generateModelResponseText.bind(this)
});
const chunkedLastInteraction = chunkChatItems(lastInteraction, {
generateModelResponseText: this.generateModelResponseText.bind(this)
});
const contextText = LlamaText(new SpecialToken("BOS"), chunkedChatHistory.map(({ system, user, model }) => {
return LlamaText([
new SpecialTokensText("[INST]"),
LlamaText.joinValues("\n\n", [
system,
user
].filter((item) => item.values.length > 0)),
new SpecialTokensText("[/INST]"),
model,
this._addSpaceBeforeEos
? " "
: "",
new SpecialToken("EOS")
]);
}), toolsText === ""
? ""
: [
new SpecialTokensText("[AVAILABLE_TOOLS]"),
toolsText,
new SpecialTokensText("[/AVAILABLE_TOOLS]")
], chunkedLastInteraction.map(({ system, user, model }, index) => {
const isLastItem = index === chunkedLastInteraction.length - 1;
return LlamaText([
new SpecialTokensText("[INST]"),
(isLastItem && LlamaText(systemMessage).values.length > 0)
? [systemMessage, "\n\n"]
: "",
LlamaText.joinValues("\n\n", [
system,
user
].filter((item) => item.values.length > 0)),
new SpecialTokensText("[/INST]"),
model,
this._addSpaceBeforeEos
? " "
: "",
isLastItem
? LlamaText([])
: new SpecialToken("EOS")
]);
}));
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText("</s>")
]
};
}
generateInitialChatHistory({ systemPrompt } = {}) {
if (systemPrompt == null || systemPrompt.trim() === "")
return [];
return [{
type: "system",
text: LlamaText(systemPrompt).toJSON()
}];
}
generateFunctionCallResult(functionName, functionParams, result) {
if (this._stringifyFunctionCallResult && result !== undefined)
return super.generateFunctionCallResult(functionName, functionParams, jsonDumps(result));
return super.generateFunctionCallResult(functionName, functionParams, result);
}
/** @internal */
_generateAvailableToolsText({ availableFunctions, documentFunctionParams = true }) {
const availableFunctionNames = Object.keys(availableFunctions ?? {});
if (availableFunctions == null || availableFunctionNames.length === 0)
return "";
const availableTools = availableFunctionNames.map((functionName) => {
const functionDefinition = availableFunctions[functionName];
return {
type: "function",
function: {
name: functionName,
description: functionDefinition?.description != null && functionDefinition.description.trim() !== ""
? functionDefinition.description
: undefined,
parameters: documentFunctionParams && functionDefinition?.params != null
? functionDefinition.params
: undefined
}
};
});
return jsonDumps(availableTools);
}
/** @internal */
_splitSystemMessageFromChatHistory(history) {
const systemMessages = [];
const newHistory = history.slice();
while (newHistory.length > 0 && newHistory[0].type === "system")
systemMessages.push(LlamaText.fromJSON(newHistory.shift().text));
return {
systemMessage: LlamaText.joinValues("\n\n", systemMessages),
chatHistory: newHistory
};
}
/** @internal */
_splitLastInteractionFromChatHistory(history) {
const lastInteraction = [];
const newHistory = history.slice();
while (newHistory.length > 0) {
const item = newHistory.pop();
lastInteraction.unshift(item);
if (item.type === "user")
break;
}
return {
lastInteraction,
chatHistory: newHistory
};
}
/** @internal */
static _getOptionConfigurationsToTestIfCanSupersedeJinjaTemplate() {
return [
[{ addSpaceBeforeEos: false, _noFunctionNameInResult: true, _stringifyFunctionCallResult: true }, { addSpaceBeforeEos: false }],
[{ addSpaceBeforeEos: true, _noFunctionNameInResult: true, _stringifyFunctionCallResult: true }, { addSpaceBeforeEos: true }],
[{ addSpaceBeforeEos: false, _noFunctionNameInResult: true }, { addSpaceBeforeEos: false }],
[{ addSpaceBeforeEos: true, _noFunctionNameInResult: true }, { addSpaceBeforeEos: true }],
[{ addSpaceBeforeEos: false }, { addSpaceBeforeEos: false }],
[{ addSpaceBeforeEos: true }, { addSpaceBeforeEos: true }]
];
}
}
//# sourceMappingURL=MistralChatWrapper.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,29 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { ChatModelFunctions, ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState, ChatWrapperSettings } from "../types.js";
export declare class QwenChatWrapper extends ChatWrapper {
readonly wrapperName: string;
readonly keepOnlyLastThought: boolean;
readonly thoughts: "auto" | "discourage";
readonly settings: ChatWrapperSettings;
constructor(options?: {
/**
* Whether to keep only the chain of thought from the last model response.
*
* Setting this to `false` will keep all the chain of thoughts from the model responses in the context state.
*
* Defaults to `true`.
*/
keepOnlyLastThought?: boolean;
/**
* Control the usage of thoughts in the model responses.
*
* Defaults to `"auto"`.
*/
thoughts?: "auto" | "discourage";
});
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState;
generateFunctionCallResult(functionName: string, functionParams: any, result: any): import("../utils/LlamaText.js")._LlamaText;
generateAvailableFunctionsSystemText(availableFunctions: ChatModelFunctions, { documentParams }: {
documentParams?: boolean;
}): import("../utils/LlamaText.js")._LlamaText;
}

View File

@@ -0,0 +1,224 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { isChatModelResponseFunctionCall, isChatModelResponseSegment } from "../types.js";
import { LlamaText, SpecialToken, SpecialTokensText } from "../utils/LlamaText.js";
import { GgufArchitectureType } from "../gguf/types/GgufMetadataTypes.js";
import { ChatModelFunctionsDocumentationGenerator } from "./utils/ChatModelFunctionsDocumentationGenerator.js";
// source: https://huggingface.co/Qwen/Qwen2.5-14B-Instruct-1M/blob/main/tokenizer_config.json#L197
export class QwenChatWrapper extends ChatWrapper {
wrapperName = "Qwen";
keepOnlyLastThought;
thoughts;
/** @internal */ _flatFunctionResultString;
settings;
constructor(options = {}) {
super();
const { keepOnlyLastThought = true, thoughts = "auto", _lineBreakBeforeFunctionCallPrefix = false, _flatFunctionResultString = false } = options;
this.keepOnlyLastThought = keepOnlyLastThought;
this.thoughts = thoughts;
this._flatFunctionResultString = _flatFunctionResultString;
this.settings = {
supportsSystemMessages: true,
functions: {
call: {
optionalPrefixSpace: true,
prefix: LlamaText([
_lineBreakBeforeFunctionCallPrefix
? "\n"
: "",
new SpecialTokensText("<tool_call>"), '\n{"name": "'
]),
paramsPrefix: '", "arguments": ',
suffix: LlamaText("}\n", new SpecialTokensText("</tool_call>")),
emptyCallParamsPlaceholder: {}
},
result: {
prefix: LlamaText(new SpecialTokensText("\n<tool_response>\n")),
suffix: LlamaText(new SpecialTokensText("\n</tool_response>"))
},
parallelism: {
call: {
sectionPrefix: "",
betweenCalls: _lineBreakBeforeFunctionCallPrefix
? ""
: "\n",
sectionSuffix: LlamaText(new SpecialTokensText("<|im_end|>\n"))
},
result: {
sectionPrefix: LlamaText(new SpecialTokensText("<|im_start|>user")),
sectionSuffix: LlamaText(new SpecialTokensText("<|im_end|>\n<|im_start|>assistant\n"))
}
}
},
segments: {
reiterateStackAfterFunctionCalls: true,
thought: {
prefix: LlamaText(new SpecialTokensText("<think>\n")),
suffix: LlamaText(new SpecialTokensText("\n</think>"))
}
}
};
}
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(chatHistory, availableFunctions, {
documentParams: documentFunctionParams
});
const resultItems = [];
let systemTexts = [];
let userTexts = [];
let modelTexts = [];
let currentAggregateFocus = null;
function flush() {
if (systemTexts.length > 0 || userTexts.length > 0 || modelTexts.length > 0)
resultItems.push({
system: LlamaText.joinValues("\n\n", systemTexts),
user: LlamaText.joinValues("\n\n", userTexts),
model: LlamaText.joinValues("\n\n", modelTexts)
});
systemTexts = [];
userTexts = [];
modelTexts = [];
}
for (let i = 0; i < historyWithFunctions.length; i++) {
const item = historyWithFunctions[i];
const isLastItem = i === historyWithFunctions.length - 1;
if (item.type === "system") {
if (currentAggregateFocus !== "system")
flush();
currentAggregateFocus = "system";
systemTexts.push(LlamaText.fromJSON(item.text));
}
else if (item.type === "user") {
flush();
currentAggregateFocus = null;
userTexts.push(LlamaText(item.text));
}
else if (item.type === "model") {
flush();
const transformedModelResponse = (this.thoughts === "discourage" && isLastItem)
? discourageThoughtsInModelResponse(item.response)
: item.response;
currentAggregateFocus = null;
modelTexts.push(this.generateModelResponseText((this.keepOnlyLastThought && !isLastItem)
? transformedModelResponse.filter((response) => (!isChatModelResponseSegment(response) || response.segmentType !== "thought"))
: transformedModelResponse));
}
else
void item;
}
flush();
const contextText = LlamaText(resultItems.map(({ system, user, model }, index) => {
const isLastItem = index === resultItems.length - 1;
return LlamaText([
(system.values.length === 0)
? LlamaText([])
: LlamaText([
new SpecialTokensText("<|im_start|>system\n"),
system,
new SpecialTokensText("<|im_end|>\n")
]),
(user.values.length === 0)
? LlamaText([])
: LlamaText([
new SpecialTokensText("<|im_start|>user\n"),
user,
new SpecialTokensText("<|im_end|>\n")
]),
(model.values.length === 0 && !isLastItem)
? LlamaText([])
: LlamaText([
new SpecialTokensText("<|im_start|>assistant\n"),
model,
isLastItem
? LlamaText([])
: new SpecialTokensText("<|im_end|>\n")
])
]);
}));
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialTokensText("<|im_end|>")),
LlamaText("<|im_end|>")
]
};
}
generateFunctionCallResult(functionName, functionParams, result) {
if (this._flatFunctionResultString && typeof result === "string")
return super._generateFunctionCallResult(functionName, functionParams, result);
return super.generateFunctionCallResult(functionName, functionParams, result);
}
generateAvailableFunctionsSystemText(availableFunctions, { documentParams = true }) {
const functionsDocumentationGenerator = new ChatModelFunctionsDocumentationGenerator(availableFunctions);
if (!functionsDocumentationGenerator.hasAnyFunctions)
return LlamaText([]);
return LlamaText.joinValues("\n", [
"# Tools",
"",
"You may call one or more functions to assist with the user query.",
"",
LlamaText("You are provided with function signatures within ", new SpecialTokensText("<tools></tools>"), " XML tags:"),
LlamaText(new SpecialTokensText("<tools>")),
functionsDocumentationGenerator.getQwenFunctionSignatures({ documentParams }),
LlamaText(new SpecialTokensText("</tools>")),
"",
LlamaText("For each function call, return a json object with function name and arguments within ", new SpecialTokensText("<tool_call></tool_call>"), " XML tags:"),
LlamaText(new SpecialTokensText("<tool_call>")),
'{"name": <function-name>, "arguments": <args-json-object>}',
LlamaText(new SpecialTokensText("</tool_call>"))
]);
}
/** @internal */
static _checkModelCompatibility(options) {
const architecture = options.fileInfo?.metadata.general.architecture;
return (architecture == null ||
architecture === GgufArchitectureType.qwen2 ||
architecture === GgufArchitectureType.qwen2moe ||
architecture === GgufArchitectureType.qwen2vl ||
architecture === GgufArchitectureType.qwen3 ||
architecture === GgufArchitectureType.qwen3moe ||
architecture === GgufArchitectureType.qwen3vl ||
architecture === GgufArchitectureType.qwen3vlmoe);
}
/** @internal */
static _getOptionConfigurationsToTestIfCanSupersedeJinjaTemplate() {
return [
[{}, {}, { _requireFunctionCallSettingsExtraction: true }],
[{ _lineBreakBeforeFunctionCallPrefix: true }, {}, { _requireFunctionCallSettingsExtraction: true }],
[{ thoughts: "discourage" }, {}, { _requireFunctionCallSettingsExtraction: true }],
[{ thoughts: "discourage", _lineBreakBeforeFunctionCallPrefix: true }, {}, { _requireFunctionCallSettingsExtraction: true }],
[{ _flatFunctionResultString: true }, {}, { _requireFunctionCallSettingsExtraction: true }],
[
{ _flatFunctionResultString: true, _lineBreakBeforeFunctionCallPrefix: true },
{},
{ _requireFunctionCallSettingsExtraction: true }
],
[{ _flatFunctionResultString: true, thoughts: "discourage" }, {}, { _requireFunctionCallSettingsExtraction: true }],
[
{ _flatFunctionResultString: true, thoughts: "discourage", _lineBreakBeforeFunctionCallPrefix: true },
{},
{ _requireFunctionCallSettingsExtraction: true }
]
];
}
}
function discourageThoughtsInModelResponse(response) {
const emptyThought = {
type: "segment",
segmentType: "thought",
ended: true,
text: "\n\n",
raw: LlamaText(new SpecialTokensText("<think>\n\n</think>\n\n")).toJSON()
};
const res = [...response];
for (let i = res.length - 1; i >= 0; i--) {
const item = res[i];
if (isChatModelResponseFunctionCall(item)) {
res.splice(i + 1, 0, emptyThought);
return res;
}
}
res.unshift(emptyThought);
return res;
}
//# sourceMappingURL=QwenChatWrapper.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,25 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { ChatModelFunctions, ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState, ChatWrapperSettings } from "../types.js";
export declare class SeedChatWrapper extends ChatWrapper {
readonly wrapperName: string;
readonly thinkingBudget: number | 0 | null;
readonly settings: ChatWrapperSettings;
constructor(options?: {
/**
* The thinking budget to instruct the model to conform to.
*
* This is purely a request, the model may ignore it.
*
* Set to `0` to instruct the model to not use any reasoning.
*
* When set to `null`, the instruction will be omitted (unlimited reasoning).
*
* Defaults to `null`.
*/
thinkingBudget?: number | 0 | null;
});
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState;
generateAvailableFunctionsSystemText(availableFunctions: ChatModelFunctions, { documentParams }: {
documentParams?: boolean;
}): import("../utils/LlamaText.js")._LlamaText;
}

View File

@@ -0,0 +1,183 @@
import { ChatWrapper } from "../ChatWrapper.js";
import { isChatModelResponseSegment } from "../types.js";
import { SpecialToken, LlamaText, SpecialTokensText } from "../utils/LlamaText.js";
import { ChatModelFunctionsDocumentationGenerator } from "./utils/ChatModelFunctionsDocumentationGenerator.js";
const defaultThinkingBudget = null;
// source: https://huggingface.co/ByteDance-Seed/Seed-OSS-36B-Instruct/blob/main/chat_template.jinja
export class SeedChatWrapper extends ChatWrapper {
wrapperName = "Seed";
thinkingBudget;
settings = {
supportsSystemMessages: true,
functions: {
call: {
optionalPrefixSpace: true,
prefix: LlamaText(new SpecialTokensText("<seed:tool_call>\n"), "<function="),
paramsPrefix: LlamaText(new SpecialTokensText(">")),
suffix: LlamaText(new SpecialTokensText("\n</function>\n</seed:tool_call>\n")),
emptyCallParamsPlaceholder: {}
},
result: {
prefix: LlamaText(new SpecialTokensText("<seed:bos>tool\n")),
suffix: LlamaText(new SpecialTokensText("<seed:eos>"))
}
},
segments: {
thought: {
prefix: LlamaText(new SpecialTokensText("<seed:think>")),
suffix: LlamaText(new SpecialTokensText("</seed:think>")),
reopenAfterFunctionCalls: true
}
}
};
constructor(options = {}) {
super();
const { thinkingBudget = defaultThinkingBudget } = options;
this.thinkingBudget = thinkingBudget;
}
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
const hasFunctions = Object.keys(availableFunctions ?? {}).length > 0;
const modifiedChatHistory = chatHistory.slice();
let systemMessage = LlamaText();
if (modifiedChatHistory[0]?.type === "system") {
systemMessage = LlamaText.fromJSON(modifiedChatHistory[0].text);
modifiedChatHistory.shift();
}
const contextContent = [];
if (systemMessage.values.length > 0 || hasFunctions)
contextContent.push(LlamaText([
new SpecialTokensText("<seed:bos>system\n"),
this._getFirstSystemMessage(systemMessage, availableFunctions, { documentParams: documentFunctionParams }),
new SpecialTokensText("\n<seed:eos>")
]));
const thinkingBudgetSystemMessage = this._getThinkingBudgetSystemMessage();
if (thinkingBudgetSystemMessage.values.length > 0)
contextContent.push(LlamaText([
new SpecialTokensText("<seed:bos>system\n"),
thinkingBudgetSystemMessage,
new SpecialTokensText("\n<seed:eos>")
]));
for (let i = 0; i < modifiedChatHistory.length; i++) {
const isLastItem = i === modifiedChatHistory.length - 1;
const item = modifiedChatHistory[i];
if (item == null)
continue;
if (item.type === "system") {
contextContent.push(LlamaText([
new SpecialTokensText("<seed:bos>system\n"),
LlamaText.fromJSON(item.text),
isLastItem
? LlamaText([])
: new SpecialTokensText("\n<seed:eos>")
]));
}
else if (item.type === "user") {
contextContent.push(LlamaText([
new SpecialTokensText("<seed:bos>system\n"),
item.text,
isLastItem
? LlamaText([])
: new SpecialTokensText("\n<seed:eos>")
]));
}
else if (item.type === "model") {
const injectNoThinkingThought = this.thinkingBudget === 0 && (isLastItem ||
!item.response.some((item) => (isChatModelResponseSegment(item) && item.segmentType === "thought")));
contextContent.push(LlamaText([
new SpecialTokensText("<seed:bos>assistant\n"),
!injectNoThinkingThought
? []
: [
new SpecialTokensText("<seed:think>\n"),
[
new SpecialTokensText("<seed:cot_budget_reflect>"),
"The current thinking budget is 0, so I will directly start answering the question.",
new SpecialTokensText("</seed:cot_budget_reflect>")
],
new SpecialTokensText("\n</seed:think>")
],
this.generateModelResponseText(item.response, true),
isLastItem
? LlamaText([])
: new SpecialTokensText("\n<seed:eos>")
]));
}
else
void item;
}
const contextText = LlamaText(contextContent);
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(new SpecialTokensText("<seed:eos>")),
LlamaText("<seed:eos>")
]
};
}
generateAvailableFunctionsSystemText(availableFunctions, { documentParams = true }) {
const functionsDocumentationGenerator = new ChatModelFunctionsDocumentationGenerator(availableFunctions);
if (!functionsDocumentationGenerator.hasAnyFunctions)
return LlamaText([]);
return LlamaText.joinValues("\n", [
"",
"Tool List:",
("You are authorized to use the following tools (described in JSON Schema format). " +
"Before performing any task, you must decide how to call them based on the descriptions and parameters of these tools."),
functionsDocumentationGenerator.getSeedFunctionSignatures({ documentParams }),
"When invoking tools, strictly adhere to the following format:", // the original text for this is in Chinese, translated to English here
new SpecialTokensText("<seed:tool_call>\n<function=example_function_name>\n{\"example_parameter_1\": \"value_1\", \"example_parameter_2\": \"This is the value for the second parameter\"}</function>\n</seed:tool_call>")
]);
}
/** @internal */
_getFirstSystemMessage(systemPrompt, availableFunctions, { documentParams = true } = {}) {
const res = [];
const functionsDocumentationGenerator = new ChatModelFunctionsDocumentationGenerator(availableFunctions);
if (systemPrompt.values.length === 0 && functionsDocumentationGenerator.hasAnyFunctions)
res.push(LlamaText("You are Doubao, a helpful AI assistant. You may call one or more functions to assist with the user query."));
else if (systemPrompt.values.length > 0)
res.push(systemPrompt);
if (functionsDocumentationGenerator.hasAnyFunctions)
res.push(this.generateAvailableFunctionsSystemText(availableFunctions, { documentParams }));
return LlamaText(res);
}
/** @internal */
_getThinkingBudgetSystemMessage() {
if (this.thinkingBudget == null || this.thinkingBudget < 0)
return LlamaText([]);
if (this.thinkingBudget === 0)
return LlamaText([
"You are an intelligent assistant that can answer questions in one step without the need for reasoning and thinking, " +
"that is, your thinking budget is 0. " +
"Next, please skip the thinking process and directly start answering the user's questions."
]);
let reflectionInterval = 1024;
const reflectionIntervals = new Map([
[16384, 1024],
[8192, 1024],
[4096, 512],
[2048, 512],
[1024, 256],
[512, 128],
[0, 0]
]);
for (const [maxBudget, interval] of reflectionIntervals.entries()) {
if (this.thinkingBudget <= maxBudget) {
reflectionInterval = interval;
break;
}
}
return LlamaText([
new SpecialTokensText("<seed:bos>system\n"),
"You are an intelligent assistant with reflective ability. In the process of thinking and reasoning, you need to strictly follow the thinking budget, which is ",
this.thinkingBudget,
". That is, you need to complete your thinking within ",
this.thinkingBudget,
" tokens and start answering the user's questions. You will reflect on your thinking process every ",
reflectionInterval,
" tokens, stating how many tokens have been used and how many are left.",
new SpecialTokensText("\n<seed:eos>")
]);
}
}
//# sourceMappingURL=SeedChatWrapper.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,138 @@
import { ChatHistoryItem, ChatModelFunctions, ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState, ChatWrapperSettings, Tokenizer } from "../../types.js";
import { LlamaText } from "../../utils/LlamaText.js";
import { ChatWrapper } from "../../ChatWrapper.js";
import { ChatHistoryFunctionCallMessageTemplate } from "./utils/chatHistoryFunctionCallMessageTemplate.js";
import { TemplateChatWrapperSegmentsOptions } from "./utils/templateSegmentOptionsToChatWrapperSettings.js";
export type JinjaTemplateChatWrapperOptions = {
template: string;
/**
* Defaults to `"assistant"`.
*/
modelRoleName?: string;
/**
* Defaults to `"user"`.
*/
userRoleName?: string;
/**
* Defaults to `"system"`.
*/
systemRoleName?: string;
/**
* Some Jinja templates may not support system messages, and in such cases,
* it'll be detected and system messages can be converted to user messages.
*
* You can specify the format of the converted user message.
* - **"auto"**: Convert system messages to user messages only if the template does not support system messages.
* - **`true`**: Always convert system messages to user messages.
* - **`false`**: Never convert system messages to user messages.
* May throw an error if some system messages don't appear in the template.
* - **`{use: "ifNeeded", format: "..."}`**: Convert system messages to user messages only if the template does not support system
* messages with the specified format.
* - **`{use: "always", format: "..."}`**: Always convert system messages to user messages with the specified format.
*
* Defaults to `"auto"`.
*/
convertUnsupportedSystemMessagesToUserMessages?: "auto" | boolean | JinjaTemplateChatWrapperOptionsConvertMessageFormat;
/**
* Template format for how functions can be called by the model and how their results are fed to the model after function calls.
*
* - **`"auto"`**: Extract the function call message template from the Jinja template.
* Fallback to the default template if not found.
* - **`"noJinja"`**: Use the default template.
* - **Custom template**: Use the specified {@link ChatHistoryFunctionCallMessageTemplate template}.
* See {@link ChatHistoryFunctionCallMessageTemplate `ChatHistoryFunctionCallMessageTemplate`} for more details.
*
* Defaults to `"auto"`.
*/
functionCallMessageTemplate?: "auto" | "noJinja" | ChatHistoryFunctionCallMessageTemplate;
/**
* Whether to join adjacent messages of the same type.
* Some Jinja templates may throw an error if this is not set to `true`.
*
* Defaults to `true`.
*/
joinAdjacentMessagesOfTheSameType?: boolean;
/**
* Whether to trim leading whitespace in responses.
*
* Defaults to `true`.
*/
trimLeadingWhitespaceInResponses?: boolean;
/**
* Additional parameters to use for rendering the Jinja template.
*/
additionalRenderParameters?: Record<string, any>;
/**
* Format of the segments generated by the model (like thought segments)
*/
segments?: TemplateChatWrapperSegmentsOptions;
/**
* Pass a model's tokenizer to attempt to detect common tokens used for chat formatting from it.
*
* Currently only used for detecting support for `<think>` tags for thought segments.
*/
tokenizer?: Tokenizer;
};
export type JinjaTemplateChatWrapperOptionsConvertMessageFormat = {
use?: "always" | "ifNeeded";
format: `${string}{{message}}${string}`;
};
/**
* A chat wrapper based on a Jinja template.
* Useful for using the original model's Jinja template as-is without any additional conversion work to chat with a model.
*
* If you want to create a new chat wrapper from scratch, using this chat wrapper is not recommended, and instead you better inherit
* from the `ChatWrapper` class and implement a custom chat wrapper of your own in TypeScript.
*
* For a simpler way to create a chat wrapper, see the `TemplateChatWrapper` class.
* @example
* <span v-pre>
*
* ```ts
* import {JinjaTemplateChatWrapper} from "node-llama-cpp";
*
* const chatWrapper = new JinjaTemplateChatWrapper({
* template: "<Jinja template here>",
* // functionCallMessageTemplate: { // optional
* // call: "[[call: {{functionName}}({{functionParams}})]]",
* // result: " [[result: {{functionCallResult}}]]"
* // },
* // segments: {
* // thoughtTemplate: "<think>{{content}}</think>",
* // reopenThoughtAfterFunctionCalls: true
* // }
* });
* ```
*
* </span>
*/
export declare class JinjaTemplateChatWrapper extends ChatWrapper {
readonly wrapperName = "JinjaTemplate";
readonly settings: ChatWrapperSettings;
readonly template: string;
readonly modelRoleName: string;
readonly userRoleName: string;
readonly systemRoleName: string;
readonly convertUnsupportedSystemMessagesToUserMessages?: JinjaTemplateChatWrapperOptionsConvertMessageFormat;
readonly joinAdjacentMessagesOfTheSameType: boolean;
readonly trimLeadingWhitespaceInResponses: boolean;
readonly additionalRenderParameters?: Record<string, any>;
/**
* @param options
*/
constructor(options: JinjaTemplateChatWrapperOptions);
/**
* Whether the function call syntax settings were extracted from the given Jinja template.
*
* The function call syntax settings can be accessed using the `.settings.functions` property.
*/
get usingJinjaFunctionCallTemplate(): boolean;
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState & {
transformedSystemMessagesToUserMessages: boolean;
};
addAvailableFunctionsSystemMessageToHistory(history: readonly ChatHistoryItem[], availableFunctions?: ChatModelFunctions, options?: {
documentParams?: boolean;
}): readonly ChatHistoryItem[];
generateFunctionCall(name: string, params: any): LlamaText;
generateFunctionCallResult(functionName: string, functionParams: any, result: any): LlamaText;
}

View File

@@ -0,0 +1,626 @@
import { Template } from "@huggingface/jinja";
import { splitText } from "lifecycle-utils";
import { SpecialToken, LlamaText, SpecialTokensText } from "../../utils/LlamaText.js";
import { ChatWrapper } from "../../ChatWrapper.js";
import { fromChatHistoryToIntermediateOpenAiMessages, fromIntermediateToCompleteOpenAiMessages } from "../../utils/OpenAIFormat.js";
import { removeUndefinedFields } from "../../utils/removeNullFields.js";
import { jsonDumps } from "../utils/jsonDumps.js";
import { tryMatrix } from "../../utils/optionsMatrix.js";
import { parseFunctionCallMessageTemplate } from "./utils/chatHistoryFunctionCallMessageTemplate.js";
import { templateSegmentOptionsToChatWrapperSettings } from "./utils/templateSegmentOptionsToChatWrapperSettings.js";
import { UniqueIdGenerator } from "./utils/UniqueIdGenerator.js";
import { extractFunctionCallSettingsFromJinjaTemplate } from "./utils/extractFunctionCallSettingsFromJinjaTemplate.js";
import { squashChatHistoryItems } from "./utils/squashChatHistoryItems.js";
import { extractSegmentSettingsFromTokenizerAndChatTemplate } from "./utils/extractSegmentSettingsFromTokenizerAndChatTemplate.js";
const defaultConvertUnsupportedSystemMessagesToUserMessagesFormat = {
format: "### System message\n\n{{message}}\n\n----"
};
/**
* A chat wrapper based on a Jinja template.
* Useful for using the original model's Jinja template as-is without any additional conversion work to chat with a model.
*
* If you want to create a new chat wrapper from scratch, using this chat wrapper is not recommended, and instead you better inherit
* from the `ChatWrapper` class and implement a custom chat wrapper of your own in TypeScript.
*
* For a simpler way to create a chat wrapper, see the `TemplateChatWrapper` class.
* @example
* <span v-pre>
*
* ```ts
* import {JinjaTemplateChatWrapper} from "node-llama-cpp";
*
* const chatWrapper = new JinjaTemplateChatWrapper({
* template: "<Jinja template here>",
* // functionCallMessageTemplate: { // optional
* // call: "[[call: {{functionName}}({{functionParams}})]]",
* // result: " [[result: {{functionCallResult}}]]"
* // },
* // segments: {
* // thoughtTemplate: "<think>{{content}}</think>",
* // reopenThoughtAfterFunctionCalls: true
* // }
* });
* ```
*
* </span>
*/
export class JinjaTemplateChatWrapper extends ChatWrapper {
wrapperName = "JinjaTemplate";
settings;
template;
modelRoleName;
userRoleName;
systemRoleName;
convertUnsupportedSystemMessagesToUserMessages;
joinAdjacentMessagesOfTheSameType;
trimLeadingWhitespaceInResponses;
additionalRenderParameters;
/** @internal */ _jinjaTemplate;
/** @internal */ _usingJinjaFunctionCallTemplate = false;
/** @internal */ _stringifyFunctionParams = false;
/** @internal */ _stringifyFunctionResult = false;
/** @internal */ _combineJinjaModelMessageAndToolCalls = true;
/** @internal */ _endJinjaMessagesWithUserMessage = false;
/**
* @param options
*/
constructor(options) {
super();
const { template, modelRoleName = "assistant", userRoleName = "user", systemRoleName = "system", convertUnsupportedSystemMessagesToUserMessages = defaultConvertUnsupportedSystemMessagesToUserMessagesFormat, functionCallMessageTemplate = "auto", joinAdjacentMessagesOfTheSameType = true, trimLeadingWhitespaceInResponses = true, additionalRenderParameters, segments, tokenizer, _requireFunctionCallSettingsExtraction = false } = options;
if (template == null)
throw new Error("template cannot be null");
this.template = template;
this.modelRoleName = modelRoleName;
this.userRoleName = userRoleName;
this.systemRoleName = systemRoleName;
this.convertUnsupportedSystemMessagesToUserMessages =
resolveConvertUnsupportedSystemMessagesToUserMessagesOption(convertUnsupportedSystemMessagesToUserMessages);
this.joinAdjacentMessagesOfTheSameType = joinAdjacentMessagesOfTheSameType;
this.trimLeadingWhitespaceInResponses = trimLeadingWhitespaceInResponses;
this.additionalRenderParameters = additionalRenderParameters;
if (this.convertUnsupportedSystemMessagesToUserMessages != null && !this.convertUnsupportedSystemMessagesToUserMessages.format.includes("{{message}}"))
throw new Error('convertUnsupportedSystemMessagesToUserMessages format must include "{{message}}"');
this._jinjaTemplate = new Template(this.template);
this.settings = {
...ChatWrapper.defaultSettings,
segments: templateSegmentOptionsToChatWrapperSettings(segments)
};
const { supportsSystemMessages, needsToEndJinjaMessagesWithUserMessage } = this._runSanityTest();
this.settings = {
...this.settings,
supportsSystemMessages,
segments: {
...this.settings.segments,
...extractSegmentSettingsFromTokenizerAndChatTemplate(this.template, tokenizer)
}
};
if (needsToEndJinjaMessagesWithUserMessage)
this._endJinjaMessagesWithUserMessage = true;
let functionCallSettings = parseFunctionCallMessageTemplate((functionCallMessageTemplate === "auto" || functionCallMessageTemplate === "noJinja")
? undefined
: functionCallMessageTemplate);
if (functionCallSettings == null && functionCallMessageTemplate !== "noJinja") {
try {
const idsGenerator = new UniqueIdGenerator(this.template + this.modelRoleName + this.userRoleName + this.systemRoleName +
(this.convertUnsupportedSystemMessagesToUserMessages?.format ?? ""));
const extractedSettings = extractFunctionCallSettingsFromJinjaTemplate({
idsGenerator,
renderTemplate: ({ chatHistory, functions, additionalParams, stringifyFunctionParams, stringifyFunctionResults, combineModelMessageAndToolCalls, squashModelTextResponses = true }) => {
const render = (convertSystemMessagesToUserMessagesFormat, wipeFunctionCallIds) => {
const { messages: intermediateMessages, tools } = fromChatHistoryToIntermediateOpenAiMessages({
chatHistory: this._transformChatHistory(chatHistory, {
convertSystemMessagesToUserMessagesFormat,
joinAdjacentMessagesOfTheSameType: !squashModelTextResponses
? false
: undefined
}).transformedHistory,
chatWrapperSettings: this.settings,
useRawValues: false,
functions,
stringifyFunctionParams,
stringifyFunctionResults,
combineModelMessageAndToolCalls,
squashModelTextResponses
});
const messages = fromIntermediateToCompleteOpenAiMessages(intermediateMessages)
.map((item) => {
if (!wipeFunctionCallIds)
return item;
if (item.role === "assistant" && item["tool_calls"] != null && item["tool_calls"].length > 0) {
for (const toolCall of item["tool_calls"]) {
if (wipeFunctionCallIds === "align")
toolCall.id = "fc_1_0001";
else
delete toolCall.id;
}
}
else if (item.role === "tool") {
if (wipeFunctionCallIds === "align")
item["tool_call_id"] = "fc_1_0001";
else
delete item["tool_call_id"];
}
return item;
});
const lastJinjaItem = messages.at(-1);
let eraseRenderedJinjaFromId;
if (this._endJinjaMessagesWithUserMessage && lastJinjaItem?.role === this.modelRoleName &&
typeof lastJinjaItem.content === "string" &&
lastJinjaItem.content.length > 0 &&
(lastJinjaItem["tool_calls"] == null ||
lastJinjaItem["tool_calls"]?.length === 0)) {
eraseRenderedJinjaFromId = lastJinjaItem.content;
messages.push({
role: this.userRoleName,
content: idsGenerator.generateId()
});
}
let res = this._jinjaTemplate.render({
...(this.additionalRenderParameters == null
? {}
: structuredClone(this.additionalRenderParameters)),
...additionalParams,
messages,
...removeUndefinedFields({ tools })
});
if (eraseRenderedJinjaFromId != null) {
const eraseIndex = res.lastIndexOf(eraseRenderedJinjaFromId);
if (eraseIndex >= 0)
res = res.slice(0, eraseIndex + eraseRenderedJinjaFromId.length);
}
// attempt to remove the ID pattern from the output
if (wipeFunctionCallIds === "align")
res = res
.replaceAll(/,\s*"(tool_call_id|call_id|id)":\s*"fc_1_0001"/g, "")
.replaceAll(/"(tool_call_id|call_id|id)":\s*"fc_1_0001"\s*,/g, "");
return res;
};
return tryMatrix({
convertSystemMessagesToUserMessagesFormat: getConvertUnsupportedSystemMessagesToUserMessagesTryOptions(this.convertUnsupportedSystemMessagesToUserMessages),
wipeFunctionCallIds: [true, "align", false]
}, ({ convertSystemMessagesToUserMessagesFormat, wipeFunctionCallIds }) => {
return render(convertSystemMessagesToUserMessagesFormat, wipeFunctionCallIds);
});
}
});
functionCallSettings = extractedSettings.settings;
if (functionCallSettings != null) {
this._usingJinjaFunctionCallTemplate = true;
this._stringifyFunctionParams = extractedSettings.stringifyParams;
this._stringifyFunctionResult = extractedSettings.stringifyResult;
}
}
catch (err) {
// do nothing
}
if (functionCallSettings == null && _requireFunctionCallSettingsExtraction)
throw new Error("failed to extract function call settings from the Jinja template");
}
this.settings = {
...this.settings,
functions: functionCallSettings ?? ChatWrapper.defaultSettings.functions
};
}
/**
* Whether the function call syntax settings were extracted from the given Jinja template.
*
* The function call syntax settings can be accessed using the `.settings.functions` property.
*/
get usingJinjaFunctionCallTemplate() {
return this._usingJinjaFunctionCallTemplate;
}
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
const { contextText, stopGenerationTriggers, ignoreStartText, functionCall, transformedSystemMessagesToUserMessages } = this._generateContextState({
chatHistory, availableFunctions, documentFunctionParams,
endJinjaMessagesWithUserMessage: this._endJinjaMessagesWithUserMessage
});
return { contextText, stopGenerationTriggers, ignoreStartText, functionCall, transformedSystemMessagesToUserMessages };
}
addAvailableFunctionsSystemMessageToHistory(history, availableFunctions, options = {}) {
if (this._usingJinjaFunctionCallTemplate)
return history;
return super.addAvailableFunctionsSystemMessageToHistory(history, availableFunctions, options);
}
generateFunctionCall(name, params) {
if (!this._stringifyFunctionParams)
return super.generateFunctionCall(name, params);
const emptyCallParamsPlaceholder = this.settings.functions.call.emptyCallParamsPlaceholder;
return LlamaText([
this.settings.functions.call.prefix,
name,
this.settings.functions.call.paramsPrefix,
(params === undefined
? (emptyCallParamsPlaceholder === undefined || emptyCallParamsPlaceholder === "")
? ""
: JSON.stringify(jsonDumps(emptyCallParamsPlaceholder))
: JSON.stringify(jsonDumps(params))),
this.settings.functions.call.suffix
]);
}
generateFunctionCallResult(functionName, functionParams, result) {
const resolveParameters = (text) => {
return LlamaText(text)
.mapValues((value) => {
if (typeof value !== "string")
return value;
const funcParamsText = functionParams === undefined
? ""
: jsonDumps(functionParams);
return value
.replaceAll("{{functionName}}", functionName)
.replaceAll("{{functionParams}}", (this._stringifyFunctionParams && funcParamsText !== "")
? JSON.stringify(funcParamsText)
: funcParamsText);
});
};
const resultText = result === undefined
? "void"
: jsonDumps(result);
return LlamaText([
resolveParameters(this.settings.functions.result.prefix),
((this._stringifyFunctionResult && result !== undefined)
? JSON.stringify(resultText)
: resultText),
resolveParameters(this.settings.functions.result.suffix)
]);
}
/** @internal */
_generateContextState({ chatHistory, availableFunctions, documentFunctionParams, endJinjaMessagesWithUserMessage }) {
return tryMatrix({
convertSystemMessagesToUserMessagesFormat: getConvertUnsupportedSystemMessagesToUserMessagesTryOptions(this.convertUnsupportedSystemMessagesToUserMessages),
endJinjaMessagesWithUserMessage: endJinjaMessagesWithUserMessage == null
? [false, true]
: [endJinjaMessagesWithUserMessage],
useMessagesWithEmbeddedTools: this._usingJinjaFunctionCallTemplate
? [undefined, true]
: [undefined]
}, ({ useMessagesWithEmbeddedTools, endJinjaMessagesWithUserMessage, convertSystemMessagesToUserMessagesFormat }) => {
return this._generateContextText(chatHistory, {
convertSystemMessagesToUserMessagesFormat, availableFunctions, documentFunctionParams,
endJinjaMessagesWithUserMessage,
useMessagesWithEmbeddedTools
});
});
}
/** @internal */
_transformChatHistory(history, { convertSystemMessagesToUserMessagesFormat, availableFunctions, documentFunctionParams = true, joinAdjacentMessagesOfTheSameType = this.joinAdjacentMessagesOfTheSameType }) {
const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(history, availableFunctions, {
documentParams: documentFunctionParams
});
let transformedSystemMessagesToUserMessages = false;
const transformedHistory = convertSystemMessagesToUserMessagesFormat == null
? historyWithFunctions
: historyWithFunctions.map((item) => {
if (item.type === "system") {
transformedSystemMessagesToUserMessages = true;
return {
type: "user",
text: LlamaText.joinValues(LlamaText.fromJSON(item.text), convertSystemMessagesToUserMessagesFormat.split("{{message}}")).toString()
};
}
return item;
});
return {
transformedHistory: joinAdjacentMessagesOfTheSameType
? squashChatHistoryItems(transformedHistory)
: transformedHistory,
transformedSystemMessagesToUserMessages
};
}
/** @internal */
_generateContextText(history, { convertSystemMessagesToUserMessagesFormat, availableFunctions, documentFunctionParams = true, endJinjaMessagesWithUserMessage, useMessagesWithEmbeddedTools = false }) {
const { transformedSystemMessagesToUserMessages, transformedHistory } = this._transformChatHistory(history, { convertSystemMessagesToUserMessagesFormat, availableFunctions, documentFunctionParams });
const generateMessagesWithEmbeddedTools = (chatHistory) => ({
messages: chatHistory.map((item) => {
if (item.type === "system")
return {
role: "system",
content: LlamaText.fromJSON(item.text)
};
else if (item.type === "user")
return {
role: "user",
content: LlamaText(item.text)
};
else if (item.type === "model")
return {
role: "assistant",
content: this.generateModelResponseText(item.response)
};
void item;
return { role: "user", content: LlamaText("") };
}),
tools: undefined
});
const generateMessagesWithTools = (chatHistory) => (fromChatHistoryToIntermediateOpenAiMessages({
chatHistory,
chatWrapperSettings: this.settings,
useRawValues: false,
functions: (availableFunctions != null && !documentFunctionParams)
? Object.fromEntries(Object.entries(availableFunctions)
.map(([funcName, { description, ...func }]) => [funcName, func]))
: availableFunctions,
stringifyFunctionParams: this._stringifyFunctionParams,
stringifyFunctionResults: this._stringifyFunctionResult,
combineModelMessageAndToolCalls: this._combineJinjaModelMessageAndToolCalls
}));
const lastItemIsModelMessage = transformedHistory.at(-1)?.type === "model";
const { messages: intermediateMessages, tools } = this._usingJinjaFunctionCallTemplate
? useMessagesWithEmbeddedTools
? {
messages: generateMessagesWithEmbeddedTools(transformedHistory).messages,
tools: generateMessagesWithTools(transformedHistory).tools
}
: generateMessagesWithTools(transformedHistory)
: generateMessagesWithEmbeddedTools(transformedHistory);
const idsGenerator = new UniqueIdGenerator(this.template + this.modelRoleName + this.userRoleName + this.systemRoleName +
(convertSystemMessagesToUserMessagesFormat ?? "") +
intermediateMessages.map(({ content }) => (content?.toString() ?? "")).join("\n\n"));
const jinjaItems = [];
const jinjaRoleMap = {
system: this.systemRoleName,
user: this.userRoleName,
assistant: this.modelRoleName,
tool: "tool"
};
const idToContent = new Map();
const modelMessageIds = new Set();
const messageIds = new Set();
for (const intermediateMessage of intermediateMessages) {
if (intermediateMessage.content == null) {
jinjaItems.push({
...intermediateMessage,
role: jinjaRoleMap[intermediateMessage.role] ?? intermediateMessage.role
});
continue;
}
const id = idsGenerator.generateId(intermediateMessage.role === "tool");
messageIds.add(id);
idToContent.set(id, LlamaText(intermediateMessage.content));
jinjaItems.push({
...intermediateMessage,
role: jinjaRoleMap[intermediateMessage.role] ?? intermediateMessage.role,
content: id
});
if (intermediateMessage.role === "assistant" || intermediateMessage.role === "tool")
modelMessageIds.add(id);
}
const bosTokenId = idsGenerator.generateId();
const eosTokenId = idsGenerator.generateId();
const eotTokenId = idsGenerator.generateId();
idToContent.set(bosTokenId, new SpecialToken("BOS"));
idToContent.set(eosTokenId, new SpecialToken("EOS"));
idToContent.set(eotTokenId, new SpecialToken("EOT"));
const lastJinjaItem = jinjaItems.at(-1);
let eraseRenderedJinjaFromId;
if (endJinjaMessagesWithUserMessage && lastJinjaItem?.role === this.modelRoleName &&
typeof lastJinjaItem.content === "string" &&
lastJinjaItem.content.length > 0 &&
(lastJinjaItem["tool_calls"] == null ||
lastJinjaItem["tool_calls"]?.length === 0)) {
eraseRenderedJinjaFromId = lastJinjaItem.content;
jinjaItems.push({
role: this.userRoleName,
content: idsGenerator.generateId()
});
}
const renderJinjaText = () => {
let res = tryMatrix({
options: [{}, { "add_generation_prompt": true }]
}, ({ options }) => (this._jinjaTemplate.render({
...(this.additionalRenderParameters == null
? {}
: structuredClone(this.additionalRenderParameters)),
messages: jinjaItems,
...removeUndefinedFields({ tools }),
"bos_token": bosTokenId,
"eos_token": eosTokenId,
"eot_token": eotTokenId,
...options
})));
if (eraseRenderedJinjaFromId != null) {
const eraseIndex = res.lastIndexOf(eraseRenderedJinjaFromId);
if (eraseIndex >= 0)
res = res.slice(0, eraseIndex + eraseRenderedJinjaFromId.length);
}
return res;
};
const validateThatAllMessageIdsAreUsed = (parts) => {
const messageIdsLeft = new Set(messageIds);
for (const part of parts) {
if (typeof part === "string")
continue;
messageIdsLeft.delete(part.separator);
}
if (messageIdsLeft.size !== 0)
throw new Error("Some input messages are not present in the generated Jinja template output");
};
const renderJinjaAndSplitIntoParts = () => {
const splitJinjaParts = splitText(renderJinjaText(), [...idToContent.keys()]);
if (lastItemIsModelMessage) {
let lastModelResponseIndex = -1;
for (let i = splitJinjaParts.length - 1; i >= 0; i--) {
const part = splitJinjaParts[i];
if (part == null || typeof part === "string")
continue;
if (modelMessageIds.has(part.separator)) {
lastModelResponseIndex = i;
break;
}
else if (messageIds.has(part.separator)) {
validateThatAllMessageIdsAreUsed(splitJinjaParts);
throw new Error("Last message was expected to be a model message, but it was not");
}
}
if (lastModelResponseIndex < 0) {
validateThatAllMessageIdsAreUsed(splitJinjaParts);
throw new Error("A model message was expected to be the last message, but it was not found");
}
return {
splitJinjaParts: splitJinjaParts.slice(0, lastModelResponseIndex + 1),
stopGenerationJinjaParts: splitJinjaParts.slice(lastModelResponseIndex + 1)
};
}
return {
splitJinjaParts,
stopGenerationJinjaParts: []
};
};
const { splitJinjaParts, stopGenerationJinjaParts } = renderJinjaAndSplitIntoParts();
const messageIdsLeftToProcess = new Set(messageIds);
const contextText = LlamaText(splitJinjaParts.map((part) => {
if (typeof part === "string")
return new SpecialTokensText(part); // things that are not message content can be tokenized with special tokens
const message = idToContent.get(part.separator);
if (message == null)
throw new Error(`Message with id "${part.separator}" not found`);
messageIdsLeftToProcess.delete(part.separator);
return message;
}));
if (messageIdsLeftToProcess.size !== 0)
throw new Error("Some input messages are not present in the generated Jinja template output");
return {
contextText,
ignoreStartText: !this.trimLeadingWhitespaceInResponses
? []
: [
// ignore up to 4 leading spaces
...Array(4).fill(0)
.map((_, index) => LlamaText(" ".repeat(index + 1))),
LlamaText("\t"),
LlamaText("\t\t"),
LlamaText("\t "),
LlamaText(" \t")
],
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
...(stopGenerationJinjaParts.length === 0
? []
: [
LlamaText(stopGenerationJinjaParts.map((part) => {
if (typeof part === "string")
return new SpecialTokensText(part);
const message = idToContent.get(part.separator);
if (message == null)
throw new Error(`Message with id "${part.separator}" not found`);
return message;
}))
])
],
transformedSystemMessagesToUserMessages,
endJinjaMessagesWithUserMessage
};
}
/**
* Validate that this Jinja template can be rendered
* @internal
*/
_runSanityTest(needsToEndJinjaMessagesWithUserMessage = false) {
try {
let supportsSystemMessages = true;
for (const chatHistory of chatHistoriesForSanityTest) {
const { transformedSystemMessagesToUserMessages, endJinjaMessagesWithUserMessage: endedJinjaMessagesWithUserMessage } = this._generateContextState({
chatHistory,
endJinjaMessagesWithUserMessage: needsToEndJinjaMessagesWithUserMessage
? true
: undefined
});
if (transformedSystemMessagesToUserMessages)
supportsSystemMessages = false;
if (!needsToEndJinjaMessagesWithUserMessage && endedJinjaMessagesWithUserMessage) {
if (chatHistory !== chatHistoriesForSanityTest[0])
// validate tha this doesn't break the template
return this._runSanityTest(true);
else
needsToEndJinjaMessagesWithUserMessage = true;
}
}
return { supportsSystemMessages, needsToEndJinjaMessagesWithUserMessage };
}
catch (err) {
throw new Error("The provided Jinja template failed the sanity test: " + String(err) + ". Inspect the Jinja template to find out what went wrong");
}
}
}
function resolveConvertUnsupportedSystemMessagesToUserMessagesOption(convertUnsupportedSystemMessagesToUserMessages) {
if (convertUnsupportedSystemMessagesToUserMessages === false)
return undefined;
if (convertUnsupportedSystemMessagesToUserMessages === true)
return {
...defaultConvertUnsupportedSystemMessagesToUserMessagesFormat,
use: "always"
};
if (convertUnsupportedSystemMessagesToUserMessages === "auto")
return {
...defaultConvertUnsupportedSystemMessagesToUserMessagesFormat,
use: "ifNeeded"
};
if (typeof convertUnsupportedSystemMessagesToUserMessages === "object")
return {
...convertUnsupportedSystemMessagesToUserMessages,
use: convertUnsupportedSystemMessagesToUserMessages.use ?? "ifNeeded"
};
return { ...defaultConvertUnsupportedSystemMessagesToUserMessagesFormat, use: "ifNeeded" };
}
function getConvertUnsupportedSystemMessagesToUserMessagesTryOptions(convertUnsupportedSystemMessagesToUserMessages) {
if (convertUnsupportedSystemMessagesToUserMessages == null)
return [undefined];
else if (convertUnsupportedSystemMessagesToUserMessages.use === "always")
return [convertUnsupportedSystemMessagesToUserMessages.format];
return [undefined, convertUnsupportedSystemMessagesToUserMessages.format];
}
const chatHistoriesForSanityTest = [
[{
type: "system",
text: "System message ~!@#$%^&*()\n*"
}, {
type: "user",
text: "Message 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: [""]
}],
[{
type: "system",
text: "System message ~!@#$%^&*()\n*"
}, {
type: "user",
text: "Message 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: ["Result 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"]
}],
[{
type: "system",
text: "System message ~!@#$%^&*()\n*"
}, {
type: "user",
text: "Message 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: ["Result 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"]
}, {
type: "user",
text: "Message2 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: [""]
}],
[{
type: "system",
text: "System message ~!@#$%^&*()\n*"
}, {
type: "user",
text: "Message 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: ["Result 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"]
}, {
type: "user",
text: "Message2 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: ["Result2 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"]
}]
];
//# sourceMappingURL=JinjaTemplateChatWrapper.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,76 @@
import { ChatWrapperGenerateContextStateOptions, ChatWrapperGeneratedContextState, ChatWrapperSettings } from "../../types.js";
import { ChatWrapper } from "../../ChatWrapper.js";
import { ChatHistoryFunctionCallMessageTemplate } from "./utils/chatHistoryFunctionCallMessageTemplate.js";
import { TemplateChatWrapperSegmentsOptions } from "./utils/templateSegmentOptionsToChatWrapperSettings.js";
export type TemplateChatWrapperOptions = {
template: `${"" | `${string}{{systemPrompt}}`}${string}{{history}}${string}{{completion}}${string}`;
historyTemplate: {
system: `${string}{{message}}${string}`;
user: `${string}{{message}}${string}`;
model: `${string}{{message}}${string}`;
};
functionCallMessageTemplate?: ChatHistoryFunctionCallMessageTemplate;
/**
* Whether to join adjacent messages of the same type.
*
* Defaults to `true`.
*/
joinAdjacentMessagesOfTheSameType?: boolean;
/**
* Format of the segments generated by the model (like thought segments)
*/
segments?: TemplateChatWrapperSegmentsOptions;
};
/**
* A chat wrapper based on a simple template.
* @example
* <span v-pre>
*
* ```ts
* import {TemplateChatWrapper} from "node-llama-cpp";
*
* const chatWrapper = new TemplateChatWrapper({
* template: "{{systemPrompt}}\n{{history}}model: {{completion}}\nuser: ",
* historyTemplate: {
* system: "system: {{message}}\n",
* user: "user: {{message}}\n",
* model: "model: {{message}}\n"
* },
* // functionCallMessageTemplate: { // optional
* // call: "[[call: {{functionName}}({{functionParams}})]]",
* // result: " [[result: {{functionCallResult}}]]"
* // },
* // segments: {
* // thoughtTemplate: "<think>{{content}}</think>",
* // reopenThoughtAfterFunctionCalls: true
* // }
* });
* ```
*
* </span>
*
* **<span v-pre>`{{systemPrompt}}`</span>** is optional and is replaced with the first system message
* (when is does, that system message is not included in the history).
*
* **<span v-pre>`{{history}}`</span>** is replaced with the chat history.
* Each message in the chat history is converted using the template passed to `historyTemplate` for the message role,
* and all messages are joined together.
*
* **<span v-pre>`{{completion}}`</span>** is where the model's response is generated.
* The text that comes after <span v-pre>`{{completion}}`</span> is used to determine when the model has finished generating the response,
* and thus is mandatory.
*
* **`functionCallMessageTemplate`** is used to specify the format in which functions can be called by the model and
* how their results are fed to the model after the function call.
*
* **`segments`** is used to specify the format of the segments generated by the model (like thought segments).
*/
export declare class TemplateChatWrapper extends ChatWrapper {
readonly wrapperName = "Template";
readonly settings: ChatWrapperSettings;
readonly template: TemplateChatWrapperOptions["template"];
readonly historyTemplate: Readonly<TemplateChatWrapperOptions["historyTemplate"]>;
readonly joinAdjacentMessagesOfTheSameType: boolean;
constructor({ template, historyTemplate, functionCallMessageTemplate, joinAdjacentMessagesOfTheSameType, segments }: TemplateChatWrapperOptions);
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }: ChatWrapperGenerateContextStateOptions): ChatWrapperGeneratedContextState;
}

View File

@@ -0,0 +1,212 @@
import { SpecialToken, LlamaText, SpecialTokensText } from "../../utils/LlamaText.js";
import { ChatWrapper } from "../../ChatWrapper.js";
import { parseTextTemplate } from "../../utils/parseTextTemplate.js";
import { parseFunctionCallMessageTemplate } from "./utils/chatHistoryFunctionCallMessageTemplate.js";
import { templateSegmentOptionsToChatWrapperSettings } from "./utils/templateSegmentOptionsToChatWrapperSettings.js";
/**
* A chat wrapper based on a simple template.
* @example
* <span v-pre>
*
* ```ts
* import {TemplateChatWrapper} from "node-llama-cpp";
*
* const chatWrapper = new TemplateChatWrapper({
* template: "{{systemPrompt}}\n{{history}}model: {{completion}}\nuser: ",
* historyTemplate: {
* system: "system: {{message}}\n",
* user: "user: {{message}}\n",
* model: "model: {{message}}\n"
* },
* // functionCallMessageTemplate: { // optional
* // call: "[[call: {{functionName}}({{functionParams}})]]",
* // result: " [[result: {{functionCallResult}}]]"
* // },
* // segments: {
* // thoughtTemplate: "<think>{{content}}</think>",
* // reopenThoughtAfterFunctionCalls: true
* // }
* });
* ```
*
* </span>
*
* **<span v-pre>`{{systemPrompt}}`</span>** is optional and is replaced with the first system message
* (when is does, that system message is not included in the history).
*
* **<span v-pre>`{{history}}`</span>** is replaced with the chat history.
* Each message in the chat history is converted using the template passed to `historyTemplate` for the message role,
* and all messages are joined together.
*
* **<span v-pre>`{{completion}}`</span>** is where the model's response is generated.
* The text that comes after <span v-pre>`{{completion}}`</span> is used to determine when the model has finished generating the response,
* and thus is mandatory.
*
* **`functionCallMessageTemplate`** is used to specify the format in which functions can be called by the model and
* how their results are fed to the model after the function call.
*
* **`segments`** is used to specify the format of the segments generated by the model (like thought segments).
*/
export class TemplateChatWrapper extends ChatWrapper {
wrapperName = "Template";
settings;
template;
historyTemplate;
joinAdjacentMessagesOfTheSameType;
/** @internal */ _parsedChatTemplate;
/** @internal */ _parsedChatHistoryTemplate;
constructor({ template, historyTemplate, functionCallMessageTemplate, joinAdjacentMessagesOfTheSameType = true, segments }) {
super();
if (template == null || historyTemplate == null)
throw new Error("Template chat wrapper settings must have a template and historyTemplate.");
if (historyTemplate.system == null || historyTemplate.user == null || historyTemplate.model == null)
throw new Error("Template chat wrapper historyTemplate must have system, user, and model templates.");
this.template = template;
this.historyTemplate = historyTemplate;
this.joinAdjacentMessagesOfTheSameType = joinAdjacentMessagesOfTheSameType;
this._parsedChatTemplate = parseChatTemplate(template);
this._parsedChatHistoryTemplate = {
system: parseChatHistoryTemplate(historyTemplate.system),
user: parseChatHistoryTemplate(historyTemplate.user),
model: parseChatHistoryTemplate(historyTemplate.model)
};
this.settings = {
...ChatWrapper.defaultSettings,
functions: parseFunctionCallMessageTemplate(functionCallMessageTemplate) ?? ChatWrapper.defaultSettings.functions,
segments: templateSegmentOptionsToChatWrapperSettings(segments)
};
}
generateContextState({ chatHistory, availableFunctions, documentFunctionParams }) {
const historyWithFunctions = this.addAvailableFunctionsSystemMessageToHistory(chatHistory, availableFunctions, {
documentParams: documentFunctionParams
});
const resultItems = [];
const systemTexts = [];
const userTexts = [];
const modelTexts = [];
let currentAggregateFocus = null;
function flush() {
if (systemTexts.length > 0 || userTexts.length > 0 || modelTexts.length > 0)
resultItems.push({
system: LlamaText.joinValues("\n\n", systemTexts),
user: LlamaText.joinValues("\n\n", userTexts),
model: LlamaText.joinValues("\n\n", modelTexts)
});
systemTexts.length = 0;
userTexts.length = 0;
modelTexts.length = 0;
}
for (const item of historyWithFunctions) {
if (item.type === "system") {
if (!this.joinAdjacentMessagesOfTheSameType || currentAggregateFocus !== "system")
flush();
currentAggregateFocus = "system";
systemTexts.push(LlamaText.fromJSON(item.text));
}
else if (item.type === "user") {
if (!this.joinAdjacentMessagesOfTheSameType || (currentAggregateFocus !== "system" && currentAggregateFocus !== "user"))
flush();
currentAggregateFocus = "user";
userTexts.push(LlamaText(item.text));
}
else if (item.type === "model") {
if (!this.joinAdjacentMessagesOfTheSameType)
flush();
currentAggregateFocus = "model";
modelTexts.push(this.generateModelResponseText(item.response));
}
else
void item;
}
flush();
const getHistoryItem = (role, text, prefix) => {
const { messagePrefix, messageSuffix } = this._parsedChatHistoryTemplate[role];
return LlamaText([
new SpecialTokensText((prefix ?? "") + messagePrefix),
text,
new SpecialTokensText(messageSuffix)
]);
};
const contextText = LlamaText(resultItems.map(({ system, user, model }, index) => {
const isFirstItem = index === 0;
const isLastItem = index === resultItems.length - 1;
const res = LlamaText([
isFirstItem
? system.values.length === 0
? new SpecialTokensText((this._parsedChatTemplate.systemPromptPrefix ?? "") + this._parsedChatTemplate.historyPrefix)
: this._parsedChatTemplate.systemPromptPrefix != null
? LlamaText([
new SpecialTokensText(this._parsedChatTemplate.systemPromptPrefix),
system,
new SpecialTokensText(this._parsedChatTemplate.historyPrefix)
])
: getHistoryItem("system", system, this._parsedChatTemplate.historyPrefix)
: system.values.length === 0
? LlamaText([])
: getHistoryItem("system", system),
user.values.length === 0
? LlamaText([])
: getHistoryItem("user", user),
model.values.length === 0
? LlamaText([])
: !isLastItem
? getHistoryItem("model", model)
: LlamaText([
new SpecialTokensText(this._parsedChatTemplate.completionPrefix),
model
])
]);
return LlamaText(res.values.reduce((res, value) => {
if (value instanceof SpecialTokensText) {
const lastItem = res[res.length - 1];
if (lastItem == null || !(lastItem instanceof SpecialTokensText))
return res.concat([value]);
return res.slice(0, -1).concat([
new SpecialTokensText(lastItem.value + value.value)
]);
}
return res.concat([value]);
}, []));
}));
return {
contextText,
stopGenerationTriggers: [
LlamaText(new SpecialToken("EOS")),
LlamaText(this._parsedChatTemplate.completionSuffix),
LlamaText(new SpecialTokensText(this._parsedChatTemplate.completionSuffix))
]
};
}
}
function parseChatTemplate(template) {
const parsedTemplate = parseTextTemplate(template, [{
text: "{{systemPrompt}}",
key: "systemPrompt",
optional: true
}, {
text: "{{history}}",
key: "history"
}, {
text: "{{completion}}",
key: "completion"
}]);
if (parsedTemplate.completion.suffix.length == 0)
throw new Error('Chat template must have text after "{{completion}}"');
return {
systemPromptPrefix: parsedTemplate.systemPrompt?.prefix ?? null,
historyPrefix: parsedTemplate.history.prefix,
completionPrefix: parsedTemplate.completion.prefix,
completionSuffix: parsedTemplate.completion.suffix
};
}
function parseChatHistoryTemplate(template) {
const parsedTemplate = parseTextTemplate(template, [{
text: "{{message}}",
key: "message"
}]);
return {
messagePrefix: parsedTemplate.message.prefix,
messageSuffix: parsedTemplate.message.suffix
};
}
//# sourceMappingURL=TemplateChatWrapper.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,7 @@
export declare class UniqueIdGenerator {
readonly antiText: string;
private readonly _ids;
constructor(antiText: string);
generateId(numbersOnly?: boolean): string;
removeId(id: string): void;
}

View File

@@ -0,0 +1,30 @@
export class UniqueIdGenerator {
antiText;
_ids = new Set();
constructor(antiText) {
this.antiText = antiText;
}
generateId(numbersOnly = false) {
let id;
do {
if (numbersOnly) {
do {
id = (Math.random()
.toString(10)
.slice(2)
.slice(0, String(Number.MAX_SAFE_INTEGER).length - 1));
} while (id.startsWith("0"));
}
else
id = "W" + (Math.random()
.toString(36)
.slice(2)) + "W";
} while (this._ids.has(id) || this.antiText.includes(id));
this._ids.add(id);
return id;
}
removeId(id) {
this._ids.delete(id);
}
}
//# sourceMappingURL=UniqueIdGenerator.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"UniqueIdGenerator.js","sourceRoot":"","sources":["../../../../src/chatWrappers/generic/utils/UniqueIdGenerator.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,iBAAiB;IACV,QAAQ,CAAS;IAChB,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE1C,YAAmB,QAAgB;QAC/B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;IAEM,UAAU,CAAC,cAAuB,KAAK;QAC1C,IAAI,EAAU,CAAC;QAEf,GAAG,CAAC;YACA,IAAI,WAAW,EAAE,CAAC;gBACd,GAAG,CAAC;oBACA,EAAE,GAAG,CACD,IAAI,CAAC,MAAM,EAAE;yBACR,QAAQ,CAAC,EAAE,CAAC;yBACZ,KAAK,CAAC,CAAC,CAAC;yBACR,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAC5D,CAAC;gBACN,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;YACjC,CAAC;;gBACG,EAAE,GAAG,GAAG,GAAG,CACP,IAAI,CAAC,MAAM,EAAE;qBACR,QAAQ,CAAC,EAAE,CAAC;qBACZ,KAAK,CAAC,CAAC,CAAC,CAChB,GAAG,GAAG,CAAC;QAChB,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE;QAE1D,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAElB,OAAO,EAAE,CAAC;IACd,CAAC;IAEM,QAAQ,CAAC,EAAU;QACtB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;CACJ"}

View File

@@ -0,0 +1,24 @@
import { ChatWrapperSettings } from "../../../types.js";
export declare function parseFunctionCallMessageTemplate(template?: ChatHistoryFunctionCallMessageTemplate): ChatWrapperSettings["functions"] | null;
/**
* Template format for how functions can be called by the model and how their results are fed to the model after function calls.
*
* Consists of an object with two properties:
* 1. **`call`**: The function call template.
* 2. **`result`**: The function call result template.
*
* For example:
* ```ts
* const template: ChatHistoryFunctionCallMessageTemplate = {
* call: "[[call: {{functionName}}({{functionParams}})]]",
* result: " [[result: {{functionCallResult}}]]"
* };
* ```
*
* It's mandatory for the call template to have text before <span v-pre>`{{functionName}}`</span> in order for the chat wrapper know when
* to activate the function calling grammar.
*/
export type ChatHistoryFunctionCallMessageTemplate = {
call: `${string}{{functionName}}${string}{{functionParams}}${string}`;
result: `${string}{{functionCallResult}}${string}`;
};

View File

@@ -0,0 +1,45 @@
import { parseTextTemplate } from "../../../utils/parseTextTemplate.js";
export function parseFunctionCallMessageTemplate(template) {
if (template == null)
return null;
const { call: functionCallTemplate, result: functionCallResultTemplate } = template;
if (functionCallTemplate == null || functionCallResultTemplate == null)
throw new Error("Both function call and function call result templates are required");
const parsedFunctionCallTemplate = parseTextTemplate(functionCallTemplate, [{
text: "{{functionName}}",
key: "functionName"
}, {
text: "{{functionParams}}",
key: "functionParams"
}]);
const parsedFunctionCallResultTemplate = parseTextTemplate(functionCallResultTemplate, [{
text: "{{functionCallResult}}",
key: "functionCallResult"
}]);
const callPrefix = parsedFunctionCallTemplate.functionName.prefix;
const callParamsPrefix = parsedFunctionCallTemplate.functionParams.prefix;
const callSuffix = parsedFunctionCallTemplate.functionParams.suffix;
const resultPrefix = parsedFunctionCallResultTemplate.functionCallResult.prefix;
const resultSuffix = parsedFunctionCallResultTemplate.functionCallResult.suffix;
if (callPrefix.length === 0)
throw new Error("Function call template must have text before \"{{functionName}}\"");
if (callSuffix.length === 0)
throw new Error("Function call template must have text after \"{{functionParams}}\"");
if (resultPrefix.length === 0)
throw new Error("Function call result template must have text before \"{{functionCallResult}}\"");
if (resultSuffix.length === 0)
throw new Error("Function call result template must have text after \"{{functionCallResult}}\"");
return {
call: {
optionalPrefixSpace: true,
prefix: callPrefix,
paramsPrefix: callParamsPrefix,
suffix: callSuffix
},
result: {
prefix: resultPrefix,
suffix: resultSuffix
}
};
}
//# sourceMappingURL=chatHistoryFunctionCallMessageTemplate.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"chatHistoryFunctionCallMessageTemplate.js","sourceRoot":"","sources":["../../../../src/chatWrappers/generic/utils/chatHistoryFunctionCallMessageTemplate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,iBAAiB,EAAC,MAAM,qCAAqC,CAAC;AAGtE,MAAM,UAAU,gCAAgC,CAC5C,QAAiD;IAEjD,IAAI,QAAQ,IAAI,IAAI;QAChB,OAAO,IAAI,CAAC;IAEhB,MAAM,EACF,IAAI,EAAE,oBAAoB,EAC1B,MAAM,EAAE,0BAA0B,EACrC,GAAG,QAAQ,CAAC;IAEb,IAAI,oBAAoB,IAAI,IAAI,IAAI,0BAA0B,IAAI,IAAI;QAClE,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;IAE1F,MAAM,0BAA0B,GAAG,iBAAiB,CAAC,oBAAoB,EAAE,CAAC;YACxE,IAAI,EAAE,kBAAkB;YACxB,GAAG,EAAE,cAAc;SACtB,EAAE;YACC,IAAI,EAAE,oBAAoB;YAC1B,GAAG,EAAE,gBAAgB;SACxB,CAAC,CAAC,CAAC;IACJ,MAAM,gCAAgC,GAAG,iBAAiB,CAAC,0BAA0B,EAAE,CAAC;YACpF,IAAI,EAAE,wBAAwB;YAC9B,GAAG,EAAE,oBAAoB;SAC5B,CAAC,CAAC,CAAC;IAEJ,MAAM,UAAU,GAAG,0BAA0B,CAAC,YAAY,CAAC,MAAM,CAAC;IAClE,MAAM,gBAAgB,GAAG,0BAA0B,CAAC,cAAc,CAAC,MAAM,CAAC;IAC1E,MAAM,UAAU,GAAG,0BAA0B,CAAC,cAAc,CAAC,MAAM,CAAC;IAEpE,MAAM,YAAY,GAAG,gCAAgC,CAAC,kBAAkB,CAAC,MAAM,CAAC;IAChF,MAAM,YAAY,GAAG,gCAAgC,CAAC,kBAAkB,CAAC,MAAM,CAAC;IAEhF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;IAEzF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;IAE1F,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,gFAAgF,CAAC,CAAC;IAEtG,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,+EAA+E,CAAC,CAAC;IAErG,OAAO;QACH,IAAI,EAAE;YACF,mBAAmB,EAAE,IAAI;YACzB,MAAM,EAAE,UAAU;YAClB,YAAY,EAAE,gBAAgB;YAC9B,MAAM,EAAE,UAAU;SACrB;QACD,MAAM,EAAE;YACJ,MAAM,EAAE,YAAY;YACpB,MAAM,EAAE,YAAY;SACvB;KACJ,CAAC;AACN,CAAC"}

View File

@@ -0,0 +1,19 @@
import { ChatHistoryItem, ChatModelFunctions, ChatWrapperSettings } from "../../../types.js";
import { UniqueIdGenerator } from "./UniqueIdGenerator.js";
export declare function extractFunctionCallSettingsFromJinjaTemplate({ idsGenerator, renderTemplate }: {
idsGenerator: UniqueIdGenerator;
renderTemplate({}: {
chatHistory: ChatHistoryItem[];
functions: ChatModelFunctions;
additionalParams: Record<string, unknown>;
stringifyFunctionParams: boolean;
stringifyFunctionResults: boolean;
combineModelMessageAndToolCalls: boolean;
squashModelTextResponses?: boolean;
}): string;
}): {
settings: ChatWrapperSettings["functions"] | null;
stringifyParams: boolean;
stringifyResult: boolean;
combineModelMessageAndToolCalls: boolean;
};

View File

@@ -0,0 +1,521 @@
import { splitText } from "lifecycle-utils";
import { LlamaText, SpecialToken, SpecialTokensText } from "../../../utils/LlamaText.js";
import { getFirstValidResult } from "./getFirstValidResult.js";
export function extractFunctionCallSettingsFromJinjaTemplate({ idsGenerator, renderTemplate }) {
const idToStaticContent = new Map();
const bosTokenId = idsGenerator.generateId();
const eosTokenId = idsGenerator.generateId();
const eotTokenId = idsGenerator.generateId();
idToStaticContent.set(bosTokenId, new SpecialToken("BOS"));
idToStaticContent.set(eosTokenId, new SpecialToken("EOS"));
idToStaticContent.set(eotTokenId, new SpecialToken("EOT"));
const contentIds = new Set();
const addContentId = (id) => {
contentIds.add(id);
return id;
};
const systemMessage = addContentId(idsGenerator.generateId());
const userMessage1 = addContentId(idsGenerator.generateId());
const modelMessage1 = addContentId(idsGenerator.generateId());
const func1name = addContentId(idsGenerator.generateId());
const func1description = addContentId(idsGenerator.generateId());
const func1params = addContentId(idsGenerator.generateId(true));
const func1result = addContentId(idsGenerator.generateId(true));
const func2name = addContentId(idsGenerator.generateId());
const func2description = addContentId(idsGenerator.generateId());
const func2params = addContentId(idsGenerator.generateId(true));
const func2result = addContentId(idsGenerator.generateId(true));
const modelMessage2 = addContentId(idsGenerator.generateId());
const func1StringifyParam = addContentId(idsGenerator.generateId());
const func1StringifyResult = addContentId(idsGenerator.generateId());
const functions1 = {
[func1name]: {
description: func1description,
params: {
type: "number"
}
}
};
const functions2 = {
...functions1,
[func2name]: {
description: func2description,
params: {
type: "number"
}
}
};
const baseChatHistory = [{
type: "system",
text: systemMessage
}, {
type: "user",
text: userMessage1
}];
const chatHistory1Call = [...baseChatHistory, {
type: "model",
response: [
modelMessage1,
{
type: "functionCall",
name: func1name,
// convert to number since this will go through JSON.stringify,
// and we want to avoid escaping characters in the rendered output
params: Number(func1params),
result: Number(func1result),
startsNewChunk: true
},
modelMessage2
]
}];
const chatHistoryOnlyCall = [...baseChatHistory, {
type: "model",
response: [
{
type: "functionCall",
name: func1name,
// convert to number since this will go through JSON.stringify,
// and we want to avoid escaping characters in the rendered output
params: Number(func1params),
result: Number(func1result),
startsNewChunk: true
},
modelMessage2
]
}];
const chatHistory2Calls = [...baseChatHistory, {
type: "model",
response: [
modelMessage1,
{
type: "functionCall",
name: func1name,
// convert to number since this will go through JSON.stringify,
// and we want to avoid escaping characters in the rendered output
params: Number(func1params),
result: Number(func1result),
startsNewChunk: true
},
{
type: "functionCall",
name: func2name,
params: Number(func2params),
result: Number(func2result),
startsNewChunk: false
},
modelMessage2
]
}];
const chatHistory2CallsNewChunk = [...baseChatHistory, {
type: "model",
response: [
modelMessage1,
{
type: "functionCall",
name: func1name,
// convert to number since this will go through JSON.stringify,
// and we want to avoid escaping characters in the rendered output
params: Number(func1params),
result: Number(func1result),
startsNewChunk: true
},
{
type: "functionCall",
name: func2name,
params: Number(func2params),
result: Number(func2result),
startsNewChunk: true
},
modelMessage2
]
}];
const additionalParams = {
"bos_token": bosTokenId,
"eos_token": eosTokenId,
"eot_token": eotTokenId
};
let combineModelMessageAndToolCalls = true;
let stringifyParams = true;
let stringifyResult = true;
try {
const paramsObjectTest = renderTemplate({
chatHistory: [...baseChatHistory, {
type: "model",
response: [
modelMessage1,
{
type: "functionCall",
name: func1name,
params: { [func1StringifyParam]: "test" },
result: func1StringifyResult,
startsNewChunk: true
},
modelMessage2
]
}],
functions: functions1,
additionalParams,
stringifyFunctionParams: false,
stringifyFunctionResults: false,
combineModelMessageAndToolCalls
});
stringifyParams = (!paramsObjectTest.includes(`"${func1StringifyParam}":`) &&
!paramsObjectTest.includes(`'${func1StringifyParam}':`));
}
catch (err) {
// do nothing
}
try {
const resultObjectTest = renderTemplate({
chatHistory: [...baseChatHistory, {
type: "model",
response: [
modelMessage1,
{
type: "functionCall",
name: func1name,
params: func1StringifyParam,
result: { [func1StringifyResult]: "test" },
startsNewChunk: true
},
modelMessage2
]
}],
functions: functions1,
additionalParams,
stringifyFunctionParams: false,
stringifyFunctionResults: false,
combineModelMessageAndToolCalls
});
stringifyResult = (!resultObjectTest.includes(`"${func1StringifyResult}":`) &&
!resultObjectTest.includes(`'${func1StringifyResult}':`));
}
catch (err) {
// do nothing
}
combineModelMessageAndToolCalls = renderTemplate({
chatHistory: chatHistory1Call,
functions: functions1,
additionalParams,
stringifyFunctionParams: true,
stringifyFunctionResults: true,
combineModelMessageAndToolCalls
}).includes(modelMessage1);
let textBetween2TextualModelResponses = LlamaText();
if (!combineModelMessageAndToolCalls) {
try {
const betweenModelTextualResponsesTest = renderTemplate({
chatHistory: [...baseChatHistory, {
type: "model",
response: [modelMessage1]
}, {
type: "model",
response: [modelMessage2]
}],
functions: {},
additionalParams,
stringifyFunctionParams: false,
stringifyFunctionResults: false,
combineModelMessageAndToolCalls,
squashModelTextResponses: false
});
const textDiff = getTextBetweenIds(betweenModelTextualResponsesTest, modelMessage1, modelMessage2).text ?? "";
textBetween2TextualModelResponses = reviveSeparatorText(textDiff, idToStaticContent, contentIds);
}
catch (err) {
// do nothing
}
}
let usedNewChunkFor2Calls = false;
const rendered1Call = renderTemplate({
chatHistory: chatHistory1Call,
functions: functions1,
additionalParams,
stringifyFunctionParams: stringifyParams,
stringifyFunctionResults: stringifyResult,
combineModelMessageAndToolCalls
});
const renderedOnlyCall = getFirstValidResult([
() => renderTemplate({
chatHistory: chatHistoryOnlyCall,
functions: functions1,
additionalParams,
stringifyFunctionParams: stringifyParams,
stringifyFunctionResults: stringifyResult,
combineModelMessageAndToolCalls
}),
() => undefined
]);
const rendered2Calls = getFirstValidResult([
() => renderTemplate({
chatHistory: chatHistory2Calls,
functions: functions2,
additionalParams,
stringifyFunctionParams: stringifyParams,
stringifyFunctionResults: stringifyResult,
combineModelMessageAndToolCalls
}),
() => {
usedNewChunkFor2Calls = true;
return renderTemplate({
chatHistory: chatHistory2CallsNewChunk,
functions: functions2,
additionalParams,
stringifyFunctionParams: stringifyParams,
stringifyFunctionResults: stringifyResult,
combineModelMessageAndToolCalls
});
}
]);
const modelMessage1ToFunc1Name = getTextBetweenIds(rendered2Calls, modelMessage1, func1name);
const func1NameToFunc1Params = getTextBetweenIds(rendered2Calls, func1name, func1params, modelMessage1ToFunc1Name.endIndex);
const func1ResultIndex = rendered2Calls.indexOf(func1result, func1NameToFunc1Params.endIndex);
const func2NameIndex = rendered2Calls.indexOf(func2name, modelMessage1ToFunc1Name.endIndex);
if (modelMessage1ToFunc1Name.text == null ||
func1NameToFunc1Params.text == null ||
func1ResultIndex < 0 ||
func2NameIndex < 0)
return { settings: null, stringifyParams, stringifyResult, combineModelMessageAndToolCalls };
const supportsParallelCalls = func1ResultIndex > func2NameIndex;
if (!supportsParallelCalls || usedNewChunkFor2Calls) {
const prefix = getTextBetweenIds(rendered1Call, modelMessage1, func1name);
const paramsPrefix = getTextBetweenIds(rendered1Call, func1name, func1params, prefix.endIndex);
const resultPrefix = getTextBetweenIds(rendered1Call, func1params, func1result, paramsPrefix.endIndex);
const resultSuffix = getTextBetweenIds(rendered1Call, func1result, modelMessage2, resultPrefix.endIndex);
if (prefix.text == null || prefix.text === "" || paramsPrefix.text == null || resultPrefix.text == null || resultSuffix.text == null)
return { settings: null, stringifyParams, stringifyResult, combineModelMessageAndToolCalls };
return {
stringifyParams,
stringifyResult,
combineModelMessageAndToolCalls,
settings: {
call: {
optionalPrefixSpace: true,
prefix: removeCommonRevivedPrefix(reviveSeparatorText(prefix.text, idToStaticContent, contentIds), !combineModelMessageAndToolCalls
? textBetween2TextualModelResponses
: LlamaText()),
paramsPrefix: reviveSeparatorText(paramsPrefix.text, idToStaticContent, contentIds),
suffix: "",
emptyCallParamsPlaceholder: {}
},
result: {
prefix: reviveSeparatorText(resultPrefix.text, new Map([
...idToStaticContent.entries(),
[func1name, LlamaText("{{functionName}}")],
[func1params, LlamaText("{{functionParams}}")]
]), contentIds),
suffix: reviveSeparatorText(resultSuffix.text, new Map([
...idToStaticContent.entries(),
[func1name, LlamaText("{{functionName}}")],
[func1params, LlamaText("{{functionParams}}")]
]), contentIds)
}
}
};
}
const func1ParamsToFunc2Name = getTextBetweenIds(rendered2Calls, func1params, func2name, func1NameToFunc1Params.endIndex);
const func2ParamsToFunc1Result = getTextBetweenIds(rendered2Calls, func2params, func1result, func1ParamsToFunc2Name.endIndex);
const func1ResultToFunc2Result = getTextBetweenIds(rendered2Calls, func1result, func2result, func2ParamsToFunc1Result.endIndex);
const func2ResultToModelMessage2 = getTextBetweenIds(rendered2Calls, func2result, modelMessage2, func1ResultToFunc2Result.endIndex);
if (func1ParamsToFunc2Name.text == null || func2ParamsToFunc1Result.text == null || func1ResultToFunc2Result.text == null ||
func2ResultToModelMessage2.text == null)
return { settings: null, stringifyParams, stringifyResult, combineModelMessageAndToolCalls };
const callPrefixLength = findCommonEndLength(modelMessage1ToFunc1Name.text, func1ParamsToFunc2Name.text);
const callPrefixText = func1ParamsToFunc2Name.text.slice(-callPrefixLength);
const parallelismCallPrefix = modelMessage1ToFunc1Name.text.slice(0, -callPrefixLength);
const callSuffixLength = findCommandStartLength(func1ParamsToFunc2Name.text, func2ParamsToFunc1Result.text);
const callSuffixText = func1ParamsToFunc2Name.text.slice(0, callSuffixLength);
const parallelismBetweenCallsText = func1ParamsToFunc2Name.text.slice(callSuffixLength, -callPrefixLength);
const callParamsPrefixText = func1NameToFunc1Params.text;
const resultPrefixLength = findCommonEndLength(func2ParamsToFunc1Result.text, func1ResultToFunc2Result.text);
const resultPrefixText = func2ParamsToFunc1Result.text.slice(-resultPrefixLength);
const resultSuffixLength = findCommandStartLength(func1ResultToFunc2Result.text, func2ResultToModelMessage2.text);
const resultSuffixText = func1ResultToFunc2Result.text.slice(0, resultSuffixLength);
const parallelismResultBetweenResultsText = func1ResultToFunc2Result.text.slice(resultSuffixLength, -resultPrefixLength);
const parallelismResultSuffixText = func2ResultToModelMessage2.text.slice(resultSuffixLength);
const resolveParallelismBetweenSectionsParts = (betweenSectionsText) => {
const { index: endTokenIndex, text: endTokenId } = findFirstTextMatch(betweenSectionsText, [eosTokenId, eosTokenId]);
if (endTokenIndex >= 0 && endTokenId != null)
return {
parallelismCallSuffixText: betweenSectionsText.slice(0, endTokenIndex + endTokenId.length),
parallelismResultPrefix: betweenSectionsText.slice(endTokenIndex + endTokenId.length)
};
const bosIndex = betweenSectionsText.indexOf(bosTokenId);
if (bosIndex >= 0)
return {
parallelismCallSuffixText: betweenSectionsText.slice(0, bosIndex),
parallelismResultPrefix: betweenSectionsText.slice(bosIndex)
};
return {
parallelismCallSuffixText: betweenSectionsText,
parallelismResultPrefix: ""
};
};
const { parallelismCallSuffixText, parallelismResultPrefix } = resolveParallelismBetweenSectionsParts(func2ParamsToFunc1Result.text.slice(callSuffixLength, -resultPrefixLength));
let revivedCallPrefix = reviveSeparatorText(callPrefixText, idToStaticContent, contentIds);
const revivedParallelismCallSectionPrefix = removeCommonRevivedPrefix(reviveSeparatorText(parallelismCallPrefix, idToStaticContent, contentIds), !combineModelMessageAndToolCalls
? textBetween2TextualModelResponses
: LlamaText());
let revivedParallelismCallBetweenCalls = reviveSeparatorText(parallelismBetweenCallsText, idToStaticContent, contentIds);
if (revivedParallelismCallSectionPrefix.values.length === 0 && renderedOnlyCall != null) {
const userMessage1ToModelMessage1Start = getTextBetweenIds(rendered1Call, userMessage1, modelMessage1);
const onlyCallUserMessage1ToFunc1Name = getTextBetweenIds(renderedOnlyCall, userMessage1, func1name);
if (userMessage1ToModelMessage1Start.text != null && onlyCallUserMessage1ToFunc1Name.text != null) {
const onlyCallModelMessagePrefixLength = findCommandStartLength(userMessage1ToModelMessage1Start.text, onlyCallUserMessage1ToFunc1Name.text);
const onlyCallCallPrefixText = onlyCallUserMessage1ToFunc1Name.text.slice(onlyCallModelMessagePrefixLength);
const revivedOnlyCallCallPrefixText = reviveSeparatorText(onlyCallCallPrefixText, idToStaticContent, contentIds);
const optionalCallPrefix = removeCommonRevivedSuffix(revivedCallPrefix, revivedOnlyCallCallPrefixText);
if (optionalCallPrefix.values.length > 0) {
revivedCallPrefix = removeCommonRevivedPrefix(revivedCallPrefix, optionalCallPrefix);
revivedParallelismCallBetweenCalls = LlamaText([
optionalCallPrefix,
revivedParallelismCallBetweenCalls
]);
}
}
}
return {
stringifyParams,
stringifyResult,
combineModelMessageAndToolCalls,
settings: {
call: {
optionalPrefixSpace: true,
prefix: revivedCallPrefix,
paramsPrefix: reviveSeparatorText(callParamsPrefixText, idToStaticContent, contentIds),
suffix: reviveSeparatorText(callSuffixText, idToStaticContent, contentIds),
emptyCallParamsPlaceholder: {}
},
result: {
prefix: reviveSeparatorText(resultPrefixText, new Map([
...idToStaticContent.entries(),
[func1name, LlamaText("{{functionName}}")],
[func1params, LlamaText("{{functionParams}}")]
]), contentIds),
suffix: reviveSeparatorText(resultSuffixText, new Map([
...idToStaticContent.entries(),
[func1name, LlamaText("{{functionName}}")],
[func1params, LlamaText("{{functionParams}}")]
]), contentIds)
},
parallelism: {
call: {
sectionPrefix: revivedParallelismCallSectionPrefix,
betweenCalls: revivedParallelismCallBetweenCalls,
sectionSuffix: reviveSeparatorText(parallelismCallSuffixText, idToStaticContent, contentIds)
},
result: {
sectionPrefix: reviveSeparatorText(parallelismResultPrefix, idToStaticContent, contentIds),
betweenResults: reviveSeparatorText(parallelismResultBetweenResultsText, idToStaticContent, contentIds),
sectionSuffix: reviveSeparatorText(parallelismResultSuffixText, idToStaticContent, contentIds)
}
}
}
};
}
function getTextBetweenIds(text, startId, endId, startIndex = 0) {
const foundStartIndex = text.indexOf(startId, startIndex);
if (foundStartIndex < 0)
return { text: undefined, endIndex: -1 };
const foundEndIndex = text.indexOf(endId, foundStartIndex + startId.length);
if (foundEndIndex < 0)
return { text: undefined, endIndex: -1 };
return {
text: text.slice(foundStartIndex + startId.length, foundEndIndex),
endIndex: foundEndIndex
};
}
function reviveSeparatorText(text, idMap, contentIds) {
return LlamaText(splitText(text, [...new Set([...idMap.keys(), ...contentIds])])
.map((item) => {
if (typeof item === "string")
return new SpecialTokensText(item);
const mappedItem = idMap.get(item.separator);
if (mappedItem != null)
return mappedItem;
if (contentIds.has(item.separator))
throw new Error("Content ID found in separator text");
return new SpecialTokensText(item.separator);
}));
}
function removeCommonRevivedPrefix(target, matchStart) {
for (let commonStartLength = 0; commonStartLength < target.values.length && commonStartLength < matchStart.values.length; commonStartLength++) {
const targetValue = target.values[commonStartLength];
const matchStartValue = matchStart.values[commonStartLength];
if (typeof targetValue === "string" && typeof matchStartValue === "string") {
if (targetValue === matchStartValue)
continue;
}
else if (targetValue instanceof SpecialTokensText && matchStartValue instanceof SpecialTokensText) {
const commonLength = findCommandStartLength(targetValue.value, matchStartValue.value);
if (commonLength === targetValue.value.length && commonLength === matchStartValue.value.length)
continue;
return LlamaText([
new SpecialTokensText(targetValue.value.slice(commonLength)),
...target.values.slice(commonStartLength + 1)
]);
}
else if (targetValue instanceof SpecialToken && matchStartValue instanceof SpecialToken) {
if (targetValue.value === matchStartValue.value)
continue;
}
else if (LlamaText(targetValue ?? "").compare(LlamaText(matchStartValue ?? "")))
continue;
return LlamaText(target.values.slice(commonStartLength));
}
return LlamaText(target.values.slice(matchStart.values.length));
}
function removeCommonRevivedSuffix(target, matchEnd) {
for (let commonEndLength = 0; commonEndLength < target.values.length && commonEndLength < matchEnd.values.length; commonEndLength++) {
const targetValue = target.values[target.values.length - commonEndLength - 1];
const matchEndValue = matchEnd.values[matchEnd.values.length - commonEndLength - 1];
if (typeof targetValue === "string" && typeof matchEndValue === "string") {
if (targetValue === matchEndValue)
continue;
}
else if (targetValue instanceof SpecialTokensText && matchEndValue instanceof SpecialTokensText) {
const commonLength = findCommonEndLength(targetValue.value, matchEndValue.value);
if (commonLength === targetValue.value.length && commonLength === matchEndValue.value.length)
continue;
return LlamaText([
...target.values.slice(0, target.values.length - commonEndLength - 1),
new SpecialTokensText(targetValue.value.slice(0, targetValue.value.length - commonLength))
]);
}
else if (targetValue instanceof SpecialToken && matchEndValue instanceof SpecialToken) {
if (targetValue.value === matchEndValue.value)
continue;
}
else if (LlamaText(targetValue ?? "").compare(LlamaText(matchEndValue ?? "")))
continue;
return LlamaText(target.values.slice(0, target.values.length - commonEndLength - 1));
}
return LlamaText(target.values.slice(0, target.values.length - matchEnd.values.length));
}
function findCommandStartLength(text1, text2) {
let commonStartLength = 0;
while (commonStartLength < text1.length && commonStartLength < text2.length) {
if (text1[commonStartLength] !== text2[commonStartLength])
break;
commonStartLength++;
}
return commonStartLength;
}
function findCommonEndLength(text1, text2) {
let commonEndLength = 0;
while (commonEndLength < text1.length && commonEndLength < text2.length) {
if (text1[text1.length - commonEndLength - 1] !== text2[text2.length - commonEndLength - 1])
break;
commonEndLength++;
}
return commonEndLength;
}
function findFirstTextMatch(text, matchTexts, startIndex = 0) {
for (const matchText of matchTexts) {
const index = text.indexOf(matchText, startIndex);
if (index >= 0)
return { index, text: matchText };
}
return { index: -1, text: undefined };
}
//# sourceMappingURL=extractFunctionCallSettingsFromJinjaTemplate.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
import { ChatWrapperSettings, Tokenizer } from "../../../types.js";
export declare function extractSegmentSettingsFromTokenizerAndChatTemplate(chatTemplate: string | undefined, tokenizer?: Tokenizer): ChatWrapperSettings["segments"];

View File

@@ -0,0 +1,64 @@
import { LlamaText, SpecialTokensText } from "../../../utils/LlamaText.js";
import { removeUndefinedFields } from "../../../utils/removeNullFields.js";
export function extractSegmentSettingsFromTokenizerAndChatTemplate(chatTemplate, tokenizer) {
function tryMatchPrefixSuffixPair(tryMatchGroups) {
if (chatTemplate != null) {
for (const [prefix, suffix] of tryMatchGroups) {
if ((hasAll(chatTemplate.replaceAll(prefix + "\\n\\n" + suffix, ""), [
prefix + "\\n\\n",
"\\n\\n" + suffix
])) || (hasAll(chatTemplate.replaceAll(prefix + "\n\n" + suffix, ""), [
prefix + "\n\n",
"\n\n" + suffix
])))
return {
prefix: LlamaText(new SpecialTokensText(prefix + "\n\n")),
suffix: LlamaText(new SpecialTokensText("\n\n" + suffix))
};
if ((hasAll(chatTemplate.replaceAll(prefix + "\\n" + suffix, ""), [
prefix + "\\n",
"\\n" + suffix
])) || (hasAll(chatTemplate.replaceAll(prefix + "\n" + suffix, ""), [
prefix + "\n",
"\n" + suffix
])))
return {
prefix: LlamaText(new SpecialTokensText(prefix + "\n")),
suffix: LlamaText(new SpecialTokensText("\n" + suffix))
};
if (chatTemplate.includes(prefix) && chatTemplate.includes(suffix))
return {
prefix: LlamaText(new SpecialTokensText(prefix)),
suffix: LlamaText(new SpecialTokensText(suffix))
};
}
}
if (tokenizer != null) {
for (const [prefix, suffix] of tryMatchGroups) {
const thinkTokens = tokenizer(prefix, true, "trimLeadingSpace");
const thinkEndTokens = tokenizer(suffix, true, "trimLeadingSpace");
const [thinkToken] = thinkTokens;
const [thinkEndToken] = thinkEndTokens;
if (thinkTokens.length === 1 && thinkEndTokens.length === 1 &&
thinkToken != null && thinkEndToken != null) {
return {
prefix: LlamaText(new SpecialTokensText(prefix)),
suffix: LlamaText(new SpecialTokensText(suffix))
};
}
}
}
return undefined;
}
return removeUndefinedFields({
thought: tryMatchPrefixSuffixPair([
["<think>", "</think>"], // DeepSeek, QwQ
["<thought>", "</thought>"], // EXAONE Deep
["<|START_THINKING|>", "<|END_THINKING|>"] // Command R7B
])
});
}
function hasAll(text, matches) {
return matches.every((match) => text.includes(match));
}
//# sourceMappingURL=extractSegmentSettingsFromTokenizerAndChatTemplate.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"extractSegmentSettingsFromTokenizerAndChatTemplate.js","sourceRoot":"","sources":["../../../../src/chatWrappers/generic/utils/extractSegmentSettingsFromTokenizerAndChatTemplate.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,6 @@
/**
* Call the functions in the array one by one and return the result of the first one that doesn't throw an error.
*
* If all functions throw an error, throw the error of the last function.
*/
export declare function getFirstValidResult<const T extends (() => any)[]>(options: T): ReturnType<T[number]>;

View File

@@ -0,0 +1,19 @@
/**
* Call the functions in the array one by one and return the result of the first one that doesn't throw an error.
*
* If all functions throw an error, throw the error of the last function.
*/
export function getFirstValidResult(options) {
for (let i = 0; i < options.length; i++) {
if (i === options.length - 1)
return options[i]();
try {
return options[i]();
}
catch (err) {
// do nothing
}
}
throw new Error("All options failed");
}
//# sourceMappingURL=getFirstValidResult.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getFirstValidResult.js","sourceRoot":"","sources":["../../../../src/chatWrappers/generic/utils/getFirstValidResult.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAgC,OAAU;IACzE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,CAAC,KAAK,OAAO,CAAC,MAAM,GAAG,CAAC;YACxB,OAAO,OAAO,CAAC,CAAC,CAAE,EAAE,CAAC;QAEzB,IAAI,CAAC;YACD,OAAO,OAAO,CAAC,CAAC,CAAE,EAAE,CAAC;QACzB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,aAAa;QACjB,CAAC;IACL,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;AAC1C,CAAC"}

View File

@@ -0,0 +1,2 @@
import { ChatHistoryItem } from "../../../types.js";
export declare function squashChatHistoryItems(history: readonly ChatHistoryItem[]): ChatHistoryItem[];

View File

@@ -0,0 +1,35 @@
import { LlamaText } from "../../../utils/LlamaText.js";
export function squashChatHistoryItems(history) {
const res = [];
for (const item of history) {
const lastItem = res.at(-1);
if (lastItem == null) {
res.push(structuredClone(item));
continue;
}
if (lastItem.type === "system" && item.type === "system")
lastItem.text = LlamaText.joinValues("\n\n", [
LlamaText.fromJSON(lastItem.text),
LlamaText.fromJSON(item.text)
]).toJSON();
else if (lastItem.type === "user" && item.type === "user")
lastItem.text += "\n\n" + item.text;
else if (lastItem.type === "model" && item.type === "model") {
const responsesToAdd = ["\n\n", ...item.response];
while (typeof responsesToAdd[0] === "string" && typeof lastItem.response.at(-1) === "string") {
const lastResponses = lastItem.response.pop();
if (typeof lastResponses !== "string") {
lastItem.response.push(lastResponses);
break;
}
lastItem.response.push(lastResponses + responsesToAdd.shift());
}
while (responsesToAdd.length > 0)
lastItem.response.push(responsesToAdd.shift());
}
else
res.push(structuredClone(item));
}
return res;
}
//# sourceMappingURL=squashChatHistoryItems.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"squashChatHistoryItems.js","sourceRoot":"","sources":["../../../../src/chatWrappers/generic/utils/squashChatHistoryItems.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,SAAS,EAAC,MAAM,6BAA6B,CAAC;AAEtD,MAAM,UAAU,sBAAsB,CAAC,OAAmC;IACtE,MAAM,GAAG,GAAsB,EAAE,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACnB,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;YAChC,SAAS;QACb,CAAC;QAED,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YACpD,QAAQ,CAAC,IAAI,GAAG,SAAS,CAAC,UAAU,CAAC,MAAM,EAAE;gBACzC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;gBACjC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;aAChC,CAAC,CAAC,MAAM,EAAE,CAAC;aACX,IAAI,QAAQ,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;YACrD,QAAQ,CAAC,IAAI,IAAI,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC;aACnC,IAAI,QAAQ,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC1D,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;YAElD,OAAO,OAAO,cAAc,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,OAAO,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;gBAC3F,MAAM,aAAa,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAG,CAAC;gBAC/C,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE,CAAC;oBACpC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;oBACtC,MAAM;gBACV,CAAC;gBAED,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,GAAG,cAAc,CAAC,KAAK,EAAG,CAAC,CAAC;YACpE,CAAC;YAED,OAAO,cAAc,CAAC,MAAM,GAAG,CAAC;gBAC5B,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,EAAG,CAAC,CAAC;QACxD,CAAC;;YACG,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,GAAG,CAAC;AACf,CAAC"}

View File

@@ -0,0 +1,22 @@
import { ChatWrapperSettings } from "../../../types.js";
export declare function templateSegmentOptionsToChatWrapperSettings(templateOptions?: TemplateChatWrapperSegmentsOptions): ChatWrapperSettings["segments"];
export type TemplateChatWrapperSegmentsOptions = {
/** Template for a thought segment */
thoughtTemplate?: `${string}{{content}}${string}`;
/**
* Automatically reopen a thought segment after function calls.
*
* Useful for aligning the output of models that assume that a thought segment is already open after function calls.
*
* Defaults to `false`.
*/
reopenThoughtAfterFunctionCalls?: boolean;
/** Consider all segments to be closed when this text is detected */
closeAllSegmentsTemplate?: string;
/**
* After function calls, reiterate the stack of the active segments to remind the model of the context.
*
* Defaults to `false`.
*/
reiterateStackAfterFunctionCalls?: boolean;
};

View File

@@ -0,0 +1,28 @@
import { parseTextTemplate } from "../../../utils/parseTextTemplate.js";
import { removeUndefinedFields } from "../../../utils/removeNullFields.js";
export function templateSegmentOptionsToChatWrapperSettings(templateOptions) {
if (templateOptions == null)
return {};
function getThoughtSegmentOptions() {
if (templateOptions?.thoughtTemplate == null)
return undefined;
const parsedThoughtTemplate = parseTextTemplate(templateOptions.thoughtTemplate, [{
text: "{{content}}",
key: "content"
}]);
const prefix = parsedThoughtTemplate.content.prefix;
if (prefix.length === 0)
throw new Error("Thought template must have text before \"{{content}}\"");
return removeUndefinedFields({
prefix,
suffix: parsedThoughtTemplate.content.suffix || undefined,
reopenAfterFunctionCalls: templateOptions.reopenThoughtAfterFunctionCalls
});
}
return removeUndefinedFields({
closeAllSegments: templateOptions.closeAllSegmentsTemplate || undefined,
reiterateStackAfterFunctionCalls: templateOptions.reiterateStackAfterFunctionCalls,
thought: getThoughtSegmentOptions()
});
}
//# sourceMappingURL=templateSegmentOptionsToChatWrapperSettings.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"templateSegmentOptionsToChatWrapperSettings.js","sourceRoot":"","sources":["../../../../src/chatWrappers/generic/utils/templateSegmentOptionsToChatWrapperSettings.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,iBAAiB,EAAC,MAAM,qCAAqC,CAAC;AACtE,OAAO,EAAC,qBAAqB,EAAC,MAAM,oCAAoC,CAAC;AAEzE,MAAM,UAAU,2CAA2C,CACvD,eAAoD;IAEpD,IAAI,eAAe,IAAI,IAAI;QACvB,OAAO,EAAE,CAAC;IAEd,SAAS,wBAAwB;QAC7B,IAAI,eAAe,EAAE,eAAe,IAAI,IAAI;YACxC,OAAO,SAAS,CAAC;QAErB,MAAM,qBAAqB,GAAG,iBAAiB,CAAC,eAAe,CAAC,eAAe,EAAE,CAAC;gBAC9E,IAAI,EAAE,aAAa;gBACnB,GAAG,EAAE,SAAS;aACjB,CAAC,CAAC,CAAC;QAEJ,MAAM,MAAM,GAAG,qBAAqB,CAAC,OAAO,CAAC,MAAM,CAAC;QACpD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAE9E,OAAO,qBAAqB,CAAC;YACzB,MAAM;YACN,MAAM,EAAE,qBAAqB,CAAC,OAAO,CAAC,MAAM,IAAI,SAAS;YACzD,wBAAwB,EAAE,eAAe,CAAC,+BAA+B;SAC5E,CAAC,CAAC;IACP,CAAC;IAED,OAAO,qBAAqB,CAAC;QACzB,gBAAgB,EAAE,eAAe,CAAC,wBAAwB,IAAI,SAAS;QACvE,gCAAgC,EAAE,eAAe,CAAC,gCAAgC;QAElF,OAAO,EAAE,wBAAwB,EAAE;KACtC,CAAC,CAAC;AACP,CAAC"}

View File

@@ -0,0 +1,76 @@
import { ChatModelFunctions } from "../../types.js";
/**
* Generate documentation about the functions that are available for a model to call.
* Useful for generating a system message with information about the available functions as part of a chat wrapper.
*/
export declare class ChatModelFunctionsDocumentationGenerator {
readonly chatModelFunctions?: ChatModelFunctions;
readonly hasAnyFunctions: boolean;
constructor(chatModelFunctions: ChatModelFunctions | undefined);
/**
* Example:
* ```ts
* // Retrieve the current date
* function getDate();
*
* // Retrieve the current time
* function getTime(params: {hours: "24" | "12", seconds: boolean});
* ```
* @param options
* @param [options.documentParams] - Whether to document the parameters of the functions
*/
getTypeScriptFunctionSignatures({ documentParams }?: {
documentParams?: boolean;
}): string;
/**
* Example:
* ```ts
* // Retrieve the current date
* type getDate = () => any;
*
* // Retrieve the current time
* type getTime = (_: {hours: "24" | "12", seconds: boolean}) => any;
* ```
* @param options
* @param [options.documentParams] - Whether to document the parameters of the functions
* @param [options.reservedFunctionNames] - Function names that are reserved and cannot be used
*/
getTypeScriptFunctionTypes({ documentParams, reservedFunctionNames }?: {
documentParams?: boolean;
reservedFunctionNames?: string[];
}): string;
/**
* Example:
* ```
* Use the function 'getDate' to: Retrieve the current date
* {"name": "getDate", "description": "Retrieve the current date"}
*
* Use the function 'getTime' to: Retrieve the current time
* {"name": "getTime", "description": "Retrieve the current time", "parameters": {"type": "object", "properties": {"hours": {"enum": ["24", "12"]}, "seconds": {"type": "boolean"}}}}
* ```
* @param options
* @param [options.documentParams] - Whether to document the parameters of the functions
*/
getLlama3_1FunctionSignatures({ documentParams }?: {
documentParams?: boolean;
}): string;
/**
* Example:
* ```
* {"name": "getDate", "description": "Retrieve the current date"}
*
* {"name": "getTime", "description": "Retrieve the current time", "parameters": {"type": "object", "properties": {"hours": {"enum": ["24", "12"]}, "seconds": {"type": "boolean"}}}}
* ```
* @param options
* @param [options.documentParams] - Whether to document the parameters of the functions
*/
getLlama3_2LightweightFunctionSignatures({ documentParams }?: {
documentParams?: boolean;
}): string;
getQwenFunctionSignatures({ documentParams }?: {
documentParams?: boolean;
}): string;
getSeedFunctionSignatures({ documentParams }?: {
documentParams?: boolean;
}): string;
}

View File

@@ -0,0 +1,177 @@
import { getTypeScriptTypeStringForGbnfJsonSchema } from "../../utils/getTypeScriptTypeStringForGbnfJsonSchema.js";
import { jsonDumps } from "./jsonDumps.js";
/**
* Generate documentation about the functions that are available for a model to call.
* Useful for generating a system message with information about the available functions as part of a chat wrapper.
*/
export class ChatModelFunctionsDocumentationGenerator {
chatModelFunctions;
hasAnyFunctions;
constructor(chatModelFunctions) {
this.chatModelFunctions = chatModelFunctions;
this.hasAnyFunctions = Object.keys(this.chatModelFunctions ?? {}).length > 0;
}
/**
* Example:
* ```ts
* // Retrieve the current date
* function getDate();
*
* // Retrieve the current time
* function getTime(params: {hours: "24" | "12", seconds: boolean});
* ```
* @param options
* @param [options.documentParams] - Whether to document the parameters of the functions
*/
getTypeScriptFunctionSignatures({ documentParams = true } = {}) {
const chatModelFunctions = this.chatModelFunctions;
if (!this.hasAnyFunctions || chatModelFunctions == null)
return "";
const functionNames = Object.keys(chatModelFunctions);
return functionNames
.map((functionName) => {
const functionDefinition = chatModelFunctions[functionName];
let res = "";
if (functionDefinition?.description != null && functionDefinition.description.trim() !== "")
res += "// " + functionDefinition.description.split("\n").join("\n// ") + "\n";
res += "function " + functionName + "(";
if (documentParams && functionDefinition?.params != null)
res += "params: " + getTypeScriptTypeStringForGbnfJsonSchema(functionDefinition.params);
else if (!documentParams && functionDefinition?.params != null)
res += "params";
res += ");";
return res;
})
.join("\n\n");
}
/**
* Example:
* ```ts
* // Retrieve the current date
* type getDate = () => any;
*
* // Retrieve the current time
* type getTime = (_: {hours: "24" | "12", seconds: boolean}) => any;
* ```
* @param options
* @param [options.documentParams] - Whether to document the parameters of the functions
* @param [options.reservedFunctionNames] - Function names that are reserved and cannot be used
*/
getTypeScriptFunctionTypes({ documentParams = true, reservedFunctionNames = [] } = {}) {
const chatModelFunctions = this.chatModelFunctions;
if (!this.hasAnyFunctions || chatModelFunctions == null)
return "";
const functionNames = Object.keys(chatModelFunctions);
const reservedFunctionNamesSet = new Set(reservedFunctionNames);
return functionNames
.map((functionName) => {
if (reservedFunctionNamesSet.has(functionName))
throw new Error(`Function name "${functionName}" is reserved and cannot be used`);
const functionDefinition = chatModelFunctions[functionName];
let res = "";
if (functionDefinition?.description != null && functionDefinition.description.trim() !== "")
res += "// " + functionDefinition.description.split("\n").join("\n// ") + "\n";
res += "type " + functionName + " = (";
if (documentParams && functionDefinition?.params != null)
res += "_: " + getTypeScriptTypeStringForGbnfJsonSchema(functionDefinition.params);
res += ") => any;";
return res;
})
.join("\n\n");
}
/* eslint-disable @stylistic/max-len */
/**
* Example:
* ```
* Use the function 'getDate' to: Retrieve the current date
* {"name": "getDate", "description": "Retrieve the current date"}
*
* Use the function 'getTime' to: Retrieve the current time
* {"name": "getTime", "description": "Retrieve the current time", "parameters": {"type": "object", "properties": {"hours": {"enum": ["24", "12"]}, "seconds": {"type": "boolean"}}}}
* ```
* @param options
* @param [options.documentParams] - Whether to document the parameters of the functions
*/
getLlama3_1FunctionSignatures({ documentParams = true } = {}) {
const chatModelFunctions = this.chatModelFunctions;
if (!this.hasAnyFunctions || chatModelFunctions == null)
return "";
const functionNames = Object.keys(chatModelFunctions);
return functionNames
.map((functionName) => {
const functionDefinition = chatModelFunctions[functionName];
let res = `Use the function '${functionName}'`;
const addDescription = functionDefinition?.description != null && functionDefinition.description.trim() !== "";
if (addDescription)
res += " to: " + functionDefinition.description.split("\n").join("\n// ") + "\n";
else
res += ".\n";
res += jsonDumps({
name: functionName,
...(addDescription ? { description: functionDefinition.description } : {}),
...(documentParams && functionDefinition?.params != null ? { parameters: functionDefinition.params } : {})
});
return res;
})
.join("\n\n");
}
/* eslint-enable @stylistic/max-len */
/* eslint-disable @stylistic/max-len */
/**
* Example:
* ```
* {"name": "getDate", "description": "Retrieve the current date"}
*
* {"name": "getTime", "description": "Retrieve the current time", "parameters": {"type": "object", "properties": {"hours": {"enum": ["24", "12"]}, "seconds": {"type": "boolean"}}}}
* ```
* @param options
* @param [options.documentParams] - Whether to document the parameters of the functions
*/
getLlama3_2LightweightFunctionSignatures({ documentParams = true } = {}) {
const chatModelFunctions = this.chatModelFunctions;
if (!this.hasAnyFunctions || chatModelFunctions == null)
return "";
const functionNames = Object.keys(chatModelFunctions);
const functionsLines = functionNames
.map((functionName) => {
const functionDefinition = chatModelFunctions[functionName];
const addDescription = functionDefinition?.description != null && functionDefinition.description.trim() !== "";
return jsonDumps({
name: functionName,
...(addDescription ? { description: functionDefinition.description } : {}),
...(documentParams && functionDefinition?.params != null ? { parameters: functionDefinition.params } : {})
});
})
.join("\n\n");
return functionsLines;
}
/* eslint-enable @stylistic/max-len */
getQwenFunctionSignatures({ documentParams = true } = {}) {
return this._convertToJinjaTools({ documentParams })
.map((tool) => jsonDumps(tool))
.join("\n");
}
getSeedFunctionSignatures({ documentParams = true } = {}) {
return jsonDumps(this._convertToJinjaTools({ documentParams }));
}
/** @internal */
_convertToJinjaTools({ documentParams = true } = {}) {
const chatModelFunctions = this.chatModelFunctions;
if (!this.hasAnyFunctions || chatModelFunctions == null)
return [];
return [...Object.entries(chatModelFunctions)]
.map(([functionName, functionDefinition]) => {
return {
type: "function",
function: {
name: functionName,
description: functionDefinition.description,
parameters: documentParams
? functionDefinition.params
: undefined
}
};
});
}
}
//# sourceMappingURL=ChatModelFunctionsDocumentationGenerator.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
import { ChatHistoryItem, ChatModelResponse } from "../../types.js";
import { LlamaText } from "../../utils/LlamaText.js";
export declare function chunkChatItems(chatHistory: readonly ChatHistoryItem[], { generateModelResponseText, joinAdjacentMessagesOfTheSameType }: {
generateModelResponseText: (modelResponse: ChatModelResponse["response"]) => LlamaText;
joinAdjacentMessagesOfTheSameType?: boolean;
}): {
system: LlamaText;
user: LlamaText;
model: LlamaText;
}[];

View File

@@ -0,0 +1,44 @@
import { LlamaText } from "../../utils/LlamaText.js";
export function chunkChatItems(chatHistory, { generateModelResponseText, joinAdjacentMessagesOfTheSameType = true }) {
const resultItems = [];
let systemTexts = [];
let userTexts = [];
let modelTexts = [];
let currentAggregateFocus = null;
function flush() {
if (systemTexts.length > 0 || userTexts.length > 0 || modelTexts.length > 0)
resultItems.push({
system: LlamaText.joinValues("\n\n", systemTexts),
user: LlamaText.joinValues("\n\n", userTexts),
model: LlamaText.joinValues("\n\n", modelTexts)
});
systemTexts = [];
userTexts = [];
modelTexts = [];
}
for (const item of chatHistory) {
if (item.type === "system") {
if (!joinAdjacentMessagesOfTheSameType || currentAggregateFocus !== "system")
flush();
currentAggregateFocus = "system";
systemTexts.push(LlamaText.fromJSON(item.text));
}
else if (item.type === "user") {
if (!joinAdjacentMessagesOfTheSameType || (currentAggregateFocus !== "system" && currentAggregateFocus !== "user"))
flush();
currentAggregateFocus = "user";
userTexts.push(LlamaText(item.text));
}
else if (item.type === "model") {
if (!joinAdjacentMessagesOfTheSameType)
flush();
currentAggregateFocus = "model";
modelTexts.push(generateModelResponseText(item.response));
}
else
void item;
}
flush();
return resultItems;
}
//# sourceMappingURL=chunkChatItems.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"chunkChatItems.js","sourceRoot":"","sources":["../../../src/chatWrappers/utils/chunkChatItems.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAEnD,MAAM,UAAU,cAAc,CAAC,WAAuC,EAAE,EACpE,yBAAyB,EACzB,iCAAiC,GAAG,IAAI,EAI3C;IACG,MAAM,WAAW,GAIZ,EAAE,CAAC;IAER,IAAI,WAAW,GAAgB,EAAE,CAAC;IAClC,IAAI,SAAS,GAAgB,EAAE,CAAC;IAChC,IAAI,UAAU,GAAgB,EAAE,CAAC;IACjC,IAAI,qBAAqB,GAAuC,IAAI,CAAC;IAErE,SAAS,KAAK;QACV,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YACvE,WAAW,CAAC,IAAI,CAAC;gBACb,MAAM,EAAE,SAAS,CAAC,UAAU,CAAC,MAAM,EAAE,WAAW,CAAC;gBACjD,IAAI,EAAE,SAAS,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC;gBAC7C,KAAK,EAAE,SAAS,CAAC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC;aAClD,CAAC,CAAC;QAEP,WAAW,GAAG,EAAE,CAAC;QACjB,SAAS,GAAG,EAAE,CAAC;QACf,UAAU,GAAG,EAAE,CAAC;IACpB,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC7B,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACzB,IAAI,CAAC,iCAAiC,IAAI,qBAAqB,KAAK,QAAQ;gBACxE,KAAK,EAAE,CAAC;YAEZ,qBAAqB,GAAG,QAAQ,CAAC;YACjC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACpD,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC9B,IAAI,CAAC,iCAAiC,IAAI,CAAC,qBAAqB,KAAK,QAAQ,IAAI,qBAAqB,KAAK,MAAM,CAAC;gBAC9G,KAAK,EAAE,CAAC;YAEZ,qBAAqB,GAAG,MAAM,CAAC;YAC/B,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACzC,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC/B,IAAI,CAAC,iCAAiC;gBAClC,KAAK,EAAE,CAAC;YAEZ,qBAAqB,GAAG,OAAO,CAAC;YAChC,UAAU,CAAC,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC9D,CAAC;;YACG,KAAM,IAAqB,CAAC;IACpC,CAAC;IAED,KAAK,EAAE,CAAC;IAER,OAAO,WAAW,CAAC;AACvB,CAAC"}

View File

@@ -0,0 +1,2 @@
import { GgufMetadata } from "../../gguf/types/GgufMetadataTypes.js";
export declare function getModelLinageNames(ggufMetadata?: GgufMetadata): string[][];

View File

@@ -0,0 +1,18 @@
export function getModelLinageNames(ggufMetadata) {
const res = [];
if (ggufMetadata == null)
return res;
const currentModelInfo = [ggufMetadata?.general?.name, ggufMetadata?.general?.basename]
.filter((v) => v != null);
if (currentModelInfo.length > 0)
res.push(currentModelInfo);
if (typeof ggufMetadata?.general?.base_model?.count === "number") {
for (let i = 0; i < ggufMetadata.general.base_model.count; i++) {
const baseModel = ggufMetadata.general.base_model[String(i)];
if (baseModel?.name != null)
res.push([baseModel.name]);
}
}
return res;
}
//# sourceMappingURL=getModelLinageNames.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getModelLinageNames.js","sourceRoot":"","sources":["../../../src/chatWrappers/utils/getModelLinageNames.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,mBAAmB,CAAC,YAA2B;IAC3D,MAAM,GAAG,GAAe,EAAE,CAAC;IAE3B,IAAI,YAAY,IAAI,IAAI;QACpB,OAAO,GAAG,CAAC;IAEf,MAAM,gBAAgB,GAAG,CAAC,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,CAAC;SAClF,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;IAC3C,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC;QAC3B,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAE/B,IAAI,OAAO,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7D,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAgB,CAAC,CAAC;YAC5E,IAAI,SAAS,EAAE,IAAI,IAAI,IAAI;gBACvB,GAAG,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QACnC,CAAC;IACL,CAAC;IAED,OAAO,GAAG,CAAC;AACf,CAAC"}

View File

@@ -0,0 +1,4 @@
import { ChatWrapper } from "../../ChatWrapper.js";
import { Tokenizer } from "../../types.js";
import { JinjaTemplateChatWrapperOptions } from "../generic/JinjaTemplateChatWrapper.js";
export declare function isJinjaTemplateEquivalentToSpecializedChatWrapper(jinjaTemplateWrapperOptions: JinjaTemplateChatWrapperOptions, specializedChatWrapper: ChatWrapper, tokenizer?: Tokenizer): boolean;

View File

@@ -0,0 +1,394 @@
import { splitText } from "lifecycle-utils";
import { JinjaTemplateChatWrapper } from "../generic/JinjaTemplateChatWrapper.js";
import { SpecialToken, LlamaText, SpecialTokensText } from "../../utils/LlamaText.js";
import { compareTokens } from "../../utils/compareTokens.js";
import { StopGenerationDetector } from "../../utils/StopGenerationDetector.js";
import { jsonDumps } from "./jsonDumps.js";
export function isJinjaTemplateEquivalentToSpecializedChatWrapper(jinjaTemplateWrapperOptions, specializedChatWrapper, tokenizer) {
const getCheckChatHistories = (jinjaChatWrapper) => [
...testChatHistories,
...((jinjaChatWrapper.usingJinjaFunctionCallTemplate || jinjaTemplateWrapperOptions.functionCallMessageTemplate === "auto")
? testChatHistoriesWithFunctionCalls
: [])
];
const canTestMultipleConvertSystemMessagesToUserMessages = jinjaTemplateWrapperOptions.convertUnsupportedSystemMessagesToUserMessages == null ||
jinjaTemplateWrapperOptions.convertUnsupportedSystemMessagesToUserMessages === "auto";
try {
const jinjaChatWrapper = new JinjaTemplateChatWrapper({
...jinjaTemplateWrapperOptions,
convertUnsupportedSystemMessagesToUserMessages: canTestMultipleConvertSystemMessagesToUserMessages
? false
: jinjaTemplateWrapperOptions.convertUnsupportedSystemMessagesToUserMessages,
trimLeadingWhitespaceInResponses: false
});
const checkChatHistories = getCheckChatHistories(jinjaChatWrapper);
if (checkEquivalence(jinjaChatWrapper, specializedChatWrapper, checkChatHistories, tokenizer))
return true;
}
catch (err) {
// Do nothing
}
try {
const jinjaChatWrapperWithLeadingWhitespaceTrimming = new JinjaTemplateChatWrapper({
...jinjaTemplateWrapperOptions,
convertUnsupportedSystemMessagesToUserMessages: canTestMultipleConvertSystemMessagesToUserMessages
? false
: jinjaTemplateWrapperOptions.convertUnsupportedSystemMessagesToUserMessages,
trimLeadingWhitespaceInResponses: true
});
const checkChatHistories = getCheckChatHistories(jinjaChatWrapperWithLeadingWhitespaceTrimming);
if (checkEquivalence(jinjaChatWrapperWithLeadingWhitespaceTrimming, specializedChatWrapper, checkChatHistories, tokenizer))
return true;
}
catch (err) {
// Do nothing
}
if (!canTestMultipleConvertSystemMessagesToUserMessages)
return false;
const convertSystemMessagesToUserMessagesTemplate = "### System message\n\n{{message}}\n\n----";
try {
const jinjaChatWrapper = new JinjaTemplateChatWrapper({
...jinjaTemplateWrapperOptions,
convertUnsupportedSystemMessagesToUserMessages: {
use: "always",
format: convertSystemMessagesToUserMessagesTemplate
},
trimLeadingWhitespaceInResponses: false
});
const checkChatHistories = getCheckChatHistories(jinjaChatWrapper);
const transformedCheckChatHistories = convertTestChatHistoriesSystemMessagesToUserMessages(checkChatHistories, convertSystemMessagesToUserMessagesTemplate);
if (checkEquivalence(jinjaChatWrapper, specializedChatWrapper, transformedCheckChatHistories, tokenizer))
return true;
}
catch (err) {
// Do nothing
}
try {
const jinjaChatWrapperWithLeadingWhitespaceTrimming = new JinjaTemplateChatWrapper({
...jinjaTemplateWrapperOptions,
convertUnsupportedSystemMessagesToUserMessages: {
use: "always",
format: convertSystemMessagesToUserMessagesTemplate
},
trimLeadingWhitespaceInResponses: true
});
const checkChatHistories = getCheckChatHistories(jinjaChatWrapperWithLeadingWhitespaceTrimming);
const transformedCheckChatHistories = convertTestChatHistoriesSystemMessagesToUserMessages(checkChatHistories, convertSystemMessagesToUserMessagesTemplate);
if (checkEquivalence(jinjaChatWrapperWithLeadingWhitespaceTrimming, specializedChatWrapper, transformedCheckChatHistories, tokenizer))
return true;
}
catch (err) {
// Do nothing
}
return false;
}
function checkEquivalence(jinjaChatWrapper, specializedChatWrapper, testChatHistories, tokenizer) {
for (const testChatHistory of testChatHistories) {
const jinjaRes = jinjaChatWrapper.generateContextState({ chatHistory: testChatHistory });
jinjaRes.contextText = convertFunctionNameAndParamsToRegularText(jinjaRes.contextText, testChatHistory);
const convertedSettings = convertChatWrapperSettingsToUseSpecialTokensText(specializedChatWrapper.settings);
const originalSpecializedSettings = specializedChatWrapper.settings;
if (convertedSettings != null)
specializedChatWrapper.settings = convertedSettings;
let specializedWrapperRes;
try {
specializedWrapperRes = specializedChatWrapper.generateContextState({ chatHistory: testChatHistory });
}
finally {
if (convertedSettings != null)
specializedChatWrapper.settings = originalSpecializedSettings;
}
if (!compareContextTexts(jinjaRes.contextText, specializedWrapperRes.contextText, tokenizer))
return false;
const specializedStopGenerationTriggers = [
...specializedWrapperRes.stopGenerationTriggers,
...(specializedWrapperRes.rerender?.triggers == null
? []
: specializedWrapperRes.rerender.triggers)
];
const jinjaHasAllSpecializedStopGenerationTriggers = jinjaRes.stopGenerationTriggers
.every((trigger) => {
return [trigger, trigger.trimEnd(), trigger.trimStart(), trigger.trimStart().trimEnd()].some((normalizedJinjaTrigger) => {
if (normalizedJinjaTrigger.values.length === 0)
return true;
const foundSimilarTriggers = specializedStopGenerationTriggers.some((specializedTrigger) => (normalizedJinjaTrigger.includes(specializedTrigger)));
if (foundSimilarTriggers)
return true;
if (tokenizer != null) {
const resolvedStopGenerationTrigger = StopGenerationDetector.resolveLlamaTextTrigger(normalizedJinjaTrigger, tokenizer);
const foundSimilarOrShorterTokenizedTriggers = specializedStopGenerationTriggers
.some((specializedTrigger) => {
const resolvedSpecializedTrigger = StopGenerationDetector.resolveLlamaTextTrigger(specializedTrigger, tokenizer);
return resolvedSpecializedTrigger.every((item, index) => {
const resolveTriggerItem = resolvedStopGenerationTrigger[index];
if (typeof item === "string" && typeof resolveTriggerItem === "string")
return item === resolveTriggerItem;
else if (typeof item === "string" || typeof resolveTriggerItem === "string" ||
resolveTriggerItem == null)
return false;
return compareTokens(item, resolveTriggerItem);
});
});
if (foundSimilarOrShorterTokenizedTriggers)
return true;
}
return false;
});
});
if (!jinjaHasAllSpecializedStopGenerationTriggers)
return false;
}
return true;
}
function compareContextTexts(text1, text2, tokenizer) {
function compare(text1, text2) {
if (LlamaText.compare(text1, text2))
return true;
if (tokenizer != null) {
const tokenizedText1 = text1.tokenize(tokenizer);
const tokenizedText2 = text2.tokenize(tokenizer);
if (tokenizedText1.length === tokenizedText2.length)
return tokenizedText1.every((token, index) => compareTokens(token, tokenizedText2[index]));
}
return false;
}
const trimmedText1 = text1.trimEnd();
const trimmedText2 = text2.trimEnd();
const normalizedText1 = removeLeadingBos(trimmedText1);
const normalizedText2 = removeLeadingBos(trimmedText2);
const texts1 = (normalizedText1.values.length !== trimmedText1.values.length && tokenizer != null)
? [trimmedText1, normalizedText1]
: [normalizedText1];
const texts2 = (normalizedText2.values.length !== trimmedText2.values.length && tokenizer != null)
? [trimmedText2, normalizedText2]
: [normalizedText2];
return texts1.some((text1) => (texts2.some((text2) => (compare(text1, text2)))));
}
function convertTestChatHistoriesSystemMessagesToUserMessages(chatHistories, template) {
return chatHistories
.map((history) => (history
.slice()
.map((item, index, array) => {
if (item.type === "system") {
if (index === 0 && array.length > 1 && array[1].type === "user") {
array[1] = {
type: "user",
text: LlamaText([
LlamaText.joinValues(LlamaText.fromJSON(item.text), template.split("{{message}}")),
"\n\n",
array[1].text
]).toString()
};
return null;
}
return {
type: "user",
text: LlamaText.joinValues(LlamaText.fromJSON(item.text), template.split("{{message}}")).toString()
};
}
return item;
})
.filter((item) => item != null)));
}
function convertChatWrapperSettingsToUseSpecialTokensText(settings) {
if (settings?.functions == null)
return null;
function convertToSpecialTokensText(value, keepTexts) {
if (value == null)
return value;
return LlamaText(LlamaText(value).values
.map((item) => {
if (typeof item !== "string")
return item;
if (keepTexts == null || keepTexts.length === 0)
return new SpecialTokensText(item);
return splitText(item, keepTexts).map((textPart) => {
if (typeof textPart === "string")
return new SpecialTokensText(textPart);
return textPart.separator;
});
}));
}
return {
...settings,
functions: {
...settings.functions,
call: {
...settings.functions.call,
prefix: convertToSpecialTokensText(settings.functions.call.prefix),
suffix: convertToSpecialTokensText(settings.functions.call.suffix),
paramsPrefix: convertToSpecialTokensText(settings.functions.call.paramsPrefix)
},
result: {
...settings.functions.result,
prefix: convertToSpecialTokensText(settings.functions.result.prefix, ["{{functionName}}", "{{functionParams}}"]),
suffix: convertToSpecialTokensText(settings.functions.result.suffix, ["{{functionName}}", "{{functionParams}}"])
},
parallelism: settings.functions.parallelism == null
? settings.functions.parallelism
: {
...settings.functions.parallelism,
call: {
...settings.functions.parallelism.call,
sectionPrefix: convertToSpecialTokensText(settings.functions.parallelism.call.sectionPrefix),
betweenCalls: convertToSpecialTokensText(settings.functions.parallelism.call.betweenCalls),
sectionSuffix: convertToSpecialTokensText(settings.functions.parallelism.call.sectionSuffix)
},
result: settings.functions.parallelism.result == null
? settings.functions.parallelism.result
: {
...settings.functions.parallelism.result,
sectionPrefix: convertToSpecialTokensText(settings.functions.parallelism.result.sectionPrefix),
betweenResults: convertToSpecialTokensText(settings.functions.parallelism.result.betweenResults),
sectionSuffix: convertToSpecialTokensText(settings.functions.parallelism.result.sectionSuffix)
}
}
}
};
}
function convertFunctionNameAndParamsToRegularText(contextText, chatHistory) {
const ensureRegularTextItems = new Set();
for (const item of chatHistory) {
if (item.type !== "model")
continue;
for (const response of item.response) {
if (typeof response === "string" || response.type !== "functionCall")
continue;
ensureRegularTextItems.add(response.name);
if (response.params !== undefined && response.params !== "")
ensureRegularTextItems.add(jsonDumps(response.params));
}
}
const ensureRegularTextItemsArray = [...ensureRegularTextItems];
return LlamaText(contextText.values.map((item) => {
if (!(item instanceof SpecialTokensText))
return item;
return splitText(item.value, ensureRegularTextItemsArray)
.map((textPart) => {
if (typeof textPart === "string")
return new SpecialTokensText(textPart);
return textPart.separator;
});
}));
}
const testChatHistories = [
[{
type: "system",
text: "System message ~!@#$%^&*()\n*"
}, {
type: "user",
text: "Message 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: [""]
}],
[{
type: "system",
text: "System message ~!@#$%^&*()\n*"
}, {
type: "user",
text: "Message 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: ["Result 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"]
}],
[{
type: "system",
text: "System message ~!@#$%^&*()\n*"
}, {
type: "user",
text: "Message 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: ["Result 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"]
}, {
type: "user",
text: "Message2 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: [""]
}],
[{
type: "system",
text: "System message ~!@#$%^&*()\n*"
}, {
type: "user",
text: "Message 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: ["Result 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"]
}, {
type: "user",
text: "Message2 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: ["Result2 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"]
}]
];
const testChatHistoriesWithFunctionCalls = [
[{
type: "system",
text: "System message ~!@#$%^&*()\n*"
}, {
type: "user",
text: "Message 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: ["Result 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"]
}, {
type: "user",
text: "Message2 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: [
"Result2 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~",
{
type: "functionCall",
name: "func1name",
params: { param1: "value1" },
result: "func1result"
},
"Result3 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
]
}],
[{
type: "system",
text: "System message ~!@#$%^&*()\n*"
}, {
type: "user",
text: "Message 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: ["Result 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"]
}, {
type: "user",
text: "Message2 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
}, {
type: "model",
response: [
"Result2 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~",
{
type: "functionCall",
name: "func1name",
params: { param1: "value1" },
result: "func1result"
},
{
type: "functionCall",
name: "func2name",
params: { param1: "value2" },
result: "func2result"
},
"Result3 1234567890!@#$%^&*()_+-=[]{}|\\:;\"',./<>?`~"
]
}]
];
function removeLeadingBos(llamaText) {
if (llamaText.values.length === 0)
return llamaText;
const firstValue = llamaText.values[0];
if (firstValue instanceof SpecialToken && firstValue.value === "BOS")
return LlamaText(llamaText.values.slice(1));
return llamaText;
}
//# sourceMappingURL=isJinjaTemplateEquivalentToSpecializedChatWrapper.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
import { ChatWrapperCheckModelCompatibilityParams } from "../../types.js";
export declare function isLlama3_2LightweightModel(options: ChatWrapperCheckModelCompatibilityParams): boolean;

View File

@@ -0,0 +1,9 @@
import { includesText } from "../../utils/includesText.js";
import { getModelLinageNames } from "./getModelLinageNames.js";
export function isLlama3_2LightweightModel(options) {
const isLlama3_2 = getModelLinageNames(options.fileInfo?.metadata)
.some((modelNames) => includesText(modelNames, ["llama 3.2", "llama-3.2", "llama3.2"]));
const isSmallModel = ["1B", "3B"].includes(options.fileInfo?.metadata?.general?.size_label ?? "");
return isLlama3_2 && isSmallModel;
}
//# sourceMappingURL=isLlama3_2LightweightModel.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"isLlama3_2LightweightModel.js","sourceRoot":"","sources":["../../../src/chatWrappers/utils/isLlama3_2LightweightModel.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,YAAY,EAAC,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAC,mBAAmB,EAAC,MAAM,0BAA0B,CAAC;AAE7D,MAAM,UAAU,0BAA0B,CAAC,OAAiD;IACxF,MAAM,UAAU,GAAG,mBAAmB,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC;SAC7D,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAC5F,MAAM,YAAY,GAAI,CAAC,IAAI,EAAE,IAAI,CAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,IAAI,EAAE,CAAC,CAAC;IAEhH,OAAO,UAAU,IAAI,YAAY,CAAC;AACtC,CAAC"}

View File

@@ -0,0 +1,7 @@
/**
* Like `JSON.stringify` but results in a value formatted in the format that Python produces when using `json.dumps(value)`.
*
* We need to format results this way since this is what many models use in their training data,
* so this is what many models expect to have in their context state.
*/
export declare function jsonDumps(value: any): string;

View File

@@ -0,0 +1,18 @@
/**
* Like `JSON.stringify` but results in a value formatted in the format that Python produces when using `json.dumps(value)`.
*
* We need to format results this way since this is what many models use in their training data,
* so this is what many models expect to have in their context state.
*/
export function jsonDumps(value) {
return JSON.stringify(value, null, 1)
.split("\n")
.map((line) => {
line = line.trim();
if (line.endsWith(","))
line += " ";
return line;
})
.join("");
}
//# sourceMappingURL=jsonDumps.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"jsonDumps.js","sourceRoot":"","sources":["../../../src/chatWrappers/utils/jsonDumps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,KAAU;IAChC,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;SAChC,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACV,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAEnB,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;YAClB,IAAI,IAAI,GAAG,CAAC;QAEhB,OAAO,IAAI,CAAC;IAChB,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC"}

View File

@@ -0,0 +1,148 @@
import { Llama3ChatWrapper } from "../Llama3ChatWrapper.js";
import { Llama2ChatWrapper } from "../Llama2ChatWrapper.js";
import { ChatMLChatWrapper } from "../ChatMLChatWrapper.js";
import { GeneralChatWrapper } from "../GeneralChatWrapper.js";
import { FalconChatWrapper } from "../FalconChatWrapper.js";
import { FunctionaryChatWrapper } from "../FunctionaryChatWrapper.js";
import { AlpacaChatWrapper } from "../AlpacaChatWrapper.js";
import { GemmaChatWrapper } from "../GemmaChatWrapper.js";
import { JinjaTemplateChatWrapper } from "../generic/JinjaTemplateChatWrapper.js";
import { TemplateChatWrapper } from "../generic/TemplateChatWrapper.js";
import { Llama3_1ChatWrapper } from "../Llama3_1ChatWrapper.js";
import { Llama3_2LightweightChatWrapper } from "../Llama3_2LightweightChatWrapper.js";
import { DeepSeekChatWrapper } from "../DeepSeekChatWrapper.js";
import { MistralChatWrapper } from "../MistralChatWrapper.js";
import { Tokenizer } from "../../types.js";
import { LlamaModel } from "../../evaluator/LlamaModel/LlamaModel.js";
import { QwenChatWrapper } from "../QwenChatWrapper.js";
import { HarmonyChatWrapper } from "../HarmonyChatWrapper.js";
import { SeedChatWrapper } from "../SeedChatWrapper.js";
import type { GgufFileInfo } from "../../gguf/types/GgufFileInfoTypes.js";
export declare const specializedChatWrapperTypeNames: readonly ["general", "deepSeek", "qwen", "llama3.2-lightweight", "llama3.1", "llama3", "llama2Chat", "mistral", "alpacaChat", "functionary", "chatML", "falconChat", "gemma", "harmony", "seed"];
export type SpecializedChatWrapperTypeName = (typeof specializedChatWrapperTypeNames)[number];
export declare const templateChatWrapperTypeNames: readonly ["template", "jinjaTemplate"];
export type TemplateChatWrapperTypeName = (typeof templateChatWrapperTypeNames)[number];
export declare const resolvableChatWrapperTypeNames: readonly ["auto", "general", "deepSeek", "qwen", "llama3.2-lightweight", "llama3.1", "llama3", "llama2Chat", "mistral", "alpacaChat", "functionary", "chatML", "falconChat", "gemma", "harmony", "seed", "template", "jinjaTemplate"];
export type ResolvableChatWrapperTypeName = (typeof resolvableChatWrapperTypeNames)[number];
export declare const chatWrappers: Readonly<{
readonly general: typeof GeneralChatWrapper;
readonly deepSeek: typeof DeepSeekChatWrapper;
readonly qwen: typeof QwenChatWrapper;
readonly "llama3.1": typeof Llama3_1ChatWrapper;
readonly "llama3.2-lightweight": typeof Llama3_2LightweightChatWrapper;
readonly llama3: typeof Llama3ChatWrapper;
readonly llama2Chat: typeof Llama2ChatWrapper;
readonly mistral: typeof MistralChatWrapper;
readonly alpacaChat: typeof AlpacaChatWrapper;
readonly functionary: typeof FunctionaryChatWrapper;
readonly chatML: typeof ChatMLChatWrapper;
readonly falconChat: typeof FalconChatWrapper;
readonly gemma: typeof GemmaChatWrapper;
readonly harmony: typeof HarmonyChatWrapper;
readonly seed: typeof SeedChatWrapper;
readonly template: typeof TemplateChatWrapper;
readonly jinjaTemplate: typeof JinjaTemplateChatWrapper;
}>;
export type BuiltInChatWrapperType = InstanceType<typeof chatWrappers[keyof typeof chatWrappers]>;
export type ResolveChatWrapperOptions = {
/**
* Resolve to a specific chat wrapper type.
* You better not set this option unless you need to force a specific chat wrapper type.
*
* Defaults to `"auto"`.
*/
type?: "auto" | SpecializedChatWrapperTypeName | TemplateChatWrapperTypeName;
bosString?: string | null;
filename?: string;
fileInfo?: GgufFileInfo;
tokenizer?: Tokenizer;
customWrapperSettings?: {
[wrapper in keyof typeof chatWrappers]?: ConstructorParameters<(typeof chatWrappers)[wrapper]>[0];
};
/**
* Defaults to `true`.
*/
warningLogs?: boolean;
/**
* Defaults to `true`.
*/
fallbackToOtherWrappersOnJinjaError?: boolean;
/**
* Don't resolve to a Jinja chat wrapper unless `type` is set to a Jinja chat wrapper type.
*
* Defaults to `false`.
*/
noJinja?: boolean;
};
export type ResolveChatWrapperWithModelOptions = {
/**
* Resolve to a specific chat wrapper type.
* You better not set this option unless you need to force a specific chat wrapper type.
*
* Defaults to `"auto"`.
*/
type?: "auto" | SpecializedChatWrapperTypeName | TemplateChatWrapperTypeName;
customWrapperSettings?: {
[wrapper in keyof typeof chatWrappers]?: typeof JinjaTemplateChatWrapper extends (typeof chatWrappers)[wrapper] ? Partial<ConstructorParameters<(typeof chatWrappers)[wrapper]>[0]> : ConstructorParameters<(typeof chatWrappers)[wrapper]>[0];
};
/**
* Defaults to `true`.
*/
warningLogs?: boolean;
/**
* Defaults to `true`.
*/
fallbackToOtherWrappersOnJinjaError?: boolean;
/**
* Don't resolve to a Jinja chat wrapper unless `type` is set to a Jinja chat wrapper type.
*
* Defaults to `false`.
*/
noJinja?: boolean;
};
/**
* Resolve to a chat wrapper instance based on the provided information.
* The more information provided, the better the resolution will be (except for `type`).
*
* It's recommended to not set `type` to a specific chat wrapper in order for the resolution to be more flexible, but it is useful for when
* you need to provide the ability to force a specific chat wrapper type.
* Note that when setting `type` to a generic chat wrapper type (such as `"template"` or `"jinjaTemplate"`), the `customWrapperSettings`
* must contain the necessary settings for that chat wrapper to be created.
*
* When loading a Jinja chat template from either `fileInfo` or `customWrapperSettings.jinjaTemplate.template`,
* if the chat template format is invalid, it fallbacks to resolve other chat wrappers,
* unless `fallbackToOtherWrappersOnJinjaError` is set to `false` (in which case, it will throw an error).
* @example
* ```typescript
* import {getLlama, resolveChatWrapper, GeneralChatWrapper} from "node-llama-cpp";
*
* const llama = await getLlama();
* const model = await llama.loadModel({modelPath: "path/to/model.gguf"});
*
* const chatWrapper = resolveChatWrapper(model, {
* customWrapperSettings: {
* "llama3.1": {
* cuttingKnowledgeDate: new Date("2025-01-01T00:00:00Z")
* }
* }
* }) ?? new GeneralChatWrapper()
* ```
* @example
*```typescript
* import {getLlama, resolveChatWrapper, GeneralChatWrapper} from "node-llama-cpp";
*
* const llama = await getLlama();
* const model = await llama.loadModel({modelPath: "path/to/model.gguf"});
*
* const chatWrapper = resolveChatWrapper({
* bosString: model.tokens.bosString,
* filename: model.filename,
* fileInfo: model.fileInfo,
* tokenizer: model.tokenizer
* }) ?? new GeneralChatWrapper()
* ```
*/
export declare function resolveChatWrapper(model: LlamaModel, options?: ResolveChatWrapperWithModelOptions): BuiltInChatWrapperType;
export declare function resolveChatWrapper(options: ResolveChatWrapperOptions): BuiltInChatWrapperType | null;
export declare function isSpecializedChatWrapperType(type: string): type is SpecializedChatWrapperTypeName;
export declare function isTemplateChatWrapperType(type: string): type is TemplateChatWrapperTypeName;

View File

@@ -0,0 +1,325 @@
import { parseModelFileName } from "../../utils/parseModelFileName.js";
import { Llama3ChatWrapper } from "../Llama3ChatWrapper.js";
import { Llama2ChatWrapper } from "../Llama2ChatWrapper.js";
import { ChatMLChatWrapper } from "../ChatMLChatWrapper.js";
import { GeneralChatWrapper } from "../GeneralChatWrapper.js";
import { FalconChatWrapper } from "../FalconChatWrapper.js";
import { FunctionaryChatWrapper } from "../FunctionaryChatWrapper.js";
import { AlpacaChatWrapper } from "../AlpacaChatWrapper.js";
import { GemmaChatWrapper } from "../GemmaChatWrapper.js";
import { JinjaTemplateChatWrapper } from "../generic/JinjaTemplateChatWrapper.js";
import { TemplateChatWrapper } from "../generic/TemplateChatWrapper.js";
import { getConsoleLogPrefix } from "../../utils/getConsoleLogPrefix.js";
import { Llama3_1ChatWrapper } from "../Llama3_1ChatWrapper.js";
import { Llama3_2LightweightChatWrapper } from "../Llama3_2LightweightChatWrapper.js";
import { DeepSeekChatWrapper } from "../DeepSeekChatWrapper.js";
import { MistralChatWrapper } from "../MistralChatWrapper.js";
import { includesText } from "../../utils/includesText.js";
import { LlamaModel } from "../../evaluator/LlamaModel/LlamaModel.js";
import { QwenChatWrapper } from "../QwenChatWrapper.js";
import { HarmonyChatWrapper } from "../HarmonyChatWrapper.js";
import { SeedChatWrapper } from "../SeedChatWrapper.js";
import { isJinjaTemplateEquivalentToSpecializedChatWrapper } from "./isJinjaTemplateEquivalentToSpecializedChatWrapper.js";
import { getModelLinageNames } from "./getModelLinageNames.js";
export const specializedChatWrapperTypeNames = Object.freeze([
"general", "deepSeek", "qwen", "llama3.2-lightweight", "llama3.1", "llama3", "llama2Chat", "mistral", "alpacaChat", "functionary",
"chatML", "falconChat", "gemma", "harmony", "seed"
]);
export const templateChatWrapperTypeNames = Object.freeze([
"template", "jinjaTemplate"
]);
export const resolvableChatWrapperTypeNames = Object.freeze([
"auto",
...specializedChatWrapperTypeNames,
...templateChatWrapperTypeNames
]);
export const chatWrappers = Object.freeze({
"general": GeneralChatWrapper,
"deepSeek": DeepSeekChatWrapper,
"qwen": QwenChatWrapper,
"llama3.1": Llama3_1ChatWrapper,
"llama3.2-lightweight": Llama3_2LightweightChatWrapper,
"llama3": Llama3ChatWrapper,
"llama2Chat": Llama2ChatWrapper,
"mistral": MistralChatWrapper,
"alpacaChat": AlpacaChatWrapper,
"functionary": FunctionaryChatWrapper,
"chatML": ChatMLChatWrapper,
"falconChat": FalconChatWrapper,
"gemma": GemmaChatWrapper,
"harmony": HarmonyChatWrapper,
"seed": SeedChatWrapper,
"template": TemplateChatWrapper,
"jinjaTemplate": JinjaTemplateChatWrapper
});
const chatWrapperToConfigType = new Map(Object.entries(chatWrappers)
.map(([configType, Wrapper]) => ([Wrapper, configType])));
const specializedChatWrapperRelatedTexts = {
"harmony": ["gpt", "gpt-oss"]
};
export function resolveChatWrapper(options, modelOptions) {
if (options instanceof LlamaModel)
return resolveChatWrapper({
...(modelOptions ?? {}),
customWrapperSettings: modelOptions?.customWrapperSettings,
bosString: options.tokens.bosString,
filename: options.filename,
fileInfo: options.fileInfo,
tokenizer: options.tokenizer
}) ?? new GeneralChatWrapper();
const { type = "auto", bosString, filename, fileInfo, tokenizer, customWrapperSettings, warningLogs = true, fallbackToOtherWrappersOnJinjaError = true, noJinja = false } = options;
function createSpecializedChatWrapper(specializedChatWrapper, defaultSettings = {}) {
const chatWrapperConfigType = chatWrapperToConfigType.get(specializedChatWrapper);
const chatWrapperSettings = customWrapperSettings?.[chatWrapperConfigType];
return new specializedChatWrapper({
...(defaultSettings ?? {}),
...(chatWrapperSettings ?? {})
});
}
if (type !== "auto" && type != null) {
if (isTemplateChatWrapperType(type)) {
const Wrapper = chatWrappers[type];
if (isClassReference(Wrapper, TemplateChatWrapper)) {
const wrapperSettings = customWrapperSettings?.template;
if (wrapperSettings == null || wrapperSettings?.template == null || wrapperSettings?.historyTemplate == null ||
wrapperSettings.historyTemplate.system == null || wrapperSettings.historyTemplate.user == null ||
wrapperSettings.historyTemplate.model == null) {
if (warningLogs)
console.warn(getConsoleLogPrefix() + "Template chat wrapper settings must have a template, historyTemplate, historyTemplate.system, historyTemplate.user, and historyTemplate.model. Falling back to resolve other chat wrapper types.");
}
else
return new TemplateChatWrapper(wrapperSettings);
}
else if (isClassReference(Wrapper, JinjaTemplateChatWrapper)) {
const jinjaTemplate = customWrapperSettings?.jinjaTemplate?.template ?? fileInfo?.metadata?.tokenizer?.chat_template;
if (jinjaTemplate == null) {
if (warningLogs)
console.warn(getConsoleLogPrefix() + "Jinja template chat wrapper received no template. Falling back to resolve other chat wrapper types.");
}
else {
try {
return new JinjaTemplateChatWrapper({
tokenizer,
...(customWrapperSettings?.jinjaTemplate ?? {}),
template: jinjaTemplate
});
}
catch (err) {
if (!fallbackToOtherWrappersOnJinjaError)
throw err;
else if (warningLogs)
console.error(getConsoleLogPrefix() + "Error creating Jinja template chat wrapper. Falling back to resolve other chat wrappers. Error:", err);
}
}
}
else
void Wrapper;
}
else if (Object.hasOwn(chatWrappers, type)) {
const Wrapper = chatWrappers[type];
const wrapperSettings = customWrapperSettings?.[type];
return new Wrapper(wrapperSettings);
}
}
const modelJinjaTemplate = customWrapperSettings?.jinjaTemplate?.template ?? fileInfo?.metadata?.tokenizer?.chat_template;
if (modelJinjaTemplate != null && modelJinjaTemplate.trim() !== "") {
const jinjaTemplateChatWrapperOptions = {
tokenizer,
...(customWrapperSettings?.jinjaTemplate ?? {}),
template: modelJinjaTemplate
};
const chatWrapperNamesToCheck = orderChatWrapperNamesByAssumedCompatibilityWithModel(specializedChatWrapperTypeNames, { filename, fileInfo });
for (const specializedChatWrapperTypeName of chatWrapperNamesToCheck) {
const Wrapper = chatWrappers[specializedChatWrapperTypeName];
const wrapperSettings = customWrapperSettings?.[specializedChatWrapperTypeName];
const isCompatible = Wrapper._checkModelCompatibility({
tokenizer,
fileInfo
});
if (!isCompatible)
continue;
const testOptionConfigurations = Wrapper._getOptionConfigurationsToTestIfCanSupersedeJinjaTemplate?.() ?? [];
if (testOptionConfigurations.length === 0)
testOptionConfigurations.push({});
for (const testConfigurationOrPair of testOptionConfigurations) {
const testConfig = testConfigurationOrPair instanceof Array
? (testConfigurationOrPair[0] ?? {})
: testConfigurationOrPair;
const applyConfig = testConfigurationOrPair instanceof Array
? (testConfigurationOrPair[1] ?? {})
: testConfigurationOrPair;
const additionalJinjaOptions = testConfigurationOrPair instanceof Array
? testConfigurationOrPair[2]
: undefined;
const testChatWrapperSettings = {
...(wrapperSettings ?? {}),
...(testConfig ?? {})
};
const applyChatWrapperSettings = {
...(wrapperSettings ?? {}),
...(applyConfig ?? {})
};
const chatWrapper = new Wrapper(testChatWrapperSettings);
const jinjaTemplateChatWrapperOptionsWithAdditionalParameters = {
...(additionalJinjaOptions ?? {}),
...jinjaTemplateChatWrapperOptions,
additionalRenderParameters: additionalJinjaOptions?.additionalRenderParameters == null
? jinjaTemplateChatWrapperOptions.additionalRenderParameters
: {
...(jinjaTemplateChatWrapperOptions.additionalRenderParameters ?? {}),
...additionalJinjaOptions.additionalRenderParameters
}
};
if (isJinjaTemplateEquivalentToSpecializedChatWrapper(jinjaTemplateChatWrapperOptionsWithAdditionalParameters, chatWrapper, tokenizer))
return new Wrapper(applyChatWrapperSettings);
}
}
if (!noJinja) {
if (!fallbackToOtherWrappersOnJinjaError)
return new JinjaTemplateChatWrapper(jinjaTemplateChatWrapperOptions);
try {
return new JinjaTemplateChatWrapper(jinjaTemplateChatWrapperOptions);
}
catch (err) {
console.error(getConsoleLogPrefix() + "Error creating Jinja template chat wrapper. Falling back to resolve other chat wrappers. Error:", err);
}
}
}
for (const modelNames of getModelLinageNames(fileInfo?.metadata)) {
if (includesText(modelNames, ["llama 3.2", "llama-3.2", "llama3.2"]) && Llama3_2LightweightChatWrapper._checkModelCompatibility({ tokenizer, fileInfo }))
return createSpecializedChatWrapper(Llama3_2LightweightChatWrapper);
else if (includesText(modelNames, ["llama 3.1", "llama-3.1", "llama3.1"]) && Llama3_1ChatWrapper._checkModelCompatibility({ tokenizer, fileInfo }))
return createSpecializedChatWrapper(Llama3_1ChatWrapper);
else if (includesText(modelNames, ["llama 3", "llama-3", "llama3"]))
return createSpecializedChatWrapper(Llama3ChatWrapper);
else if (includesText(modelNames, ["Mistral", "Mistral Large", "Mistral Large Instruct", "Mistral-Large", "Codestral"]))
return createSpecializedChatWrapper(MistralChatWrapper);
else if (includesText(modelNames, ["Gemma", "Gemma 2"]))
return createSpecializedChatWrapper(GemmaChatWrapper);
else if (includesText(modelNames, ["gpt-oss", "Gpt Oss", "Gpt-Oss", "openai_gpt-oss", "Openai_Gpt Oss", "openai.gpt-oss", "Openai.Gpt Oss"]))
return createSpecializedChatWrapper(HarmonyChatWrapper);
else if (includesText(modelNames, ["seed-oss", "Seed Oss", "Seed OSS", "Seed-Oss", "Seed-OSS", "ByteDance-Seed_Seed-OSS", "ByteDance-Seed.Seed-OSS"]))
return createSpecializedChatWrapper(SeedChatWrapper);
}
// try to find a pattern in the Jinja template to resolve to a specialized chat wrapper,
// with a logic similar to `llama.cpp`'s `llama_chat_apply_template_internal` function
if (modelJinjaTemplate != null && modelJinjaTemplate.trim() !== "") {
if (modelJinjaTemplate.includes("<seed:think>") || (modelJinjaTemplate.includes("<seed:bos>") && modelJinjaTemplate.includes("<seed:eos>")))
return createSpecializedChatWrapper(SeedChatWrapper);
else if (modelJinjaTemplate.includes("<|start|>") && modelJinjaTemplate.includes("<|channel|>"))
return createSpecializedChatWrapper(HarmonyChatWrapper);
else if (modelJinjaTemplate.includes("<|im_start|>"))
return createSpecializedChatWrapper(ChatMLChatWrapper);
else if (modelJinjaTemplate.includes("[INST]"))
return createSpecializedChatWrapper(Llama2ChatWrapper, {
addSpaceBeforeEos: modelJinjaTemplate.includes("' ' + eos_token")
});
else if (modelJinjaTemplate.includes("<|start_header_id|>") && modelJinjaTemplate.includes("<|end_header_id|>")) {
if (Llama3_1ChatWrapper._checkModelCompatibility({ tokenizer, fileInfo }))
return createSpecializedChatWrapper(Llama3_1ChatWrapper);
else
return createSpecializedChatWrapper(Llama3ChatWrapper);
}
else if (modelJinjaTemplate.includes("<start_of_turn>"))
return createSpecializedChatWrapper(GemmaChatWrapper);
}
if (filename != null) {
const { name, subType, fileType, otherInfo } = parseModelFileName(filename);
if (fileType?.toLowerCase() === "gguf") {
const lowercaseName = name?.toLowerCase();
const lowercaseSubType = subType?.toLowerCase();
const splitLowercaseSubType = (lowercaseSubType?.split("-") ?? []).concat(otherInfo.map((info) => info.toLowerCase()));
const firstSplitLowercaseSubType = splitLowercaseSubType[0];
if (lowercaseName === "llama") {
if (splitLowercaseSubType.includes("chat"))
return createSpecializedChatWrapper(Llama2ChatWrapper);
return createSpecializedChatWrapper(GeneralChatWrapper);
}
else if (lowercaseName === "codellama")
return createSpecializedChatWrapper(GeneralChatWrapper);
else if (lowercaseName === "yarn" && firstSplitLowercaseSubType === "llama")
return createSpecializedChatWrapper(Llama2ChatWrapper);
else if (lowercaseName === "orca")
return createSpecializedChatWrapper(ChatMLChatWrapper);
else if (lowercaseName === "phind" && lowercaseSubType === "codellama")
return createSpecializedChatWrapper(Llama2ChatWrapper);
else if (lowercaseName === "mistral")
return createSpecializedChatWrapper(GeneralChatWrapper);
else if (firstSplitLowercaseSubType === "llama")
return createSpecializedChatWrapper(Llama2ChatWrapper);
else if (lowercaseSubType === "alpaca")
return createSpecializedChatWrapper(AlpacaChatWrapper);
else if (lowercaseName === "functionary")
return createSpecializedChatWrapper(FunctionaryChatWrapper);
else if (lowercaseName === "dolphin" && splitLowercaseSubType.includes("mistral"))
return createSpecializedChatWrapper(ChatMLChatWrapper);
else if (lowercaseName === "gemma")
return createSpecializedChatWrapper(GemmaChatWrapper);
else if (splitLowercaseSubType.includes("chatml"))
return createSpecializedChatWrapper(ChatMLChatWrapper);
}
}
if (bosString !== "" && bosString != null) {
if ("<s>[INST] <<SYS>>\n".startsWith(bosString)) {
return createSpecializedChatWrapper(Llama2ChatWrapper);
}
else if ("<|im_start|>system\n".startsWith(bosString)) {
return createSpecializedChatWrapper(ChatMLChatWrapper);
}
}
if (fileInfo != null) {
const arch = fileInfo.metadata.general?.architecture;
if (arch === "llama")
return createSpecializedChatWrapper(GeneralChatWrapper);
else if (arch === "falcon")
return createSpecializedChatWrapper(FalconChatWrapper);
else if (arch === "gemma" || arch === "gemma2")
return createSpecializedChatWrapper(GemmaChatWrapper);
}
return null;
}
export function isSpecializedChatWrapperType(type) {
return specializedChatWrapperTypeNames.includes(type);
}
export function isTemplateChatWrapperType(type) {
return templateChatWrapperTypeNames.includes(type);
}
// this is needed because TypeScript guards don't work automatically with class references
function isClassReference(value, classReference) {
return value === classReference;
}
function orderChatWrapperNamesByAssumedCompatibilityWithModel(chatWrapperNames, { filename, fileInfo }) {
const rankPoints = {
modelName: 3,
modelNamePosition: 4,
fileName: 2,
fileNamePosition: 3
};
function getPointsForTextMatch(pattern, fullText, existsPoints, positionPoints) {
if (fullText == null)
return 0;
const index = fullText.toLowerCase().indexOf(pattern.toLowerCase());
if (index >= 0)
return existsPoints + (((index + 1) / fullText.length) * positionPoints);
return 0;
}
function getPointsForWrapperName(wrapperName, fullText, existsPoints, positionPoints) {
const additionalNames = specializedChatWrapperRelatedTexts[wrapperName] ?? [];
return [wrapperName, ...additionalNames]
.map((pattern) => getPointsForTextMatch(pattern, fullText, existsPoints, positionPoints))
.reduce((res, item) => Math.max(res, item), 0);
}
const modelName = fileInfo?.metadata?.general?.name;
return chatWrapperNames
.slice()
.sort((a, b) => {
let aPoints = 0;
let bPoints = 0;
aPoints += getPointsForWrapperName(a, modelName, rankPoints.modelName, rankPoints.modelNamePosition);
bPoints += getPointsForWrapperName(b, modelName, rankPoints.modelName, rankPoints.modelNamePosition);
aPoints += getPointsForWrapperName(a, filename, rankPoints.fileName, rankPoints.fileNamePosition);
bPoints += getPointsForWrapperName(b, filename, rankPoints.fileName, rankPoints.fileNamePosition);
return bPoints - aPoints;
});
}
//# sourceMappingURL=resolveChatWrapper.js.map

File diff suppressed because one or more lines are too long