add developer info for models

This commit is contained in:
Andrew Pareles 2025-02-14 01:52:17 -08:00
parent 9cfcf396c1
commit 0975f1bf5f
3 changed files with 206 additions and 122 deletions

View file

@ -11,7 +11,7 @@ import { registerSingleton, InstantiationType } from '../../../../platform/insta
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
import { IMetricsService } from './metricsService.js';
import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, modelInfoOfDefaultModelNames, VoidModelInfo, GlobalSettings, GlobalSettingName, defaultGlobalSettings, displayInfoOfProviderName, defaultProviderSettings } from './voidSettingsTypes.js';
import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, VoidModelInfo, GlobalSettings, GlobalSettingName, defaultGlobalSettings, displayInfoOfProviderName, defaultProviderSettings, developerInfoOfRecognizedModel, modelInfoOfAutodetectedModelNames } from './voidSettingsTypes.js';
const STORAGE_KEY = 'void.settingsServiceStorage'
@ -289,27 +289,26 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService {
setAutodetectedModels(providerName: ProviderName, newDefaultModelNames: string[], logging: object) {
setAutodetectedModels(providerName: ProviderName, autodetectedModelNames: string[], logging: object) {
const { models } = this.state.settingsOfProvider[providerName]
const oldModelNames = models.map(m => m.modelName)
const newDefaultModelInfo = modelInfoOfDefaultModelNames(newDefaultModelNames, { isAutodetected: true, existingModels: models })
const newModelInfo = [
...newDefaultModelInfo, // swap out all the default models for the new default models
...models.filter(m => !m.isDefault), // keep any non-defaul (custom) models
const newDefaultModels = modelInfoOfAutodetectedModelNames(autodetectedModelNames, { existingModels: models })
const newModels = [
...newDefaultModels, // swap out all the default models for the new default models
...models.filter(m => !m.isDefault), // keep any non-default (custom) models
]
this.setSettingOfProvider(providerName, 'models', newModelInfo)
this.setSettingOfProvider(providerName, 'models', newModels)
// if the models changed, log it
const new_names = newModelInfo.map(m => m.modelName)
const new_names = newModels.map(m => m.modelName)
if (!(oldModelNames.length === new_names.length
&& oldModelNames.every((_, i) => oldModelNames[i] === new_names[i]))
) {
this._metricsService.capture('Autodetect Models', { providerName, newModels: newModelInfo, ...logging })
this._metricsService.capture('Autodetect Models', { providerName, newModels: newModels, ...logging })
}
}
toggleModelHidden(providerName: ProviderName, modelName: string) {
@ -335,7 +334,7 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService {
if (existingIdx !== -1) return // if exists, do nothing
const newModels = [
...models,
{ modelName, isDefault: false, isHidden: false }
{ ...developerInfoOfRecognizedModel(modelName), modelName, isDefault: false, isHidden: false }
]
this.setSettingOfProvider(providerName, 'models', newModels)

View file

@ -7,45 +7,217 @@
import { VoidSettingsState } from './voidSettingsService.js'
export type VoidModelInfo = {
// developer info used in sendLLMMessage
type VoidModelDeveloperInfo = {
supportsSystemMessage: 'system' | 'developer' | false, // if null, we will just do a string of system message
supportsTools: boolean, // we will just do a string of tool use if it doesn't support
supportsAutocompleteFIM: boolean, // we will just do a description of FIM if it doens't support <|fim_hole|>
supportsStreaming: boolean, // (o1 does NOT) we will just dump the final result if doesn't support it
maxTokens: number, // required, DEFAULT is Infinity
}
export type VoidModelInfo = { // <-- STATEFUL
modelName: string,
isDefault: boolean, // whether or not it's a default for its provider
isHidden: boolean, // whether or not the user is hiding it (switched off)
isAutodetected?: boolean, // whether the model was autodetected by polling
}
} & VoidModelDeveloperInfo
// creates `modelInfo` from `modelNames`
export const modelInfoOfDefaultModelNames = (defaultModelNames: string[], options?: { isAutodetected: true, existingModels: VoidModelInfo[] }): VoidModelInfo[] => {
const { isAutodetected, existingModels } = options ?? {}
if (!existingModels) { // default settings
return defaultModelNames.map((modelName, i) => ({
modelName,
isDefault: true,
isAutodetected: isAutodetected,
isHidden: defaultModelNames.length >= 10 // hide all models if there are a ton of them, and make user enable them individually
}))
} else { // settings if there are existing models (keep existing `isHidden` property)
const existingModelsMap: Record<string, VoidModelInfo> = {}
for (const existingModel of existingModels) {
existingModelsMap[existingModel.modelName] = existingModel
}
return defaultModelNames.map((modelName, i) => ({
modelName,
isDefault: true,
isAutodetected: isAutodetected,
isHidden: !!existingModelsMap[modelName]?.isHidden,
}))
export const recognizedModels = [
// chat
'OpenAI 4o',
'Anthropic Claude',
'Llama 3.x',
'Deepseek Chat', // deepseek coder v2 is now merged into chat (V3) https://api-docs.deepseek.com/updates#deepseek-coder--deepseek-chat-upgraded-to-deepseek-v25-model
// 'xAI Grok',
// 'Google Gemini, Gemma',
// 'Microsoft Phi4',
// coding (autocomplete)
'Alibaba Qwen2.5 Coder Instruct', // we recommend this over Qwen2.5
'Mistral Codestral',
// thinking
'OpenAI o1, o3',
'Deepseek R1',
// general
// 'Mixtral 8x7b'
// 'Qwen2.5',
] as const
type RecognizedModel = (typeof recognizedModels)[number] | '<GENERAL>'
// const modelCapabilities: { [recognizedModel in RecognizedModel]: ({ }) => string } = {
// 'OpenAI 4o': {
// template: ({ prefix, suffix, }: { prefix: string; suffix: string; }) => `\
// `
// }
// }
export function getRecognizedModel(modelName: string): RecognizedModel {
const lower = modelName.toLowerCase();
if (lower.includes('gpt-4o')) {
return 'OpenAI 4o';
}
if (lower.includes('claude')) {
return 'Anthropic Claude';
}
if (lower.includes('llama')) {
return 'Llama 3.x';
}
if (lower.includes('qwen2.5-coder')) {
return 'Alibaba Qwen2.5 Coder Instruct';
}
if (lower.includes('mistral')) {
return 'Mistral Codestral';
}
// Check for "o1" or "o3"
if (/\bo1\b/.test(lower) || /\bo3\b/.test(lower)) {
return 'OpenAI o1, o3';
}
if (lower.includes('deepseek-r1') || lower.includes('deepseek-reasoner')) {
return 'Deepseek R1';
}
// Fallback:
return '<GENERAL>';
}
export const developerInfoOfRecognizedModel = (modelName: string) => {
const devInfo: { [recognizedModel in RecognizedModel]: VoidModelDeveloperInfo } = {
'OpenAI 4o': {
supportsSystemMessage: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
maxTokens: 4096,
},
'Anthropic Claude': {
supportsSystemMessage: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
maxTokens: 4096,
},
'Llama 3.x': {
supportsSystemMessage: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
maxTokens: 4096,
},
'Deepseek Chat': {
supportsSystemMessage: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
maxTokens: 4096,
},
'Alibaba Qwen2.5 Coder Instruct': {
supportsSystemMessage: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
maxTokens: 4096,
},
'Mistral Codestral': {
supportsSystemMessage: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
maxTokens: 4096,
},
'OpenAI o1, o3': {
supportsSystemMessage: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
maxTokens: 4096,
},
'Deepseek R1': {
supportsSystemMessage: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
maxTokens: 4096,
},
'<GENERAL>': {
supportsSystemMessage: false,
supportsTools: false,
supportsAutocompleteFIM: false,
supportsStreaming: false,
maxTokens: 4096,
},
}
const modelName_ = getRecognizedModel(modelName)
return devInfo[modelName_]
}
// creates `modelInfo` from `modelNames`
export const modelInfoOfDefaultModelNames = (defaultModelNames: string[]): VoidModelInfo[] => {
return defaultModelNames.map((modelName, i) => ({
modelName,
isDefault: true,
isAutodetected: false,
isHidden: defaultModelNames.length >= 10, // hide all models if there are a ton of them, and make user enable them individually
...developerInfoOfRecognizedModel(modelName)
}))
}
export const modelInfoOfAutodetectedModelNames = (defaultModelNames: string[], options: { existingModels: VoidModelInfo[] }) => {
const { existingModels } = options
const existingModelsMap: Record<string, VoidModelInfo> = {}
for (const existingModel of existingModels) {
existingModelsMap[existingModel.modelName] = existingModel
}
return defaultModelNames.map((modelName, i) => ({
modelName,
isDefault: true,
isAutodetected: true,
isHidden: !!existingModelsMap[modelName]?.isHidden,
...developerInfoOfRecognizedModel(modelName)
}))
}
// https://docs.anthropic.com/en/docs/about-claude/models
export const defaultAnthropicModels = modelInfoOfDefaultModelNames([
'claude-3-5-sonnet-20241022',
@ -530,77 +702,3 @@ export const globalSettingNames = Object.keys(defaultGlobalSettings) as GlobalSe
export const recognizedModels = [
// chat
'OpenAI 4o',
'Anthropic Claude',
'Llama 3.x',
'Deepseek Chat', // deepseek coder v2 is now merged into chat (V3) https://api-docs.deepseek.com/updates#deepseek-coder--deepseek-chat-upgraded-to-deepseek-v25-model
// 'xAI Grok',
// 'Google Gemini, Gemma',
// 'Microsoft Phi4',
// coding (autocomplete)
'Alibaba Qwen2.5 Coder Instruct', // we recommend this over Qwen2.5
'Mistral Codestral',
// thinking
'OpenAI o1, o3',
'Deepseek R1',
// general
'<General>'
// 'Mixtral 8x7b'
// 'Qwen2.5',
] as const
type RecognizedModel = (typeof recognizedModels)[number]
// const modelCapabilities: { [recognizedModel in RecognizedModel]: ({ }) => string } = {
// 'OpenAI 4o': {
// template: ({ prefix, suffix, }: { prefix: string; suffix: string; }) => `\
// `
// }
// }
export function getRecognizedModel(modelName: string): RecognizedModel {
const lower = modelName.toLowerCase();
if (lower.includes('gpt-4o')) {
return 'OpenAI 4o';
}
if (lower.includes('claude')) {
return 'Anthropic Claude';
}
if (lower.includes('llama')) {
return 'Llama 3.x';
}
if (lower.includes('qwen2.5-coder')) {
return 'Alibaba Qwen2.5 Coder Instruct';
}
if (lower.includes('mistral')) {
return 'Mistral Codestral';
}
// Check for "o1" or "o3"
if (/\bo1\b/.test(lower) || /\bo3\b/.test(lower)) {
return 'OpenAI o1, o3';
}
if (lower.includes('deepseek-r1') || lower.includes('deepseek-reasoner')) {
return 'Deepseek R1';
}
// Fallback:
return '<General>';
}

View file

@ -1,13 +0,0 @@
/*
modelName -> {
system_message_type: 'system' | 'developer' (openai) | null // if null, we will just do a string of system message
supports_tools: boolean // we will just do a string of tool use if it doesn't support
supports_autocomplete_FIM (suffix) // we will just do a description of FIM if it doens't support <|fim_hole|>
supports_streaming: boolean // (o1 does NOT) we will just dump the final result if doesn't support it
max_tokens: number // required, DEFAULT is Infinity
}
*/