mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
add new config type for each feature, UI dump
This commit is contained in:
parent
77fc2d2d4a
commit
e5ac36facd
21 changed files with 667 additions and 552 deletions
|
|
@ -4,19 +4,19 @@
|
|||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
const voidProviderDefaults = {
|
||||
anthropic: {
|
||||
apiKey: '',
|
||||
|
||||
export const voidInitModelOptions = {
|
||||
anthropic: () => ({
|
||||
model: 'claude-3-5-sonnet-20240620',
|
||||
models: [
|
||||
'claude-3-5-sonnet-20240620',
|
||||
'claude-3-opus-20240229',
|
||||
'claude-3-sonnet-20240229',
|
||||
'claude-3-haiku-20240307'
|
||||
],
|
||||
model: 'claude-3-5-sonnet-20240620',
|
||||
},
|
||||
openAI: {
|
||||
apiKey: '',
|
||||
}),
|
||||
openAI: () => ({
|
||||
model: 'gpt-4o',
|
||||
models: [
|
||||
'o1-preview',
|
||||
'o1-mini',
|
||||
|
|
@ -34,241 +34,256 @@ const voidProviderDefaults = {
|
|||
'gpt-4-0613',
|
||||
'gpt-3.5-turbo-0125',
|
||||
'gpt-3.5-turbo',
|
||||
'gpt-3.5-turbo-1106'
|
||||
'gpt-3.5-turbo-1106',
|
||||
],
|
||||
model: 'gpt-4o',
|
||||
},
|
||||
ollama: {
|
||||
endpoint: 'http://127.0.0.1:11434', //'The endpoint of your Ollama instance.',
|
||||
models: ['codestral', 'qwen2.5-coder', 'qwen2.5-coder:0.5b', 'qwen2.5-coder:1.5b', 'qwen2.5-coder:3b', 'qwen2.5-coder:7b', 'qwen2.5-coder:14b', 'qwen2.5-coder:32b', 'codegemma', 'codegemma:2b', 'codegemma:7b', 'codellama', 'codellama:7b', 'codellama:13b', 'codellama:34b', 'codellama:70b', 'codellama:code', 'codellama:python', 'command-r', 'command-r:35b', 'command-r-plus', 'command-r-plus:104b', 'deepseek-coder-v2', 'deepseek-coder-v2:16b', 'deepseek-coder-v2:236b', 'falcon2', 'falcon2:11b', 'firefunction-v2', 'firefunction-v2:70b', 'gemma', 'gemma:2b', 'gemma:7b', 'gemma2', 'gemma2:2b', 'gemma2:9b', 'gemma2:27b', 'llama2', 'llama2:7b', 'llama2:13b', 'llama2:70b', 'llama3', 'llama3:8b', 'llama3:70b', 'llama3-chatqa', 'llama3-chatqa:8b', 'llama3-chatqa:70b', 'llama3-gradient', 'llama3-gradient:8b', 'llama3-gradient:70b', 'llama3.1', 'llama3.1:8b', 'llama3.1:70b', 'llama3.1:405b', 'llava', 'llava:7b', 'llava:13b', 'llava:34b', 'llava-llama3', 'llava-llama3:8b', 'llava-phi3', 'llava-phi3:3.8b', 'mistral', 'mistral:7b', 'mistral-large', 'mistral-large:123b', 'mistral-nemo', 'mistral-nemo:12b', 'mixtral', 'mixtral:8x7b', 'mixtral:8x22b', 'moondream', 'moondream:1.8b', 'openhermes', 'openhermes:v2.5', 'phi3', 'phi3:3.8b', 'phi3:14b', 'phi3.5', 'phi3.5:3.8b', 'qwen', 'qwen:7b', 'qwen:14b', 'qwen:32b', 'qwen:72b', 'qwen:110b', 'qwen2', 'qwen2:0.5b', 'qwen2:1.5b', 'qwen2:7b', 'qwen2:72b', 'smollm', 'smollm:135m', 'smollm:360m', 'smollm:1.7b'] as const,
|
||||
}),
|
||||
ollama: () => ({ // TODO make this do a fetch to get the models
|
||||
model: 'codestral',
|
||||
|
||||
},
|
||||
openRouter: {
|
||||
apiKey: '',
|
||||
models: ['openai/gpt-4o'],
|
||||
models: [
|
||||
'codestral',
|
||||
'qwen2.5-coder',
|
||||
'qwen2.5-coder:0.5b',
|
||||
'qwen2.5-coder:1.5b',
|
||||
'qwen2.5-coder:3b',
|
||||
'qwen2.5-coder:7b',
|
||||
'qwen2.5-coder:14b',
|
||||
'qwen2.5-coder:32b',
|
||||
'codegemma',
|
||||
'codegemma:2b',
|
||||
'codegemma:7b',
|
||||
'codellama',
|
||||
'codellama:7b',
|
||||
'codellama:13b',
|
||||
'codellama:34b',
|
||||
'codellama:70b',
|
||||
'codellama:code',
|
||||
'codellama:python',
|
||||
'command-r',
|
||||
'command-r:35b',
|
||||
'command-r-plus',
|
||||
'command-r-plus:104b',
|
||||
'deepseek-coder-v2',
|
||||
'deepseek-coder-v2:16b',
|
||||
'deepseek-coder-v2:236b',
|
||||
'falcon2',
|
||||
'falcon2:11b',
|
||||
'firefunction-v2',
|
||||
'firefunction-v2:70b',
|
||||
'gemma',
|
||||
'gemma:2b',
|
||||
'gemma:7b',
|
||||
'gemma2',
|
||||
'gemma2:2b',
|
||||
'gemma2:9b',
|
||||
'gemma2:27b',
|
||||
'llama2',
|
||||
'llama2:7b',
|
||||
'llama2:13b',
|
||||
'llama2:70b',
|
||||
'llama3',
|
||||
'llama3:8b',
|
||||
'llama3:70b',
|
||||
'llama3-chatqa',
|
||||
'llama3-chatqa:8b',
|
||||
'llama3-chatqa:70b',
|
||||
'llama3-gradient',
|
||||
'llama3-gradient:8b',
|
||||
'llama3-gradient:70b',
|
||||
'llama3.1',
|
||||
'llama3.1:8b',
|
||||
'llama3.1:70b',
|
||||
'llama3.1:405b',
|
||||
'llava',
|
||||
'llava:7b',
|
||||
'llava:13b',
|
||||
'llava:34b',
|
||||
'llava-llama3',
|
||||
'llava-llama3:8b',
|
||||
'llava-phi3',
|
||||
'llava-phi3:3.8b',
|
||||
'mistral',
|
||||
'mistral:7b',
|
||||
'mistral-large',
|
||||
'mistral-large:123b',
|
||||
'mistral-nemo',
|
||||
'mistral-nemo:12b',
|
||||
'mixtral',
|
||||
'mixtral:8x7b',
|
||||
'mixtral:8x22b',
|
||||
'moondream',
|
||||
'moondream:1.8b',
|
||||
'openhermes',
|
||||
'openhermes:v2.5',
|
||||
'phi3',
|
||||
'phi3:3.8b',
|
||||
'phi3:14b',
|
||||
'phi3.5',
|
||||
'phi3.5:3.8b',
|
||||
'qwen',
|
||||
'qwen:7b',
|
||||
'qwen:14b',
|
||||
'qwen:32b',
|
||||
'qwen:72b',
|
||||
'qwen:110b',
|
||||
'qwen2',
|
||||
'qwen2:0.5b',
|
||||
'qwen2:1.5b',
|
||||
'qwen2:7b',
|
||||
'qwen2:72b',
|
||||
'smollm',
|
||||
'smollm:135m',
|
||||
'smollm:360m',
|
||||
'smollm:1.7b',
|
||||
],
|
||||
}),
|
||||
openRouter: () => ({
|
||||
model: 'openai/gpt-4o',
|
||||
},
|
||||
openAICompatible: {
|
||||
apiKey: '',
|
||||
endpoint: 'http://127.0.0.1:11434/v1', //'The baseUrl (exluding /chat/completions).',
|
||||
models: ['gpt-4o'],
|
||||
model: 'gpt-4o',
|
||||
},
|
||||
gemini: {
|
||||
apiKey: '',
|
||||
models: null, // any
|
||||
}),
|
||||
openAICompatible: () => ({
|
||||
model: 'openai/gpt-4o',
|
||||
models: null, // any
|
||||
}),
|
||||
gemini: () => ({
|
||||
model: 'gemini-1.5-flash',
|
||||
models: [
|
||||
'gemini-1.5-flash',
|
||||
'gemini-1.5-pro',
|
||||
'gemini-1.5-flash-8b',
|
||||
'gemini-1.0-pro'
|
||||
],
|
||||
model: 'gemini-1.5-flash',
|
||||
}),
|
||||
groq: () => ({
|
||||
model: 'mixtral-8x7b-32768',
|
||||
models: [
|
||||
"mixtral-8x7b-32768",
|
||||
"llama2-70b-4096",
|
||||
"gemma-7b-it"
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
export const voidProviderDefaults = {
|
||||
anthropic: {
|
||||
apiKey: '',
|
||||
},
|
||||
openAI: {
|
||||
apiKey: '',
|
||||
},
|
||||
ollama: {
|
||||
endpoint: 'http://127.0.0.1:11434',
|
||||
},
|
||||
openRouter: {
|
||||
apiKey: '',
|
||||
},
|
||||
openAICompatible: {
|
||||
apiKey: '',
|
||||
endpoint: 'http://127.0.0.1:11434/v1',
|
||||
},
|
||||
gemini: {
|
||||
apiKey: '',
|
||||
},
|
||||
groq: {
|
||||
apiKey: ''
|
||||
}
|
||||
} as const
|
||||
|
||||
|
||||
type VoidSettings = typeof voidProviderDefaults
|
||||
|
||||
|
||||
export type ProviderName = keyof typeof voidProviderDefaults
|
||||
export const providerNames = Object.keys(voidProviderDefaults) as ProviderName[]
|
||||
|
||||
|
||||
export const featureNames = ['Ctrl+L', 'Ctrl+K', 'Autocomplete']
|
||||
export type FeatureName = typeof featureNames[number]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// was whichApi:
|
||||
const providerOptions = _uiConfig(
|
||||
'API Provider.',
|
||||
'anthropic',
|
||||
allowedProviders,
|
||||
)
|
||||
|
||||
const voidFeatureOptions = {
|
||||
maxTokens: _uiConfig(
|
||||
'Max number of tokens to output.',
|
||||
'undefined',
|
||||
[
|
||||
'undefined',
|
||||
'1024',
|
||||
'2048',
|
||||
'4096',
|
||||
'8192'
|
||||
] as const,
|
||||
)
|
||||
} as const
|
||||
|
||||
|
||||
type AllVoidProvidersState = {
|
||||
[providerName in ProviderName]: {
|
||||
[option in keyof typeof voidProviderOptions[providerName]['providerOptions']]: string // optionName (e.g. apikey) -> string
|
||||
}
|
||||
export type VoidConfigState = {
|
||||
[providerName in ProviderName]: ({
|
||||
enabled: string, // 'true' | 'false'
|
||||
models: string[],
|
||||
model: string,
|
||||
maxTokens: string,
|
||||
} & {
|
||||
[optionName in keyof typeof voidProviderDefaults[providerName]]: string
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// const features = ['ctrl+L', 'ctrl+K', 'autocomplete'] as const
|
||||
// type FeatureName = (typeof features)[number]
|
||||
type UnionOfKeys<T> = T extends T ? keyof T : never;
|
||||
export const descOfSettingName = (providerName: ProviderName, settingName: UnionOfKeys<VoidConfigState[ProviderName]>) => {
|
||||
if (settingName === 'apiKey')
|
||||
return 'API Key'
|
||||
else if (settingName === 'endpoint') {
|
||||
if (providerName === 'ollama') return 'The endpoint of your Ollama instance.'
|
||||
if (providerName === 'openAICompatible') return 'The baseUrl (exluding /chat/completions).'
|
||||
}
|
||||
else if (settingName === 'maxTokens')
|
||||
return 'Max Tokens'
|
||||
else if (settingName === 'model')
|
||||
return 'Model'
|
||||
else if (settingName === 'enabled')
|
||||
return 'Enabled'
|
||||
else if (settingName === 'models')
|
||||
return 'Available Models'
|
||||
|
||||
throw new Error(`Unknown setting name: "${settingName}"`)
|
||||
}
|
||||
|
||||
|
||||
// not very important (remember past user options):
|
||||
// type AllVoidFeaturesState = {
|
||||
// [featureName in FeatureName]: {
|
||||
// [providerName in ProviderName]: {
|
||||
// [modelName in (typeof voidProviderOptions)[providerName]['modelOptions']['defaultVal']]: {
|
||||
// options: { [option in keyof typeof voidProviderOptions[providerName]]: string }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
type VoidFeatureState<
|
||||
CtrlLProvider extends ProviderName,
|
||||
CtrlKProvider extends ProviderName,
|
||||
AutocompleteProvider extends ProviderName,
|
||||
> = {
|
||||
'ctrl+L': {
|
||||
provider: CtrlLProvider,
|
||||
model: (typeof voidProviderOptions)[CtrlLProvider]['modelOptions']['defaultVal'],
|
||||
// promptTemplate?
|
||||
// systemTemplate?
|
||||
// maxTokens?
|
||||
export const defaultVoidConfigState: VoidConfigState = {
|
||||
anthropic: {
|
||||
enabled: 'false',
|
||||
models: [],
|
||||
model: '',
|
||||
apiKey: '',
|
||||
maxTokens: '1000',
|
||||
},
|
||||
'ctrl+K': {
|
||||
provider: CtrlKProvider,
|
||||
model: (typeof voidProviderOptions)[CtrlKProvider]['modelOptions']['defaultVal'],
|
||||
openAI: {
|
||||
enabled: 'false',
|
||||
models: [],
|
||||
model: '',
|
||||
apiKey: '',
|
||||
maxTokens: '1000',
|
||||
},
|
||||
'autocomplete': {
|
||||
provider: AutocompleteProvider,
|
||||
model: (typeof voidProviderOptions)[AutocompleteProvider]['modelOptions']['defaultVal'],
|
||||
ollama: {
|
||||
enabled: 'false',
|
||||
models: [],
|
||||
model: '',
|
||||
endpoint: '',
|
||||
maxTokens: '1000',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const PartialVoidState = {
|
||||
|
||||
}
|
||||
|
||||
const VoidState = {}
|
||||
|
||||
|
||||
// this is the type that comes with metadata like desc, default val, etc
|
||||
export type VoidConfigInfo = typeof voidProviderOptions
|
||||
export type VoidConfigField = keyof typeof voidProviderOptions // typeof configFields[number]
|
||||
|
||||
// this is the type that specifies the user's actual config
|
||||
export type PartialVoidConfig = {
|
||||
[K in keyof typeof voidProviderOptions]?: {
|
||||
[P in keyof typeof voidProviderOptions[K]]?: typeof voidProviderOptions[K][P]['defaultVal']
|
||||
openRouter: {
|
||||
enabled: 'false',
|
||||
models: [],
|
||||
model: '',
|
||||
apiKey: '',
|
||||
maxTokens: '1000',
|
||||
},
|
||||
openAICompatible: {
|
||||
enabled: 'false',
|
||||
models: [],
|
||||
model: '',
|
||||
apiKey: '',
|
||||
endpoint: '',
|
||||
maxTokens: '1000',
|
||||
},
|
||||
gemini: {
|
||||
enabled: 'false',
|
||||
models: [],
|
||||
model: '',
|
||||
apiKey: '',
|
||||
maxTokens: '1000',
|
||||
},
|
||||
groq: {
|
||||
enabled: 'false',
|
||||
models: [],
|
||||
model: '',
|
||||
apiKey: '',
|
||||
maxTokens: '1000',
|
||||
}
|
||||
}
|
||||
|
||||
export type VoidConfig = {
|
||||
[K in keyof typeof voidProviderOptions]: {
|
||||
[P in keyof typeof voidProviderOptions[K]]: typeof voidProviderOptions[K][P]['defaultVal']
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const getVoidConfig = (partialVoidConfig: PartialVoidConfig): VoidConfig => {
|
||||
const config = {} as PartialVoidConfig
|
||||
for (const field of [...allowedProviders, 'default'] as const) {
|
||||
config[field] = {}
|
||||
for (const prop in voidProviderOptions[field]) {
|
||||
config[field][prop] = partialVoidConfig[field]?.[prop]?.trim() || voidProviderOptions[field][prop].defaultVal
|
||||
}
|
||||
}
|
||||
return config as VoidConfig
|
||||
}
|
||||
|
||||
|
||||
const VOID_CONFIG_KEY = 'void.partialVoidConfig'
|
||||
|
||||
export type SetFieldFnType = <K extends VoidConfigField>(field: K, param: keyof VoidConfigInfo[K], newVal: string) => Promise<void>;
|
||||
|
||||
export type ConfigState = {
|
||||
partialVoidConfig: PartialVoidConfig; // free parameter
|
||||
voidConfig: VoidConfig; // computed from partialVoidConfig
|
||||
}
|
||||
|
||||
export interface IVoidConfigStateService {
|
||||
readonly _serviceBrand: undefined;
|
||||
readonly state: ConfigState;
|
||||
readonly voidConfigInfo: VoidConfigInfo;
|
||||
onDidChangeState: Event<void>;
|
||||
setField: SetFieldFnType;
|
||||
}
|
||||
|
||||
export const IVoidConfigStateService = createDecorator<IVoidConfigStateService>('VoidConfigStateService');
|
||||
class VoidConfigStateService extends Disposable implements IVoidConfigStateService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private readonly _onDidChangeState = new Emitter<void>();
|
||||
readonly onDidChangeState: Event<void> = this._onDidChangeState.event; // this is primarily for use in react, so react can listen + update on state changes
|
||||
|
||||
state: ConfigState;
|
||||
readonly voidConfigInfo: VoidConfigInfo = voidProviderOptions; // just putting this here for simplicity, it's static though
|
||||
|
||||
constructor(
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
@IEncryptionService private readonly _encryptionService: IEncryptionService,
|
||||
// could have used this, but it's clearer the way it is (+ slightly different eg StorageTarget.USER)
|
||||
// @ISecretStorageService private readonly _secretStorageService: ISecretStorageService,
|
||||
) {
|
||||
super()
|
||||
|
||||
// at the start, we haven't read the partial config yet, but we need to set state to something, just treat partialVoidConfig like it's empty
|
||||
this.state = {
|
||||
partialVoidConfig: {},
|
||||
voidConfig: getVoidConfig({}),
|
||||
}
|
||||
|
||||
// read and update the actual state immediately
|
||||
this._readPartialVoidConfig().then(partialVoidConfig => {
|
||||
this._setState(partialVoidConfig)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
private async _readPartialVoidConfig(): Promise<PartialVoidConfig> {
|
||||
const encryptedPartialConfig = this._storageService.get(VOID_CONFIG_KEY, StorageScope.APPLICATION)
|
||||
|
||||
if (!encryptedPartialConfig)
|
||||
return {}
|
||||
|
||||
const partialVoidConfigStr = await this._encryptionService.decrypt(encryptedPartialConfig)
|
||||
return JSON.parse(partialVoidConfigStr)
|
||||
}
|
||||
|
||||
|
||||
private async _storePartialVoidConfig(partialVoidConfig: PartialVoidConfig) {
|
||||
const encryptedPartialConfigStr = await this._encryptionService.encrypt(JSON.stringify(partialVoidConfig))
|
||||
this._storageService.store(VOID_CONFIG_KEY, encryptedPartialConfigStr, StorageScope.APPLICATION, StorageTarget.USER)
|
||||
}
|
||||
|
||||
|
||||
// Set field on PartialVoidConfig
|
||||
setField: SetFieldFnType = async <K extends VoidConfigField>(field: K, param: keyof VoidConfigInfo[K], newVal: string) => {
|
||||
const { partialVoidConfig } = this.state
|
||||
|
||||
const newPartialConfig: PartialVoidConfig = {
|
||||
...partialVoidConfig,
|
||||
[field]: {
|
||||
...partialVoidConfig[field],
|
||||
[param]: newVal
|
||||
}
|
||||
}
|
||||
await this._storePartialVoidConfig(newPartialConfig)
|
||||
this._setState(newPartialConfig)
|
||||
}
|
||||
|
||||
// internal function to update state, should be called every time state changes
|
||||
private async _setState(partialVoidConfig: PartialVoidConfig) {
|
||||
this.state = {
|
||||
partialVoidConfig: partialVoidConfig,
|
||||
voidConfig: getVoidConfig(partialVoidConfig),
|
||||
}
|
||||
this._onDidChangeState.fire()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
registerSingleton(IVoidConfigStateService, VoidConfigStateService, InstantiationType.Eager);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VoidConfig } from './configTypes.js'
|
||||
import { ProviderName, VoidConfigState } from './configTypes'
|
||||
|
||||
|
||||
// ---------- type definitions ----------
|
||||
|
|
@ -27,11 +27,13 @@ export type LLMMessageServiceParams = {
|
|||
onError: OnError;
|
||||
|
||||
messages: LLMMessage[];
|
||||
voidConfig: VoidConfig | null;
|
||||
voidConfig: VoidConfigState | null;
|
||||
|
||||
logging: {
|
||||
loggingName: string,
|
||||
};
|
||||
providerName: ProviderName;
|
||||
|
||||
}
|
||||
|
||||
export type SendLLMMMessageParams = {
|
||||
|
|
@ -40,11 +42,12 @@ export type SendLLMMMessageParams = {
|
|||
onError: OnError;
|
||||
|
||||
messages: LLMMessage[];
|
||||
voidConfig: VoidConfig | null;
|
||||
voidConfig: VoidConfigState | null;
|
||||
|
||||
logging: {
|
||||
loggingName: string,
|
||||
};
|
||||
providerName: ProviderName;
|
||||
abortRef: AbortRef;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onTex
|
|||
|
||||
const thisConfig = voidConfig.anthropic
|
||||
|
||||
const anthropic = new Anthropic({ apiKey: thisConfig.apikey, dangerouslyAllowBrowser: true }); // defaults to process.env["ANTHROPIC_API_KEY"]
|
||||
const anthropic = new Anthropic({ apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true });
|
||||
|
||||
// find system messages and concatenate them
|
||||
const systemMessage = messages
|
||||
|
|
@ -25,7 +25,7 @@ export const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onTex
|
|||
system: systemMessage,
|
||||
messages: anthropicMessages,
|
||||
model: thisConfig.model,
|
||||
max_tokens: parseMaxTokensStr(voidConfig.default.maxTokens)!, // this might be undefined, but it will just throw an error for the user
|
||||
max_tokens: parseMaxTokensStr(thisConfig.maxTokens)!, // this might be undefined, but it will just throw an error for the user to see
|
||||
});
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export const sendGeminiMsg: SendLLMMessageFnTypeInternal = async ({ messages, on
|
|||
|
||||
const thisConfig = voidConfig.gemini
|
||||
|
||||
const genAI = new GoogleGenerativeAI(thisConfig.apikey);
|
||||
const genAI = new GoogleGenerativeAI(thisConfig.apiKey);
|
||||
const model = genAI.getGenerativeModel({ model: thisConfig.model });
|
||||
|
||||
// remove system messages that get sent to Gemini
|
||||
|
|
|
|||
|
|
@ -1,64 +1,64 @@
|
|||
|
||||
// Greptile
|
||||
// https://docs.greptile.com/api-reference/query
|
||||
// https://docs.greptile.com/quickstart#sample-response-streamed
|
||||
// // Greptile
|
||||
// // https://docs.greptile.com/api-reference/query
|
||||
// // https://docs.greptile.com/quickstart#sample-response-streamed
|
||||
|
||||
import { SendLLMMessageFnTypeInternal } from './util';
|
||||
// import { SendLLMMessageFnTypeInternal } from './util';
|
||||
|
||||
export const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
// export const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
|
||||
let fullText = ''
|
||||
// let fullText = ''
|
||||
|
||||
const thisConfig = voidConfig.greptile
|
||||
// const thisConfig = voidConfig.greptile
|
||||
|
||||
fetch('https://api.greptile.com/v2/query', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${thisConfig.apikey}`,
|
||||
'X-Github-Token': `${thisConfig.githubPAT}`,
|
||||
'Content-Type': `application/json`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages,
|
||||
stream: true,
|
||||
repositories: [thisConfig.repoinfo],
|
||||
}),
|
||||
})
|
||||
// this is {message}\n{message}\n{message}...\n
|
||||
.then(async response => {
|
||||
const text = await response.text()
|
||||
console.log('got greptile', text)
|
||||
return JSON.parse(`[${text.trim().split('\n').join(',')}]`)
|
||||
})
|
||||
// TODO make this actually stream, right now it just sends one message at the end
|
||||
// TODO add _setAborter() when add streaming
|
||||
.then(async responseArr => {
|
||||
// fetch('https://api.greptile.com/v2/query', {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// 'Authorization': `Bearer ${thisConfig.apikey}`,
|
||||
// 'X-Github-Token': `${thisConfig.githubPAT}`,
|
||||
// 'Content-Type': `application/json`,
|
||||
// },
|
||||
// body: JSON.stringify({
|
||||
// messages,
|
||||
// stream: true,
|
||||
// repositories: [thisConfig.repoinfo],
|
||||
// }),
|
||||
// })
|
||||
// // this is {message}\n{message}\n{message}...\n
|
||||
// .then(async response => {
|
||||
// const text = await response.text()
|
||||
// console.log('got greptile', text)
|
||||
// return JSON.parse(`[${text.trim().split('\n').join(',')}]`)
|
||||
// })
|
||||
// // TODO make this actually stream, right now it just sends one message at the end
|
||||
// // TODO add _setAborter() when add streaming
|
||||
// .then(async responseArr => {
|
||||
|
||||
for (const response of responseArr) {
|
||||
const type: string = response['type']
|
||||
const message = response['message']
|
||||
// for (const response of responseArr) {
|
||||
// const type: string = response['type']
|
||||
// const message = response['message']
|
||||
|
||||
// when receive text
|
||||
if (type === 'message') {
|
||||
fullText += message
|
||||
onText({ newText: message, fullText })
|
||||
}
|
||||
else if (type === 'sources') {
|
||||
const { filepath, linestart: _, lineend: _2 } = message as { filepath: string; linestart: number | null; lineend: number | null }
|
||||
fullText += filepath
|
||||
onText({ newText: filepath, fullText })
|
||||
}
|
||||
// type: 'status' with an empty 'message' means last message
|
||||
else if (type === 'status') {
|
||||
if (!message) {
|
||||
onFinalMessage({ fullText })
|
||||
}
|
||||
}
|
||||
}
|
||||
// // when receive text
|
||||
// if (type === 'message') {
|
||||
// fullText += message
|
||||
// onText({ newText: message, fullText })
|
||||
// }
|
||||
// else if (type === 'sources') {
|
||||
// const { filepath, linestart: _, lineend: _2 } = message as { filepath: string; linestart: number | null; lineend: number | null }
|
||||
// fullText += filepath
|
||||
// onText({ newText: filepath, fullText })
|
||||
// }
|
||||
// // type: 'status' with an empty 'message' means last message
|
||||
// else if (type === 'status') {
|
||||
// if (!message) {
|
||||
// onFinalMessage({ fullText })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
onError({ error })
|
||||
});
|
||||
// })
|
||||
// .catch(error => {
|
||||
// onError({ error })
|
||||
// });
|
||||
|
||||
}
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -6,18 +6,20 @@ import { parseMaxTokensStr } from './util.js';
|
|||
export const sendGroqMsg: SendLLMMessageFnTypeInternal = async ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
let fullText = '';
|
||||
|
||||
const thisConfig = voidConfig.groq
|
||||
|
||||
const groq = new Groq({
|
||||
apiKey: voidConfig.groq.apikey,
|
||||
apiKey: thisConfig.apiKey,
|
||||
dangerouslyAllowBrowser: true
|
||||
});
|
||||
|
||||
await groq.chat.completions
|
||||
.create({
|
||||
messages: messages,
|
||||
model: voidConfig.groq.model,
|
||||
model: thisConfig.model,
|
||||
stream: true,
|
||||
temperature: 0.7,
|
||||
max_tokens: parseMaxTokensStr(voidConfig.default.maxTokens),
|
||||
max_tokens: parseMaxTokensStr(thisConfig.maxTokens),
|
||||
})
|
||||
.then(async response => {
|
||||
_setAborter(() => response.controller.abort())
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText,
|
|||
model: thisConfig.model,
|
||||
messages: messages,
|
||||
stream: true,
|
||||
options: { num_predict: parseMaxTokensStr(voidConfig.default.maxTokens) } // this is max_tokens
|
||||
options: { num_predict: parseMaxTokensStr(thisConfig.maxTokens) } // this is max_tokens
|
||||
})
|
||||
.then(async stream => {
|
||||
_setAborter(() => stream.abort())
|
||||
|
|
|
|||
|
|
@ -4,39 +4,38 @@ import { parseMaxTokensStr } from './util.js';
|
|||
|
||||
|
||||
// OpenAI, OpenRouter, OpenAICompatible
|
||||
export const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
export const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName }) => {
|
||||
|
||||
let fullText = ''
|
||||
|
||||
let openai: OpenAI
|
||||
let options: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming
|
||||
|
||||
const maxTokens = parseMaxTokensStr(voidConfig.default.maxTokens)
|
||||
|
||||
if (voidConfig.default.whichApi === 'openAI') {
|
||||
if (providerName === 'openAI') {
|
||||
const thisConfig = voidConfig.openAI
|
||||
openai = new OpenAI({ apiKey: thisConfig.apikey, dangerouslyAllowBrowser: true });
|
||||
options = { model: thisConfig.model, messages: messages, stream: true, max_completion_tokens: maxTokens }
|
||||
openai = new OpenAI({ apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true });
|
||||
options = { model: thisConfig.model, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) }
|
||||
}
|
||||
else if (voidConfig.default.whichApi === 'openRouter') {
|
||||
else if (providerName === 'openRouter') {
|
||||
const thisConfig = voidConfig.openRouter
|
||||
openai = new OpenAI({
|
||||
baseURL: 'https://openrouter.ai/api/v1', apiKey: thisConfig.apikey, dangerouslyAllowBrowser: true,
|
||||
baseURL: 'https://openrouter.ai/api/v1', apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true,
|
||||
defaultHeaders: {
|
||||
'HTTP-Referer': 'https://voideditor.com', // Optional, for including your app on openrouter.ai rankings.
|
||||
'X-Title': 'Void Editor', // Optional. Shows in rankings on openrouter.ai.
|
||||
},
|
||||
});
|
||||
options = { model: thisConfig.model, messages: messages, stream: true, max_completion_tokens: maxTokens }
|
||||
options = { model: thisConfig.model, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) }
|
||||
}
|
||||
else if (voidConfig.default.whichApi === 'openAICompatible') {
|
||||
else if (providerName === 'openAICompatible') {
|
||||
const thisConfig = voidConfig.openAICompatible
|
||||
openai = new OpenAI({ baseURL: thisConfig.endpoint, apiKey: thisConfig.apikey, dangerouslyAllowBrowser: true })
|
||||
options = { model: thisConfig.model, messages: messages, stream: true, max_completion_tokens: maxTokens }
|
||||
openai = new OpenAI({ baseURL: thisConfig.endpoint, apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true })
|
||||
options = { model: thisConfig.model, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) }
|
||||
}
|
||||
else {
|
||||
console.error(`sendOpenAIMsg: invalid whichApi: ${voidConfig.default.whichApi}`)
|
||||
throw new Error(`voidConfig.whichAPI was invalid: ${voidConfig.default.whichApi}`)
|
||||
console.error(`sendOpenAIMsg: invalid providerName: ${providerName}`)
|
||||
throw new Error(`providerName was invalid: ${providerName}`)
|
||||
}
|
||||
|
||||
openai.chat.completions
|
||||
|
|
|
|||
|
|
@ -2,18 +2,21 @@ import { SendLLMMMessageParams, OnText, OnFinalMessage, OnError } from '../../co
|
|||
import { IMetricsService } from '../../common/metricsService.js';
|
||||
|
||||
import { sendAnthropicMsg } from './anthropic.js';
|
||||
import { sendGeminiMsg } from './gemini.js';
|
||||
import { sendGreptileMsg } from './greptile.js';
|
||||
import { sendGroqMsg } from './groq.js';
|
||||
import { sendOllamaMsg } from './ollama.js';
|
||||
import { sendOpenAIMsg } from './openai.js';
|
||||
import { sendGeminiMsg } from './gemini.js';
|
||||
import { sendGroqMsg } from './groq.js';
|
||||
|
||||
|
||||
export const sendLLMMessage = (
|
||||
{
|
||||
messages, onText: onText_, onFinalMessage: onFinalMessage_, onError: onError_,
|
||||
abortRef: abortRef_, voidConfig, logging: { loggingName },
|
||||
}: SendLLMMMessageParams,
|
||||
export const sendLLMMessage = ({
|
||||
messages,
|
||||
onText: onText_,
|
||||
onFinalMessage: onFinalMessage_,
|
||||
onError: onError_,
|
||||
abortRef: abortRef_,
|
||||
voidConfig,
|
||||
logging: { loggingName },
|
||||
providerName
|
||||
}: SendLLMMMessageParams,
|
||||
|
||||
metricsService: IMetricsService
|
||||
) => {
|
||||
|
|
@ -25,7 +28,7 @@ export const sendLLMMessage = (
|
|||
// only captures number of messages and message "shape", no actual code, instructions, prompts, etc
|
||||
const captureChatEvent = (eventId: string, extras?: object) => {
|
||||
metricsService.capture(eventId, {
|
||||
whichApi: voidConfig.default['whichApi'],
|
||||
providerName,
|
||||
numMessages: messages?.length,
|
||||
messagesShape: messages?.map(msg => ({ role: msg.role, length: msg.content.length })),
|
||||
version: '2024-11-14',
|
||||
|
|
@ -69,29 +72,29 @@ export const sendLLMMessage = (
|
|||
captureChatEvent(`${loggingName} - Sending Message`, { messageLength: messages[messages.length - 1]?.content.length })
|
||||
|
||||
try {
|
||||
switch (voidConfig.default.whichApi) {
|
||||
switch (providerName) {
|
||||
case 'anthropic':
|
||||
sendAnthropicMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
sendAnthropicMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName });
|
||||
break;
|
||||
case 'openAI':
|
||||
case 'openRouter':
|
||||
case 'openAICompatible':
|
||||
sendOpenAIMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
sendOpenAIMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName });
|
||||
break;
|
||||
case 'gemini':
|
||||
sendGeminiMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
sendGeminiMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName });
|
||||
break;
|
||||
case 'ollama':
|
||||
sendOllamaMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
break;
|
||||
case 'greptile':
|
||||
sendGreptileMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
sendOllamaMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName });
|
||||
break;
|
||||
// case 'greptile':
|
||||
// sendGreptileMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName });
|
||||
// break;
|
||||
case 'groq':
|
||||
sendGroqMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
sendGroqMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName });
|
||||
break;
|
||||
default:
|
||||
onError({ error: `Error: whichApi was "${voidConfig.default.whichApi}", which is not recognized!` })
|
||||
onError({ error: `Error: whichApi was "${providerName}", which is not recognized!` })
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { VoidConfig } from '../../common/configTypes'
|
||||
import { ProviderName, VoidConfigState } from '../../common/configTypes'
|
||||
import { LLMMessage, OnText, OnFinalMessage, OnError } from '../../common/llmMessageTypes'
|
||||
|
||||
export const parseMaxTokensStr = (maxTokensStr: string) => {
|
||||
|
|
@ -15,7 +15,8 @@ export type SendLLMMessageFnTypeInternal = (params: {
|
|||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: OnError;
|
||||
voidConfig: VoidConfig;
|
||||
voidConfig: VoidConfigState;
|
||||
providerName: ProviderName;
|
||||
|
||||
_setAborter: (aborter: () => void) => void;
|
||||
}) => void
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ const CodeButtonsOnHover = ({ diffRepr: text }: { diffRepr: string }) => {
|
|||
className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
|
||||
onClick={async () => {
|
||||
|
||||
inlineDiffService.startStreaming('ctrl+l', text)
|
||||
inlineDiffService.startStreaming({ type: 'ctrl+l', providerName: 'anthropic' }, text)
|
||||
}}
|
||||
>
|
||||
Apply
|
||||
|
|
@ -125,7 +125,7 @@ const RenderToken = ({ token, nested = false }: { token: Token | string, nested?
|
|||
{item.task && (
|
||||
<input type="checkbox" checked={item.checked} readOnly />
|
||||
)}
|
||||
<MarkdownRender string={item.text} nested={true} />
|
||||
<ChatMarkdownRender string={item.text} nested={true} />
|
||||
</li>
|
||||
))}
|
||||
</ListTag>
|
||||
|
|
@ -209,7 +209,7 @@ const RenderToken = ({ token, nested = false }: { token: Token | string, nested?
|
|||
)
|
||||
}
|
||||
|
||||
export const MarkdownRender = ({ string, nested = false }: { string: string, nested?: boolean }) => {
|
||||
export const ChatMarkdownRender = ({ string, nested = false }: { string: string, nested?: boolean }) => {
|
||||
const tokens = marked.lexer(string); // https://marked.js.org/using_pro#renderer
|
||||
return (
|
||||
<>
|
||||
|
|
@ -5,7 +5,9 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { mountFnGenerator } from '../util/mountFnGenerator.js'
|
||||
|
||||
import { SidebarSettings } from './SidebarSettings.js';
|
||||
// import { SidebarSettings } from './SidebarSettings.js';
|
||||
|
||||
|
||||
import { useSidebarState } from '../util/services.js';
|
||||
// import { SidebarThreadSelector } from './SidebarThreadSelector.js';
|
||||
// import { SidebarChat } from './SidebarChat.js';
|
||||
|
|
@ -13,6 +15,8 @@ import { useSidebarState } from '../util/services.js';
|
|||
import '../styles.css'
|
||||
import { SidebarThreadSelector } from './SidebarThreadSelector.js';
|
||||
import { SidebarChat } from './SidebarChat.js';
|
||||
import { SidebarModelSettings } from './SidebarModelSettings.js';
|
||||
import { SidebarProviderSettings } from './SidebarProviderSettings.js';
|
||||
|
||||
const Sidebar = () => {
|
||||
const sidebarState = useSidebarState()
|
||||
|
|
@ -37,7 +41,8 @@ const Sidebar = () => {
|
|||
</div>
|
||||
|
||||
<div className={`${tab === 'settings' ? '' : 'hidden'}`}>
|
||||
<SidebarSettings />
|
||||
{false && <SidebarModelSettings />}
|
||||
<SidebarProviderSettings />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { userInstructionsStr } from '../../../prompt/stringifyFiles.js';
|
|||
import { ChatMessage, CodeSelection, CodeStagingSelection } from '../../../registerThreads.js';
|
||||
|
||||
import { BlockCode } from '../markdown/BlockCode.js';
|
||||
import { MarkdownRender } from '../markdown/MarkdownRender.js';
|
||||
import { ChatMarkdownRender } from '../markdown/ChatMarkdownRender.js';
|
||||
import { IModelService } from '../../../../../../../editor/common/services/model.js';
|
||||
import { URI } from '../../../../../../../base/common/uri.js';
|
||||
import { EndOfLinePreference } from '../../../../../../../editor/common/model.js';
|
||||
|
|
@ -110,7 +110,7 @@ const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
|
|||
</>
|
||||
}
|
||||
else if (role === 'assistant') {
|
||||
chatbubbleContents = <MarkdownRender string={children} /> // sectionsHTML
|
||||
chatbubbleContents = <ChatMarkdownRender string={children} /> // sectionsHTML
|
||||
}
|
||||
|
||||
return <div className={`${role === 'user' ? 'text-right' : 'text-left'}`}>
|
||||
|
|
@ -141,8 +141,8 @@ export const SidebarChat = () => {
|
|||
}, [sidebarStateService, chatInputRef])
|
||||
|
||||
// config state
|
||||
const configState = useConfigState()
|
||||
const { voidConfig } = configState
|
||||
const voidConfigState = useConfigState()
|
||||
|
||||
|
||||
// threads state
|
||||
const threadsState = useThreadsState()
|
||||
|
|
@ -220,7 +220,8 @@ export const SidebarChat = () => {
|
|||
|
||||
setLatestError(error)
|
||||
},
|
||||
voidConfig,
|
||||
voidConfig: voidConfigState,
|
||||
providerName: 'anthropic',
|
||||
}
|
||||
|
||||
const latestRequestId = sendLLMMessageService.sendLLMMessage(object)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { FeatureName, featureNames, providerNames } from '../../../../../../../platform/void/common/configTypes.js'
|
||||
import { useConfigState } from '../util/services.js'
|
||||
|
||||
|
||||
|
||||
|
||||
export const SidebarModelSettingsForFeature = ({ featureName }: { featureName: FeatureName }) => {
|
||||
|
||||
const voidConfigState = useConfigState()
|
||||
|
||||
const models: [string,string][] = []
|
||||
for (const providerName of providerNames) {
|
||||
const providerConfig = voidConfigState[providerName]
|
||||
if (!providerConfig.enabled) continue
|
||||
providerConfig.models.forEach(model => {
|
||||
models.push([providerName,model])
|
||||
})
|
||||
}
|
||||
|
||||
return <>
|
||||
<h1>Settings - {featureName}</h1>
|
||||
{models.map(([providerName,model], i) => <span key={i}>{providerName} - {model}</span>)}
|
||||
</>
|
||||
}
|
||||
|
||||
export const SidebarModelSettings = () => {
|
||||
return <>
|
||||
{featureNames.map(featureName => <SidebarModelSettingsForFeature key={featureName} featureName={featureName} />)}
|
||||
|
||||
|
||||
</>
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import React, { Fragment } from 'react'
|
||||
import { descOfSettingName, ProviderName, providerNames, voidProviderDefaults } from '../../../../../../../platform/void/common/configTypes.js'
|
||||
import { VoidInputBox, VoidSelectBox } from './inputs.js'
|
||||
import { useConfigState, useService } from '../util/services.js'
|
||||
|
||||
const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) => {
|
||||
const voidConfigState = useConfigState()
|
||||
const voidConfigService = useService('configStateService')
|
||||
console.log('CONFIG', voidConfigState)
|
||||
console.log('provider:', providerName, voidConfigState[providerName])
|
||||
const { models, model, ...others } = voidConfigState[providerName]
|
||||
|
||||
return <>
|
||||
<h1>{providerName}</h1>
|
||||
|
||||
{/* other settings (e.g. api key) */}
|
||||
{Object.entries(others).map(([settingName, defaultVal], i) => {
|
||||
console.log('--- entry:', providerName, settingName, defaultVal)
|
||||
const sName = settingName as keyof typeof others
|
||||
|
||||
return <Fragment key={i}>
|
||||
<h2>{descOfSettingName(providerName, sName)}</h2>
|
||||
<VoidInputBox
|
||||
initVal={defaultVal}
|
||||
onChangeText={(newVal) => { () => { voidConfigService.setState(providerName, sName, newVal) } }}
|
||||
placeholder={settingName}
|
||||
multiline={false}
|
||||
inputBoxRef={{ current: null }}
|
||||
/>
|
||||
</Fragment>
|
||||
})}
|
||||
|
||||
<h2>{'Models'}</h2>
|
||||
<VoidSelectBox
|
||||
initVal={models[0]}
|
||||
options={models}
|
||||
onChangeSelection={(newVal) => { () => { } }}
|
||||
selectBoxRef={{ current: null }}
|
||||
/>
|
||||
|
||||
<h2>{'Enabled'}</h2>
|
||||
todo
|
||||
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
export const SidebarProviderSettings = () => {
|
||||
|
||||
return <>
|
||||
{providerNames.map(providerName =>
|
||||
<SettingsForProvider key={providerName} providerName={providerName} />
|
||||
)}
|
||||
|
||||
|
||||
</>
|
||||
}
|
||||
|
|
@ -1,150 +1,150 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useConfigState, useService } from '../util/services.js';
|
||||
// /*---------------------------------------------------------------------------------------------
|
||||
// * Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
// * Void Editor additions licensed under the AGPLv3 License.
|
||||
// *--------------------------------------------------------------------------------------------*/
|
||||
// import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
// import { useConfigState, useService } from '../util/services.js';
|
||||
|
||||
import { HistoryInputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js';
|
||||
import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js';
|
||||
import { ConfigState, IVoidConfigStateService } from '../../../registerConfig.js';
|
||||
import { nonDefaultConfigFields, VoidConfigField } from '../../../../../../../platform/void/common/configTypes.js';
|
||||
// import { HistoryInputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js';
|
||||
// import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js';
|
||||
// import { ConfigState, IVoidConfigStateService } from '../../../registerConfig.js';
|
||||
// import { nonDefaultConfigFields, VoidConfigField } from '../../../../../../../platform/void/common/configTypes.js';
|
||||
|
||||
|
||||
const SettingOfFieldAndParam = ({ field, param, configState, configStateService }:
|
||||
{ field: VoidConfigField; param: string; configState: ConfigState; configStateService: IVoidConfigStateService }) => {
|
||||
// const SettingOfFieldAndParam = ({ field, param, configState, configStateService }:
|
||||
// { field: VoidConfigField; param: string; configState: ConfigState; configStateService: IVoidConfigStateService }) => {
|
||||
|
||||
const { partialVoidConfig } = configState
|
||||
// const { partialVoidConfig } = configState
|
||||
|
||||
|
||||
const { enumArr, defaultVal, description } = configStateService.voidConfigInfo[field][param]
|
||||
const val = partialVoidConfig[field]?.[param] ?? defaultVal // current value of this item
|
||||
const initValRef = useRef(val)
|
||||
// const { enumArr, defaultVal, description } = configStateService.voidConfigInfo[field][param]
|
||||
// const val = partialVoidConfig[field]?.[param] ?? defaultVal // current value of this item
|
||||
// const initValRef = useRef(val)
|
||||
|
||||
const updateState = useCallback((newValue: string) => {
|
||||
configStateService.setField(field, param, newValue)
|
||||
}, [configStateService, field, param])
|
||||
// const updateState = useCallback((newValue: string) => {
|
||||
// configStateService.setField(field, param, newValue)
|
||||
// }, [configStateService, field, param])
|
||||
|
||||
|
||||
const inputBoxRef = useRef<HistoryInputBox | null>(null);
|
||||
const selectBoxRef = useRef<SelectBox | null>(null);
|
||||
const forceState = useCallback((newValue: string) => {
|
||||
if (inputBoxRef.current) {
|
||||
inputBoxRef.current.value = newValue;
|
||||
}
|
||||
if (selectBoxRef.current) {
|
||||
selectBoxRef.current.select(enumArr?.indexOf(newValue) ?? 0);
|
||||
}
|
||||
// updateState is called automatically when the change happens
|
||||
}, [enumArr, updateState])
|
||||
// const inputBoxRef = useRef<HistoryInputBox | null>(null);
|
||||
// const selectBoxRef = useRef<SelectBox | null>(null);
|
||||
// const forceState = useCallback((newValue: string) => {
|
||||
// if (inputBoxRef.current) {
|
||||
// inputBoxRef.current.value = newValue;
|
||||
// }
|
||||
// if (selectBoxRef.current) {
|
||||
// selectBoxRef.current.select(enumArr?.indexOf(newValue) ?? 0);
|
||||
// }
|
||||
// // updateState is called automatically when the change happens
|
||||
// }, [enumArr, updateState])
|
||||
|
||||
|
||||
const resetButton = <button
|
||||
disabled={val === defaultVal}
|
||||
title={val === defaultVal ? 'This is the default value.' : `Revert value to '${defaultVal}'?`}
|
||||
className='group btn btn-sm disabled:opacity-75 disabled:cursor-default'
|
||||
onClick={() => forceState(defaultVal)}
|
||||
>
|
||||
<svg
|
||||
className='size-5 group-disabled:stroke-current group-disabled:fill-current group-hover:stroke-red-600 group-hover:fill-red-600 duration-200'
|
||||
fill='currentColor' strokeWidth='0' viewBox='0 0 16 16' height='200px' width='200px' xmlns='http://www.w3.org/2000/svg'><path fillRule='evenodd' clipRule='evenodd' d='M3.5 2v3.5L4 6h3.5V5H4.979l.941-.941a3.552 3.552 0 1 1 5.023 5.023L5.746 14.28l.72.72 5.198-5.198A4.57 4.57 0 0 0 5.2 3.339l-.7.7V2h-1z'></path>
|
||||
</svg>
|
||||
</button>
|
||||
// const resetButton = <button
|
||||
// disabled={val === defaultVal}
|
||||
// title={val === defaultVal ? 'This is the default value.' : `Revert value to '${defaultVal}'?`}
|
||||
// className='group btn btn-sm disabled:opacity-75 disabled:cursor-default'
|
||||
// onClick={() => forceState(defaultVal)}
|
||||
// >
|
||||
// <svg
|
||||
// className='size-5 group-disabled:stroke-current group-disabled:fill-current group-hover:stroke-red-600 group-hover:fill-red-600 duration-200'
|
||||
// fill='currentColor' strokeWidth='0' viewBox='0 0 16 16' height='200px' width='200px' xmlns='http://www.w3.org/2000/svg'><path fillRule='evenodd' clipRule='evenodd' d='M3.5 2v3.5L4 6h3.5V5H4.979l.941-.941a3.552 3.552 0 1 1 5.023 5.023L5.746 14.28l.72.72 5.198-5.198A4.57 4.57 0 0 0 5.2 3.339l-.7.7V2h-1z'></path>
|
||||
// </svg>
|
||||
// </button>
|
||||
|
||||
|
||||
const inputElement = enumArr === undefined ?
|
||||
// string
|
||||
// (<VoidInputBox
|
||||
// onChangeText={updateState}
|
||||
// initVal={initValRef.current}
|
||||
// multiline={false}
|
||||
// placeholder=''
|
||||
// inputBoxRef={inputBoxRef}
|
||||
// />)
|
||||
<input
|
||||
className='input p-1 w-full'
|
||||
type='text'
|
||||
value={val}
|
||||
onChange={(e) => updateState(e.target.value)}
|
||||
/>
|
||||
:
|
||||
// enum
|
||||
// (<VoidSelectBox
|
||||
// onChangeSelection={updateState}
|
||||
// initVal={initValRef.current}
|
||||
// options={enumArr}
|
||||
// selectBoxRef={selectBoxRef}
|
||||
// />)
|
||||
(<select
|
||||
className='dropdown p-1 w-full'
|
||||
value={val}
|
||||
onChange={(e) => updateState(e.target.value)}
|
||||
>
|
||||
{enumArr.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>)
|
||||
// const inputElement = enumArr === undefined ?
|
||||
// // string
|
||||
// // (<VoidInputBox
|
||||
// // onChangeText={updateState}
|
||||
// // initVal={initValRef.current}
|
||||
// // multiline={false}
|
||||
// // placeholder=''
|
||||
// // inputBoxRef={inputBoxRef}
|
||||
// // />)
|
||||
// <input
|
||||
// className='input p-1 w-full'
|
||||
// type='text'
|
||||
// value={val}
|
||||
// onChange={(e) => updateState(e.target.value)}
|
||||
// />
|
||||
// :
|
||||
// // enum
|
||||
// // (<VoidSelectBox
|
||||
// // onChangeSelection={updateState}
|
||||
// // initVal={initValRef.current}
|
||||
// // options={enumArr}
|
||||
// // selectBoxRef={selectBoxRef}
|
||||
// // />)
|
||||
// (<select
|
||||
// className='dropdown p-1 w-full'
|
||||
// value={val}
|
||||
// onChange={(e) => updateState(e.target.value)}
|
||||
// >
|
||||
// {enumArr.map((option) => (
|
||||
// <option key={option} value={option}>
|
||||
// {option}
|
||||
// </option>
|
||||
// ))}
|
||||
// </select>)
|
||||
|
||||
return <div>
|
||||
<label className='hidden'>{param}</label>
|
||||
<span>{description}</span>
|
||||
<div className='flex items-center'>
|
||||
{inputElement}
|
||||
{resetButton}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
// return <div>
|
||||
// <label className='hidden'>{param}</label>
|
||||
// <span>{description}</span>
|
||||
// <div className='flex items-center'>
|
||||
// {inputElement}
|
||||
// {resetButton}
|
||||
// </div>
|
||||
// </div>
|
||||
// }
|
||||
|
||||
|
||||
export const SidebarSettings = () => {
|
||||
// export const SidebarSettings = () => {
|
||||
|
||||
const configState = useConfigState()
|
||||
const configStateService = useService('configStateService')
|
||||
// const configState = useConfigState()
|
||||
// const configStateService = useService('configStateService')
|
||||
|
||||
const { voidConfig } = configState
|
||||
const current_field = voidConfig.default['whichApi'] as VoidConfigField
|
||||
// const { voidConfig } = configState
|
||||
// const current_field = voidConfig.default['whichApi'] as VoidConfigField
|
||||
|
||||
return (
|
||||
<div className='space-y-4 py-2 overflow-y-auto'>
|
||||
// return (
|
||||
// <div className='space-y-4 py-2 overflow-y-auto'>
|
||||
|
||||
{/* choose the field */}
|
||||
<div className='outline-vscode-input-bg'>
|
||||
<SettingOfFieldAndParam
|
||||
configState={configState}
|
||||
configStateService={configStateService}
|
||||
field='default'
|
||||
param='whichApi'
|
||||
/>
|
||||
<SettingOfFieldAndParam
|
||||
configState={configState}
|
||||
configStateService={configStateService}
|
||||
field='default'
|
||||
param='maxTokens'
|
||||
/>
|
||||
</div>
|
||||
// {/* choose the field */}
|
||||
// <div className='outline-vscode-input-bg'>
|
||||
// <SettingOfFieldAndParam
|
||||
// configState={configState}
|
||||
// configStateService={configStateService}
|
||||
// field='default'
|
||||
// param='whichApi'
|
||||
// />
|
||||
// <SettingOfFieldAndParam
|
||||
// configState={configState}
|
||||
// configStateService={configStateService}
|
||||
// field='default'
|
||||
// param='maxTokens'
|
||||
// />
|
||||
// </div>
|
||||
|
||||
<hr />
|
||||
// <hr />
|
||||
|
||||
{/* render all fields, but hide the ones not visible for fast tab switching */}
|
||||
{nonDefaultConfigFields.map(field => {
|
||||
return <div
|
||||
key={field}
|
||||
className={`flex flex-col gap-y-2 ${field !== current_field ? 'hidden' : ''}`}
|
||||
>
|
||||
{Object.keys(configStateService.voidConfigInfo[field]).map((param) => (
|
||||
<SettingOfFieldAndParam
|
||||
key={param}
|
||||
configState={configState}
|
||||
configStateService={configStateService}
|
||||
field={field}
|
||||
param={param}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
// {/* render all fields, but hide the ones not visible for fast tab switching */}
|
||||
// {nonDefaultConfigFields.map(field => {
|
||||
// return <div
|
||||
// key={field}
|
||||
// className={`flex flex-col gap-y-2 ${field !== current_field ? 'hidden' : ''}`}
|
||||
// >
|
||||
// {Object.keys(configStateService.voidConfigInfo[field]).map((param) => (
|
||||
// <SettingOfFieldAndParam
|
||||
// key={param}
|
||||
// configState={configState}
|
||||
// configStateService={configStateService}
|
||||
// field={field}
|
||||
// param={param}
|
||||
// />
|
||||
// ))}
|
||||
// </div>
|
||||
// })}
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
|
||||
|
|
|
|||
|
|
@ -61,44 +61,44 @@ export const VoidInputBox = ({ onChangeText, initVal, placeholder, inputBoxRef,
|
|||
|
||||
|
||||
|
||||
// export const VoidSelectBox = ({ onChangeSelection, initVal, selectBoxRef, options }: {
|
||||
// onChangeSelection: (value: string) => void;
|
||||
// initVal: string;
|
||||
// selectBoxRef: React.MutableRefObject<SelectBox | null>;
|
||||
// options: readonly string[];
|
||||
export const VoidSelectBox = ({ onChangeSelection, initVal, selectBoxRef, options }: {
|
||||
onChangeSelection: (value: string) => void;
|
||||
initVal: string;
|
||||
selectBoxRef: React.MutableRefObject<SelectBox | null>;
|
||||
options: readonly string[];
|
||||
|
||||
// }) => {
|
||||
// const containerRef = useRef<HTMLDivElement>(null);
|
||||
// const contextViewProvider = useService('contextViewService');
|
||||
}) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const contextViewProvider = useService('contextViewService');
|
||||
|
||||
// useEffect(() => {
|
||||
// if (!containerRef.current) return;
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
// const defaultIndex = options.indexOf(initVal);
|
||||
const defaultIndex = options.indexOf(initVal);
|
||||
|
||||
// selectBoxRef.current = new SelectBox(
|
||||
// options.map(opt => ({ text: opt })),
|
||||
// defaultIndex,
|
||||
// contextViewProvider,
|
||||
// unthemedSelectBoxStyles
|
||||
// );
|
||||
selectBoxRef.current = new SelectBox(
|
||||
options.map(opt => ({ text: opt })),
|
||||
defaultIndex,
|
||||
contextViewProvider,
|
||||
unthemedSelectBoxStyles
|
||||
);
|
||||
|
||||
// selectBoxRef.current.render(containerRef.current);
|
||||
selectBoxRef.current.render(containerRef.current);
|
||||
|
||||
// selectBoxRef.current.onDidSelect(e => { onChangeSelection(e.selected); });
|
||||
selectBoxRef.current.onDidSelect(e => { onChangeSelection(e.selected); });
|
||||
|
||||
// // cleanup
|
||||
// return () => {
|
||||
// if (selectBoxRef.current) {
|
||||
// selectBoxRef.current.dispose();
|
||||
// if (containerRef.current) {
|
||||
// while (containerRef.current.firstChild) {
|
||||
// containerRef.current.removeChild(containerRef.current.firstChild);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// }, [options, initVal, onChangeSelection, contextViewProvider, selectBoxRef]);
|
||||
// cleanup
|
||||
return () => {
|
||||
if (selectBoxRef.current) {
|
||||
selectBoxRef.current.dispose();
|
||||
if (containerRef.current) {
|
||||
while (containerRef.current.firstChild) {
|
||||
containerRef.current.removeChild(containerRef.current.firstChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}, [options, initVal, onChangeSelection, contextViewProvider, selectBoxRef]);
|
||||
|
||||
// return <div ref={containerRef} className="w-full" />;
|
||||
// };
|
||||
return <div ref={containerRef} className="w-full" />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useState, useEffect } from 'react'
|
||||
import { ConfigState } from '../../../registerConfig.js'
|
||||
import { VoidSidebarState, ReactServicesType } from '../../../registerSidebar.js'
|
||||
import { ThreadsState } from '../../../registerThreads.js'
|
||||
import { VoidConfigState } from '../../../../../../../platform/void/common/configTypes.js'
|
||||
|
||||
|
||||
// normally to do this you'd use a useEffect that calls .onDidChangeState(), but useEffect mounts too late and misses initial state changes
|
||||
|
|
@ -10,12 +10,12 @@ let services: ReactServicesType
|
|||
|
||||
// even if React hasn't mounted yet, these variables are always updated to the latest state:
|
||||
let sidebarState: VoidSidebarState
|
||||
let configState: ConfigState
|
||||
let configState: VoidConfigState
|
||||
let threadsState: ThreadsState
|
||||
|
||||
// React listens by adding a setState function to these:
|
||||
const sidebarStateListeners: Set<(s: VoidSidebarState) => void> = new Set()
|
||||
const configStateListeners: Set<(s: ConfigState) => void> = new Set()
|
||||
const configStateListeners: Set<(s: VoidConfigState) => void> = new Set()
|
||||
const threadsStateListeners: Set<(s: ThreadsState) => void> = new Set()
|
||||
|
||||
// must call this before you can use any of the hooks below
|
||||
|
|
|
|||
|
|
@ -5,40 +5,29 @@
|
|||
|
||||
import { Emitter, Event } from '../../../../base/common/event.js';
|
||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
import { deepClone } from '../../../../base/common/objects.js';
|
||||
import { IEncryptionService } from '../../../../platform/encryption/common/encryptionService.js';
|
||||
import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js';
|
||||
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
|
||||
import { PartialVoidConfig, VoidConfig, nonDefaultConfigFields, voidConfigInfo, VoidConfigField, VoidConfigInfo } from '../../../../platform/void/common/configTypes.js';
|
||||
import { defaultVoidConfigState, ProviderName, VoidConfigState } from '../../../../platform/void/common/configTypes.js';
|
||||
|
||||
|
||||
const getVoidConfig = (partialVoidConfig: PartialVoidConfig): VoidConfig => {
|
||||
const config = {} as PartialVoidConfig
|
||||
for (const field of [...nonDefaultConfigFields, 'default'] as const) {
|
||||
config[field] = {}
|
||||
for (const prop in voidConfigInfo[field]) {
|
||||
config[field][prop] = partialVoidConfig[field]?.[prop]?.trim() || voidConfigInfo[field][prop].defaultVal
|
||||
}
|
||||
}
|
||||
return config as VoidConfig
|
||||
}
|
||||
|
||||
|
||||
const VOID_CONFIG_KEY = 'void.partialVoidConfig'
|
||||
|
||||
export type SetFieldFnType = <K extends VoidConfigField>(field: K, param: keyof VoidConfigInfo[K], newVal: string) => Promise<void>;
|
||||
type SetStateFn = <K extends ProviderName>(
|
||||
providerName: K,
|
||||
option: keyof VoidConfigState[K],
|
||||
newVal: string
|
||||
) => Promise<void>;
|
||||
|
||||
export type ConfigState = {
|
||||
partialVoidConfig: PartialVoidConfig; // free parameter
|
||||
voidConfig: VoidConfig; // computed from partialVoidConfig
|
||||
}
|
||||
|
||||
export interface IVoidConfigStateService {
|
||||
readonly _serviceBrand: undefined;
|
||||
readonly state: ConfigState;
|
||||
readonly voidConfigInfo: VoidConfigInfo;
|
||||
readonly state: VoidConfigState;
|
||||
onDidChangeState: Event<void>;
|
||||
setField: SetFieldFnType;
|
||||
setState: SetStateFn;
|
||||
}
|
||||
|
||||
export const IVoidConfigStateService = createDecorator<IVoidConfigStateService>('VoidConfigStateService');
|
||||
|
|
@ -48,8 +37,9 @@ class VoidConfigStateService extends Disposable implements IVoidConfigStateServi
|
|||
private readonly _onDidChangeState = new Emitter<void>();
|
||||
readonly onDidChangeState: Event<void> = this._onDidChangeState.event; // this is primarily for use in react, so react can listen + update on state changes
|
||||
|
||||
state: ConfigState;
|
||||
readonly voidConfigInfo: VoidConfigInfo = voidConfigInfo; // just putting this here for simplicity, it's static though
|
||||
state: VoidConfigState;
|
||||
|
||||
// readonly voidConfigInfo: VoidConfigInfo = voidConfigInfo; // just putting this here for simplicity, it's static though
|
||||
|
||||
constructor(
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
|
|
@ -60,56 +50,48 @@ class VoidConfigStateService extends Disposable implements IVoidConfigStateServi
|
|||
super()
|
||||
|
||||
// at the start, we haven't read the partial config yet, but we need to set state to something, just treat partialVoidConfig like it's empty
|
||||
this.state = {
|
||||
partialVoidConfig: {},
|
||||
voidConfig: getVoidConfig({}),
|
||||
}
|
||||
this.state = deepClone(defaultVoidConfigState)
|
||||
|
||||
// read and update the actual state immediately
|
||||
this._readPartialVoidConfig().then(partialVoidConfig => {
|
||||
this._setState(partialVoidConfig)
|
||||
this._readVoidConfigState().then(voidConfigState => {
|
||||
this._setState(voidConfigState)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
private async _readPartialVoidConfig(): Promise<PartialVoidConfig> {
|
||||
private async _readVoidConfigState(): Promise<VoidConfigState> {
|
||||
const encryptedPartialConfig = this._storageService.get(VOID_CONFIG_KEY, StorageScope.APPLICATION)
|
||||
|
||||
if (!encryptedPartialConfig)
|
||||
return {}
|
||||
return deepClone(defaultVoidConfigState)
|
||||
|
||||
const partialVoidConfigStr = await this._encryptionService.decrypt(encryptedPartialConfig)
|
||||
return JSON.parse(partialVoidConfigStr)
|
||||
const voidConfigStateStr = await this._encryptionService.decrypt(encryptedPartialConfig)
|
||||
return JSON.parse(voidConfigStateStr)
|
||||
}
|
||||
|
||||
|
||||
private async _storePartialVoidConfig(partialVoidConfig: PartialVoidConfig) {
|
||||
const encryptedPartialConfigStr = await this._encryptionService.encrypt(JSON.stringify(partialVoidConfig))
|
||||
this._storageService.store(VOID_CONFIG_KEY, encryptedPartialConfigStr, StorageScope.APPLICATION, StorageTarget.USER)
|
||||
private async _storeVoidConfigState(voidConfigState: VoidConfigState) {
|
||||
const encryptedVoidConfigStr = await this._encryptionService.encrypt(JSON.stringify(voidConfigState))
|
||||
this._storageService.store(VOID_CONFIG_KEY, encryptedVoidConfigStr, StorageScope.APPLICATION, StorageTarget.USER)
|
||||
}
|
||||
|
||||
|
||||
// Set field on PartialVoidConfig
|
||||
setField: SetFieldFnType = async <K extends VoidConfigField>(field: K, param: keyof VoidConfigInfo[K], newVal: string) => {
|
||||
const { partialVoidConfig } = this.state
|
||||
|
||||
const newPartialConfig: PartialVoidConfig = {
|
||||
...partialVoidConfig,
|
||||
[field]: {
|
||||
...partialVoidConfig[field],
|
||||
[param]: newVal
|
||||
setState: SetStateFn = async (providerName, option, newVal) => {
|
||||
const newState: VoidConfigState = {
|
||||
...this.state,
|
||||
[providerName]: {
|
||||
...this.state[providerName],
|
||||
[option]: newVal,
|
||||
}
|
||||
}
|
||||
await this._storePartialVoidConfig(newPartialConfig)
|
||||
this._setState(newPartialConfig)
|
||||
await this._storeVoidConfigState(newState)
|
||||
this._setState(newState)
|
||||
}
|
||||
|
||||
// internal function to update state, should be called every time state changes
|
||||
private async _setState(partialVoidConfig: PartialVoidConfig) {
|
||||
this.state = {
|
||||
partialVoidConfig: partialVoidConfig,
|
||||
voidConfig: getVoidConfig(partialVoidConfig),
|
||||
}
|
||||
private async _setState(voidConfigState: VoidConfigState) {
|
||||
this.state = voidConfigState
|
||||
this._onDidChangeState.fire()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import { Widget } from '../../../../base/browser/ui/widget.js';
|
|||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { LLMMessageServiceParams } from '../../../../platform/void/common/llmMessageTypes.js';
|
||||
import { ISendLLMMessageService } from '../../../../platform/void/browser/llmMessageService.js';
|
||||
import { ProviderName } from '../../../../platform/void/common/configTypes.js';
|
||||
|
||||
|
||||
// gets converted to --vscode-void-greenBG, see void.css
|
||||
|
|
@ -110,10 +111,18 @@ type HistorySnapshot = {
|
|||
})
|
||||
|
||||
|
||||
type StartStreamingOptions = {
|
||||
type: 'ctrl+k',
|
||||
providerName: ProviderName,
|
||||
range: IRange
|
||||
} | {
|
||||
type: 'ctrl+l',
|
||||
providerName: ProviderName
|
||||
}
|
||||
|
||||
export interface IInlineDiffsService {
|
||||
readonly _serviceBrand: undefined;
|
||||
startStreaming(type: 'ctrl+k' | 'ctrl+l', userMessage: string): void;
|
||||
|
||||
startStreaming(params: StartStreamingOptions, str: string): void;
|
||||
}
|
||||
|
||||
export const IInlineDiffsService = createDecorator<IInlineDiffsService>('inlineDiffAreasService');
|
||||
|
|
@ -637,7 +646,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
|
||||
|
||||
|
||||
private async _initializeStream(uri: URI, diffRepr: string) {
|
||||
private async _initializeStream(uri: URI, diffRepr: string, providerName: ProviderName) {
|
||||
|
||||
// diff area begin and end line
|
||||
const numLines = this._getNumLines(uri)
|
||||
|
|
@ -689,7 +698,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
this.diffAreaOfId[diffArea.diffareaid] = diffArea
|
||||
|
||||
// actually call the LLM
|
||||
const { voidConfig } = this._voidConfigStateService.state
|
||||
const voidConfigState = this._voidConfigStateService.state
|
||||
const promptContent = `\
|
||||
ORIGINAL_CODE
|
||||
\`\`\`
|
||||
|
|
@ -761,7 +770,8 @@ Please finish writing the new file by applying the diff to the original file. Re
|
|||
diffArea._sweepState = { isStreaming: false, line: null }
|
||||
resolve();
|
||||
},
|
||||
voidConfig,
|
||||
voidConfig: voidConfigState,
|
||||
providerName,
|
||||
}
|
||||
|
||||
streamRequestId = this._sendLLMMessageService.sendLLMMessage(object)
|
||||
|
|
@ -776,7 +786,7 @@ Please finish writing the new file by applying the diff to the original file. Re
|
|||
|
||||
|
||||
|
||||
async startStreaming(type: 'ctrl+k' | 'ctrl+l', userMessage: string) {
|
||||
async startStreaming(params: StartStreamingOptions, userMessage: string) {
|
||||
|
||||
const editor = this._editorService.getActiveCodeEditor()
|
||||
if (!editor) return
|
||||
|
|
@ -788,7 +798,7 @@ Please finish writing the new file by applying the diff to the original file. Re
|
|||
|
||||
// TODO deselect user's cursor
|
||||
|
||||
this._initializeStream(uri, userMessage)
|
||||
this._initializeStream(uri, userMessage, params.providerName)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -53,8 +53,7 @@ import { IClipboardService } from '../../../../platform/clipboard/common/clipboa
|
|||
// import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
|
||||
|
||||
|
||||
// compare against search.contribution.ts and https://app.greptile.com/chat/w1nsmt3lauwzculipycpn?repo=github%3Amain%3Amicrosoft%2Fvscode
|
||||
// and debug.contribution.ts, scm.contribution.ts (source control)
|
||||
// compare against search.contribution.ts and debug.contribution.ts, scm.contribution.ts (source control)
|
||||
|
||||
export type VoidSidebarState = {
|
||||
isHistoryOpen: boolean;
|
||||
|
|
@ -91,10 +90,6 @@ class VoidSidebarViewPane extends ViewPane {
|
|||
@IOpenerService openerService: IOpenerService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IHoverService hoverService: IHoverService,
|
||||
// Void:
|
||||
// @IVoidSidebarStateService private readonly _voidSidebarStateService: IVoidSidebarStateService,
|
||||
// @IThreadHistoryService private readonly _threadHistoryService: IThreadHistoryService,
|
||||
// TODO chat service
|
||||
) {
|
||||
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue