diff --git a/src/lang/en.ts b/src/lang/en.ts index d7fb1ba3..5b47a1b2 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -1112,4 +1112,5 @@ export const languageEnglish = { fallbackModel: "Fallback Model", fallbackWhenBlankResponse: "Fallback When Blank Response", doNotChangeFallbackModels: "Do Not Change Fallback Models on Preset Change", + customModels: "Custom Models", } diff --git a/src/lib/Setting/Pages/AdvancedSettings.svelte b/src/lib/Setting/Pages/AdvancedSettings.svelte index ec9ed426..805cd9dc 100644 --- a/src/lib/Setting/Pages/AdvancedSettings.svelte +++ b/src/lib/Setting/Pages/AdvancedSettings.svelte @@ -14,6 +14,8 @@ import { Capacitor } from "@capacitor/core"; import { capStorageInvestigation } from "src/ts/storage/mobileStorage"; import Arcodion from "src/lib/UI/Arcodion.svelte"; + import { PlusIcon, TrashIcon } from "lucide-svelte"; + import { v4 } from "uuid"; let estaStorage:{ key:string, @@ -254,6 +256,117 @@ {/each} +{#snippet CustomFlagButton(index:number,name:string,flag:number)} + +{/snippet} + + + + {#each DBState.db.customModels as model, index} + + {language.name} + + {language.proxyRequestModel} + + URL + + {language.tokenizer} + { + DBState.db.customModels[index].tokenizer = parseInt(e.currentTarget.value) + }}> + tiktokenCl100kBase + tiktokenO200Base + Mistral + Llama + NovelAI + Claude + NovelList + Llama3 + Gemma + GoogleCloud + Cohere + DeepSeek + + {language.format} + { + DBState.db.customModels[index].format = parseInt(e.currentTarget.value) + }}> + OpenAICompatible + OpenAILegacyInstruct + Anthropic + AnthropicLegacy + Mistral + GoogleCloud + VertexAIGemini + NovelList + Cohere + NovelAI + OobaLegacy + Ooba + Kobold + AWSBedrockClaude + OpenAIResponseAPI + + {language.proxyAPIKey} + + {language.additionalParams} + + + {@render CustomFlagButton(index,'hasImageInput', 0)} + {@render CustomFlagButton(index,'hasImageOutput', 1)} + {@render CustomFlagButton(index,'hasAudioInput', 2)} + {@render CustomFlagButton(index,'hasAudioOutput', 3)} + {@render CustomFlagButton(index,'hasPrefill', 4)} + {@render CustomFlagButton(index,'hasCache', 5)} + {@render CustomFlagButton(index,'hasFullSystemPrompt', 6)} + {@render CustomFlagButton(index,'hasFirstSystemPrompt', 7)} + {@render CustomFlagButton(index,'hasStreaming', 8)} + {@render CustomFlagButton(index,'requiresAlternateRole', 9)} + {@render CustomFlagButton(index,'mustStartWithUserInput', 10)} + {@render CustomFlagButton(index,'hasVideoInput', 12)} + {@render CustomFlagButton(index,'OAICompletionTokens', 13)} + {@render CustomFlagButton(index,'DeveloperRole', 14)} + {@render CustomFlagButton(index,'geminiThinking', 15)} + {@render CustomFlagButton(index,'geminiBlockOff', 16)} + {@render CustomFlagButton(index,'deepSeekPrefix', 17)} + {@render CustomFlagButton(index,'deepSeekThinkingInput', 18)} + {@render CustomFlagButton(index,'deepSeekThinkingOutput', 19)} + + + {/each} +
+ + +
+
+ {#if open} -
+
{@render children?.()}
{/if} diff --git a/src/lib/UI/ModelList.svelte b/src/lib/UI/ModelList.svelte index 620549d2..99b01c32 100644 --- a/src/lib/UI/ModelList.svelte +++ b/src/lib/UI/ModelList.svelte @@ -74,6 +74,17 @@ {/await} + {#if DBState?.db.customModels?.length > 0} + + {#each DBState.db.customModels as model} + + {/each} + + + {/if} + + + {#if blankable} {/if} diff --git a/src/ts/model/modellist.ts b/src/ts/model/modellist.ts index c75163c6..eb9e21fa 100644 --- a/src/ts/model/modellist.ts +++ b/src/ts/model/modellist.ts @@ -1450,6 +1450,24 @@ export function getModelInfo(id: string): LLMModel{ tokenizer: LLMTokenizer.Unknown } } + if(id.startsWith('xcustom:::')){ + const customModels = db?.customModels || [] + const found = customModels.find((model) => model.id === id) + if(found){ + return { + id: found.id, + name: found.name, + shortName: found.name, + fullName: found.name, + internalID: found.internalId, + provider: LLMProvider.AsIs, + format: found.format, + flags: found.flags, + parameters: ['temperature', 'top_p', 'frequency_penalty', 'presence_penalty', 'repetition_penalty', 'min_p', 'top_a', 'top_k', 'thinking_tokens'], + tokenizer: found.tokenizer + } + } + } return { id, diff --git a/src/ts/process/request.ts b/src/ts/process/request.ts index 1ca1eaec..8502f2d9 100644 --- a/src/ts/process/request.ts +++ b/src/ts/process/request.ts @@ -55,6 +55,7 @@ interface RequestDataArgumentExtended extends requestDataArgument{ modelInfo?:LLMModel customURL?:string mode?:ModelModeExtended + key?:string } type requestDataResponse = { @@ -515,6 +516,11 @@ export async function requestChatDataMain(arg:requestDataArgument, model:ModelMo targ.modelInfo.format = db.customAPIFormat targ.customURL = db.forceReplaceUrl } + if(targ.aiModel.startsWith('xcustom:::')){ + const found = db.customModels.find(m => m.id === targ.aiModel) + targ.customURL = found?.url + targ.key = found?.key + } if(db.seperateModelsForAxModels && !arg.staticModel){ if(db.seperateModels[model]){ @@ -775,7 +781,7 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise v !== ''), otherAx: data.fallbackModels.otherAx.filter((v) => v !== '') } + data.customModels ??= [] changeLanguage(data.language) setDatabaseLite(data) } @@ -968,6 +969,17 @@ export interface Database{ } doNotChangeFallbackModels: boolean fallbackWhenBlankResponse: boolean + customModels: { + id: string + internalId: string + url: string + format: LLMFormat + tokenizer: LLMTokenizer + key: string + name: string + params: string + flags: LLMFlags[] + }[] } interface SeparateParameters{ @@ -1784,7 +1796,7 @@ import type { RisuModule } from '../process/modules'; import type { SerializableHypaV2Data } from '../process/memory/hypav2'; import { decodeRPack, encodeRPack } from '../rpack/rpack_bg'; import { DBState, selectedCharID } from '../stores.svelte'; -import { LLMFlags, LLMFormat } from '../model/modellist'; +import { LLMFlags, LLMFormat, LLMTokenizer } from '../model/modellist'; import type { Parameter } from '../process/request'; import type { HypaModel } from '../process/memory/hypamemory'; import type { SerializableHypaV3Data } from '../process/memory/hypav3';