Skip to content

Instantly share code, notes, and snippets.

@BLamy
Last active May 7, 2023 06:50
Show Gist options
  • Save BLamy/4b4c369ab4a0915bf61f99e46516fc86 to your computer and use it in GitHub Desktop.
Save BLamy/4b4c369ab4a0915bf61f99e46516fc86 to your computer and use it in GitHub Desktop.
const messages = [
system(`this is a system message
It will end up typed as a literal because It is called as a function
`),
user`typed as
a string This`,
assistant(`
will also
be typed
as a literal`)
]
console.log(messages)
// ^?
//----------------------------------
// implementation
//----------------------------------
export function system<T extends string>(
literals: TemplateStringsArray | T,
...placeholders: unknown[]
) {
return {
role: "system" as const,
content: dedent(literals, ...placeholders),
};
}
export function user<T extends string>(
literals: TemplateStringsArray | T,
...placeholders: unknown[]
) {
return {
role: "user" as const,
content: dedent(literals, ...placeholders),
};
}
export function assistant<T extends string>(
literals: TemplateStringsArray | T,
...placeholders: unknown[]
) {
return {
role: "assistant" as const,
content: dedent(literals, ...placeholders),
};
}
export function dedent<T extends string>(
templ: TemplateStringsArray | T,
...values: unknown[]
): typeof templ extends TemplateStringsArray ? string : T {
let strings = Array.from(typeof templ === "string" ? [templ] : templ);
// 1. Remove trailing whitespace.
strings[strings.length - 1] = strings[strings.length - 1].replace(
/\r?\n([\t ]*)$/,
""
);
// 2. Find all line breaks to determine the highest common indentation level.
const indentLengths = strings.reduce<number[]>((arr, str) => {
const matches = str.match(/\n([\t ]+|(?!\s).)/g);
if (matches) {
return arr.concat(
matches.map((match) => match.match(/[\t ]/g)?.length ?? 0)
);
}
return arr;
}, []);
// 3. Remove the common indentation from all strings.
if (indentLengths.length) {
const pattern = new RegExp(`\n[\t ]{${Math.min(...indentLengths)}}`, "g");
strings = strings.map((str) => str.replace(pattern, "\n"));
}
// 4. Remove leading whitespace.
strings[0] = strings[0].replace(/^\r?\n/, "");
// 5. Perform interpolation.
let string = strings[0];
values.forEach((value, i) => {
// 5.1 Read current indentation level
const endentations = string.match(/(?:^|\n)( *)$/);
const endentation = endentations ? endentations[1] : "";
let indentedValue = value;
// 5.2 Add indentation to values with multiline strings
if (typeof value === "string" && value.includes("\n")) {
indentedValue = String(value)
.split("\n")
.map((str, i) => {
return i === 0 ? str : `${endentation}${str}`;
})
.join("\n");
}
string += indentedValue + strings[i + 1];
});
return string as any;
}
@BLamy
Copy link
Author

BLamy commented May 5, 2023

TS Playground Demo

dedent-message-creation-helpers

const messages = [
     system`this is my system prompt
            it can span many lines
            and the extra padding will be stripped`,
     user`this is a user message
          it can do the same`,
    assistant(`All template strings can also be called as functions`)
];

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment