mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-28 05:11:24 +08:00
103 lines
3.8 KiB
TypeScript
103 lines
3.8 KiB
TypeScript
import { transliterate } from 'transliteration'
|
|
|
|
/**
|
|
* Transliterate non-ASCII characters to ASCII equivalents
|
|
* - Chinese → Pinyin (e.g., 行驶证 → xingshizheng)
|
|
* - Japanese → Romaji (e.g., ユーザー → yūzā)
|
|
* - Korean → Romanization (e.g., 사용자 → sayongja)
|
|
* - Other special characters → underscores
|
|
*/
|
|
/**
|
|
* Transliterates non-ASCII text (including CJK characters) to ASCII-compatible format.
|
|
*
|
|
* Converts input text to lowercase ASCII representation, replacing spaces with underscores
|
|
* and removing special characters. Unknown or special characters are replaced with underscores.
|
|
*
|
|
* @param text - The input string to transliterate, may contain Unicode characters including CJK
|
|
* @returns A lowercase ASCII string with spaces converted to underscores and special characters removed,
|
|
* preserving only alphanumeric characters, underscores, dots, hyphens, and colons
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* transliterateToAscii("Hello World") // returns "hello_world"
|
|
* transliterateToAscii("你好世界") // returns transliterated version with underscores
|
|
* transliterateToAscii("Café-123") // returns "cafe_123"
|
|
* ```
|
|
*/
|
|
function transliterateToAscii(text: string): string {
|
|
// Use transliteration library which supports CJK (Chinese, Japanese, Korean)
|
|
const result = transliterate(text, {
|
|
// Unknown/special characters become underscores
|
|
unknown: '_',
|
|
ignore: []
|
|
})
|
|
|
|
// Convert to lowercase, remove spaces, and clean up special chars
|
|
return result
|
|
.toLowerCase()
|
|
.replace(/\s+/g, '_')
|
|
.replace(/[^a-z0-9_.\-:]/g, '_')
|
|
}
|
|
|
|
export function buildFunctionCallToolName(serverName: string, toolName: string, serverId?: string) {
|
|
// First, transliterate non-ASCII characters to ASCII
|
|
const transliteratedServer = transliterateToAscii(serverName.trim())
|
|
const transliteratedTool = transliterateToAscii(toolName.trim())
|
|
|
|
const sanitizedServer = transliteratedServer.replace(/-/g, '_')
|
|
const sanitizedTool = transliteratedTool.replace(/-/g, '_')
|
|
|
|
// Calculate suffix first to reserve space for it
|
|
// Suffix format: "_" + 6 alphanumeric chars = 7 chars total
|
|
let serverIdSuffix = ''
|
|
if (serverId) {
|
|
// Take the last 6 characters of the serverId for brevity
|
|
serverIdSuffix = serverId.slice(-6).replace(/[^a-zA-Z0-9]/g, '')
|
|
|
|
// Fallback: if suffix becomes empty (all non-alphanumeric chars), use a simple hash
|
|
if (!serverIdSuffix) {
|
|
const hash = serverId.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0)
|
|
serverIdSuffix = hash.toString(36).slice(-6) || 'x'
|
|
}
|
|
}
|
|
|
|
// Reserve space for suffix when calculating max base name length
|
|
const SUFFIX_LENGTH = serverIdSuffix ? serverIdSuffix.length + 1 : 0 // +1 for underscore
|
|
const MAX_BASE_LENGTH = 63 - SUFFIX_LENGTH
|
|
|
|
// Combine server name and tool name
|
|
let name = sanitizedTool
|
|
if (!sanitizedTool.includes(sanitizedServer.slice(0, 7))) {
|
|
name = `${sanitizedServer.slice(0, 7) || ''}-${sanitizedTool || ''}`
|
|
}
|
|
|
|
// Replace invalid characters with underscores
|
|
// Keep only a-z, A-Z, 0-9, underscores, dashes, dots, colons (AI model compatible)
|
|
name = name.replace(/[^a-zA-Z0-9_.\-:]/g, '_')
|
|
|
|
// Ensure name starts with a letter or underscore (AI model requirement)
|
|
if (!/^[a-zA-Z_]/.test(name)) {
|
|
name = `tool_${name}`
|
|
}
|
|
|
|
// Remove consecutive underscores/dashes (optional improvement)
|
|
name = name.replace(/[_-]{2,}/g, '_')
|
|
|
|
// Truncate base name BEFORE adding suffix to ensure suffix is never cut off
|
|
if (name.length > MAX_BASE_LENGTH) {
|
|
name = name.slice(0, MAX_BASE_LENGTH)
|
|
}
|
|
|
|
// Handle edge case: ensure we still have a valid name if truncation left invalid chars at edges
|
|
if (name.endsWith('_') || name.endsWith('-')) {
|
|
name = name.slice(0, -1)
|
|
}
|
|
|
|
// Now append the suffix - it will always fit within 63 chars
|
|
if (serverIdSuffix) {
|
|
name = `${name}_${serverIdSuffix}`
|
|
}
|
|
|
|
return name
|
|
}
|