Files
wehub-resource-sync 3cd11ababe
Check Markdown links / linkChecker (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:38:56 +08:00

248 lines
6.7 KiB
TypeScript

import {
HookEventType,
PluginContext,
PluginHandler,
PluginParameters,
} from '../types';
import { getCurrentContentPart, setCurrentContentPart } from '../utils';
const addPrefixToCompletion = (
context: PluginContext,
prefix: string,
eventType: HookEventType
): Record<string, any> => {
const transformedData: Record<string, any> = {
request: { json: null },
response: { json: null },
};
const { content, textArray } = getCurrentContentPart(context, eventType);
if (!content) {
return transformedData;
}
const updatedTexts = (
Array.isArray(textArray) ? textArray : [String(textArray)]
).map((text, index) => (index === 0 ? `${prefix}${text ?? ''}` : text));
setCurrentContentPart(context, eventType, transformedData, updatedTexts);
return transformedData;
};
const addPrefixToChatCompletion = (
context: PluginContext,
prefix: string,
applyToRole: string = 'user',
addToExisting: boolean = true,
onlyIfEmpty: boolean = false,
eventType: HookEventType
): Record<string, any> => {
const json = context.request.json;
const updatedJson = { ...json };
const messages = Array.isArray(json.messages) ? [...json.messages] : [];
// Find the target role message
const targetIndex = messages.findIndex((msg) => msg.role === applyToRole);
// Helper to build a message content with the prefix in both chatComplete and messages formats
const buildPrefixedContent = (existing: any): any => {
if (existing == null || typeof existing === 'string') {
return `${prefix}${existing ?? ''}`;
}
if (Array.isArray(existing)) {
if (existing.length > 0 && existing[0]?.type === 'text') {
const cloned = existing.map((item) => ({ ...item }));
cloned[0].text = `${prefix}${cloned[0]?.text ?? ''}`;
return cloned;
}
return [{ type: 'text', text: prefix }, ...existing];
}
return `${prefix}${String(existing)}`;
};
// If the target role exists
if (targetIndex !== -1) {
const targetMsg = messages[targetIndex];
const content = targetMsg?.content;
const isEmptyContent =
(typeof content === 'string' && content.trim().length === 0) ||
(Array.isArray(content) && content.length === 0);
if (onlyIfEmpty && !isEmptyContent) {
// Respect onlyIfEmpty by skipping modification when non-empty
return {
request: { json: updatedJson },
response: { json: null },
};
}
if (addToExisting) {
// If this is the last message, leverage utils to ensure messages route compatibility
if (targetIndex === messages.length - 1) {
const transformedData: Record<string, any> = {
request: { json: null },
response: { json: null },
};
const { content: currentContent, textArray } = getCurrentContentPart(
context,
eventType
);
if (currentContent !== null) {
const updatedTexts = (
Array.isArray(textArray) ? textArray : [String(textArray)]
).map((text, idx) => (idx === 0 ? `${prefix}${text ?? ''}` : text));
setCurrentContentPart(
context,
eventType,
transformedData,
updatedTexts
);
}
return transformedData;
}
// Otherwise, modify the specific message inline
messages[targetIndex] = {
...targetMsg,
content: buildPrefixedContent(targetMsg.content),
};
} else {
// Create new message with prefix before the existing one
const newMessage = {
role: applyToRole,
content:
context.requestType === 'messages'
? [{ type: 'text', text: prefix }]
: prefix,
};
messages.splice(targetIndex, 0, newMessage);
}
} else {
// No message with target role exists, create one
const newMessage = {
role: applyToRole,
content:
context.requestType === 'messages'
? [{ type: 'text', text: prefix }]
: prefix,
};
if (applyToRole === 'system') {
messages.unshift(newMessage);
} else {
messages.push(newMessage);
}
}
updatedJson.messages = messages;
return {
request: {
json: updatedJson,
},
response: {
json: null,
},
};
};
export const handler: PluginHandler = async (
context: PluginContext,
parameters: PluginParameters,
eventType: HookEventType
) => {
let error = null;
let verdict = true; // Always allow the request to continue
let data = null;
const transformedData: Record<string, any> = {
request: {
json: null,
},
response: {
json: null,
},
};
let transformed = false;
try {
// Only process before request and only for completion/chat completion/messages
if (
eventType !== 'beforeRequestHook' ||
!['complete', 'chatComplete', 'messages'].includes(
context.requestType || ''
)
) {
return {
error: null,
verdict: true,
data: null,
transformedData,
transformed,
};
}
// Get prefix from parameters
const prefix = parameters.prefix;
if (!prefix || typeof prefix !== 'string') {
return {
error: { message: 'Prefix parameter is required and must be a string' },
verdict: true,
data: null,
transformedData,
transformed,
};
}
// Check if request JSON exists
if (!context.request?.json) {
return {
error: { message: 'Request JSON is empty or missing' },
verdict: true,
data: null,
transformedData,
transformed,
};
}
let newTransformedData;
if (
context.requestType &&
['chatComplete', 'messages'].includes(context.requestType)
) {
// Handle chat completion
newTransformedData = addPrefixToChatCompletion(
context,
prefix,
parameters.applyToRole || 'user',
parameters.addToExisting !== false, // default to true
parameters.onlyIfEmpty === true, // default to false
eventType
);
} else {
// Handle regular completion
newTransformedData = addPrefixToCompletion(context, prefix, eventType);
}
Object.assign(transformedData, newTransformedData);
transformed = true;
data = {
prefix: prefix,
requestType: context.requestType,
applyToRole: parameters.applyToRole || 'user',
addToExisting: parameters.addToExisting !== false,
onlyIfEmpty: parameters.onlyIfEmpty === true,
};
} catch (e: any) {
delete e.stack;
error = {
message: `Error in addPrefix plugin: ${e.message || 'Unknown error'}`,
originalError: e,
};
}
return { error, verdict, data, transformedData, transformed };
};