2023-12-19 15:38:11 +01:00
import { characters , main _api , api _server , nai _settings , online _status , this _chid } from '../script.js' ;
2023-12-02 19:04:51 +01:00
import { power _user , registerDebugFunction } from './power-user.js' ;
import { chat _completion _sources , model _list , oai _settings } from './openai.js' ;
import { groups , selected _group } from './group-chats.js' ;
import { getStringHash } from './utils.js' ;
import { kai _flags } from './kai-settings.js' ;
2024-05-03 22:59:39 +02:00
import { textgen _types , textgenerationwebui _settings as textgen _settings , getTextGenServer , getTextGenModel } from './textgen-settings.js' ;
2024-03-08 07:40:03 +01:00
import { getCurrentDreamGenModelTokenizer , getCurrentOpenRouterModelTokenizer , openRouterModels } from './textgen-models.js' ;
2023-12-03 16:03:19 +01:00
2024-06-20 03:13:32 +02:00
const { OOBA , TABBY , KOBOLDCPP , VLLM , APHRODITE , LLAMACPP , OPENROUTER , DREAMGEN } = textgen _types ;
2023-08-23 01:38:43 +02:00
export const CHARACTERS _PER _TOKEN _RATIO = 3.35 ;
2024-09-22 16:44:19 +02:00
export const TOKENIZER _WARNING _KEY = 'tokenizationWarningShown' ;
export const TOKENIZER _SUPPORTED _KEY = 'tokenizationSupported' ;
2023-08-23 01:38:43 +02:00
export const tokenizers = {
NONE : 0 ,
2023-08-27 17:27:34 +02:00
GPT2 : 1 ,
2023-11-05 21:45:37 +01:00
OPENAI : 2 ,
2023-08-23 01:38:43 +02:00
LLAMA : 3 ,
NERD : 4 ,
NERD2 : 5 ,
2023-12-10 14:56:38 +01:00
API _CURRENT : 6 ,
2023-11-06 00:26:13 +01:00
MISTRAL : 7 ,
2023-11-20 23:21:58 +01:00
YI : 8 ,
2023-12-10 02:20:53 +01:00
API _TEXTGENERATIONWEBUI : 9 ,
2023-12-10 14:56:38 +01:00
API _KOBOLD : 10 ,
2024-05-03 22:59:39 +02:00
CLAUDE : 11 ,
LLAMA3 : 12 ,
2024-08-15 23:00:43 +02:00
GEMMA : 13 ,
2024-08-26 11:07:36 +02:00
JAMBA : 14 ,
2024-09-06 18:28:34 +02:00
QWEN2 : 15 ,
COMMAND _R : 16 ,
2024-09-18 23:10:22 +02:00
NEMO : 17 ,
2023-08-23 01:38:43 +02:00
BEST _MATCH : 99 ,
} ;
2024-09-17 15:01:19 +02:00
// A list of local tokenizers that support encoding and decoding token ids.
export const ENCODE _TOKENIZERS = [
2023-11-21 00:04:27 +01:00
tokenizers . LLAMA ,
tokenizers . MISTRAL ,
tokenizers . YI ,
2024-05-03 22:59:39 +02:00
tokenizers . LLAMA3 ,
2024-08-15 23:00:43 +02:00
tokenizers . GEMMA ,
2024-08-26 11:07:36 +02:00
tokenizers . JAMBA ,
2024-09-17 15:01:19 +02:00
tokenizers . QWEN2 ,
tokenizers . COMMAND _R ,
2024-09-18 23:10:22 +02:00
tokenizers . NEMO ,
2023-11-21 00:04:27 +01:00
// uncomment when NovelAI releases Kayra and Clio weights, lol
//tokenizers.NERD,
//tokenizers.NERD2,
] ;
2024-09-17 15:01:19 +02:00
// A list of Text Completion sources that support remote tokenization.
2024-06-20 03:13:32 +02:00
export const TEXTGEN _TOKENIZERS = [ OOBA , TABBY , KOBOLDCPP , LLAMACPP , VLLM , APHRODITE ] ;
2024-01-05 15:17:06 +01:00
2023-12-10 02:48:41 +01:00
const TOKENIZER _URLS = {
[ tokenizers . GPT2 ] : {
encode : '/api/tokenizers/gpt2/encode' ,
decode : '/api/tokenizers/gpt2/decode' ,
count : '/api/tokenizers/gpt2/encode' ,
} ,
[ tokenizers . OPENAI ] : {
encode : '/api/tokenizers/openai/encode' ,
decode : '/api/tokenizers/openai/decode' ,
count : '/api/tokenizers/openai/encode' ,
} ,
[ tokenizers . LLAMA ] : {
encode : '/api/tokenizers/llama/encode' ,
decode : '/api/tokenizers/llama/decode' ,
count : '/api/tokenizers/llama/encode' ,
} ,
[ tokenizers . NERD ] : {
encode : '/api/tokenizers/nerdstash/encode' ,
decode : '/api/tokenizers/nerdstash/decode' ,
count : '/api/tokenizers/nerdstash/encode' ,
} ,
[ tokenizers . NERD2 ] : {
encode : '/api/tokenizers/nerdstash_v2/encode' ,
decode : '/api/tokenizers/nerdstash_v2/decode' ,
count : '/api/tokenizers/nerdstash_v2/encode' ,
} ,
[ tokenizers . API _KOBOLD ] : {
count : '/api/tokenizers/remote/kobold/count' ,
2023-12-14 00:28:18 +01:00
encode : '/api/tokenizers/remote/kobold/count' ,
2023-12-10 02:48:41 +01:00
} ,
[ tokenizers . MISTRAL ] : {
encode : '/api/tokenizers/mistral/encode' ,
decode : '/api/tokenizers/mistral/decode' ,
count : '/api/tokenizers/mistral/encode' ,
} ,
[ tokenizers . YI ] : {
encode : '/api/tokenizers/yi/encode' ,
decode : '/api/tokenizers/yi/decode' ,
count : '/api/tokenizers/yi/encode' ,
} ,
2024-05-03 22:59:39 +02:00
[ tokenizers . CLAUDE ] : {
encode : '/api/tokenizers/claude/encode' ,
decode : '/api/tokenizers/claude/decode' ,
count : '/api/tokenizers/claude/encode' ,
} ,
[ tokenizers . LLAMA3 ] : {
encode : '/api/tokenizers/llama3/encode' ,
decode : '/api/tokenizers/llama3/decode' ,
count : '/api/tokenizers/llama3/encode' ,
} ,
2024-08-15 23:00:43 +02:00
[ tokenizers . GEMMA ] : {
encode : '/api/tokenizers/gemma/encode' ,
decode : '/api/tokenizers/gemma/decode' ,
count : '/api/tokenizers/gemma/encode' ,
} ,
2024-08-26 11:07:36 +02:00
[ tokenizers . JAMBA ] : {
encode : '/api/tokenizers/jamba/encode' ,
decode : '/api/tokenizers/jamba/decode' ,
count : '/api/tokenizers/jamba/encode' ,
} ,
2024-09-06 18:28:34 +02:00
[ tokenizers . QWEN2 ] : {
encode : '/api/tokenizers/qwen2/encode' ,
decode : '/api/tokenizers/qwen2/decode' ,
count : '/api/tokenizers/qwen2/encode' ,
} ,
[ tokenizers . COMMAND _R ] : {
encode : '/api/tokenizers/command-r/encode' ,
decode : '/api/tokenizers/command-r/decode' ,
count : '/api/tokenizers/command-r/encode' ,
} ,
2024-09-18 23:10:22 +02:00
[ tokenizers . NEMO ] : {
encode : '/api/tokenizers/nemo/encode' ,
decode : '/api/tokenizers/nemo/decode' ,
count : '/api/tokenizers/nemo/encode' ,
} ,
2023-12-10 02:48:41 +01:00
[ tokenizers . API _TEXTGENERATIONWEBUI ] : {
encode : '/api/tokenizers/remote/textgenerationwebui/encode' ,
count : '/api/tokenizers/remote/textgenerationwebui/encode' ,
} ,
} ;
2023-12-02 19:04:51 +01:00
const objectStore = new localforage . createInstance ( { name : 'SillyTavern_ChatCompletions' } ) ;
2023-08-23 01:38:43 +02:00
let tokenCache = { } ;
2023-08-24 19:19:57 +02:00
/ * *
* Guesstimates the token count for a string .
* @ param { string } str String to tokenize .
* @ returns { number } Token count .
* /
export function guesstimate ( str ) {
return Math . ceil ( str . length / CHARACTERS _PER _TOKEN _RATIO ) ;
}
2023-08-23 01:38:43 +02:00
async function loadTokenCache ( ) {
try {
2023-12-02 20:11:06 +01:00
console . debug ( 'Chat Completions: loading token cache' ) ;
2023-08-23 01:38:43 +02:00
tokenCache = await objectStore . getItem ( 'tokenCache' ) || { } ;
} catch ( e ) {
console . log ( 'Chat Completions: unable to load token cache, using default value' , e ) ;
tokenCache = { } ;
}
}
export async function saveTokenCache ( ) {
try {
2023-12-02 20:11:06 +01:00
console . debug ( 'Chat Completions: saving token cache' ) ;
2023-08-23 01:38:43 +02:00
await objectStore . setItem ( 'tokenCache' , tokenCache ) ;
} catch ( e ) {
console . log ( 'Chat Completions: unable to save token cache' , e ) ;
}
}
async function resetTokenCache ( ) {
try {
console . debug ( 'Chat Completions: resetting token cache' ) ;
Object . keys ( tokenCache ) . forEach ( key => delete tokenCache [ key ] ) ;
await objectStore . removeItem ( 'tokenCache' ) ;
2023-08-27 22:20:43 +02:00
toastr . success ( 'Token cache cleared. Please reload the chat to re-tokenize it.' ) ;
2023-08-23 01:38:43 +02:00
} catch ( e ) {
console . log ( 'Chat Completions: unable to reset token cache' , e ) ;
}
}
2024-08-19 20:18:44 +02:00
/ * *
* @ typedef { object } Tokenizer
* @ property { number } tokenizerId - The id of the tokenizer option
* @ property { string } tokenizerKey - Internal name / key of the tokenizer
* @ property { string } tokenizerName - Human - readable detailed name of the tokenizer ( as displayed in the UI )
* /
2024-08-19 01:13:26 +02:00
/ * *
2024-08-19 16:36:56 +02:00
* Gets all tokenizers available to the user .
2024-08-19 20:18:44 +02:00
* @ returns { Tokenizer [ ] } Tokenizer info .
2024-08-19 01:13:26 +02:00
* /
2024-08-19 16:36:56 +02:00
export function getAvailableTokenizers ( ) {
const tokenizerOptions = $ ( '#tokenizer' ) . find ( 'option' ) . toArray ( ) ;
return tokenizerOptions . map ( tokenizerOption => ( {
tokenizerId : Number ( tokenizerOption . value ) ,
2024-08-19 20:18:44 +02:00
tokenizerKey : Object . entries ( tokenizers ) . find ( ( [ _ , value ] ) => value === Number ( tokenizerOption . value ) ) [ 0 ] . toLocaleLowerCase ( ) ,
tokenizerName : tokenizerOption . text ,
2024-08-26 11:07:36 +02:00
} ) ) ;
2024-08-19 16:36:56 +02:00
}
2024-08-19 01:13:26 +02:00
/ * *
* Selects tokenizer if not already selected .
* @ param { number } tokenizerId Tokenizer ID .
* /
2024-08-19 20:18:44 +02:00
export function selectTokenizer ( tokenizerId ) {
2024-08-19 01:13:26 +02:00
if ( tokenizerId !== power _user . tokenizer ) {
2024-08-19 20:18:44 +02:00
const tokenizer = getAvailableTokenizers ( ) . find ( tokenizer => tokenizer . tokenizerId === tokenizerId ) ;
if ( ! tokenizer ) {
console . warn ( 'Failed to find tokenizer with id' , tokenizerId ) ;
return ;
}
$ ( '#tokenizer' ) . val ( tokenizer . tokenizerId ) . trigger ( 'change' ) ;
toastr . info ( ` Tokenizer: " ${ tokenizer . tokenizerName } " selected ` ) ;
2024-08-19 01:13:26 +02:00
}
}
2023-11-06 19:25:59 +01:00
/ * *
* Gets the friendly name of the current tokenizer .
* @ param { string } forApi API to get the tokenizer for . Defaults to the main API .
2024-08-19 20:18:44 +02:00
* @ returns { Tokenizer } Tokenizer info
2023-11-06 19:25:59 +01:00
* /
export function getFriendlyTokenizerName ( forApi ) {
if ( ! forApi ) {
forApi = main _api ;
}
2023-12-02 19:04:51 +01:00
const tokenizerOption = $ ( '#tokenizer' ) . find ( ':selected' ) ;
2023-11-06 19:25:59 +01:00
let tokenizerId = Number ( tokenizerOption . val ( ) ) ;
let tokenizerName = tokenizerOption . text ( ) ;
if ( forApi !== 'openai' && tokenizerId === tokenizers . BEST _MATCH ) {
tokenizerId = getTokenizerBestMatch ( forApi ) ;
2023-12-10 15:03:25 +01:00
switch ( tokenizerId ) {
case tokenizers . API _KOBOLD :
tokenizerName = 'API (KoboldAI Classic)' ;
break ;
case tokenizers . API _TEXTGENERATIONWEBUI :
tokenizerName = 'API (Text Completion)' ;
break ;
default :
tokenizerName = $ ( ` #tokenizer option[value=" ${ tokenizerId } "] ` ) . text ( ) ;
break ;
}
2023-11-06 19:25:59 +01:00
}
tokenizerName = forApi == 'openai'
? getTokenizerModel ( )
: tokenizerName ;
tokenizerId = forApi == 'openai'
? tokenizers . OPENAI
: tokenizerId ;
2024-08-19 20:18:44 +02:00
const tokenizerKey = Object . entries ( tokenizers ) . find ( ( [ _ , value ] ) => value === tokenizerId ) [ 0 ] . toLocaleLowerCase ( ) ;
return { tokenizerName , tokenizerKey , tokenizerId } ;
2023-11-06 19:25:59 +01:00
}
/ * *
* Gets the best tokenizer for the current API .
* @ param { string } forApi API to get the tokenizer for . Defaults to the main API .
* @ returns { number } Tokenizer type .
* /
export function getTokenizerBestMatch ( forApi ) {
if ( ! forApi ) {
forApi = main _api ;
}
if ( forApi === 'novel' ) {
2023-08-23 01:38:43 +02:00
if ( nai _settings . model _novel . includes ( 'clio' ) ) {
return tokenizers . NERD ;
}
if ( nai _settings . model _novel . includes ( 'kayra' ) ) {
return tokenizers . NERD2 ;
}
2024-09-24 02:10:31 +02:00
if ( nai _settings . model _novel . includes ( 'erato' ) ) {
return tokenizers . LLAMA3 ;
}
2023-08-23 01:38:43 +02:00
}
2023-11-06 19:25:59 +01:00
if ( forApi === 'kobold' || forApi === 'textgenerationwebui' || forApi === 'koboldhorde' ) {
2023-08-24 20:23:35 +02:00
// Try to use the API tokenizer if possible:
// - API must be connected
// - Kobold must pass a version check
// - Tokenizer haven't reported an error previously
2023-12-02 18:42:15 +01:00
const hasTokenizerError = sessionStorage . getItem ( TOKENIZER _WARNING _KEY ) ;
2024-09-22 16:44:19 +02:00
const hasValidEndpoint = sessionStorage . getItem ( TOKENIZER _SUPPORTED _KEY ) ;
2023-12-02 18:42:15 +01:00
const isConnected = online _status !== 'no_connection' ;
2024-09-22 16:44:19 +02:00
const isTokenizerSupported = TEXTGEN _TOKENIZERS . includes ( textgen _settings . type ) && ( textgen _settings . type !== OOBA || hasValidEndpoint ) ;
2023-12-02 18:42:15 +01:00
if ( ! hasTokenizerError && isConnected ) {
if ( forApi === 'kobold' && kai _flags . can _use _tokenization ) {
2023-12-10 02:20:53 +01:00
return tokenizers . API _KOBOLD ;
2023-12-02 18:42:15 +01:00
}
if ( forApi === 'textgenerationwebui' && isTokenizerSupported ) {
2023-12-10 02:20:53 +01:00
return tokenizers . API _TEXTGENERATIONWEBUI ;
2023-12-02 18:42:15 +01:00
}
2024-02-25 21:47:07 +01:00
if ( forApi === 'textgenerationwebui' && textgen _settings . type === OPENROUTER ) {
return getCurrentOpenRouterModelTokenizer ( ) ;
}
2024-03-08 07:40:03 +01:00
if ( forApi === 'textgenerationwebui' && textgen _settings . type === DREAMGEN ) {
return getCurrentDreamGenModelTokenizer ( ) ;
}
2023-08-24 20:23:35 +02:00
}
2024-05-03 22:59:39 +02:00
if ( forApi === 'textgenerationwebui' ) {
const model = String ( getTextGenModel ( ) || online _status ) . toLowerCase ( ) ;
if ( model . includes ( 'llama3' ) || model . includes ( 'llama-3' ) ) {
return tokenizers . LLAMA3 ;
}
if ( model . includes ( 'mistral' ) || model . includes ( 'mixtral' ) ) {
return tokenizers . MISTRAL ;
}
2024-08-15 23:00:43 +02:00
if ( model . includes ( 'gemma' ) ) {
return tokenizers . GEMMA ;
}
2024-08-26 11:07:36 +02:00
if ( model . includes ( 'yi' ) ) {
return tokenizers . YI ;
}
if ( model . includes ( 'jamba' ) ) {
return tokenizers . JAMBA ;
}
2024-09-06 18:28:34 +02:00
if ( model . includes ( 'command-r' ) ) {
return tokenizers . COMMAND _R ;
}
if ( model . includes ( 'qwen2' ) ) {
return tokenizers . QWEN2 ;
}
2024-05-03 22:59:39 +02:00
}
2023-08-23 01:38:43 +02:00
return tokenizers . LLAMA ;
}
return tokenizers . NONE ;
}
2023-12-10 05:57:21 +01:00
// Get the current remote tokenizer API based on the current text generation API.
function currentRemoteTokenizerAPI ( ) {
switch ( main _api ) {
case 'kobold' :
return tokenizers . API _KOBOLD ;
case 'textgenerationwebui' :
return tokenizers . API _TEXTGENERATIONWEBUI ;
default :
return tokenizers . NONE ;
}
}
2023-08-27 17:27:34 +02:00
/ * *
* Calls the underlying tokenizer model to the token count for a string .
* @ param { number } type Tokenizer type .
* @ param { string } str String to tokenize .
* @ returns { number } Token count .
* /
2023-12-10 02:53:16 +01:00
function callTokenizer ( type , str ) {
if ( type === tokenizers . NONE ) return guesstimate ( str ) ;
2023-12-10 02:48:41 +01:00
2023-08-27 17:27:34 +02:00
switch ( type ) {
2023-12-10 05:57:21 +01:00
case tokenizers . API _CURRENT :
return callTokenizer ( currentRemoteTokenizerAPI ( ) , str ) ;
2023-12-10 02:20:53 +01:00
case tokenizers . API _KOBOLD :
2023-12-10 02:53:16 +01:00
return countTokensFromKoboldAPI ( str ) ;
2023-12-10 02:20:53 +01:00
case tokenizers . API _TEXTGENERATIONWEBUI :
2023-12-10 02:53:16 +01:00
return countTokensFromTextgenAPI ( str ) ;
2023-12-10 02:48:41 +01:00
default : {
const endpointUrl = TOKENIZER _URLS [ type ] ? . count ;
if ( ! endpointUrl ) {
console . warn ( 'Unknown tokenizer type' , type ) ;
2023-12-10 15:21:06 +01:00
return apiFailureTokenCount ( str ) ;
2023-12-10 02:48:41 +01:00
}
2023-12-10 02:53:16 +01:00
return countTokensFromServer ( endpointUrl , str ) ;
2023-12-10 02:48:41 +01:00
}
2023-08-27 17:27:34 +02:00
}
}
2024-04-13 20:05:31 +02:00
/ * *
* Calls the underlying tokenizer model to the token count for a string .
* @ param { number } type Tokenizer type .
* @ param { string } str String to tokenize .
* @ returns { Promise < number > } Token count .
* /
function callTokenizerAsync ( type , str ) {
return new Promise ( resolve => {
if ( type === tokenizers . NONE ) {
return resolve ( guesstimate ( str ) ) ;
}
switch ( type ) {
case tokenizers . API _CURRENT :
return callTokenizerAsync ( currentRemoteTokenizerAPI ( ) , str ) . then ( resolve ) ;
case tokenizers . API _KOBOLD :
return countTokensFromKoboldAPI ( str , resolve ) ;
case tokenizers . API _TEXTGENERATIONWEBUI :
return countTokensFromTextgenAPI ( str , resolve ) ;
default : {
const endpointUrl = TOKENIZER _URLS [ type ] ? . count ;
if ( ! endpointUrl ) {
console . warn ( 'Unknown tokenizer type' , type ) ;
return resolve ( apiFailureTokenCount ( str ) ) ;
}
return countTokensFromServer ( endpointUrl , str , resolve ) ;
}
}
} ) ;
}
/ * *
* Gets the token count for a string using the current model tokenizer .
* @ param { string } str String to tokenize
* @ param { number | undefined } padding Optional padding tokens . Defaults to 0.
* @ returns { Promise < number > } Token count .
* /
export async function getTokenCountAsync ( str , padding = undefined ) {
if ( typeof str !== 'string' || ! str ? . length ) {
return 0 ;
}
let tokenizerType = power _user . tokenizer ;
if ( main _api === 'openai' ) {
if ( padding === power _user . token _padding ) {
// For main "shadow" prompt building
tokenizerType = tokenizers . NONE ;
} else {
// For extensions and WI
return counterWrapperOpenAIAsync ( str ) ;
}
}
if ( tokenizerType === tokenizers . BEST _MATCH ) {
tokenizerType = getTokenizerBestMatch ( main _api ) ;
}
if ( padding === undefined ) {
padding = 0 ;
}
const cacheObject = getTokenCacheObject ( ) ;
const hash = getStringHash ( str ) ;
const cacheKey = ` ${ tokenizerType } - ${ hash } + ${ padding } ` ;
if ( typeof cacheObject [ cacheKey ] === 'number' ) {
return cacheObject [ cacheKey ] ;
}
const result = ( await callTokenizerAsync ( tokenizerType , str ) ) + padding ;
if ( isNaN ( result ) ) {
console . warn ( 'Token count calculation returned NaN' ) ;
return 0 ;
}
cacheObject [ cacheKey ] = result ;
return result ;
}
2023-08-23 01:38:43 +02:00
/ * *
* Gets the token count for a string using the current model tokenizer .
* @ param { string } str String to tokenize
* @ param { number | undefined } padding Optional padding tokens . Defaults to 0.
* @ returns { number } Token count .
2024-04-13 20:05:31 +02:00
* @ deprecated Use getTokenCountAsync instead .
2023-08-23 01:38:43 +02:00
* /
export function getTokenCount ( str , padding = undefined ) {
if ( typeof str !== 'string' || ! str ? . length ) {
return 0 ;
}
let tokenizerType = power _user . tokenizer ;
if ( main _api === 'openai' ) {
if ( padding === power _user . token _padding ) {
// For main "shadow" prompt building
tokenizerType = tokenizers . NONE ;
} else {
// For extensions and WI
return counterWrapperOpenAI ( str ) ;
}
}
if ( tokenizerType === tokenizers . BEST _MATCH ) {
2023-11-06 19:25:59 +01:00
tokenizerType = getTokenizerBestMatch ( main _api ) ;
2023-08-23 01:38:43 +02:00
}
if ( padding === undefined ) {
padding = 0 ;
}
const cacheObject = getTokenCacheObject ( ) ;
const hash = getStringHash ( str ) ;
2023-08-23 09:32:48 +02:00
const cacheKey = ` ${ tokenizerType } - ${ hash } + ${ padding } ` ;
2023-08-23 01:38:43 +02:00
if ( typeof cacheObject [ cacheKey ] === 'number' ) {
return cacheObject [ cacheKey ] ;
}
2023-12-10 02:53:16 +01:00
const result = callTokenizer ( tokenizerType , str ) + padding ;
2023-08-23 01:38:43 +02:00
if ( isNaN ( result ) ) {
2023-12-02 19:04:51 +01:00
console . warn ( 'Token count calculation returned NaN' ) ;
2023-08-23 01:38:43 +02:00
return 0 ;
}
cacheObject [ cacheKey ] = result ;
return result ;
}
/ * *
* Gets the token count for a string using the OpenAI tokenizer .
* @ param { string } text Text to tokenize .
* @ returns { number } Token count .
2024-04-13 20:05:31 +02:00
* @ deprecated Use counterWrapperOpenAIAsync instead .
2023-08-23 01:38:43 +02:00
* /
function counterWrapperOpenAI ( text ) {
const message = { role : 'system' , content : text } ;
return countTokensOpenAI ( message , true ) ;
}
2024-04-13 20:05:31 +02:00
/ * *
* Gets the token count for a string using the OpenAI tokenizer .
* @ param { string } text Text to tokenize .
* @ returns { Promise < number > } Token count .
* /
function counterWrapperOpenAIAsync ( text ) {
const message = { role : 'system' , content : text } ;
return countTokensOpenAIAsync ( message , true ) ;
}
2023-08-23 01:38:43 +02:00
export function getTokenizerModel ( ) {
// OpenAI models always provide their own tokenizer
if ( oai _settings . chat _completion _source == chat _completion _sources . OPENAI ) {
return oai _settings . openai _model ;
}
2023-10-19 12:37:08 +02:00
const turbo0301Tokenizer = 'gpt-3.5-turbo-0301' ;
2023-08-23 01:38:43 +02:00
const turboTokenizer = 'gpt-3.5-turbo' ;
const gpt4Tokenizer = 'gpt-4' ;
2024-08-15 23:00:43 +02:00
const gpt4oTokenizer = 'gpt-4o' ;
2023-08-23 01:38:43 +02:00
const gpt2Tokenizer = 'gpt2' ;
const claudeTokenizer = 'claude' ;
2023-11-05 20:54:19 +01:00
const llamaTokenizer = 'llama' ;
2024-05-03 22:59:39 +02:00
const llama3Tokenizer = 'llama3' ;
2023-11-06 00:26:13 +01:00
const mistralTokenizer = 'mistral' ;
2023-11-20 23:21:58 +01:00
const yiTokenizer = 'yi' ;
2024-08-15 23:00:43 +02:00
const gemmaTokenizer = 'gemma' ;
2024-08-26 11:07:36 +02:00
const jambaTokenizer = 'jamba' ;
2024-09-06 18:28:34 +02:00
const qwen2Tokenizer = 'qwen2' ;
const commandRTokenizer = 'command-r' ;
2024-09-18 23:10:22 +02:00
const nemoTokenizer = 'nemo' ;
2023-08-23 01:38:43 +02:00
// Assuming no one would use it for different models.. right?
if ( oai _settings . chat _completion _source == chat _completion _sources . SCALE ) {
return gpt4Tokenizer ;
}
// Select correct tokenizer for WindowAI proxies
if ( oai _settings . chat _completion _source == chat _completion _sources . WINDOWAI && oai _settings . windowai _model ) {
if ( oai _settings . windowai _model . includes ( 'gpt-4' ) ) {
return gpt4Tokenizer ;
}
2023-10-19 12:37:08 +02:00
else if ( oai _settings . windowai _model . includes ( 'gpt-3.5-turbo-0301' ) ) {
return turbo0301Tokenizer ;
}
2023-08-23 01:38:43 +02:00
else if ( oai _settings . windowai _model . includes ( 'gpt-3.5-turbo' ) ) {
return turboTokenizer ;
}
else if ( oai _settings . windowai _model . includes ( 'claude' ) ) {
return claudeTokenizer ;
}
else if ( oai _settings . windowai _model . includes ( 'GPT-NeoXT' ) ) {
return gpt2Tokenizer ;
}
}
// And for OpenRouter (if not a site model, then it's impossible to determine the tokenizer)
2024-02-25 21:47:07 +01:00
if ( main _api == 'openai' && oai _settings . chat _completion _source == chat _completion _sources . OPENROUTER && oai _settings . openrouter _model ||
main _api == 'textgenerationwebui' && textgen _settings . type === OPENROUTER && textgen _settings . openrouter _model ) {
const model = main _api == 'openai'
? model _list . find ( x => x . id === oai _settings . openrouter _model )
: openRouterModels . find ( x => x . id === textgen _settings . openrouter _model ) ;
2023-11-05 20:54:19 +01:00
if ( model ? . architecture ? . tokenizer === 'Llama2' ) {
return llamaTokenizer ;
}
2024-05-03 22:59:39 +02:00
else if ( model ? . architecture ? . tokenizer === 'Llama3' ) {
return llama3Tokenizer ;
}
2023-11-06 00:26:13 +01:00
else if ( model ? . architecture ? . tokenizer === 'Mistral' ) {
return mistralTokenizer ;
}
2023-11-20 23:21:58 +01:00
else if ( model ? . architecture ? . tokenizer === 'Yi' ) {
return yiTokenizer ;
}
2024-08-15 23:00:43 +02:00
else if ( model ? . architecture ? . tokenizer === 'Gemini' ) {
return gemmaTokenizer ;
}
2024-09-06 18:28:34 +02:00
else if ( model ? . architecture ? . tokenizer === 'Qwen' ) {
return qwen2Tokenizer ;
}
else if ( model ? . architecture ? . tokenizer === 'Cohere' ) {
return commandRTokenizer ;
}
2024-08-15 23:00:43 +02:00
else if ( oai _settings . openrouter _model . includes ( 'gpt-4o' ) ) {
return gpt4oTokenizer ;
}
2023-11-05 20:54:19 +01:00
else if ( oai _settings . openrouter _model . includes ( 'gpt-4' ) ) {
2023-08-23 01:38:43 +02:00
return gpt4Tokenizer ;
}
2023-10-19 12:37:08 +02:00
else if ( oai _settings . openrouter _model . includes ( 'gpt-3.5-turbo-0301' ) ) {
return turbo0301Tokenizer ;
}
2023-08-23 01:38:43 +02:00
else if ( oai _settings . openrouter _model . includes ( 'gpt-3.5-turbo' ) ) {
return turboTokenizer ;
}
else if ( oai _settings . openrouter _model . includes ( 'claude' ) ) {
return claudeTokenizer ;
}
else if ( oai _settings . openrouter _model . includes ( 'GPT-NeoXT' ) ) {
return gpt2Tokenizer ;
}
2024-08-26 11:07:36 +02:00
else if ( oai _settings . openrouter _model . includes ( 'jamba' ) ) {
return jambaTokenizer ;
}
2023-08-23 01:38:43 +02:00
}
2024-09-06 18:28:34 +02:00
if ( oai _settings . chat _completion _source == chat _completion _sources . COHERE ) {
return commandRTokenizer ;
}
2023-12-14 19:57:43 +01:00
if ( oai _settings . chat _completion _source == chat _completion _sources . MAKERSUITE ) {
2024-08-15 23:00:43 +02:00
return gemmaTokenizer ;
2023-12-14 06:49:50 +01:00
}
2024-08-26 11:07:36 +02:00
if ( oai _settings . chat _completion _source == chat _completion _sources . AI21 ) {
return jambaTokenizer ;
}
2023-08-23 01:38:43 +02:00
if ( oai _settings . chat _completion _source == chat _completion _sources . CLAUDE ) {
return claudeTokenizer ;
}
2023-12-15 23:47:51 +01:00
if ( oai _settings . chat _completion _source == chat _completion _sources . MISTRALAI ) {
2024-09-18 23:10:22 +02:00
if ( oai _settings . mistralai _model . includes ( 'nemo' ) || oai _settings . mistralai _model . includes ( 'pixtral' ) ) {
return nemoTokenizer ;
}
2023-12-15 23:47:51 +01:00
return mistralTokenizer ;
}
2023-12-20 17:29:03 +01:00
if ( oai _settings . chat _completion _source == chat _completion _sources . CUSTOM ) {
return oai _settings . custom _model ;
}
2024-04-19 23:09:38 +02:00
if ( oai _settings . chat _completion _source === chat _completion _sources . PERPLEXITY ) {
2024-05-03 22:59:39 +02:00
if ( oai _settings . perplexity _model . includes ( 'llama-3' ) || oai _settings . perplexity _model . includes ( 'llama3' ) ) {
return llama3Tokenizer ;
}
2024-04-19 23:09:38 +02:00
if ( oai _settings . perplexity _model . includes ( 'llama' ) ) {
return llamaTokenizer ;
}
2024-05-03 22:59:39 +02:00
if ( oai _settings . perplexity _model . includes ( 'mistral' ) || oai _settings . perplexity _model . includes ( 'mixtral' ) ) {
2024-04-19 23:09:38 +02:00
return mistralTokenizer ;
}
}
2024-05-05 17:53:12 +02:00
if ( oai _settings . chat _completion _source === chat _completion _sources . GROQ ) {
if ( oai _settings . groq _model . includes ( 'llama-3' ) || oai _settings . groq _model . includes ( 'llama3' ) ) {
return llama3Tokenizer ;
}
if ( oai _settings . groq _model . includes ( 'mistral' ) || oai _settings . groq _model . includes ( 'mixtral' ) ) {
return mistralTokenizer ;
}
2024-08-15 23:00:43 +02:00
if ( oai _settings . groq _model . includes ( 'gemma' ) ) {
return gemmaTokenizer ;
}
2024-05-05 17:53:12 +02:00
}
2024-06-27 23:51:09 +02:00
if ( oai _settings . chat _completion _source === chat _completion _sources . ZEROONEAI ) {
return yiTokenizer ;
}
2024-05-05 17:53:12 +02:00
2024-08-08 06:08:03 +02:00
if ( oai _settings . chat _completion _source === chat _completion _sources . BLOCKENTROPY ) {
if ( oai _settings . blockentropy _model . includes ( 'llama3' ) ) {
return llama3Tokenizer ;
}
if ( oai _settings . blockentropy _model . includes ( 'miqu' ) || oai _settings . blockentropy _model . includes ( 'mixtral' ) ) {
return mistralTokenizer ;
}
}
2023-08-23 01:38:43 +02:00
// Default to Turbo 3.5
return turboTokenizer ;
}
/ * *
* @ param { any [ ] | Object } messages
2024-04-13 20:05:31 +02:00
* @ deprecated Use countTokensOpenAIAsync instead .
2023-08-23 01:38:43 +02:00
* /
export function countTokensOpenAI ( messages , full = false ) {
2024-08-26 11:07:36 +02:00
const tokenizerEndpoint = ` /api/tokenizers/openai/count?model= ${ getTokenizerModel ( ) } ` ;
2023-08-23 01:38:43 +02:00
const cacheObject = getTokenCacheObject ( ) ;
if ( ! Array . isArray ( messages ) ) {
messages = [ messages ] ;
}
let token _count = - 1 ;
for ( const message of messages ) {
const model = getTokenizerModel ( ) ;
2024-08-26 11:07:36 +02:00
if ( model === 'claude' ) {
2023-08-23 01:38:43 +02:00
full = true ;
}
const hash = getStringHash ( JSON . stringify ( message ) ) ;
const cacheKey = ` ${ model } - ${ hash } ` ;
const cachedCount = cacheObject [ cacheKey ] ;
if ( typeof cachedCount === 'number' ) {
token _count += cachedCount ;
}
else {
jQuery . ajax ( {
async : false ,
type : 'POST' , //
2023-12-14 06:49:50 +01:00
url : tokenizerEndpoint ,
2023-08-23 01:38:43 +02:00
data : JSON . stringify ( [ message ] ) ,
2023-12-02 19:04:51 +01:00
dataType : 'json' ,
contentType : 'application/json' ,
2023-08-23 01:38:43 +02:00
success : function ( data ) {
token _count += Number ( data . token _count ) ;
cacheObject [ cacheKey ] = Number ( data . token _count ) ;
2023-12-02 21:06:57 +01:00
} ,
2023-08-23 01:38:43 +02:00
} ) ;
}
}
if ( ! full ) token _count -= 2 ;
return token _count ;
}
2024-04-13 20:05:31 +02:00
/ * *
* Returns the token count for a message using the OpenAI tokenizer .
* @ param { object [ ] | object } messages
* @ param { boolean } full
* @ returns { Promise < number > } Token count .
* /
export async function countTokensOpenAIAsync ( messages , full = false ) {
2024-08-26 11:07:36 +02:00
const tokenizerEndpoint = ` /api/tokenizers/openai/count?model= ${ getTokenizerModel ( ) } ` ;
2024-04-13 20:05:31 +02:00
const cacheObject = getTokenCacheObject ( ) ;
if ( ! Array . isArray ( messages ) ) {
messages = [ messages ] ;
}
let token _count = - 1 ;
for ( const message of messages ) {
const model = getTokenizerModel ( ) ;
2024-08-26 11:07:36 +02:00
if ( model === 'claude' ) {
2024-04-13 20:05:31 +02:00
full = true ;
}
const hash = getStringHash ( JSON . stringify ( message ) ) ;
const cacheKey = ` ${ model } - ${ hash } ` ;
const cachedCount = cacheObject [ cacheKey ] ;
if ( typeof cachedCount === 'number' ) {
token _count += cachedCount ;
}
else {
const data = await jQuery . ajax ( {
async : true ,
type : 'POST' , //
url : tokenizerEndpoint ,
data : JSON . stringify ( [ message ] ) ,
dataType : 'json' ,
contentType : 'application/json' ,
} ) ;
token _count += Number ( data . token _count ) ;
cacheObject [ cacheKey ] = Number ( data . token _count ) ;
}
}
if ( ! full ) token _count -= 2 ;
return token _count ;
}
2023-08-23 01:38:43 +02:00
/ * *
* Gets the token cache object for the current chat .
* @ returns { Object } Token cache object for the current chat .
* /
function getTokenCacheObject ( ) {
let chatId = 'undefined' ;
try {
if ( selected _group ) {
chatId = groups . find ( x => x . id == selected _group ) ? . chat _id ;
}
else if ( this _chid !== undefined ) {
chatId = characters [ this _chid ] . chat ;
}
} catch {
console . log ( 'No character / group selected. Using default cache item' ) ;
}
if ( typeof tokenCache [ chatId ] !== 'object' ) {
tokenCache [ chatId ] = { } ;
}
return tokenCache [ String ( chatId ) ] ;
}
2023-08-24 19:19:57 +02:00
/ * *
2023-12-10 02:08:48 +01:00
* Count tokens using the server API .
2023-08-24 19:19:57 +02:00
* @ param { string } endpoint API endpoint .
* @ param { string } str String to tokenize .
2024-04-13 20:05:31 +02:00
* @ param { function } [ resolve ] Promise resolve function . s
2023-12-10 02:53:16 +01:00
* @ returns { number } Token count .
2023-08-24 19:19:57 +02:00
* /
2024-04-13 20:05:31 +02:00
function countTokensFromServer ( endpoint , str , resolve ) {
const isAsync = typeof resolve === 'function' ;
2023-08-23 01:38:43 +02:00
let tokenCount = 0 ;
2023-08-24 19:19:57 +02:00
2023-08-23 01:38:43 +02:00
jQuery . ajax ( {
2024-04-13 20:05:31 +02:00
async : isAsync ,
2023-08-23 01:38:43 +02:00
type : 'POST' ,
url : endpoint ,
2023-12-10 02:35:11 +01:00
data : JSON . stringify ( { text : str } ) ,
2023-12-02 19:04:51 +01:00
dataType : 'json' ,
contentType : 'application/json' ,
2023-08-23 01:38:43 +02:00
success : function ( data ) {
2023-08-24 19:19:57 +02:00
if ( typeof data . count === 'number' ) {
tokenCount = data . count ;
} else {
2023-12-10 02:08:48 +01:00
tokenCount = apiFailureTokenCount ( str ) ;
}
2024-04-13 20:05:31 +02:00
isAsync && resolve ( tokenCount ) ;
2023-12-10 02:08:48 +01:00
} ,
} ) ;
2023-12-10 02:53:16 +01:00
return tokenCount ;
2023-12-10 02:08:48 +01:00
}
/ * *
* Count tokens using the AI provider ' s API .
* @ param { string } str String to tokenize .
2024-04-13 20:05:31 +02:00
* @ param { function } [ resolve ] Promise resolve function .
2023-12-10 02:53:16 +01:00
* @ returns { number } Token count .
2023-12-10 02:08:48 +01:00
* /
2024-04-13 20:05:31 +02:00
function countTokensFromKoboldAPI ( str , resolve ) {
const isAsync = typeof resolve === 'function' ;
2023-12-10 02:20:53 +01:00
let tokenCount = 0 ;
jQuery . ajax ( {
2024-04-13 20:05:31 +02:00
async : isAsync ,
2023-12-10 02:20:53 +01:00
type : 'POST' ,
2023-12-10 02:48:41 +01:00
url : TOKENIZER _URLS [ tokenizers . API _KOBOLD ] . count ,
2023-12-10 02:35:11 +01:00
data : JSON . stringify ( {
text : str ,
url : api _server ,
} ) ,
2023-12-10 02:20:53 +01:00
dataType : 'json' ,
contentType : 'application/json' ,
success : function ( data ) {
if ( typeof data . count === 'number' ) {
tokenCount = data . count ;
} else {
tokenCount = apiFailureTokenCount ( str ) ;
}
2024-04-13 20:05:31 +02:00
isAsync && resolve ( tokenCount ) ;
2023-12-10 02:20:53 +01:00
} ,
} ) ;
2023-12-10 02:53:16 +01:00
return tokenCount ;
2023-12-10 02:20:53 +01:00
}
2023-12-10 02:35:11 +01:00
function getTextgenAPITokenizationParams ( str ) {
return {
text : str ,
api _type : textgen _settings . type ,
2023-12-19 15:38:11 +01:00
url : getTextGenServer ( ) ,
2024-09-15 07:05:53 +02:00
legacy _api : textgen _settings . legacy _api && textgen _settings . type === OOBA ,
2024-06-20 03:13:32 +02:00
vllm _model : textgen _settings . vllm _model ,
2024-08-25 00:43:20 +02:00
aphrodite _model : textgen _settings . aphrodite _model ,
2023-12-10 02:35:11 +01:00
} ;
}
2023-12-10 02:20:53 +01:00
/ * *
* Count tokens using the AI provider ' s API .
* @ param { string } str String to tokenize .
2024-04-13 20:05:31 +02:00
* @ param { function } [ resolve ] Promise resolve function .
2023-12-10 02:53:16 +01:00
* @ returns { number } Token count .
2023-12-10 02:20:53 +01:00
* /
2024-04-13 20:05:31 +02:00
function countTokensFromTextgenAPI ( str , resolve ) {
const isAsync = typeof resolve === 'function' ;
2023-12-10 02:08:48 +01:00
let tokenCount = 0 ;
jQuery . ajax ( {
2024-04-13 20:05:31 +02:00
async : isAsync ,
2023-12-10 02:08:48 +01:00
type : 'POST' ,
2023-12-10 02:48:41 +01:00
url : TOKENIZER _URLS [ tokenizers . API _TEXTGENERATIONWEBUI ] . count ,
2023-12-10 02:20:53 +01:00
data : JSON . stringify ( getTextgenAPITokenizationParams ( str ) ) ,
2023-12-10 02:08:48 +01:00
dataType : 'json' ,
contentType : 'application/json' ,
success : function ( data ) {
if ( typeof data . count === 'number' ) {
tokenCount = data . count ;
} else {
tokenCount = apiFailureTokenCount ( str ) ;
2023-08-24 19:19:57 +02:00
}
2024-04-13 20:05:31 +02:00
isAsync && resolve ( tokenCount ) ;
2023-12-02 21:06:57 +01:00
} ,
2023-08-23 01:38:43 +02:00
} ) ;
2023-08-24 19:19:57 +02:00
2023-12-10 02:53:16 +01:00
return tokenCount ;
2023-08-23 01:38:43 +02:00
}
2023-12-10 02:08:48 +01:00
function apiFailureTokenCount ( str ) {
console . error ( 'Error counting tokens' ) ;
2024-09-22 16:25:56 +02:00
let shouldTryAgain = false ;
2023-12-10 02:08:48 +01:00
if ( ! sessionStorage . getItem ( TOKENIZER _WARNING _KEY ) ) {
2024-09-22 16:25:56 +02:00
const bestMatchBefore = getTokenizerBestMatch ( main _api ) ;
2023-12-10 02:08:48 +01:00
sessionStorage . setItem ( TOKENIZER _WARNING _KEY , String ( true ) ) ;
2024-09-22 16:25:56 +02:00
const bestMatchAfter = getTokenizerBestMatch ( main _api ) ;
if ( [ tokenizers . API _TEXTGENERATIONWEBUI , tokenizers . API _KOBOLD ] . includes ( bestMatchBefore ) && bestMatchBefore !== bestMatchAfter ) {
shouldTryAgain = true ;
}
}
// Only try again if we guarantee not to be looped by the same error
if ( shouldTryAgain && power _user . tokenizer === tokenizers . BEST _MATCH ) {
return getTokenCount ( str ) ;
2023-12-10 02:08:48 +01:00
}
return guesstimate ( str ) ;
}
2023-08-27 17:27:34 +02:00
/ * *
* Calls the underlying tokenizer model to encode a string to tokens .
* @ param { string } endpoint API endpoint .
* @ param { string } str String to tokenize .
2024-04-13 20:05:31 +02:00
* @ param { function } [ resolve ] Promise resolve function .
2023-08-27 17:27:34 +02:00
* @ returns { number [ ] } Array of token ids .
* /
2024-04-13 20:05:31 +02:00
function getTextTokensFromServer ( endpoint , str , resolve ) {
const isAsync = typeof resolve === 'function' ;
2023-08-23 01:38:43 +02:00
let ids = [ ] ;
jQuery . ajax ( {
2024-04-13 20:05:31 +02:00
async : isAsync ,
2023-08-23 01:38:43 +02:00
type : 'POST' ,
url : endpoint ,
2023-12-10 02:35:11 +01:00
data : JSON . stringify ( { text : str } ) ,
2023-12-02 19:04:51 +01:00
dataType : 'json' ,
contentType : 'application/json' ,
2023-08-23 01:38:43 +02:00
success : function ( data ) {
ids = data . ids ;
2023-11-06 01:42:51 +01:00
// Don't want to break reverse compatibility, so sprinkle in some of the JS magic
if ( Array . isArray ( data . chunks ) ) {
Object . defineProperty ( ids , 'chunks' , { value : data . chunks } ) ;
}
2024-04-13 20:05:31 +02:00
isAsync && resolve ( ids ) ;
2023-12-02 21:06:57 +01:00
} ,
2023-08-23 01:38:43 +02:00
} ) ;
return ids ;
}
2023-12-10 02:08:48 +01:00
/ * *
* Calls the AI provider ' s tokenize API to encode a string to tokens .
* @ param { string } str String to tokenize .
2024-04-13 20:05:31 +02:00
* @ param { function } [ resolve ] Promise resolve function .
2023-12-10 02:08:48 +01:00
* @ returns { number [ ] } Array of token ids .
* /
2024-04-13 20:05:31 +02:00
function getTextTokensFromTextgenAPI ( str , resolve ) {
const isAsync = typeof resolve === 'function' ;
2023-12-10 02:08:48 +01:00
let ids = [ ] ;
jQuery . ajax ( {
2024-04-13 20:05:31 +02:00
async : isAsync ,
2023-12-10 02:08:48 +01:00
type : 'POST' ,
2023-12-10 02:48:41 +01:00
url : TOKENIZER _URLS [ tokenizers . API _TEXTGENERATIONWEBUI ] . encode ,
2023-12-10 02:20:53 +01:00
data : JSON . stringify ( getTextgenAPITokenizationParams ( str ) ) ,
2023-12-10 02:08:48 +01:00
dataType : 'json' ,
contentType : 'application/json' ,
success : function ( data ) {
ids = data . ids ;
2024-04-13 20:05:31 +02:00
isAsync && resolve ( ids ) ;
2023-12-10 02:08:48 +01:00
} ,
} ) ;
return ids ;
}
2023-12-14 00:28:18 +01:00
/ * *
* Calls the AI provider ' s tokenize API to encode a string to tokens .
* @ param { string } str String to tokenize .
2024-04-13 20:05:31 +02:00
* @ param { function } [ resolve ] Promise resolve function .
2023-12-14 00:28:18 +01:00
* @ returns { number [ ] } Array of token ids .
* /
2024-04-13 20:05:31 +02:00
function getTextTokensFromKoboldAPI ( str , resolve ) {
const isAsync = typeof resolve === 'function' ;
2023-12-14 00:28:18 +01:00
let ids = [ ] ;
jQuery . ajax ( {
2024-04-13 20:05:31 +02:00
async : isAsync ,
2023-12-14 00:28:18 +01:00
type : 'POST' ,
url : TOKENIZER _URLS [ tokenizers . API _KOBOLD ] . encode ,
data : JSON . stringify ( {
text : str ,
url : api _server ,
} ) ,
dataType : 'json' ,
contentType : 'application/json' ,
success : function ( data ) {
ids = data . ids ;
2024-04-13 20:05:31 +02:00
isAsync && resolve ( ids ) ;
2023-12-14 00:28:18 +01:00
} ,
} ) ;
return ids ;
}
2023-08-27 17:27:34 +02:00
/ * *
* Calls the underlying tokenizer model to decode token ids to text .
* @ param { string } endpoint API endpoint .
* @ param { number [ ] } ids Array of token ids
2024-04-13 20:05:31 +02:00
* @ param { function } [ resolve ] Promise resolve function .
2024-01-23 06:00:31 +01:00
* @ returns { ( { text : string , chunks ? : string [ ] } ) } Decoded token text as a single string and individual chunks ( if available ) .
2023-08-27 17:27:34 +02:00
* /
2024-04-13 20:05:31 +02:00
function decodeTextTokensFromServer ( endpoint , ids , resolve ) {
const isAsync = typeof resolve === 'function' ;
2023-08-27 17:27:34 +02:00
let text = '' ;
2024-01-23 06:00:31 +01:00
let chunks = [ ] ;
2023-08-27 17:27:34 +02:00
jQuery . ajax ( {
2024-04-13 20:05:31 +02:00
async : isAsync ,
2023-08-27 17:27:34 +02:00
type : 'POST' ,
url : endpoint ,
data : JSON . stringify ( { ids : ids } ) ,
2023-12-02 19:04:51 +01:00
dataType : 'json' ,
contentType : 'application/json' ,
2023-08-27 17:27:34 +02:00
success : function ( data ) {
text = data . text ;
2024-01-23 06:00:31 +01:00
chunks = data . chunks ;
2024-04-13 20:05:31 +02:00
isAsync && resolve ( { text , chunks } ) ;
2023-12-02 21:06:57 +01:00
} ,
2023-08-27 17:27:34 +02:00
} ) ;
2024-01-23 06:00:31 +01:00
return { text , chunks } ;
2023-08-27 17:27:34 +02:00
}
/ * *
2023-12-10 01:43:33 +01:00
* Encodes a string to tokens using the server API .
2023-08-27 17:27:34 +02:00
* @ param { number } tokenizerType Tokenizer type .
* @ param { string } str String to tokenize .
* @ returns { number [ ] } Array of token ids .
* /
2023-08-23 01:38:43 +02:00
export function getTextTokens ( tokenizerType , str ) {
switch ( tokenizerType ) {
2023-12-10 05:57:21 +01:00
case tokenizers . API _CURRENT :
2023-12-10 15:09:00 +01:00
return getTextTokens ( currentRemoteTokenizerAPI ( ) , str ) ;
2023-12-10 02:20:53 +01:00
case tokenizers . API _TEXTGENERATIONWEBUI :
2023-12-10 02:48:41 +01:00
return getTextTokensFromTextgenAPI ( str ) ;
2023-12-14 00:28:18 +01:00
case tokenizers . API _KOBOLD :
return getTextTokensFromKoboldAPI ( str ) ;
2023-12-10 02:48:41 +01:00
default : {
const tokenizerEndpoints = TOKENIZER _URLS [ tokenizerType ] ;
if ( ! tokenizerEndpoints ) {
2023-12-10 15:21:06 +01:00
apiFailureTokenCount ( str ) ;
2023-12-10 02:48:41 +01:00
console . warn ( 'Unknown tokenizer type' , tokenizerType ) ;
return [ ] ;
}
let endpointUrl = tokenizerEndpoints . encode ;
if ( ! endpointUrl ) {
2023-12-10 15:21:06 +01:00
apiFailureTokenCount ( str ) ;
2023-12-10 02:48:41 +01:00
console . warn ( 'This tokenizer type does not support encoding' , tokenizerType ) ;
return [ ] ;
}
if ( tokenizerType === tokenizers . OPENAI ) {
endpointUrl += ` ?model= ${ getTokenizerModel ( ) } ` ;
}
return getTextTokensFromServer ( endpointUrl , str ) ;
}
2023-08-23 01:38:43 +02:00
}
}
2023-08-27 17:27:34 +02:00
/ * *
2023-12-10 01:43:33 +01:00
* Decodes token ids to text using the server API .
2023-09-16 17:48:06 +02:00
* @ param { number } tokenizerType Tokenizer type .
2023-08-27 17:27:34 +02:00
* @ param { number [ ] } ids Array of token ids
2024-01-23 06:00:31 +01:00
* @ returns { ( { text : string , chunks ? : string [ ] } ) } Decoded token text as a single string and individual chunks ( if available ) .
2023-08-27 17:27:34 +02:00
* /
export function decodeTextTokens ( tokenizerType , ids ) {
2023-12-10 05:57:21 +01:00
// Currently, neither remote API can decode, but this may change in the future. Put this guard here to be safe
if ( tokenizerType === tokenizers . API _CURRENT ) {
2023-12-10 15:09:00 +01:00
return decodeTextTokens ( tokenizers . NONE , ids ) ;
2023-12-10 05:57:21 +01:00
}
2023-12-10 02:48:41 +01:00
const tokenizerEndpoints = TOKENIZER _URLS [ tokenizerType ] ;
if ( ! tokenizerEndpoints ) {
console . warn ( 'Unknown tokenizer type' , tokenizerType ) ;
2024-01-23 06:00:31 +01:00
return { text : '' , chunks : [ ] } ;
2023-12-10 02:48:41 +01:00
}
let endpointUrl = tokenizerEndpoints . decode ;
if ( ! endpointUrl ) {
console . warn ( 'This tokenizer type does not support decoding' , tokenizerType ) ;
2024-01-23 06:00:31 +01:00
return { text : '' , chunks : [ ] } ;
2023-12-10 02:48:41 +01:00
}
if ( tokenizerType === tokenizers . OPENAI ) {
endpointUrl += ` ?model= ${ getTokenizerModel ( ) } ` ;
2023-08-27 17:27:34 +02:00
}
2023-12-10 02:48:41 +01:00
return decodeTextTokensFromServer ( endpointUrl , ids ) ;
2023-08-27 17:27:34 +02:00
}
2023-10-24 23:32:49 +02:00
export async function initTokenizers ( ) {
2023-08-23 01:38:43 +02:00
await loadTokenCache ( ) ;
2023-08-27 22:20:43 +02:00
registerDebugFunction ( 'resetTokenCache' , 'Reset token cache' , 'Purges the calculated token counts. Use this if you want to force a full re-tokenization of all chats or suspect the token counts are wrong.' , resetTokenCache ) ;
2023-10-24 23:32:49 +02:00
}