|
import crypto from 'node:crypto'; |
|
import { getConfigValue, tryParse } from './util.js'; |
|
|
|
const PROMPT_PLACEHOLDER = getConfigValue('promptPlaceholder', 'Let\'s get started.'); |
|
|
|
const REASONING_EFFORT = { |
|
auto: 'auto', |
|
low: 'low', |
|
medium: 'medium', |
|
high: 'high', |
|
min: 'min', |
|
max: 'max', |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function getPromptNames(request) { |
|
return { |
|
charName: String(request.body.char_name || ''), |
|
userName: String(request.body.user_name || ''), |
|
groupNames: Array.isArray(request.body.group_names) ? request.body.group_names.map(String) : [], |
|
startsWithGroupName: function (message) { |
|
return this.groupNames.some(name => message.startsWith(`${name}: `)); |
|
}, |
|
}; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill, withSysPromptSupport, useSystemPrompt, addSysHumanMsg, excludePrefixes) { |
|
|
|
|
|
|
|
if (messages.length > 0) { |
|
messages.forEach((m) => { |
|
if (!m.content) { |
|
m.content = ''; |
|
} |
|
if (m.tool_calls) { |
|
m.content += JSON.stringify(m.tool_calls); |
|
} |
|
}); |
|
if (excludePrefixes) { |
|
messages.slice(0, -1).forEach(message => message.role = 'system'); |
|
} else { |
|
messages[0].role = 'system'; |
|
} |
|
|
|
if (addAssistantPostfix) { |
|
messages.push({ |
|
role: 'assistant', |
|
content: addAssistantPrefill || '', |
|
}); |
|
} |
|
|
|
let hasUser = false; |
|
const firstAssistantIndex = messages.findIndex((message, i) => { |
|
if (i >= 0 && (message.role === 'user' || message.content.includes('\n\nHuman: '))) { |
|
hasUser = true; |
|
} |
|
return message.role === 'assistant' && i > 0; |
|
}); |
|
|
|
|
|
if (withSysPromptSupport && useSystemPrompt) { |
|
messages[0].role = 'system'; |
|
if (firstAssistantIndex > 0 && addSysHumanMsg && !hasUser) { |
|
messages.splice(firstAssistantIndex, 0, { |
|
role: 'user', |
|
content: addSysHumanMsg, |
|
}); |
|
} |
|
} else { |
|
|
|
messages[0].role = 'user'; |
|
|
|
if (firstAssistantIndex > 0 && !excludePrefixes) { |
|
messages[firstAssistantIndex - 1].role = firstAssistantIndex - 1 !== 0 && messages[firstAssistantIndex - 1].role === 'user' ? 'FixHumMsg' : messages[firstAssistantIndex - 1].role; |
|
} |
|
} |
|
} |
|
|
|
|
|
let requestPrompt = messages.map((v, i) => { |
|
|
|
let prefix = { |
|
'assistant': '\n\nAssistant: ', |
|
'user': '\n\nHuman: ', |
|
'system': i === 0 ? '' : v.name === 'example_assistant' ? '\n\nA: ' : v.name === 'example_user' ? '\n\nH: ' : excludePrefixes && v.name ? `\n\n${v.name}: ` : '\n\n', |
|
'FixHumMsg': '\n\nFirst message: ', |
|
}[v.role] ?? ''; |
|
|
|
return `${prefix}${v.name && v.role !== 'system' ? `${v.name}: ` : ''}${v.content}`; |
|
}).join(''); |
|
|
|
return requestPrompt; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function convertClaudeMessages(messages, prefillString, useSysPrompt, useTools, names) { |
|
let systemPrompt = []; |
|
if (useSysPrompt) { |
|
|
|
let i; |
|
for (i = 0; i < messages.length; i++) { |
|
if (messages[i].role !== 'system') { |
|
break; |
|
} |
|
|
|
if (names.userName && messages[i].name === 'example_user') { |
|
if (!messages[i].content.startsWith(`${names.userName}: `)) { |
|
messages[i].content = `${names.userName}: ${messages[i].content}`; |
|
} |
|
} |
|
if (names.charName && messages[i].name === 'example_assistant') { |
|
if (!messages[i].content.startsWith(`${names.charName}: `) && !names.startsWithGroupName(messages[i].content)) { |
|
messages[i].content = `${names.charName}: ${messages[i].content}`; |
|
} |
|
} |
|
systemPrompt.push({ type: 'text', text: messages[i].content }); |
|
} |
|
|
|
messages.splice(0, i); |
|
|
|
|
|
|
|
if (messages.length === 0) { |
|
messages.unshift({ |
|
role: 'user', |
|
content: PROMPT_PLACEHOLDER, |
|
}); |
|
} |
|
} |
|
|
|
|
|
const parse = (str) => typeof str === 'string' ? JSON.parse(str) : str; |
|
messages.forEach((message) => { |
|
if (message.role === 'assistant' && message.tool_calls) { |
|
message.content = message.tool_calls.map((tc) => ({ |
|
type: 'tool_use', |
|
id: tc.id, |
|
name: tc.function.name, |
|
input: parse(tc.function.arguments), |
|
})); |
|
} |
|
|
|
if (message.role === 'tool') { |
|
message.role = 'user'; |
|
message.content = [{ |
|
type: 'tool_result', |
|
tool_use_id: message.tool_call_id, |
|
content: message.content, |
|
}]; |
|
} |
|
|
|
if (message.role === 'system') { |
|
if (names.userName && message.name === 'example_user') { |
|
if (!message.content.startsWith(`${names.userName}: `)) { |
|
message.content = `${names.userName}: ${message.content}`; |
|
} |
|
} |
|
if (names.charName && message.name === 'example_assistant') { |
|
if (!message.content.startsWith(`${names.charName}: `) && !names.startsWithGroupName(message.content)) { |
|
message.content = `${names.charName}: ${message.content}`; |
|
} |
|
} |
|
message.role = 'user'; |
|
|
|
|
|
delete message.name; |
|
} |
|
|
|
|
|
if (typeof message.content === 'string') { |
|
|
|
if (message.name) { |
|
message.content = `${message.name}: ${message.content}`; |
|
} |
|
|
|
message.content = [{ type: 'text', text: message.content }]; |
|
} else if (Array.isArray(message.content)) { |
|
message.content = message.content.map((content) => { |
|
if (content.type === 'image_url') { |
|
const imageEntry = content?.image_url; |
|
const imageData = imageEntry?.url; |
|
const mimeType = imageData?.split(';')?.[0].split(':')?.[1]; |
|
const base64Data = imageData?.split(',')?.[1]; |
|
|
|
return { |
|
type: 'image', |
|
source: { |
|
type: 'base64', |
|
media_type: mimeType, |
|
data: base64Data, |
|
}, |
|
}; |
|
} |
|
|
|
if (content.type === 'text') { |
|
if (message.name) { |
|
content.text = `${message.name}: ${content.text}`; |
|
} |
|
|
|
|
|
return { type: 'text', text: content.text || '\u200b' }; |
|
} |
|
|
|
return content; |
|
}); |
|
} |
|
|
|
|
|
delete message.name; |
|
delete message.tool_calls; |
|
delete message.tool_call_id; |
|
}); |
|
|
|
|
|
for (let i = 0; i < messages.length; i++) { |
|
if (messages[i].role === 'assistant' && messages[i].content.some(c => c.type === 'image')) { |
|
|
|
let j = i + 1; |
|
while (j < messages.length && messages[j].role !== 'user') { |
|
j++; |
|
} |
|
|
|
|
|
if (j >= messages.length) { |
|
|
|
messages.splice(i + 1, 0, { role: 'user', content: [] }); |
|
} |
|
|
|
messages[j].content.push(...messages[i].content.filter(c => c.type === 'image')); |
|
messages[i].content = messages[i].content.filter(c => c.type !== 'image'); |
|
} |
|
} |
|
|
|
|
|
if (prefillString) { |
|
messages.push({ |
|
role: 'assistant', |
|
|
|
content: [{ type: 'text', text: prefillString.trimEnd() }], |
|
}); |
|
} |
|
|
|
|
|
|
|
let mergedMessages = []; |
|
messages.forEach((message) => { |
|
if (mergedMessages.length > 0 && mergedMessages[mergedMessages.length - 1].role === message.role) { |
|
mergedMessages[mergedMessages.length - 1].content.push(...message.content); |
|
} else { |
|
mergedMessages.push(message); |
|
} |
|
}); |
|
|
|
if (!useTools) { |
|
mergedMessages.forEach((message) => { |
|
message.content.forEach((content) => { |
|
if (content.type === 'tool_use') { |
|
content.type = 'text'; |
|
content.text = JSON.stringify(content.input); |
|
delete content.id; |
|
delete content.name; |
|
delete content.input; |
|
} |
|
if (content.type === 'tool_result') { |
|
content.type = 'text'; |
|
content.text = content.content; |
|
delete content.tool_use_id; |
|
delete content.content; |
|
} |
|
}); |
|
}); |
|
} |
|
|
|
return { messages: mergedMessages, systemPrompt: systemPrompt }; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function convertCohereMessages(messages, names) { |
|
if (messages.length === 0) { |
|
messages.unshift({ |
|
role: 'user', |
|
content: PROMPT_PLACEHOLDER, |
|
}); |
|
} |
|
|
|
messages.forEach((msg, index) => { |
|
|
|
if (Array.isArray(msg.tool_calls)) { |
|
if (index > 0 && messages[index - 1].role === 'assistant') { |
|
msg.content = messages[index - 1].content; |
|
messages.splice(index - 1, 1); |
|
} else { |
|
msg.content = `I'm going to call a tool for that: ${msg.tool_calls.map(tc => tc?.function?.name).join(', ')}`; |
|
} |
|
} |
|
|
|
if (msg.name) { |
|
if (msg.role == 'system' && msg.name == 'example_assistant') { |
|
if (names.charName && !msg.content.startsWith(`${names.charName}: `) && !names.startsWithGroupName(msg.content)) { |
|
msg.content = `${names.charName}: ${msg.content}`; |
|
} |
|
} |
|
if (msg.role == 'system' && msg.name == 'example_user') { |
|
if (names.userName && !msg.content.startsWith(`${names.userName}: `)) { |
|
msg.content = `${names.userName}: ${msg.content}`; |
|
} |
|
} |
|
if (msg.role !== 'system' && !msg.content.startsWith(`${msg.name}: `)) { |
|
msg.content = `${msg.name}: ${msg.content}`; |
|
} |
|
delete msg.name; |
|
} |
|
}); |
|
|
|
return { chatHistory: messages }; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function convertGooglePrompt(messages, _model, useSysPrompt, names) { |
|
const sysPrompt = []; |
|
|
|
if (useSysPrompt) { |
|
while (messages.length > 1 && messages[0].role === 'system') { |
|
|
|
if (names.userName && messages[0].name === 'example_user') { |
|
if (!messages[0].content.startsWith(`${names.userName}: `)) { |
|
messages[0].content = `${names.userName}: ${messages[0].content}`; |
|
} |
|
} |
|
if (names.charName && messages[0].name === 'example_assistant') { |
|
if (!messages[0].content.startsWith(`${names.charName}: `) && !names.startsWithGroupName(messages[0].content)) { |
|
messages[0].content = `${names.charName}: ${messages[0].content}`; |
|
} |
|
} |
|
sysPrompt.push(messages[0].content); |
|
messages.shift(); |
|
} |
|
} |
|
|
|
const system_instruction = { parts: sysPrompt.map(text => ({ text })) }; |
|
const toolNameMap = {}; |
|
|
|
const contents = []; |
|
messages.forEach((message, index) => { |
|
|
|
if (message.role === 'system' || message.role === 'tool') { |
|
message.role = 'user'; |
|
} else if (message.role === 'assistant') { |
|
message.role = 'model'; |
|
} |
|
|
|
|
|
if (!Array.isArray(message.content)) { |
|
const content = (() => { |
|
const hasToolCalls = Array.isArray(message.tool_calls) && message.tool_calls.length > 0; |
|
const hasToolCallId = typeof message.tool_call_id === 'string' && message.tool_call_id.length > 0; |
|
|
|
if (hasToolCalls) { |
|
return { type: 'tool_calls', tool_calls: message.tool_calls }; |
|
} |
|
|
|
if (hasToolCallId) { |
|
return { type: 'tool_call_id', tool_call_id: message.tool_call_id, content: String(message.content ?? '') }; |
|
} |
|
|
|
return { type: 'text', text: String(message.content ?? '') }; |
|
})(); |
|
message.content = [content]; |
|
} |
|
|
|
|
|
if (message.name) { |
|
message.content.forEach((part) => { |
|
if (part.type !== 'text') { |
|
return; |
|
} |
|
if (message.name === 'example_user') { |
|
if (names.userName && !part.text.startsWith(`${names.userName}: `)) { |
|
part.text = `${names.userName}: ${part.text}`; |
|
} |
|
} else if (message.name === 'example_assistant') { |
|
if (names.charName && !part.text.startsWith(`${names.charName}: `) && !names.startsWithGroupName(part.text)) { |
|
part.text = `${names.charName}: ${part.text}`; |
|
} |
|
} else { |
|
if (!part.text.startsWith(`${message.name}: `)) { |
|
part.text = `${message.name}: ${part.text}`; |
|
} |
|
} |
|
}); |
|
|
|
delete message.name; |
|
} |
|
|
|
|
|
const parts = []; |
|
message.content.forEach((part) => { |
|
if (part.type === 'text') { |
|
parts.push({ text: part.text }); |
|
} else if (part.type === 'tool_call_id') { |
|
const name = toolNameMap[part.tool_call_id] ?? 'unknown'; |
|
parts.push({ |
|
functionResponse: { |
|
name: name, |
|
response: { name: name, content: part.content }, |
|
}, |
|
}); |
|
} else if (part.type === 'tool_calls') { |
|
part.tool_calls.forEach((toolCall) => { |
|
parts.push({ |
|
functionCall: { |
|
name: toolCall.function.name, |
|
args: tryParse(toolCall.function.arguments) ?? toolCall.function.arguments, |
|
}, |
|
}); |
|
|
|
toolNameMap[toolCall.id] = toolCall.function.name; |
|
}); |
|
} else if (part.type === 'image_url') { |
|
const mimeType = part.image_url.url.split(';')[0].split(':')[1]; |
|
const base64Data = part.image_url.url.split(',')[1]; |
|
parts.push({ |
|
inlineData: { |
|
mimeType: mimeType, |
|
data: base64Data, |
|
}, |
|
}); |
|
} |
|
}); |
|
|
|
|
|
if (index > 0 && message.role === contents[contents.length - 1].role) { |
|
parts.forEach((part) => { |
|
if (part.text) { |
|
const textPart = contents[contents.length - 1].parts.find(p => typeof p.text === 'string'); |
|
if (textPart) { |
|
textPart.text += '\n\n' + part.text; |
|
} else { |
|
contents[contents.length - 1].parts.push(part); |
|
} |
|
} |
|
if (part.inlineData || part.functionCall || part.functionResponse) { |
|
contents[contents.length - 1].parts.push(part); |
|
} |
|
}); |
|
} else { |
|
contents.push({ |
|
role: message.role, |
|
parts: parts, |
|
}); |
|
} |
|
}); |
|
|
|
return { contents: contents, system_instruction: system_instruction }; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function convertAI21Messages(messages, names) { |
|
if (!Array.isArray(messages)) { |
|
return []; |
|
} |
|
|
|
|
|
let i = 0, systemPrompt = ''; |
|
|
|
for (i = 0; i < messages.length; i++) { |
|
if (messages[i].role !== 'system') { |
|
break; |
|
} |
|
|
|
if (names.userName && messages[i].name === 'example_user') { |
|
if (!messages[i].content.startsWith(`${names.userName}: `)) { |
|
messages[i].content = `${names.userName}: ${messages[i].content}`; |
|
} |
|
} |
|
if (names.charName && messages[i].name === 'example_assistant') { |
|
if (!messages[i].content.startsWith(`${names.charName}: `) && !names.startsWithGroupName(messages[i].content)) { |
|
messages[i].content = `${names.charName}: ${messages[i].content}`; |
|
} |
|
} |
|
systemPrompt += `${messages[i].content}\n\n`; |
|
} |
|
|
|
messages.splice(0, i); |
|
|
|
|
|
if (messages.length === 0) { |
|
messages.unshift({ |
|
role: 'user', |
|
content: PROMPT_PLACEHOLDER, |
|
}); |
|
} |
|
|
|
if (systemPrompt) { |
|
messages.unshift({ |
|
role: 'system', |
|
content: systemPrompt.trim(), |
|
}); |
|
} |
|
|
|
|
|
messages.forEach(msg => { |
|
if ('name' in msg) { |
|
if (msg.role !== 'system' && !msg.content.startsWith(`${msg.name}: `)) { |
|
msg.content = `${msg.name}: ${msg.content}`; |
|
} |
|
delete msg.name; |
|
} |
|
}); |
|
|
|
|
|
let mergedMessages = []; |
|
messages.forEach((message) => { |
|
if (mergedMessages.length > 0 && mergedMessages[mergedMessages.length - 1].role === message.role) { |
|
mergedMessages[mergedMessages.length - 1].content += '\n\n' + message.content; |
|
} else { |
|
mergedMessages.push(message); |
|
} |
|
}); |
|
|
|
return mergedMessages; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function convertMistralMessages(messages, names) { |
|
if (!Array.isArray(messages)) { |
|
return []; |
|
} |
|
|
|
|
|
const prefixEnabled = getConfigValue('mistral.enablePrefix', false, 'boolean'); |
|
const lastMsg = messages[messages.length - 1]; |
|
if (prefixEnabled && messages.length > 0 && lastMsg?.role === 'assistant') { |
|
lastMsg.prefix = true; |
|
} |
|
|
|
const sanitizeToolId = (id) => crypto.createHash('sha512').update(id).digest('hex').slice(0, 9); |
|
|
|
|
|
messages.forEach(msg => { |
|
if ('tool_calls' in msg && Array.isArray(msg.tool_calls)) { |
|
msg.tool_calls.forEach(tool => { |
|
tool.id = sanitizeToolId(tool.id); |
|
}); |
|
} |
|
if ('tool_call_id' in msg && msg.role === 'tool') { |
|
msg.tool_call_id = sanitizeToolId(msg.tool_call_id); |
|
} |
|
if (msg.role === 'system' && msg.name === 'example_assistant') { |
|
if (names.charName && !msg.content.startsWith(`${names.charName}: `) && !names.startsWithGroupName(msg.content)) { |
|
msg.content = `${names.charName}: ${msg.content}`; |
|
} |
|
delete msg.name; |
|
} |
|
|
|
if (msg.role === 'system' && msg.name === 'example_user') { |
|
if (names.userName && !msg.content.startsWith(`${names.userName}: `)) { |
|
msg.content = `${names.userName}: ${msg.content}`; |
|
} |
|
delete msg.name; |
|
} |
|
|
|
if (msg.name && msg.role !== 'system' && !msg.content.startsWith(`${msg.name}: `)) { |
|
msg.content = `${msg.name}: ${msg.content}`; |
|
delete msg.name; |
|
} |
|
}); |
|
|
|
|
|
const fixToolMessages = () => { |
|
let rerun = true; |
|
while (rerun) { |
|
rerun = false; |
|
messages.forEach((message, i) => { |
|
if (i === messages.length - 1) { |
|
return; |
|
} |
|
if (message.role === 'tool' && messages[i + 1].role === 'user') { |
|
const lastUserMessage = messages.slice(0, i).findLastIndex(m => m.role === 'user' && m.content); |
|
if (lastUserMessage !== -1) { |
|
messages[lastUserMessage].content += '\n\n' + messages[i + 1].content; |
|
messages.splice(i + 1, 1); |
|
rerun = true; |
|
} |
|
} |
|
}); |
|
} |
|
}; |
|
fixToolMessages(); |
|
|
|
|
|
for (let i = 0; i < messages.length - 1; i++) { |
|
if (messages[i].role === 'assistant' && messages[i + 1].role === 'system') { |
|
messages[i + 1].role = 'user'; |
|
} |
|
} |
|
|
|
return messages; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function convertXAIMessages(messages, names) { |
|
if (!Array.isArray(messages)) { |
|
return []; |
|
} |
|
|
|
messages.forEach(msg => { |
|
if (!msg.name || msg.role === 'user') { |
|
return; |
|
} |
|
|
|
const needsCharNamePrefix = [ |
|
{ role: 'assistant', condition: names.charName && !msg.content.startsWith(`${names.charName}: `) && !names.startsWithGroupName(msg.content) }, |
|
{ role: 'system', name: 'example_assistant', condition: names.charName && !msg.content.startsWith(`${names.charName}: `) && !names.startsWithGroupName(msg.content) }, |
|
{ role: 'system', name: 'example_user', condition: names.userName && !msg.content.startsWith(`${names.userName}: `) }, |
|
]; |
|
|
|
const matchingRule = needsCharNamePrefix.find(rule => |
|
msg.role === rule.role && (!rule.name || msg.name === rule.name) && rule.condition, |
|
); |
|
|
|
if (matchingRule) { |
|
const prefix = msg.role === 'system' && msg.name === 'example_user' ? names.userName : names.charName; |
|
msg.content = `${prefix}: ${msg.content}`; |
|
} |
|
|
|
delete msg.name; |
|
}); |
|
|
|
return messages; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function mergeMessages(messages, names, { strict = false, placeholders = false, single = false } = {}) { |
|
let mergedMessages = []; |
|
|
|
|
|
const contentTokens = new Map(); |
|
|
|
|
|
messages.forEach((message) => { |
|
if (!message.content) { |
|
message.content = ''; |
|
} |
|
|
|
if (Array.isArray(message.content)) { |
|
const text = message.content.map((content) => { |
|
if (content.type === 'text') { |
|
return content.text; |
|
} |
|
|
|
if (content.type === 'image_url') { |
|
const token = crypto.randomBytes(32).toString('base64'); |
|
contentTokens.set(token, content); |
|
return token; |
|
} |
|
return ''; |
|
}).join('\n\n'); |
|
message.content = text; |
|
} |
|
if (message.role === 'system' && message.name === 'example_assistant') { |
|
if (names.charName && !message.content.startsWith(`${names.charName}: `) && !names.startsWithGroupName(message.content)) { |
|
message.content = `${names.charName}: ${message.content}`; |
|
} |
|
} |
|
if (message.role === 'system' && message.name === 'example_user') { |
|
if (names.userName && !message.content.startsWith(`${names.userName}: `)) { |
|
message.content = `${names.userName}: ${message.content}`; |
|
} |
|
} |
|
if (message.name && message.role !== 'system') { |
|
if (!message.content.startsWith(`${message.name}: `)) { |
|
message.content = `${message.name}: ${message.content}`; |
|
} |
|
} |
|
if (message.role === 'tool') { |
|
message.role = 'user'; |
|
} |
|
if (single) { |
|
if (message.role === 'assistant') { |
|
if (names.charName && !message.content.startsWith(`${names.charName}: `) && !names.startsWithGroupName(message.content)) { |
|
message.content = `${names.charName}: ${message.content}`; |
|
} |
|
} |
|
if (message.role === 'user') { |
|
if (names.userName && !message.content.startsWith(`${names.userName}: `)) { |
|
message.content = `${names.userName}: ${message.content}`; |
|
} |
|
} |
|
|
|
message.role = 'user'; |
|
} |
|
delete message.name; |
|
delete message.tool_calls; |
|
delete message.tool_call_id; |
|
}); |
|
|
|
|
|
messages.forEach((message) => { |
|
if (mergedMessages.length > 0 && mergedMessages[mergedMessages.length - 1].role === message.role && message.content) { |
|
mergedMessages[mergedMessages.length - 1].content += '\n\n' + message.content; |
|
} else { |
|
mergedMessages.push(message); |
|
} |
|
}); |
|
|
|
|
|
if (mergedMessages.length === 0) { |
|
mergedMessages.unshift({ |
|
role: 'user', |
|
content: PROMPT_PLACEHOLDER, |
|
}); |
|
} |
|
|
|
|
|
if (contentTokens.size > 0) { |
|
mergedMessages.forEach((message) => { |
|
const hasValidToken = Array.from(contentTokens.keys()).some(token => message.content.includes(token)); |
|
|
|
if (hasValidToken) { |
|
const splitContent = message.content.split('\n\n'); |
|
const mergedContent = []; |
|
|
|
splitContent.forEach((content) => { |
|
if (contentTokens.has(content)) { |
|
mergedContent.push(contentTokens.get(content)); |
|
} else { |
|
if (mergedContent.length > 0 && mergedContent[mergedContent.length - 1].type === 'text') { |
|
mergedContent[mergedContent.length - 1].text += `\n\n${content}`; |
|
} else { |
|
mergedContent.push({ type: 'text', text: content }); |
|
} |
|
} |
|
}); |
|
|
|
message.content = mergedContent; |
|
} |
|
}); |
|
} |
|
|
|
if (strict) { |
|
for (let i = 0; i < mergedMessages.length; i++) { |
|
|
|
if (i > 0 && mergedMessages[i].role === 'system') { |
|
mergedMessages[i].role = 'user'; |
|
} |
|
} |
|
if (mergedMessages.length && placeholders) { |
|
if (mergedMessages[0].role === 'system' && (mergedMessages.length === 1 || mergedMessages[1].role !== 'user')) { |
|
mergedMessages.splice(1, 0, { role: 'user', content: PROMPT_PLACEHOLDER }); |
|
} |
|
else if (mergedMessages[0].role !== 'system' && mergedMessages[0].role !== 'user') { |
|
mergedMessages.unshift({ role: 'user', content: PROMPT_PLACEHOLDER }); |
|
} |
|
} |
|
return mergeMessages(mergedMessages, names, { strict: false, placeholders, single: false }); |
|
} |
|
|
|
return mergedMessages; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
export function convertTextCompletionPrompt(messages) { |
|
if (typeof messages === 'string') { |
|
return messages; |
|
} |
|
|
|
const messageStrings = []; |
|
messages.forEach(m => { |
|
if (m.role === 'system' && m.name === undefined) { |
|
messageStrings.push('System: ' + m.content); |
|
} |
|
else if (m.role === 'system' && m.name !== undefined) { |
|
messageStrings.push(m.name + ': ' + m.content); |
|
} |
|
else { |
|
messageStrings.push(m.role + ': ' + m.content); |
|
} |
|
}); |
|
return messageStrings.join('\n') + '\nassistant:'; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function cachingAtDepthForClaude(messages, cachingAtDepth, ttl) { |
|
let passedThePrefill = false; |
|
let depth = 0; |
|
let previousRoleName = ''; |
|
|
|
for (let i = messages.length - 1; i >= 0; i--) { |
|
if (!passedThePrefill && messages[i].role === 'assistant') { |
|
continue; |
|
} |
|
|
|
passedThePrefill = true; |
|
|
|
if (messages[i].role !== previousRoleName) { |
|
if (depth === cachingAtDepth || depth === cachingAtDepth + 2) { |
|
const content = messages[i].content; |
|
content[content.length - 1].cache_control = { type: 'ephemeral', ttl: ttl }; |
|
} |
|
|
|
if (depth === cachingAtDepth + 2) { |
|
break; |
|
} |
|
|
|
depth += 1; |
|
previousRoleName = messages[i].role; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function cachingAtDepthForOpenRouterClaude(messages, cachingAtDepth) { |
|
|
|
let passedThePrefill = false; |
|
|
|
let depth = 0; |
|
let previousRoleName = ''; |
|
for (let i = messages.length - 1; i >= 0; i--) { |
|
if (!passedThePrefill && messages[i].role === 'assistant') { |
|
continue; |
|
} |
|
|
|
passedThePrefill = true; |
|
|
|
if (messages[i].role !== previousRoleName) { |
|
if (depth === cachingAtDepth || depth === cachingAtDepth + 2) { |
|
const content = messages[i].content; |
|
if (typeof content === 'string') { |
|
messages[i].content = [{ |
|
type: 'text', |
|
text: content, |
|
cache_control: { type: 'ephemeral' }, |
|
}]; |
|
} else { |
|
const contentPartCount = content.length; |
|
content[contentPartCount - 1].cache_control = { |
|
type: 'ephemeral', |
|
}; |
|
} |
|
} |
|
|
|
if (depth === cachingAtDepth + 2) { |
|
break; |
|
} |
|
|
|
depth += 1; |
|
previousRoleName = messages[i].role; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function calculateClaudeBudgetTokens(maxTokens, reasoningEffort, stream) { |
|
let budgetTokens = 0; |
|
|
|
switch (reasoningEffort) { |
|
case REASONING_EFFORT.auto: |
|
return null; |
|
case REASONING_EFFORT.min: |
|
budgetTokens = 1024; |
|
break; |
|
case REASONING_EFFORT.low: |
|
budgetTokens = Math.floor(maxTokens * 0.1); |
|
break; |
|
case REASONING_EFFORT.medium: |
|
budgetTokens = Math.floor(maxTokens * 0.25); |
|
break; |
|
case REASONING_EFFORT.high: |
|
budgetTokens = Math.floor(maxTokens * 0.5); |
|
break; |
|
case REASONING_EFFORT.max: |
|
budgetTokens = Math.floor(maxTokens * 0.95); |
|
break; |
|
} |
|
|
|
budgetTokens = Math.max(budgetTokens, 1024); |
|
|
|
if (!stream) { |
|
budgetTokens = Math.min(budgetTokens, 21333); |
|
} |
|
|
|
return budgetTokens; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function calculateGoogleBudgetTokens(maxTokens, reasoningEffort) { |
|
let budgetTokens = 0; |
|
|
|
switch (reasoningEffort) { |
|
case REASONING_EFFORT.auto: |
|
return null; |
|
case REASONING_EFFORT.min: |
|
budgetTokens = 0; |
|
break; |
|
case REASONING_EFFORT.low: |
|
budgetTokens = Math.floor(maxTokens * 0.1); |
|
break; |
|
case REASONING_EFFORT.medium: |
|
budgetTokens = Math.floor(maxTokens * 0.25); |
|
break; |
|
case REASONING_EFFORT.high: |
|
budgetTokens = Math.floor(maxTokens * 0.5); |
|
break; |
|
case REASONING_EFFORT.max: |
|
budgetTokens = maxTokens; |
|
break; |
|
} |
|
|
|
budgetTokens = Math.min(budgetTokens, 24576); |
|
|
|
return budgetTokens; |
|
} |
|
|