@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
106 lines (90 loc) • 3.54 kB
text/typescript
import { ChatToolPayload, MessageToolCall } from '@lobechat/types';
import { Md5 } from 'ts-md5';
import { LobeChatPluginApi, LobeToolManifest } from './types';
// Tool naming constants
const PLUGIN_SCHEMA_SEPARATOR = '____';
const PLUGIN_SCHEMA_API_MD5_PREFIX = 'MD5HASH_';
/**
* Tool Name Resolver
* Handles tool name generation and resolution for function calling
*/
export class ToolNameResolver {
/**
* Generate MD5 hash for tool name shortening
* @private
*/
private genHash(name: string): string {
return Md5.hashStr(name).toString().slice(0, 12);
}
/**
* Generate tool calling name
* @param identifier - Plugin identifier
* @param name - API name
* @param type - Plugin type (default: 'default')
* @returns Generated tool name (max 64 characters)
*/
generate(identifier: string, name: string, type: string = 'default'): string {
const pluginType = type && type !== 'default' ? `${PLUGIN_SCHEMA_SEPARATOR}${type}` : '';
// Step 1: Try normal format
let apiName = identifier + PLUGIN_SCHEMA_SEPARATOR + name + pluginType;
// OpenAI GPT function_call name can't be longer than 64 characters
// Step 2: If >= 64, hash the name part
if (apiName.length >= 64) {
const nameHash = PLUGIN_SCHEMA_API_MD5_PREFIX + this.genHash(name);
apiName = identifier + PLUGIN_SCHEMA_SEPARATOR + nameHash + pluginType;
// Step 3: If still >= 64, also hash the identifier
if (apiName.length >= 64) {
const identifierHash = PLUGIN_SCHEMA_API_MD5_PREFIX + this.genHash(identifier);
apiName = identifierHash + PLUGIN_SCHEMA_SEPARATOR + nameHash + pluginType;
}
}
return apiName;
}
/**
* Resolve tool calls from AI response back to original tool information
* @param toolCalls - Tool calls from AI model response
* @param manifests - Available tool manifests mapped by identifier
* @returns Resolved tool payloads
*/
resolve(
toolCalls: MessageToolCall[],
manifests: Record<string, LobeToolManifest>,
): ChatToolPayload[] {
return toolCalls
.map((toolCall): ChatToolPayload | null => {
let [identifier, apiName, type] = toolCall.function.name.split(PLUGIN_SCHEMA_SEPARATOR);
if (!apiName) return null;
// Step 1: Resolve hashed identifier if needed
if (identifier.startsWith(PLUGIN_SCHEMA_API_MD5_PREFIX)) {
const identifierMd5 = identifier.replace(PLUGIN_SCHEMA_API_MD5_PREFIX, '');
// Find the manifest by hashed identifier
const foundIdentifier = Object.keys(manifests).find(
(id) => this.genHash(id) === identifierMd5,
);
if (foundIdentifier) {
identifier = foundIdentifier;
}
}
let payload: ChatToolPayload = {
apiName,
arguments: toolCall.function.arguments,
id: toolCall.id,
identifier,
type: (type ?? 'default') as any,
};
// Step 2: Resolve hashed apiName if needed
if (apiName.startsWith(PLUGIN_SCHEMA_API_MD5_PREFIX) && manifests[identifier]) {
const md5 = apiName.replace(PLUGIN_SCHEMA_API_MD5_PREFIX, '');
const manifest = manifests[identifier];
const api = manifest?.api.find(
(api: LobeChatPluginApi) => this.genHash(api.name) === md5,
);
if (api) {
payload.apiName = api.name;
}
}
return payload;
})
.filter(Boolean) as ChatToolPayload[];
}
}