[fix review issue]: update setting name
This commit is contained in:
parent
173f2b7fa5
commit
dc520535fc
@ -652,7 +652,7 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
|||||||
{submitMutation.isPending && (
|
{submitMutation.isPending && (
|
||||||
<button onClick={abortActiveStreams} className="infio-stop-gen-btn">
|
<button onClick={abortActiveStreams} className="infio-stop-gen-btn">
|
||||||
<CircleStop size={16} />
|
<CircleStop size={16} />
|
||||||
<div>Stop Generation</div>
|
<div>Stop generation</div>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -30,9 +30,9 @@ export default function LLMResponseInfoPopover({
|
|||||||
</Popover.Trigger>
|
</Popover.Trigger>
|
||||||
{usage ? (
|
{usage ? (
|
||||||
<Popover.Content className="infio-chat-popover-content infio-llm-info-content">
|
<Popover.Content className="infio-chat-popover-content infio-llm-info-content">
|
||||||
<div className="infio-llm-info-header">LLM Response Information</div>
|
<div className="infio-llm-info-header">LLM response information</div>
|
||||||
<div className="infio-llm-info-tokens">
|
<div className="infio-llm-info-tokens">
|
||||||
<div className="infio-llm-info-tokens-header">Token Count</div>
|
<div className="infio-llm-info-tokens-header">Token count</div>
|
||||||
<div className="infio-llm-info-tokens-grid">
|
<div className="infio-llm-info-tokens-grid">
|
||||||
<div className="infio-llm-info-token-row">
|
<div className="infio-llm-info-token-row">
|
||||||
<ArrowUp className="infio-llm-info-icon--input" />
|
<ArrowUp className="infio-llm-info-icon--input" />
|
||||||
@ -59,7 +59,7 @@ export default function LLMResponseInfoPopover({
|
|||||||
</div>
|
</div>
|
||||||
<div className="infio-llm-info-footer-row">
|
<div className="infio-llm-info-footer-row">
|
||||||
<Coins className="infio-llm-info-icon--footer" />
|
<Coins className="infio-llm-info-icon--footer" />
|
||||||
<span>Estimated Price:</span>
|
<span>Estimated price:</span>
|
||||||
<span className="infio-llm-info-footer-value">
|
<span className="infio-llm-info-footer-value">
|
||||||
{estimatedPrice === null
|
{estimatedPrice === null
|
||||||
? 'Not available'
|
? 'Not available'
|
||||||
|
|||||||
@ -150,7 +150,7 @@ function CurrentFileBadge({
|
|||||||
<span>{mentionable.file.name}</span>
|
<span>{mentionable.file.name}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="infio-chat-user-input-file-badge-name-block-suffix">
|
<div className="infio-chat-user-input-file-badge-name-block-suffix">
|
||||||
{' (Current File)'}
|
{' (Current file)'}
|
||||||
</div>
|
</div>
|
||||||
</BadgeBase>
|
</BadgeBase>
|
||||||
) : null
|
) : null
|
||||||
|
|||||||
14
src/main.ts
14
src/main.ts
@ -208,7 +208,7 @@ export default class InfioPlugin extends Plugin {
|
|||||||
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'autocomplete-accept',
|
id: 'autocomplete-accept',
|
||||||
name: 'Autocomplete Accept',
|
name: 'Autocomplete accept',
|
||||||
editorCheckCallback: (
|
editorCheckCallback: (
|
||||||
checking: boolean,
|
checking: boolean,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
@ -228,7 +228,7 @@ export default class InfioPlugin extends Plugin {
|
|||||||
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'autocomplete-predict',
|
id: 'autocomplete-predict',
|
||||||
name: 'Autocomplete Predict',
|
name: 'Autocomplete predict',
|
||||||
editorCheckCallback: (
|
editorCheckCallback: (
|
||||||
checking: boolean,
|
checking: boolean,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
@ -251,7 +251,7 @@ export default class InfioPlugin extends Plugin {
|
|||||||
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "autocomplete-toggle",
|
id: "autocomplete-toggle",
|
||||||
name: "Autocomplete Toggle",
|
name: "Autocomplete toggle",
|
||||||
callback: () => {
|
callback: () => {
|
||||||
const newValue = !this.settings.autocompleteEnabled;
|
const newValue = !this.settings.autocompleteEnabled;
|
||||||
this.setSettings({
|
this.setSettings({
|
||||||
@ -262,8 +262,8 @@ export default class InfioPlugin extends Plugin {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "infio-autocomplete-enable",
|
id: "autocomplete-enable",
|
||||||
name: "Infio Autocomplete Enable",
|
name: "Autocomplete enable",
|
||||||
checkCallback: (checking) => {
|
checkCallback: (checking) => {
|
||||||
if (checking) {
|
if (checking) {
|
||||||
return !this.settings.autocompleteEnabled;
|
return !this.settings.autocompleteEnabled;
|
||||||
@ -279,7 +279,7 @@ export default class InfioPlugin extends Plugin {
|
|||||||
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "autocomplete-disable",
|
id: "autocomplete-disable",
|
||||||
name: "Autocomplete Disable",
|
name: "Autocomplete disable",
|
||||||
checkCallback: (checking) => {
|
checkCallback: (checking) => {
|
||||||
if (checking) {
|
if (checking) {
|
||||||
return this.settings.autocompleteEnabled;
|
return this.settings.autocompleteEnabled;
|
||||||
@ -295,7 +295,7 @@ export default class InfioPlugin extends Plugin {
|
|||||||
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "ai-inline-edit",
|
id: "ai-inline-edit",
|
||||||
name: "Inline Edit",
|
name: "Inline edit",
|
||||||
// hotkeys: [
|
// hotkeys: [
|
||||||
// {
|
// {
|
||||||
// modifiers: ['Mod', 'Shift'],
|
// modifiers: ['Mod', 'Shift'],
|
||||||
|
|||||||
@ -1,610 +0,0 @@
|
|||||||
import { Notice } from "obsidian";
|
|
||||||
import * as React from "react";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
import {
|
|
||||||
InfioSettings,
|
|
||||||
} from '../types/settings';
|
|
||||||
import { checkForErrors } from "../utils/auto-complete";
|
|
||||||
|
|
||||||
import CheckBoxSettingItem from "./components/CheckBoxSettingItem";
|
|
||||||
import FewShotExampleSettings from "./components/FewShotExampleSettings";
|
|
||||||
import SettingsItem from "./components/SettingsItem";
|
|
||||||
import SliderSettingsItem from "./components/SliderSettingsItem";
|
|
||||||
import TextSettingItem from "./components/TextSettingItem";
|
|
||||||
import TriggerSettings from "./components/TriggerSettings";
|
|
||||||
import {
|
|
||||||
MAX_DELAY,
|
|
||||||
MAX_FREQUENCY_PENALTY,
|
|
||||||
MAX_MAX_CHAR_LIMIT,
|
|
||||||
MAX_MAX_TOKENS,
|
|
||||||
MAX_PRESENCE_PENALTY,
|
|
||||||
MAX_TEMPERATURE,
|
|
||||||
MAX_TOP_P,
|
|
||||||
MIN_DELAY,
|
|
||||||
MIN_FREQUENCY_PENALTY,
|
|
||||||
MIN_MAX_CHAR_LIMIT,
|
|
||||||
MIN_MAX_TOKENS,
|
|
||||||
MIN_PRESENCE_PENALTY,
|
|
||||||
MIN_TEMPERATURE,
|
|
||||||
MIN_TOP_P
|
|
||||||
} from "./versions";
|
|
||||||
|
|
||||||
type IProps = {
|
|
||||||
onSettingsChanged(settings: InfioSettings): void;
|
|
||||||
|
|
||||||
settings: InfioSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function AutoCompleteSettings(props: IProps): React.JSX.Element {
|
|
||||||
const [settings, _setSettings] = useState<InfioSettings>(props.settings);
|
|
||||||
const errors = checkForErrors(settings);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
_setSettings(props.settings);
|
|
||||||
}, [props.settings]);
|
|
||||||
|
|
||||||
const updateSettings = (update: Partial<InfioSettings>) => {
|
|
||||||
_setSettings((settings: InfioSettings) => {
|
|
||||||
const newSettings = { ...settings, ...update };
|
|
||||||
props.onSettingsChanged(newSettings);
|
|
||||||
return newSettings;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const resetSettings = () => {
|
|
||||||
// const azureOAIApiSettings = settings.azureOAIApiSettings,
|
|
||||||
// const openAIApiSettings = settings.openAIApiSettings,
|
|
||||||
// const ollamaApiSettings = settings.ollamaApiSettings
|
|
||||||
|
|
||||||
// const newSettings: SmartCopilotSettings = {
|
|
||||||
// ...settings
|
|
||||||
// azureOAIApiSettings,
|
|
||||||
// openAIApiSettings,
|
|
||||||
// ollamaApiSettings,
|
|
||||||
// advancedMode: settings.advancedMode,
|
|
||||||
// };
|
|
||||||
// updateSettings(newSettings);
|
|
||||||
new Notice("Factory reset complete.");
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// const renderAPISettings = () => {
|
|
||||||
// if (settings.apiProvider === "azure") {
|
|
||||||
// return (
|
|
||||||
// <>
|
|
||||||
// <TextSettingItem
|
|
||||||
// name={"Azure OAI API URL"}
|
|
||||||
// description={
|
|
||||||
// "The Azure OpenAI services API URL is used in the requests."
|
|
||||||
// }
|
|
||||||
// placeholder={"Your API URL..."}
|
|
||||||
// value={settings.azureOAIApiSettings.url}
|
|
||||||
// errorMessage={errors.get("azureOAIApiSettings.url")}
|
|
||||||
// setValue={(value: string) =>
|
|
||||||
// updateSettings({
|
|
||||||
// azureOAIApiSettings: {
|
|
||||||
// ...settings.azureOAIApiSettings,
|
|
||||||
// url: value,
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// />
|
|
||||||
// <TextSettingItem
|
|
||||||
// name={"Azure API key"}
|
|
||||||
// description={
|
|
||||||
// "The Azure OpenAI services API key used in the requests."
|
|
||||||
// }
|
|
||||||
// placeholder={"Your API key..."}
|
|
||||||
// password
|
|
||||||
// value={settings.azureOAIApiSettings.key}
|
|
||||||
// errorMessage={errors.get("azureOAIApiSettings.key")}
|
|
||||||
// setValue={(value: string) =>
|
|
||||||
// updateSettings({
|
|
||||||
// azureOAIApiSettings: {
|
|
||||||
// ...settings.azureOAIApiSettings,
|
|
||||||
// key: value,
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// />
|
|
||||||
// <ConnectivityCheck key={"azure"} settings={settings} />
|
|
||||||
// </>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// if (settings.apiProvider === "openai") {
|
|
||||||
// return (
|
|
||||||
// <>
|
|
||||||
// <TextSettingItem
|
|
||||||
// name={"OpenAI API URL"}
|
|
||||||
// description={
|
|
||||||
// "The URL used in the requests."
|
|
||||||
// }
|
|
||||||
// placeholder={"Your API URL..."}
|
|
||||||
// value={settings.openAIApiSettings.url}
|
|
||||||
// errorMessage={errors.get("openAIApiSettings.url")}
|
|
||||||
// setValue={(value: string) =>
|
|
||||||
// updateSettings({
|
|
||||||
// openAIApiSettings: {
|
|
||||||
// ...settings.openAIApiSettings,
|
|
||||||
// url: value,
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// />
|
|
||||||
// <TextSettingItem
|
|
||||||
// name={"OpenAI API key"}
|
|
||||||
// description={"The API key used in the requests."}
|
|
||||||
// placeholder={"Your API key..."}
|
|
||||||
// password
|
|
||||||
// value={settings.openAIApiSettings.key}
|
|
||||||
// errorMessage={errors.get("openAIApiSettings.key")}
|
|
||||||
// setValue={(value: string) =>
|
|
||||||
// updateSettings({
|
|
||||||
// openAIApiSettings: {
|
|
||||||
// ...settings.openAIApiSettings,
|
|
||||||
// key: value,
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// />
|
|
||||||
// <TextSettingItem
|
|
||||||
// name={"Model"}
|
|
||||||
// description={"The value of the model parameter in the request body."}
|
|
||||||
// placeholder="gpt-3.5-turbo"
|
|
||||||
// value={settings.openAIApiSettings.model}
|
|
||||||
// setValue={(value: string) =>
|
|
||||||
// updateSettings({
|
|
||||||
// openAIApiSettings: {
|
|
||||||
// ...settings.openAIApiSettings,
|
|
||||||
// model: value,
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// errorMessage={errors.get("openAIApiSettings.model")}
|
|
||||||
// />
|
|
||||||
|
|
||||||
// <ConnectivityCheck key={"openai"} settings={settings} />
|
|
||||||
// </>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// if (settings.apiProvider === "ollama") {
|
|
||||||
// return (
|
|
||||||
// <>
|
|
||||||
// <TextSettingItem
|
|
||||||
// name={"API URL"}
|
|
||||||
// description={
|
|
||||||
// "The URL used in the requests."
|
|
||||||
// }
|
|
||||||
// placeholder={"Your API URL..."}
|
|
||||||
// value={settings.ollamaApiSettings.url}
|
|
||||||
// errorMessage={errors.get("ollamaApiSettings.url")}
|
|
||||||
// setValue={(value: string) =>
|
|
||||||
// updateSettings({
|
|
||||||
// ollamaApiSettings: {
|
|
||||||
// ...settings.ollamaApiSettings,
|
|
||||||
// url: value,
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// />
|
|
||||||
// <TextSettingItem
|
|
||||||
// name={"Model"}
|
|
||||||
// description={"The model you have locally running using OLLAMA."}
|
|
||||||
// placeholder="Your model name..."
|
|
||||||
// value={settings.ollamaApiSettings.model}
|
|
||||||
// setValue={(value: string) =>
|
|
||||||
// updateSettings({
|
|
||||||
// ollamaApiSettings: {
|
|
||||||
// ...settings.ollamaApiSettings,
|
|
||||||
// model: value,
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// errorMessage={errors.get("ollamaApiSettings.model")}
|
|
||||||
// />
|
|
||||||
|
|
||||||
// <ConnectivityCheck key={"openai"} settings={settings} />
|
|
||||||
// </>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h2>AutoComplete</h2>
|
|
||||||
<CheckBoxSettingItem
|
|
||||||
name={"Enable"}
|
|
||||||
description={
|
|
||||||
"If disabled, nothing will trigger the extension or can result in an API call."
|
|
||||||
}
|
|
||||||
enabled={settings.autocompleteEnabled}
|
|
||||||
setEnabled={(value) => updateSettings({ autocompleteEnabled: value })}
|
|
||||||
/>
|
|
||||||
<CheckBoxSettingItem
|
|
||||||
name={"Cache completions"}
|
|
||||||
description={
|
|
||||||
"If disabled, the plugin will not cache the completions. After accepting or rejecting a completion, the plugin will not remember it. This might result in more API calls."
|
|
||||||
}
|
|
||||||
enabled={settings.cacheSuggestions}
|
|
||||||
setEnabled={(value) => updateSettings({ cacheSuggestions: value })}
|
|
||||||
/>
|
|
||||||
{/* <DropDownSettingItem
|
|
||||||
name={"API provider"}
|
|
||||||
description={
|
|
||||||
"The plugin supports multiple API providers. Each provider might require different settings."
|
|
||||||
}
|
|
||||||
value={settings.apiProvider}
|
|
||||||
setValue={(value: string) => {
|
|
||||||
if (value === "openai" || value === "azure" || value === "ollama") {
|
|
||||||
updateSettings({ apiProvider: value });
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
options={{
|
|
||||||
openai: "OpenAI API",
|
|
||||||
azure: "Azure OAI API",
|
|
||||||
ollama: "Self-hosted OLLAMA API"
|
|
||||||
}}
|
|
||||||
errorMessage={errors.get("apiProvider")}
|
|
||||||
/> */}
|
|
||||||
<CheckBoxSettingItem
|
|
||||||
name={"Debug mode"}
|
|
||||||
description={
|
|
||||||
"If enabled, various debug messages will be logged to the console, such as the complete response from the API, including the chain of thought tokens."
|
|
||||||
}
|
|
||||||
enabled={settings.debugMode}
|
|
||||||
setEnabled={(value) => updateSettings({ debugMode: value })}
|
|
||||||
/>
|
|
||||||
{/* <h2>API</h2>
|
|
||||||
{renderAPISettings()} */}
|
|
||||||
<h2>Model Options</h2>
|
|
||||||
<SliderSettingsItem
|
|
||||||
name={"Temperature"}
|
|
||||||
description={
|
|
||||||
"This parameter affects randomness in the sampling. Lower values result in more repetitive and deterministic responses. Higher temperatures will result in more unexpected or creative responses."
|
|
||||||
}
|
|
||||||
value={settings.modelOptions.temperature}
|
|
||||||
errorMessage={errors.get("modelOptions.temperature")}
|
|
||||||
setValue={(value: number) =>
|
|
||||||
updateSettings({
|
|
||||||
modelOptions: {
|
|
||||||
...settings.modelOptions,
|
|
||||||
temperature: value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
min={MIN_TEMPERATURE}
|
|
||||||
max={MAX_TEMPERATURE}
|
|
||||||
step={0.05}
|
|
||||||
/>
|
|
||||||
<SliderSettingsItem
|
|
||||||
name={"TopP"}
|
|
||||||
description={
|
|
||||||
"Like the temperature parameter, the Top P parameter affects the randomness in sampling. Lowering the value will limit the model's token selection to likelier tokens while increasing the value expands the model's token selection with lower likelihood tokens."
|
|
||||||
}
|
|
||||||
value={settings.modelOptions.top_p}
|
|
||||||
errorMessage={errors.get("modelOptions.top_p")}
|
|
||||||
setValue={(value: number) =>
|
|
||||||
updateSettings({
|
|
||||||
modelOptions: {
|
|
||||||
...settings.modelOptions,
|
|
||||||
top_p: value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
min={MIN_TOP_P}
|
|
||||||
max={MAX_TOP_P}
|
|
||||||
step={0.05}
|
|
||||||
/>
|
|
||||||
{settings.apiProvider !== "ollama" && (<>
|
|
||||||
<SliderSettingsItem
|
|
||||||
name={"Frequency Penalty"}
|
|
||||||
description={
|
|
||||||
"This parameter reduces the chance of repeating a token proportionally based on how often it has appeared in the text so far. This decreases the likelihood of repeating the exact same text in a response."
|
|
||||||
}
|
|
||||||
value={settings.modelOptions.frequency_penalty}
|
|
||||||
errorMessage={errors.get("modelOptions.frequency_penalty")}
|
|
||||||
setValue={(value: number) =>
|
|
||||||
updateSettings({
|
|
||||||
modelOptions: {
|
|
||||||
...settings.modelOptions,
|
|
||||||
frequency_penalty: value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
min={MIN_FREQUENCY_PENALTY}
|
|
||||||
max={MAX_FREQUENCY_PENALTY}
|
|
||||||
step={0.05}
|
|
||||||
/>
|
|
||||||
<SliderSettingsItem
|
|
||||||
name={"Presence Penalty"}
|
|
||||||
description={
|
|
||||||
"This parameter reduces the chance of repeating any token that has appeared in the text so far. This increases the likelihood of introducing new topics in a response."
|
|
||||||
}
|
|
||||||
value={settings.modelOptions.presence_penalty}
|
|
||||||
errorMessage={errors.get("modelOptions.presence_penalty")}
|
|
||||||
setValue={(value: number) =>
|
|
||||||
updateSettings({
|
|
||||||
modelOptions: {
|
|
||||||
...settings.modelOptions,
|
|
||||||
presence_penalty: value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
min={MIN_PRESENCE_PENALTY}
|
|
||||||
max={MAX_PRESENCE_PENALTY}
|
|
||||||
step={0.05}
|
|
||||||
/>
|
|
||||||
<SliderSettingsItem
|
|
||||||
name={"Max Tokens"}
|
|
||||||
description={
|
|
||||||
"This parameter changes the maximum number of tokens the model is allowed to generate. This includes the chain of thought tokens before the answer."
|
|
||||||
}
|
|
||||||
value={settings.modelOptions.max_tokens}
|
|
||||||
errorMessage={errors.get("modelOptions.max_tokens")}
|
|
||||||
setValue={(value: number) =>
|
|
||||||
updateSettings({
|
|
||||||
modelOptions: {
|
|
||||||
...settings.modelOptions,
|
|
||||||
max_tokens: value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
min={MIN_MAX_TOKENS}
|
|
||||||
max={MAX_MAX_TOKENS}
|
|
||||||
step={10}
|
|
||||||
/>
|
|
||||||
</>)}
|
|
||||||
|
|
||||||
<h2>Preprocessing</h2>
|
|
||||||
<CheckBoxSettingItem
|
|
||||||
name={"Don't include dataviews"}
|
|
||||||
description={
|
|
||||||
"Dataview(js) blocks can be quite long while not providing much value to the AI. If this setting is enabled, data view blocks will be removed promptly to reduce the number of tokens. This could save you some money in the long run."
|
|
||||||
}
|
|
||||||
enabled={settings.dontIncludeDataviews}
|
|
||||||
setEnabled={(value) =>
|
|
||||||
updateSettings({ dontIncludeDataviews: value })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<SliderSettingsItem
|
|
||||||
name={"Maximum Prefix Length"}
|
|
||||||
description={
|
|
||||||
"The maximum number of characters that will be included in the prefix. A larger value will increase the context for the completion, but it can also increase the cost or push you over the token limit."
|
|
||||||
}
|
|
||||||
value={settings.maxPrefixCharLimit}
|
|
||||||
errorMessage={errors.get("maxPrefixCharLimit")}
|
|
||||||
setValue={(value: number) =>
|
|
||||||
updateSettings({ maxPrefixCharLimit: value })
|
|
||||||
}
|
|
||||||
min={MIN_MAX_CHAR_LIMIT}
|
|
||||||
max={MAX_MAX_CHAR_LIMIT}
|
|
||||||
step={100}
|
|
||||||
suffix={" chars"}
|
|
||||||
/>
|
|
||||||
<SliderSettingsItem
|
|
||||||
name={"Maximum Suffix Length"}
|
|
||||||
description={
|
|
||||||
"The maximum number of characters that will be included in the suffix. A larger value will increase the context for the completion, but it can also increase the cost or push you over the token limit."
|
|
||||||
}
|
|
||||||
value={settings.maxSuffixCharLimit}
|
|
||||||
errorMessage={errors.get("maxSuffixCharLimit")}
|
|
||||||
setValue={(value: number) =>
|
|
||||||
updateSettings({ maxSuffixCharLimit: value })
|
|
||||||
}
|
|
||||||
min={MIN_MAX_CHAR_LIMIT}
|
|
||||||
max={MAX_MAX_CHAR_LIMIT}
|
|
||||||
step={100}
|
|
||||||
suffix={" chars"}
|
|
||||||
/>
|
|
||||||
<h2>Postprocessing</h2>
|
|
||||||
<CheckBoxSettingItem
|
|
||||||
name={"Auto remove duplicate mat block indicators"}
|
|
||||||
description={
|
|
||||||
"The AI model might eagerly add a math block indicator ($), even though the cursor is already inside a math block. If this setting is enabled, the plugin will automatically remove these duplicate indicators from the completion."
|
|
||||||
}
|
|
||||||
enabled={settings.removeDuplicateMathBlockIndicator}
|
|
||||||
setEnabled={(value) =>
|
|
||||||
updateSettings({ removeDuplicateMathBlockIndicator: value })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<CheckBoxSettingItem
|
|
||||||
name={"Auto remove duplicate mat block indicators"}
|
|
||||||
description={
|
|
||||||
"The AI model might eagerly add a code block indicator (`), even though the cursor is already inside a code block. If this setting is enabled, the plugin will automatically remove these duplicate indicators from the completion."
|
|
||||||
}
|
|
||||||
enabled={settings.removeDuplicateCodeBlockIndicator}
|
|
||||||
setEnabled={(value) =>
|
|
||||||
updateSettings({ removeDuplicateCodeBlockIndicator: value })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<h2>Trigger</h2>
|
|
||||||
<SliderSettingsItem
|
|
||||||
name={"Delay"}
|
|
||||||
description={
|
|
||||||
"Delay in ms between the last character typed and the completion request."
|
|
||||||
}
|
|
||||||
value={settings.delay}
|
|
||||||
errorMessage={errors.get("delay")}
|
|
||||||
setValue={(value: number) => updateSettings({ delay: value })}
|
|
||||||
min={MIN_DELAY}
|
|
||||||
max={MAX_DELAY}
|
|
||||||
step={100}
|
|
||||||
suffix={"ms"}
|
|
||||||
/>
|
|
||||||
<TriggerSettings
|
|
||||||
name={"Trigger words"}
|
|
||||||
description={
|
|
||||||
"Completions will be triggered if the text before the matches any of these words or characters. This can either be a direct string match or a regex match. When using a regex, make sure to include the end of line character ($)."
|
|
||||||
}
|
|
||||||
triggers={settings.triggers}
|
|
||||||
setValues={(triggers) => updateSettings({ triggers })}
|
|
||||||
errorMessage={errors.get("triggerWords")}
|
|
||||||
errorMessages={errors}
|
|
||||||
/>
|
|
||||||
<h2>Privacy</h2>
|
|
||||||
<SettingsItem
|
|
||||||
name={"Ignored files"}
|
|
||||||
description={
|
|
||||||
<div>
|
|
||||||
<p>This field enables you to specify files and directories that the plugin should ignore. When
|
|
||||||
you open any of these files, the plugin will automatically disable itself and display a
|
|
||||||
'disabled' status in the bottom menu. Enter one pattern per line. These patterns function
|
|
||||||
similar to glob patterns. Here are some frequently used patterns:</p>
|
|
||||||
<ul>
|
|
||||||
<li><code>path/to/folder/**</code>: This pattern ignores all files and sub folders within
|
|
||||||
this folder.
|
|
||||||
</li>
|
|
||||||
<li><code>"**/secret/**"</code>: This pattern ignores any file located inside a 'secret'
|
|
||||||
directory,
|
|
||||||
regardless of its location in the path.
|
|
||||||
</li>
|
|
||||||
<li><code>!path/to/folder/example.md</code>: This pattern explicitly undoes an ignore,
|
|
||||||
making this file noticeable to the plugin.
|
|
||||||
</li>
|
|
||||||
<li><code>**/*Python*.md</code>: This pattern ignores any file with 'Python' in its name,
|
|
||||||
irrespective of its location.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
display={"block"}
|
|
||||||
errorMessage={errors.get("ignoredFilePatterns")}
|
|
||||||
>
|
|
||||||
<textarea
|
|
||||||
className="infio-autocomplete-setting-item-textarea"
|
|
||||||
rows={10}
|
|
||||||
placeholder="Your file patterns, e.g., **/secret/**"
|
|
||||||
value={settings.ignoredFilePatterns}
|
|
||||||
onChange={(e) =>
|
|
||||||
updateSettings({
|
|
||||||
ignoredFilePatterns: e.target.value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
<SettingsItem
|
|
||||||
name={"Ignored tags"}
|
|
||||||
description={
|
|
||||||
<div>
|
|
||||||
<p>Files containing any of these tags will be ignored. When you open a file containing a
|
|
||||||
tag listed here, the plugin will automatically disable itself and display a 'disabled'
|
|
||||||
status in the bottom menu. Enter one tag per line.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
display={"block"}
|
|
||||||
errorMessage={errors.get("ignoredTags")}
|
|
||||||
>
|
|
||||||
<textarea
|
|
||||||
className="infio-autocomplete-setting-item-textarea"
|
|
||||||
rows={10}
|
|
||||||
placeholder="Your file tags, e.g., secret"
|
|
||||||
value={settings.ignoredTags}
|
|
||||||
onChange={(e) =>
|
|
||||||
updateSettings({
|
|
||||||
ignoredTags: e.target.value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
|
|
||||||
<h2>Danger zone</h2>
|
|
||||||
<SettingsItem
|
|
||||||
name={"Factory Reset"}
|
|
||||||
description={
|
|
||||||
"Messed-up the settings? No worries, press this button! After that, the plugin will go back to the default settings. The URL and API key will remain unchanged."
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
aria-label="Reset to default settings"
|
|
||||||
onClick={resetSettings}
|
|
||||||
>
|
|
||||||
Reset
|
|
||||||
</button>
|
|
||||||
</SettingsItem>
|
|
||||||
<CheckBoxSettingItem
|
|
||||||
name={"Advanced mode"}
|
|
||||||
description={
|
|
||||||
"If you are familiar with prompt engineering, you can enable this setting to view the prompt generation and a few shot example settings. Turn off this button. It will not reset your changes; use the factory reset button for that."
|
|
||||||
}
|
|
||||||
enabled={settings.advancedMode}
|
|
||||||
setEnabled={(value) => updateSettings({ advancedMode: value })}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{settings.advancedMode && (
|
|
||||||
<>
|
|
||||||
<h2>Advanced</h2>
|
|
||||||
<TextSettingItem
|
|
||||||
name={"Chain of thought removal regex"}
|
|
||||||
description={
|
|
||||||
"This regex is used to remove the chain of thought tokens from the generated answer. If it is not implemented correctly, the chain of thought tokens will be included in the suggested completion."
|
|
||||||
}
|
|
||||||
placeholder={"your regex..."}
|
|
||||||
value={settings.chainOfThoughRemovalRegex}
|
|
||||||
errorMessage={errors.get("chainOfThoughRemovalRegex")}
|
|
||||||
setValue={(value: string) =>
|
|
||||||
updateSettings({
|
|
||||||
chainOfThoughRemovalRegex: value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingsItem
|
|
||||||
name={"System Message"}
|
|
||||||
description={
|
|
||||||
"This system message gives the models all the context and instructions they need to complete the answer generation tasks. You can edit this message to your liking. If you edit the chain of thought formatting, make sure to update the extract regex and examples accordingly."
|
|
||||||
}
|
|
||||||
display={"block"}
|
|
||||||
errorMessage={errors.get("systemMessage")}
|
|
||||||
>
|
|
||||||
<textarea
|
|
||||||
className="infio-autocomplete-setting-item-textarea"
|
|
||||||
rows={10}
|
|
||||||
placeholder="Your system message..."
|
|
||||||
value={settings.systemMessage}
|
|
||||||
onChange={(e) =>
|
|
||||||
updateSettings({
|
|
||||||
systemMessage: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
|
|
||||||
<SettingsItem
|
|
||||||
name={"User Message template"}
|
|
||||||
description={
|
|
||||||
"This template defines how the prefix and suffix are formatted to create the user message. You have access to two variables: {{prefix}} and {{suffix}}. If you edit this, make sure to update the examples accordingly."
|
|
||||||
}
|
|
||||||
display={"block"}
|
|
||||||
errorMessage={errors.get("userMessageTemplate")}
|
|
||||||
>
|
|
||||||
<textarea
|
|
||||||
className="infio-autocomplete-setting-item-textarea"
|
|
||||||
rows={3}
|
|
||||||
placeholder="{{prefix}}<mask/>{{suffix}}"
|
|
||||||
value={settings.userMessageTemplate}
|
|
||||||
onChange={(e) =>
|
|
||||||
updateSettings({
|
|
||||||
userMessageTemplate: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
<FewShotExampleSettings
|
|
||||||
fewShotExamples={settings.fewShotExamples}
|
|
||||||
name={"Few Shot Examples"}
|
|
||||||
description={
|
|
||||||
"The model uses these examples to learn the expected answer format. Not all examples are sent at the same time. We only send the relevant examples, given the current cursor location. For example, the CodeBlock examples are only sent if the cursor is in a code block. If no special context is detected, we send the Text examples. Each context has a default of 2 examples, but you can add or remove examples if there is at least one per context. You can add more examples, but this will increase the inference costs."
|
|
||||||
}
|
|
||||||
setFewShotExamples={(value) =>
|
|
||||||
updateSettings({ fewShotExamples: value })
|
|
||||||
}
|
|
||||||
errorMessages={errors}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
import InfioPlugin from "../main";
|
|
||||||
import { InfioSettings } from "../types/settings";
|
|
||||||
|
|
||||||
// import ModelsSettings from "./ModelsSettings";
|
|
||||||
import ProviderSettings from "./ProviderSettings";
|
|
||||||
|
|
||||||
type CustomSettingsProps = {
|
|
||||||
plugin: InfioPlugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CustomSettings: React.FC<CustomSettingsProps> = ({ plugin }) => {
|
|
||||||
const settings = plugin.settings;
|
|
||||||
|
|
||||||
const handleSettingsUpdate = async (newSettings: InfioSettings) => {
|
|
||||||
await plugin.setSettings(newSettings);
|
|
||||||
// Force refresh the settings page to update dropdowns
|
|
||||||
plugin.settingTab.display();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1 className="infio-llm-setting-title">
|
|
||||||
<div>
|
|
||||||
Infio Settings <small>v{settings.version}</small>
|
|
||||||
</div>
|
|
||||||
</h1>
|
|
||||||
<ProviderSettings settings={settings} setSettings={handleSettingsUpdate} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CustomSettings;
|
|
||||||
@ -1,26 +1,32 @@
|
|||||||
import {
|
import {
|
||||||
App,
|
App,
|
||||||
DropdownComponent,
|
|
||||||
Modal,
|
Modal,
|
||||||
|
Notice,
|
||||||
PluginSettingTab,
|
PluginSettingTab,
|
||||||
Setting,
|
Setting,
|
||||||
TFile,
|
TFile
|
||||||
} from 'obsidian';
|
} from 'obsidian';
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
// import {
|
|
||||||
// EMBEDDING_MODEL_OPTIONS,
|
|
||||||
// } from '../constants'
|
|
||||||
|
|
||||||
import InfioPlugin from '../main';
|
import InfioPlugin from '../main';
|
||||||
|
import { InfioSettings } from '../types/settings';
|
||||||
import { findFilesMatchingPatterns } from '../utils/glob-utils';
|
import { findFilesMatchingPatterns } from '../utils/glob-utils';
|
||||||
import { getOllamaModels } from '../utils/ollama';
|
|
||||||
|
|
||||||
import AutoCompleteSettings from './AutoCompleteSettings';
|
import AdvancedSettings from './components/AdvancedSettings';
|
||||||
import CustomSettings from './CustomSettings';
|
import BasicAutoCompleteSettings from './components/BasicAutoCompleteSettings';
|
||||||
|
import DangerZoneSettings from './components/DangerZoneSettings';
|
||||||
|
import ModelParametersSettings from './components/ModelParametersSettings';
|
||||||
|
import CustomProviderSettings from './components/ModelProviderSettings';
|
||||||
|
import PostprocessingSettings from './components/PostprocessingSettings';
|
||||||
|
import PreprocessingSettings from './components/PreprocessingSettings';
|
||||||
|
import PrivacySettings from './components/PrivacySettings';
|
||||||
|
import TriggerSettingsSection from './components/TriggerSettingsSection';
|
||||||
|
|
||||||
export class InfioSettingTab extends PluginSettingTab {
|
export class InfioSettingTab extends PluginSettingTab {
|
||||||
plugin: InfioPlugin
|
plugin: InfioPlugin;
|
||||||
|
private autoCompleteContainer: HTMLElement | null = null;
|
||||||
|
private modelsContainer: HTMLElement | null = null;
|
||||||
|
|
||||||
constructor(app: App, plugin: InfioPlugin) {
|
constructor(app: App, plugin: InfioPlugin) {
|
||||||
super(app, plugin)
|
super(app, plugin)
|
||||||
@ -35,522 +41,30 @@ export class InfioSettingTab extends PluginSettingTab {
|
|||||||
this.renderAutoCompleteSection(containerEl)
|
this.renderAutoCompleteSection(containerEl)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderModelsSection(containerEl: HTMLElement): void {
|
private renderModelsContent(containerEl: HTMLElement): void {
|
||||||
const div = containerEl.createDiv("div");
|
const div = containerEl.createDiv("div");
|
||||||
const sections = createRoot(div);
|
const sections = createRoot(div);
|
||||||
sections.render(<CustomSettings plugin={this.plugin} />);
|
sections.render(
|
||||||
|
<CustomProviderSettings
|
||||||
|
plugin={this.plugin}
|
||||||
|
onSettingsUpdate={() => {
|
||||||
|
if (this.modelsContainer) {
|
||||||
|
this.modelsContainer.empty();
|
||||||
|
this.renderModelsContent(this.modelsContainer);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderAPIKeysSection(containerEl: HTMLElement): void {
|
renderModelsSection(containerEl: HTMLElement): void {
|
||||||
new Setting(containerEl)
|
const modelsDiv = containerEl.createDiv("models-section");
|
||||||
.setHeading()
|
this.modelsContainer = modelsDiv;
|
||||||
.setName('API keys')
|
this.renderModelsContent(modelsDiv);
|
||||||
new Setting(containerEl)
|
|
||||||
.setName('Infio API key')
|
|
||||||
.setClass("infio-chat-setting-item-container")
|
|
||||||
.addText((text) =>
|
|
||||||
text
|
|
||||||
.setPlaceholder('Enter your API key')
|
|
||||||
.setValue(this.plugin.settings.infioApiKey)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
infioApiKey: value,
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
new Setting(containerEl)
|
|
||||||
.setName('Anthropic API key')
|
|
||||||
.setClass("infio-chat-setting-item-container-append")
|
|
||||||
.addText((text) =>
|
|
||||||
text
|
|
||||||
.setPlaceholder('Enter your API key')
|
|
||||||
.setValue(this.plugin.settings.anthropicApiKey)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
anthropicApiKey: value,
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
new Setting(containerEl)
|
|
||||||
.setName('Deepseek API key')
|
|
||||||
.setClass("infio-chat-setting-item-container-append")
|
|
||||||
.addText((text) =>
|
|
||||||
text
|
|
||||||
.setPlaceholder('Enter your API key')
|
|
||||||
.setValue(this.plugin.settings.deepseekApiKey)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
deepseekApiKey: value,
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
new Setting(containerEl)
|
|
||||||
.setName('OpenAI API key')
|
|
||||||
.setClass("infio-chat-setting-item-container-append")
|
|
||||||
.addText((text) =>
|
|
||||||
text
|
|
||||||
.setPlaceholder('Enter your API key')
|
|
||||||
.setValue(this.plugin.settings.openAIApiKey)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
openAIApiKey: value,
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
new Setting(containerEl)
|
|
||||||
.setName('Google API key')
|
|
||||||
.setClass("infio-chat-setting-item-container-append")
|
|
||||||
.addText((text) =>
|
|
||||||
text
|
|
||||||
.setPlaceholder('Enter your API key')
|
|
||||||
.setValue(this.plugin.settings.geminiApiKey)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
geminiApiKey: value,
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
new Setting(containerEl)
|
|
||||||
.setName('Groq API key')
|
|
||||||
.setClass("infio-chat-setting-item-container-append")
|
|
||||||
.addText((text) =>
|
|
||||||
text
|
|
||||||
.setPlaceholder('Enter your API key')
|
|
||||||
.setValue(this.plugin.settings.groqApiKey)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
groqApiKey: value,
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderDefaultModelSection(containerEl: HTMLElement): void {
|
|
||||||
new Setting(containerEl).setHeading().setName('Default Model')
|
|
||||||
new Setting(containerEl)
|
|
||||||
.setName('Default chat model')
|
|
||||||
.setClass("infio-chat-setting-item-container")
|
|
||||||
.addDropdown((dropdown) =>
|
|
||||||
dropdown
|
|
||||||
.addOptions(
|
|
||||||
this.plugin.settings.activeModels
|
|
||||||
.reduce<Record<string, string>>((acc, option) => {
|
|
||||||
if (!option.isEmbeddingModel && option.enabled) {
|
|
||||||
acc[option.name] = option.name
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
}, {}),
|
|
||||||
)
|
|
||||||
.setValue(this.plugin.settings.chatModelId)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
chatModelId: value,
|
|
||||||
})
|
|
||||||
// Force refresh to show/hide Ollama and OpenAI-compatible settings
|
|
||||||
this.display()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
if (this.plugin.settings.chatModelId === 'ollama') {
|
|
||||||
this.renderOllamaChatModelSettings(containerEl)
|
|
||||||
}
|
|
||||||
if (this.plugin.settings.chatModelId === 'openai-compatible') {
|
|
||||||
this.renderOpenAICompatibleChatModelSettings(containerEl)
|
|
||||||
}
|
|
||||||
|
|
||||||
new Setting(containerEl)
|
|
||||||
.setName('Default apply model')
|
|
||||||
.setClass("infio-chat-setting-item-container-append")
|
|
||||||
.addDropdown((dropdown) =>
|
|
||||||
dropdown
|
|
||||||
.addOptions(
|
|
||||||
this.plugin.settings.activeModels
|
|
||||||
.reduce<Record<string, string>>(
|
|
||||||
(acc, option) => {
|
|
||||||
if (!option.isEmbeddingModel && option.enabled) {
|
|
||||||
acc[option.name] = option.name
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.setValue(this.plugin.settings.applyModelId)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
applyModelId: value,
|
|
||||||
})
|
|
||||||
// Force refresh to show/hide Ollama and OpenAI-compatible settings
|
|
||||||
this.display()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
if (this.plugin.settings.applyModelId === 'ollama') {
|
|
||||||
this.renderOllamaApplyModelSettings(containerEl)
|
|
||||||
}
|
|
||||||
if (this.plugin.settings.applyModelId === 'openai-compatible') {
|
|
||||||
this.renderOpenAICompatibleApplyModelSettings(containerEl)
|
|
||||||
}
|
|
||||||
|
|
||||||
new Setting(containerEl)
|
|
||||||
.setName('Default embedding model')
|
|
||||||
.setClass("infio-chat-setting-item-container-append")
|
|
||||||
.addDropdown((dropdown) =>
|
|
||||||
dropdown
|
|
||||||
.addOptions(
|
|
||||||
this.plugin.settings.activeModels
|
|
||||||
.reduce<Record<string, string>>(
|
|
||||||
(acc, option) => {
|
|
||||||
if (option.isEmbeddingModel && option.enabled) {
|
|
||||||
acc[option.name] = option.name
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.setValue(this.plugin.settings.embeddingModelId)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
embeddingModelId: value,
|
|
||||||
})
|
|
||||||
// Force refresh to show/hide Ollama settings
|
|
||||||
this.display()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
if (this.plugin.settings.embeddingModelId.startsWith('ollama/')) {
|
|
||||||
this.renderOllamaEmbeddingModelSettings(containerEl)
|
|
||||||
}
|
|
||||||
|
|
||||||
new Setting(containerEl)
|
|
||||||
.setHeading()
|
|
||||||
.setName('System prompt')
|
|
||||||
.setDesc('This prompt will be added to the beginning of every chat.')
|
|
||||||
|
|
||||||
new Setting(containerEl)
|
|
||||||
.setClass('infio-chat-settings-textarea')
|
|
||||||
.addTextArea((text) =>
|
|
||||||
text
|
|
||||||
.setValue(this.plugin.settings.systemPrompt)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
systemPrompt: value,
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderOllamaChatModelSettings(containerEl: HTMLElement): void {
|
|
||||||
const ollamaContainer = containerEl.createDiv(
|
|
||||||
'infio-chat-settings-model-container',
|
|
||||||
)
|
|
||||||
let modelDropdown: DropdownComponent | null = null // Store reference to the dropdown
|
|
||||||
|
|
||||||
// Base URL Setting
|
|
||||||
new Setting(ollamaContainer)
|
|
||||||
.setName('Base URL')
|
|
||||||
.setClass("infio-chat-setting-item-container-append")
|
|
||||||
.setDesc(
|
|
||||||
'The API endpoint for your Ollama service (e.g., http://127.0.0.1:11434)',
|
|
||||||
)
|
|
||||||
.addText((text) => {
|
|
||||||
text
|
|
||||||
.setPlaceholder('http://127.0.0.1:11434')
|
|
||||||
.setValue(this.plugin.settings.ollamaChatModel.baseUrl || '')
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
ollamaChatModel: {
|
|
||||||
...this.plugin.settings.ollamaChatModel,
|
|
||||||
baseUrl: value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if (modelDropdown) {
|
|
||||||
await this.updateOllamaModelOptions({
|
|
||||||
baseUrl: value,
|
|
||||||
dropdown: modelDropdown,
|
|
||||||
onModelChange: async (model: string) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
ollamaChatModel: {
|
|
||||||
...this.plugin.settings.ollamaChatModel,
|
|
||||||
model,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Model Setting
|
|
||||||
new Setting(ollamaContainer)
|
|
||||||
.setName('Model Name')
|
|
||||||
.setDesc('Select a model from your Ollama instance')
|
|
||||||
.addDropdown(async (dropdown) => {
|
|
||||||
const currentModel = this.plugin.settings.ollamaChatModel.model
|
|
||||||
modelDropdown = dropdown
|
|
||||||
.addOption(currentModel, currentModel)
|
|
||||||
.setValue(currentModel)
|
|
||||||
await this.updateOllamaModelOptions({
|
|
||||||
baseUrl: this.plugin.settings.ollamaChatModel.baseUrl,
|
|
||||||
dropdown,
|
|
||||||
onModelChange: async (model: string) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
ollamaChatModel: {
|
|
||||||
...this.plugin.settings.ollamaChatModel,
|
|
||||||
model,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
renderOpenAICompatibleChatModelSettings(containerEl: HTMLElement): void {
|
|
||||||
const openAICompatContainer = containerEl.createDiv(
|
|
||||||
'infio-chat-settings-model-container',
|
|
||||||
)
|
|
||||||
|
|
||||||
new Setting(openAICompatContainer)
|
|
||||||
.setName('Base URL')
|
|
||||||
.setDesc(
|
|
||||||
'The API endpoint for your OpenAI-compatible service (e.g., https://api.example.com/v1)',
|
|
||||||
)
|
|
||||||
.addText((text) =>
|
|
||||||
text
|
|
||||||
.setPlaceholder('https://api.example.com/v1')
|
|
||||||
.setValue(
|
|
||||||
this.plugin.settings.openAICompatibleChatModel.baseUrl || '',
|
|
||||||
)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
openAICompatibleChatModel: {
|
|
||||||
...this.plugin.settings.openAICompatibleChatModel,
|
|
||||||
baseUrl: value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
new Setting(openAICompatContainer)
|
|
||||||
.setName('API Key')
|
|
||||||
.setDesc('Your authentication key for the OpenAI-compatible service')
|
|
||||||
.addText((text) =>
|
|
||||||
text
|
|
||||||
.setPlaceholder('Enter your API key')
|
|
||||||
.setValue(this.plugin.settings.openAICompatibleChatModel.apiKey || '')
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
openAICompatibleChatModel: {
|
|
||||||
...this.plugin.settings.openAICompatibleChatModel,
|
|
||||||
apiKey: value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
new Setting(openAICompatContainer)
|
|
||||||
.setName('Model Name')
|
|
||||||
.setDesc(
|
|
||||||
'The specific model to use with your service (e.g., llama-3.1-70b, mixtral-8x7b)',
|
|
||||||
)
|
|
||||||
.addText((text) =>
|
|
||||||
text
|
|
||||||
.setPlaceholder('llama-3.1-70b')
|
|
||||||
.setValue(this.plugin.settings.openAICompatibleChatModel.model || '')
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
openAICompatibleChatModel: {
|
|
||||||
...this.plugin.settings.openAICompatibleChatModel,
|
|
||||||
model: value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderOllamaApplyModelSettings(containerEl: HTMLElement): void {
|
|
||||||
const ollamaContainer = containerEl.createDiv(
|
|
||||||
'infio-chat-settings-model-container',
|
|
||||||
)
|
|
||||||
let modelDropdown: DropdownComponent | null = null // Store reference to the dropdown
|
|
||||||
|
|
||||||
// Base URL Setting
|
|
||||||
new Setting(ollamaContainer)
|
|
||||||
.setName('Base URL')
|
|
||||||
.setDesc(
|
|
||||||
'The API endpoint for your Ollama service (e.g., http://127.0.0.1:11434)',
|
|
||||||
)
|
|
||||||
.addText((text) => {
|
|
||||||
text
|
|
||||||
.setPlaceholder('http://127.0.0.1:11434')
|
|
||||||
.setValue(this.plugin.settings.ollamaApplyModel.baseUrl || '')
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
ollamaApplyModel: {
|
|
||||||
...this.plugin.settings.ollamaApplyModel,
|
|
||||||
baseUrl: value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if (modelDropdown) {
|
|
||||||
await this.updateOllamaModelOptions({
|
|
||||||
baseUrl: value,
|
|
||||||
dropdown: modelDropdown,
|
|
||||||
onModelChange: async (model: string) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
ollamaApplyModel: {
|
|
||||||
...this.plugin.settings.ollamaApplyModel,
|
|
||||||
model,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Model Setting
|
|
||||||
new Setting(ollamaContainer)
|
|
||||||
.setName('Model Name')
|
|
||||||
.setDesc('Select a model from your Ollama instance')
|
|
||||||
.addDropdown(async (dropdown) => {
|
|
||||||
const currentModel = this.plugin.settings.ollamaApplyModel.model
|
|
||||||
modelDropdown = dropdown
|
|
||||||
.addOption(currentModel, currentModel)
|
|
||||||
.setValue(currentModel)
|
|
||||||
await this.updateOllamaModelOptions({
|
|
||||||
baseUrl: this.plugin.settings.ollamaApplyModel.baseUrl,
|
|
||||||
dropdown,
|
|
||||||
onModelChange: async (model: string) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
ollamaApplyModel: {
|
|
||||||
...this.plugin.settings.ollamaApplyModel,
|
|
||||||
model,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
renderOpenAICompatibleApplyModelSettings(containerEl: HTMLElement): void {
|
|
||||||
const openAICompatContainer = containerEl.createDiv(
|
|
||||||
'infio-chat-settings-model-container',
|
|
||||||
)
|
|
||||||
|
|
||||||
new Setting(openAICompatContainer)
|
|
||||||
.setName('Base URL')
|
|
||||||
.setDesc(
|
|
||||||
'The API endpoint for your OpenAI-compatible service (e.g., https://api.example.com/v1)',
|
|
||||||
)
|
|
||||||
.addText((text) =>
|
|
||||||
text
|
|
||||||
.setPlaceholder('https://api.example.com/v1')
|
|
||||||
.setValue(
|
|
||||||
this.plugin.settings.openAICompatibleApplyModel.baseUrl || '',
|
|
||||||
)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
openAICompatibleApplyModel: {
|
|
||||||
...this.plugin.settings.openAICompatibleApplyModel,
|
|
||||||
baseUrl: value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
new Setting(openAICompatContainer)
|
|
||||||
.setName('API Key')
|
|
||||||
.setDesc('Your authentication key for the OpenAI-compatible service')
|
|
||||||
.addText((text) =>
|
|
||||||
text
|
|
||||||
.setPlaceholder('Enter your API key')
|
|
||||||
.setValue(
|
|
||||||
this.plugin.settings.openAICompatibleApplyModel.apiKey || '',
|
|
||||||
)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
openAICompatibleApplyModel: {
|
|
||||||
...this.plugin.settings.openAICompatibleApplyModel,
|
|
||||||
apiKey: value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
new Setting(openAICompatContainer)
|
|
||||||
.setName('Model Name')
|
|
||||||
.setDesc(
|
|
||||||
'The specific model to use with your service (e.g., llama-3.1-70b, mixtral-8x7b)',
|
|
||||||
)
|
|
||||||
.addText((text) =>
|
|
||||||
text
|
|
||||||
.setPlaceholder('llama-3.1-70b')
|
|
||||||
.setValue(this.plugin.settings.openAICompatibleApplyModel.model || '')
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
openAICompatibleApplyModel: {
|
|
||||||
...this.plugin.settings.openAICompatibleApplyModel,
|
|
||||||
model: value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderOllamaEmbeddingModelSettings(containerEl: HTMLElement): void {
|
|
||||||
const ollamaContainer = containerEl.createDiv(
|
|
||||||
'infio-chat-settings-model-container',
|
|
||||||
)
|
|
||||||
|
|
||||||
new Setting(ollamaContainer)
|
|
||||||
.setName('Base URL')
|
|
||||||
.setDesc(
|
|
||||||
'The API endpoint for your Ollama service (e.g., http://127.0.0.1:11434)',
|
|
||||||
)
|
|
||||||
.addText((text) =>
|
|
||||||
text
|
|
||||||
.setPlaceholder('http://127.0.0.1:11434')
|
|
||||||
.setValue(this.plugin.settings.ollamaEmbeddingModel.baseUrl || '')
|
|
||||||
.onChange(async (value) => {
|
|
||||||
await this.plugin.setSettings({
|
|
||||||
...this.plugin.settings,
|
|
||||||
ollamaEmbeddingModel: {
|
|
||||||
...this.plugin.settings.ollamaEmbeddingModel,
|
|
||||||
baseUrl: value,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderRAGSection(containerEl: HTMLElement): void {
|
renderRAGSection(containerEl: HTMLElement): void {
|
||||||
new Setting(containerEl).setHeading().setName('RAG')
|
new Setting(containerEl).setHeading().setName('RAG')
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
.setName('Include patterns')
|
.setName('Include patterns')
|
||||||
.setDesc(
|
.setDesc(
|
||||||
@ -715,69 +229,115 @@ export class InfioSettingTab extends PluginSettingTab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderAutoCompleteSection(containerEl: HTMLElement): void {
|
renderAutoCompleteSection(containerEl: HTMLElement): void {
|
||||||
const div = containerEl.createDiv("div");
|
// 创建一个专门的容器来存放 AutoComplete 相关的组件
|
||||||
|
const autoCompleteDiv = containerEl.createDiv("auto-complete-section");
|
||||||
const sections = createRoot(div);
|
this.autoCompleteContainer = autoCompleteDiv;
|
||||||
sections.render(
|
this.renderAutoCompleteContent(autoCompleteDiv);
|
||||||
<React.StrictMode>
|
|
||||||
<AutoCompleteSettings
|
|
||||||
onSettingsChanged={async (settings) => {
|
|
||||||
this.plugin.setSettings(settings);
|
|
||||||
// Force refresh the settings page to update dropdowns
|
|
||||||
this.plugin.settingTab.display();
|
|
||||||
}}
|
|
||||||
settings={this.plugin.settings}
|
|
||||||
/>
|
|
||||||
</React.StrictMode>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateOllamaModelOptions({
|
private renderAutoCompleteContent(containerEl: HTMLElement): void {
|
||||||
baseUrl,
|
const updateSettings = async (update: Partial<InfioSettings>) => {
|
||||||
dropdown,
|
await this.plugin.setSettings({
|
||||||
onModelChange,
|
...this.plugin.settings,
|
||||||
}: {
|
...update
|
||||||
baseUrl: string
|
});
|
||||||
dropdown: DropdownComponent
|
|
||||||
onModelChange: (model: string) => Promise<void>
|
// 只重新渲染 AutoComplete 部分
|
||||||
}): Promise<void> {
|
if (this.autoCompleteContainer) {
|
||||||
const currentValue = dropdown.getValue()
|
this.autoCompleteContainer.empty();
|
||||||
dropdown.selectEl.empty()
|
this.renderAutoCompleteContent(this.autoCompleteContainer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
const errors = new Map();
|
||||||
const models = await getOllamaModels(baseUrl)
|
|
||||||
if (models.length > 0) {
|
|
||||||
const modelOptions = models.reduce<Record<string, string>>(
|
|
||||||
(acc, model) => {
|
|
||||||
acc[model] = model
|
|
||||||
return acc
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
)
|
|
||||||
|
|
||||||
dropdown.addOptions(modelOptions)
|
// AutoComplete base
|
||||||
|
new Setting(containerEl).setName('AutoComplete').setHeading();
|
||||||
|
this.renderComponent(containerEl,
|
||||||
|
<BasicAutoCompleteSettings
|
||||||
|
settings={this.plugin.settings}
|
||||||
|
updateSettings={updateSettings}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
if (models.includes(currentValue)) {
|
// Model parameters
|
||||||
dropdown.setValue(currentValue)
|
new Setting(containerEl).setName('Model parameters').setHeading();
|
||||||
} else {
|
this.renderComponent(containerEl,
|
||||||
dropdown.setValue(models[0])
|
<ModelParametersSettings
|
||||||
await onModelChange(models[0])
|
settings={this.plugin.settings}
|
||||||
}
|
updateSettings={updateSettings}
|
||||||
} else {
|
errors={errors}
|
||||||
dropdown.addOption('', 'No models found - check base URL')
|
/>
|
||||||
dropdown.setValue('')
|
);
|
||||||
await onModelChange('')
|
|
||||||
}
|
// Preprocessing
|
||||||
} catch (error) {
|
new Setting(containerEl).setName('Preprocessing').setHeading();
|
||||||
console.error('Failed to fetch Ollama models:', error)
|
this.renderComponent(containerEl,
|
||||||
dropdown.addOption('', 'No models found - check base URL')
|
<PreprocessingSettings
|
||||||
dropdown.setValue('')
|
settings={this.plugin.settings}
|
||||||
await onModelChange('')
|
updateSettings={updateSettings}
|
||||||
|
errors={errors}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Postprocessing
|
||||||
|
new Setting(containerEl).setName('Postprocessing').setHeading();
|
||||||
|
this.renderComponent(containerEl,
|
||||||
|
<PostprocessingSettings
|
||||||
|
settings={this.plugin.settings}
|
||||||
|
updateSettings={updateSettings}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Trigger
|
||||||
|
new Setting(containerEl).setName('Trigger').setHeading();
|
||||||
|
this.renderComponent(containerEl,
|
||||||
|
<TriggerSettingsSection
|
||||||
|
settings={this.plugin.settings}
|
||||||
|
updateSettings={updateSettings}
|
||||||
|
errors={errors}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Privacy
|
||||||
|
new Setting(containerEl).setName('Privacy').setHeading();
|
||||||
|
this.renderComponent(containerEl,
|
||||||
|
<PrivacySettings
|
||||||
|
settings={this.plugin.settings}
|
||||||
|
updateSettings={updateSettings}
|
||||||
|
errors={errors}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Danger zone
|
||||||
|
new Setting(containerEl).setName('Danger zone').setHeading();
|
||||||
|
this.renderComponent(containerEl,
|
||||||
|
<DangerZoneSettings
|
||||||
|
settings={this.plugin.settings}
|
||||||
|
updateSettings={updateSettings}
|
||||||
|
onReset={() => {
|
||||||
|
new Notice("Factory reset complete.");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Advanced
|
||||||
|
if (this.plugin.settings.advancedMode) {
|
||||||
|
new Setting(containerEl).setName('Advanced').setHeading();
|
||||||
|
this.renderComponent(containerEl,
|
||||||
|
<AdvancedSettings
|
||||||
|
settings={this.plugin.settings}
|
||||||
|
updateSettings={updateSettings}
|
||||||
|
errors={errors}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dropdown.onChange(async (value) => {
|
private renderComponent(containerEl: HTMLElement, component: React.ReactNode) {
|
||||||
await onModelChange(value)
|
const div = containerEl.createDiv("div");
|
||||||
})
|
const root = createRoot(div);
|
||||||
|
root.render(component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
91
src/settings/components/AdvancedSettings.tsx
Normal file
91
src/settings/components/AdvancedSettings.tsx
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { InfioSettings } from '../../types/settings';
|
||||||
|
import SettingsItem from "./SettingsItem";
|
||||||
|
import TextSettingItem from "./TextSettingItem";
|
||||||
|
import FewShotExampleSettings from "./FewShotExampleSettings";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
settings: InfioSettings;
|
||||||
|
updateSettings: (update: Partial<InfioSettings>) => void;
|
||||||
|
errors: Map<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AdvancedSettings({ settings, updateSettings, errors }: Props): React.JSX.Element {
|
||||||
|
if (!settings.advancedMode) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TextSettingItem
|
||||||
|
name={"Chain of thought removal regex"}
|
||||||
|
description={
|
||||||
|
"This regex is used to remove the chain of thought tokens from the generated answer. If it is not implemented correctly, the chain of thought tokens will be included in the suggested completion."
|
||||||
|
}
|
||||||
|
placeholder={"your regex..."}
|
||||||
|
value={settings.chainOfThoughRemovalRegex}
|
||||||
|
errorMessage={errors.get("chainOfThoughRemovalRegex")}
|
||||||
|
setValue={(value: string) =>
|
||||||
|
updateSettings({
|
||||||
|
chainOfThoughRemovalRegex: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SettingsItem
|
||||||
|
name={"System message"}
|
||||||
|
description={
|
||||||
|
"This system message gives the models all the context and instructions they need to complete the answer generation tasks. You can edit this message to your liking. If you edit the chain of thought formatting, make sure to update the extract regex and examples accordingly."
|
||||||
|
}
|
||||||
|
display={"block"}
|
||||||
|
errorMessage={errors.get("systemMessage")}
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
className="infio-autocomplete-setting-item-textarea"
|
||||||
|
rows={10}
|
||||||
|
placeholder="Your system message..."
|
||||||
|
value={settings.systemMessage}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateSettings({
|
||||||
|
systemMessage: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
|
||||||
|
<SettingsItem
|
||||||
|
name={"User message template"}
|
||||||
|
description={
|
||||||
|
"This template defines how the prefix and suffix are formatted to create the user message. You have access to two variables: {{prefix}} and {{suffix}}. If you edit this, make sure to update the examples accordingly."
|
||||||
|
}
|
||||||
|
display={"block"}
|
||||||
|
errorMessage={errors.get("userMessageTemplate")}
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
className="infio-autocomplete-setting-item-textarea"
|
||||||
|
rows={3}
|
||||||
|
placeholder="{{prefix}}<mask/>{{suffix}}"
|
||||||
|
value={settings.userMessageTemplate}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateSettings({
|
||||||
|
userMessageTemplate: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
|
||||||
|
<FewShotExampleSettings
|
||||||
|
fewShotExamples={settings.fewShotExamples}
|
||||||
|
name={"Few shot examples"}
|
||||||
|
description={
|
||||||
|
"The model uses these examples to learn the expected answer format. Not all examples are sent at the same time. We only send the relevant examples, given the current cursor location. For example, the CodeBlock examples are only sent if the cursor is in a code block. If no special context is detected, we send the Text examples. Each context has a default of 2 examples, but you can add or remove examples if there is at least one per context. You can add more examples, but this will increase the inference costs."
|
||||||
|
}
|
||||||
|
setFewShotExamples={(value) =>
|
||||||
|
updateSettings({ fewShotExamples: value })
|
||||||
|
}
|
||||||
|
errorMessages={errors}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
41
src/settings/components/BasicAutoCompleteSettings.tsx
Normal file
41
src/settings/components/BasicAutoCompleteSettings.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { InfioSettings } from '../../types/settings';
|
||||||
|
|
||||||
|
import CheckBoxSettingItem from "./CheckBoxSettingItem";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
settings: InfioSettings;
|
||||||
|
updateSettings: (update: Partial<InfioSettings>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BasicAutoCompleteSettings({ settings, updateSettings }: Props): React.JSX.Element {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CheckBoxSettingItem
|
||||||
|
name={"Enable"}
|
||||||
|
description={
|
||||||
|
"If disabled, nothing will trigger the extension or can result in an API call."
|
||||||
|
}
|
||||||
|
enabled={settings.autocompleteEnabled}
|
||||||
|
setEnabled={(value) => updateSettings({ autocompleteEnabled: value })}
|
||||||
|
/>
|
||||||
|
<CheckBoxSettingItem
|
||||||
|
name={"Cache completions"}
|
||||||
|
description={
|
||||||
|
"If disabled, the plugin will not cache the completions. After accepting or rejecting a completion, the plugin will not remember it. This might result in more API calls."
|
||||||
|
}
|
||||||
|
enabled={settings.cacheSuggestions}
|
||||||
|
setEnabled={(value) => updateSettings({ cacheSuggestions: value })}
|
||||||
|
/>
|
||||||
|
<CheckBoxSettingItem
|
||||||
|
name={"Debug mode"}
|
||||||
|
description={
|
||||||
|
"If enabled, various debug messages will be logged to the console, such as the complete response from the API, including the chain of thought tokens."
|
||||||
|
}
|
||||||
|
enabled={settings.debugMode}
|
||||||
|
setEnabled={(value) => updateSettings({ debugMode: value })}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
40
src/settings/components/DangerZoneSettings.tsx
Normal file
40
src/settings/components/DangerZoneSettings.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { InfioSettings } from '../../types/settings';
|
||||||
|
|
||||||
|
import CheckBoxSettingItem from "./CheckBoxSettingItem";
|
||||||
|
import SettingsItem from "./SettingsItem";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
settings: InfioSettings;
|
||||||
|
updateSettings: (update: Partial<InfioSettings>) => void;
|
||||||
|
onReset: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DangerZoneSettings({ settings, updateSettings, onReset }: Props): React.JSX.Element {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SettingsItem
|
||||||
|
name={"Factory reset"}
|
||||||
|
description={
|
||||||
|
"Messed-up the settings? No worries, press this button! After that, the plugin will go back to the default settings. The URL and API key will remain unchanged."
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-label="Reset to default settings"
|
||||||
|
onClick={onReset}
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</SettingsItem>
|
||||||
|
<CheckBoxSettingItem
|
||||||
|
name={"Advanced mode"}
|
||||||
|
description={
|
||||||
|
"If you are familiar with prompt engineering, you can enable this setting to view the prompt generation and a few shot example settings. Turn off this button. It will not reset your changes; use the factory reset button for that."
|
||||||
|
}
|
||||||
|
enabled={settings.advancedMode}
|
||||||
|
setEnabled={(value) => updateSettings({ advancedMode: value })}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -187,7 +187,7 @@ export default function FewShotExampleSettings(
|
|||||||
className="setting-item-name"
|
className="setting-item-name"
|
||||||
style={{ width: "100%", textAlign: "left" }}
|
style={{ width: "100%", textAlign: "left" }}
|
||||||
>
|
>
|
||||||
Human Message
|
Human message
|
||||||
</div>
|
</div>
|
||||||
{props.errorMessages.get(`fewShotExamples.${index}.input`) !== undefined && (
|
{props.errorMessages.get(`fewShotExamples.${index}.input`) !== undefined && (
|
||||||
<div className="setting-item-description" style={{ width: "100%", textAlign: "left" }}>
|
<div className="setting-item-description" style={{ width: "100%", textAlign: "left" }}>
|
||||||
@ -207,7 +207,7 @@ export default function FewShotExampleSettings(
|
|||||||
className="setting-item-name"
|
className="setting-item-name"
|
||||||
style={{ width: "100%", textAlign: "left" }}
|
style={{ width: "100%", textAlign: "left" }}
|
||||||
>
|
>
|
||||||
Assistant Message
|
Assistant message
|
||||||
</div>
|
</div>
|
||||||
{props.errorMessages.get(`fewShotExamples.${index}.answer`) !== undefined && (
|
{props.errorMessages.get(`fewShotExamples.${index}.answer`) !== undefined && (
|
||||||
<div className="setting-item-description" style={{ width: "100%", textAlign: "left" }}>
|
<div className="setting-item-description" style={{ width: "100%", textAlign: "left" }}>
|
||||||
|
|||||||
129
src/settings/components/ModelParametersSettings.tsx
Normal file
129
src/settings/components/ModelParametersSettings.tsx
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { InfioSettings } from '../../types/settings';
|
||||||
|
import {
|
||||||
|
MAX_FREQUENCY_PENALTY,
|
||||||
|
MAX_MAX_TOKENS,
|
||||||
|
MAX_PRESENCE_PENALTY,
|
||||||
|
MAX_TEMPERATURE,
|
||||||
|
MAX_TOP_P,
|
||||||
|
MIN_FREQUENCY_PENALTY,
|
||||||
|
MIN_MAX_TOKENS,
|
||||||
|
MIN_PRESENCE_PENALTY,
|
||||||
|
MIN_TEMPERATURE,
|
||||||
|
MIN_TOP_P
|
||||||
|
} from "../versions";
|
||||||
|
|
||||||
|
import SliderSettingsItem from "./SliderSettingsItem";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
settings: InfioSettings;
|
||||||
|
updateSettings: (update: Partial<InfioSettings>) => void;
|
||||||
|
errors: Map<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ModelParametersSettings({ settings, updateSettings, errors }: Props): React.JSX.Element {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SliderSettingsItem
|
||||||
|
name={"Temperature"}
|
||||||
|
description={
|
||||||
|
"This parameter affects randomness in the sampling. Lower values result in more repetitive and deterministic responses. Higher temperatures will result in more unexpected or creative responses."
|
||||||
|
}
|
||||||
|
value={settings.modelOptions.temperature}
|
||||||
|
errorMessage={errors.get("modelOptions.temperature")}
|
||||||
|
setValue={(value: number) =>
|
||||||
|
updateSettings({
|
||||||
|
modelOptions: {
|
||||||
|
...settings.modelOptions,
|
||||||
|
temperature: value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
min={MIN_TEMPERATURE}
|
||||||
|
max={MAX_TEMPERATURE}
|
||||||
|
step={0.05}
|
||||||
|
/>
|
||||||
|
<SliderSettingsItem
|
||||||
|
name={"TopP"}
|
||||||
|
description={
|
||||||
|
"Like the temperature parameter, the Top P parameter affects the randomness in sampling. Lowering the value will limit the model's token selection to likelier tokens while increasing the value expands the model's token selection with lower likelihood tokens."
|
||||||
|
}
|
||||||
|
value={settings.modelOptions.top_p}
|
||||||
|
errorMessage={errors.get("modelOptions.top_p")}
|
||||||
|
setValue={(value: number) =>
|
||||||
|
updateSettings({
|
||||||
|
modelOptions: {
|
||||||
|
...settings.modelOptions,
|
||||||
|
top_p: value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
min={MIN_TOP_P}
|
||||||
|
max={MAX_TOP_P}
|
||||||
|
step={0.05}
|
||||||
|
/>
|
||||||
|
{settings.apiProvider !== "ollama" && (
|
||||||
|
<>
|
||||||
|
<SliderSettingsItem
|
||||||
|
name={"Frequency penalty"}
|
||||||
|
description={
|
||||||
|
"This parameter reduces the chance of repeating a token proportionally based on how often it has appeared in the text so far. This decreases the likelihood of repeating the exact same text in a response."
|
||||||
|
}
|
||||||
|
value={settings.modelOptions.frequency_penalty}
|
||||||
|
errorMessage={errors.get("modelOptions.frequency_penalty")}
|
||||||
|
setValue={(value: number) =>
|
||||||
|
updateSettings({
|
||||||
|
modelOptions: {
|
||||||
|
...settings.modelOptions,
|
||||||
|
frequency_penalty: value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
min={MIN_FREQUENCY_PENALTY}
|
||||||
|
max={MAX_FREQUENCY_PENALTY}
|
||||||
|
step={0.05}
|
||||||
|
/>
|
||||||
|
<SliderSettingsItem
|
||||||
|
name={"Presence penalty"}
|
||||||
|
description={
|
||||||
|
"This parameter reduces the chance of repeating any token that has appeared in the text so far. This increases the likelihood of introducing new topics in a response."
|
||||||
|
}
|
||||||
|
value={settings.modelOptions.presence_penalty}
|
||||||
|
errorMessage={errors.get("modelOptions.presence_penalty")}
|
||||||
|
setValue={(value: number) =>
|
||||||
|
updateSettings({
|
||||||
|
modelOptions: {
|
||||||
|
...settings.modelOptions,
|
||||||
|
presence_penalty: value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
min={MIN_PRESENCE_PENALTY}
|
||||||
|
max={MAX_PRESENCE_PENALTY}
|
||||||
|
step={0.05}
|
||||||
|
/>
|
||||||
|
<SliderSettingsItem
|
||||||
|
name={"Max tokens"}
|
||||||
|
description={
|
||||||
|
"This parameter changes the maximum number of tokens the model is allowed to generate. This includes the chain of thought tokens before the answer."
|
||||||
|
}
|
||||||
|
value={settings.modelOptions.max_tokens}
|
||||||
|
errorMessage={errors.get("modelOptions.max_tokens")}
|
||||||
|
setValue={(value: number) =>
|
||||||
|
updateSettings({
|
||||||
|
modelOptions: {
|
||||||
|
...settings.modelOptions,
|
||||||
|
max_tokens: value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
min={MIN_MAX_TOKENS}
|
||||||
|
max={MAX_MAX_TOKENS}
|
||||||
|
step={10}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,14 +1,18 @@
|
|||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
|
|
||||||
// import { PROVIDERS } from '../constants';
|
import InfioPlugin from "../../main";
|
||||||
import { ApiProvider } from '../types/llm/model';
|
import { ApiProvider } from '../../types/llm/model';
|
||||||
import { InfioSettings } from '../types/settings';
|
import { InfioSettings } from '../../types/settings';
|
||||||
import { GetAllProviders } from '../utils/api';
|
import { GetAllProviders } from '../../utils/api';
|
||||||
// import { siliconFlowDefaultModelId } from '../utils/api';
|
|
||||||
|
|
||||||
import { DropdownComponent, TextComponent, ToggleComponent } from './FormComponents';
|
import { DropdownComponent, TextComponent, ToggleComponent } from './FormComponents';
|
||||||
import { ComboBoxComponent } from './ProviderModelsPicker';
|
import { ComboBoxComponent } from './ProviderModelsPicker';
|
||||||
|
|
||||||
|
type CustomProviderSettingsProps = {
|
||||||
|
plugin: InfioPlugin;
|
||||||
|
onSettingsUpdate?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
type ProviderSettingKey =
|
type ProviderSettingKey =
|
||||||
| 'infioProvider'
|
| 'infioProvider'
|
||||||
| 'openrouterProvider'
|
| 'openrouterProvider'
|
||||||
@ -22,11 +26,6 @@ type ProviderSettingKey =
|
|||||||
| 'ollamaProvider'
|
| 'ollamaProvider'
|
||||||
| 'openaicompatibleProvider';
|
| 'openaicompatibleProvider';
|
||||||
|
|
||||||
interface ProviderSettingsProps {
|
|
||||||
settings: InfioSettings;
|
|
||||||
setSettings: (settings: InfioSettings) => Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const keyMap: Record<ApiProvider, ProviderSettingKey> = {
|
const keyMap: Record<ApiProvider, ProviderSettingKey> = {
|
||||||
'Infio': 'infioProvider',
|
'Infio': 'infioProvider',
|
||||||
'OpenRouter': 'openrouterProvider',
|
'OpenRouter': 'openrouterProvider',
|
||||||
@ -45,19 +44,26 @@ const getProviderSettingKey = (provider: ApiProvider): ProviderSettingKey => {
|
|||||||
return keyMap[provider];
|
return keyMap[provider];
|
||||||
};
|
};
|
||||||
|
|
||||||
const PROVIDERS = GetAllProviders();
|
const CustomProviderSettings: React.FC<CustomProviderSettingsProps> = ({ plugin, onSettingsUpdate }) => {
|
||||||
|
const settings = plugin.settings;
|
||||||
const ProviderSettings: React.FC<ProviderSettingsProps> = ({ settings, setSettings }) => {
|
|
||||||
const [currProvider, setCurrProvider] = useState(settings.defaultProvider);
|
const [currProvider, setCurrProvider] = useState(settings.defaultProvider);
|
||||||
|
|
||||||
|
const handleSettingsUpdate = async (newSettings: InfioSettings) => {
|
||||||
|
await plugin.setSettings(newSettings);
|
||||||
|
// 使用父组件传入的回调函数来刷新整个容器
|
||||||
|
onSettingsUpdate?.();
|
||||||
|
};
|
||||||
|
|
||||||
const providerSetting = useMemo(() => {
|
const providerSetting = useMemo(() => {
|
||||||
const providerKey = getProviderSettingKey(currProvider);
|
const providerKey = getProviderSettingKey(currProvider);
|
||||||
return settings[providerKey] || {};
|
return settings[providerKey] || {};
|
||||||
}, [currProvider, settings]);
|
}, [currProvider, settings]);
|
||||||
|
|
||||||
|
const providers = GetAllProviders();
|
||||||
|
|
||||||
const updateProvider = (provider: ApiProvider) => {
|
const updateProvider = (provider: ApiProvider) => {
|
||||||
setCurrProvider(provider);
|
setCurrProvider(provider);
|
||||||
setSettings({
|
handleSettingsUpdate({
|
||||||
...settings,
|
...settings,
|
||||||
defaultProvider: provider
|
defaultProvider: provider
|
||||||
});
|
});
|
||||||
@ -67,7 +73,7 @@ const ProviderSettings: React.FC<ProviderSettingsProps> = ({ settings, setSettin
|
|||||||
const providerKey = getProviderSettingKey(currProvider);
|
const providerKey = getProviderSettingKey(currProvider);
|
||||||
const providerSettings = settings[providerKey];
|
const providerSettings = settings[providerKey];
|
||||||
|
|
||||||
setSettings({
|
handleSettingsUpdate({
|
||||||
...settings,
|
...settings,
|
||||||
[providerKey]: {
|
[providerKey]: {
|
||||||
...providerSettings,
|
...providerSettings,
|
||||||
@ -80,7 +86,7 @@ const ProviderSettings: React.FC<ProviderSettingsProps> = ({ settings, setSettin
|
|||||||
const providerKey = getProviderSettingKey(currProvider);
|
const providerKey = getProviderSettingKey(currProvider);
|
||||||
const providerSettings = settings[providerKey];
|
const providerSettings = settings[providerKey];
|
||||||
|
|
||||||
setSettings({
|
handleSettingsUpdate({
|
||||||
...settings,
|
...settings,
|
||||||
[providerKey]: {
|
[providerKey]: {
|
||||||
...providerSettings,
|
...providerSettings,
|
||||||
@ -93,7 +99,7 @@ const ProviderSettings: React.FC<ProviderSettingsProps> = ({ settings, setSettin
|
|||||||
const providerKey = getProviderSettingKey(currProvider);
|
const providerKey = getProviderSettingKey(currProvider);
|
||||||
const providerSettings = settings[providerKey];
|
const providerSettings = settings[providerKey];
|
||||||
|
|
||||||
setSettings({
|
handleSettingsUpdate({
|
||||||
...settings,
|
...settings,
|
||||||
[providerKey]: {
|
[providerKey]: {
|
||||||
...providerSettings,
|
...providerSettings,
|
||||||
@ -103,7 +109,7 @@ const ProviderSettings: React.FC<ProviderSettingsProps> = ({ settings, setSettin
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateChatModelId = (provider: ApiProvider, modelId: string) => {
|
const updateChatModelId = (provider: ApiProvider, modelId: string) => {
|
||||||
setSettings({
|
handleSettingsUpdate({
|
||||||
...settings,
|
...settings,
|
||||||
chatModelProvider: provider,
|
chatModelProvider: provider,
|
||||||
chatModelId: modelId
|
chatModelId: modelId
|
||||||
@ -111,7 +117,7 @@ const ProviderSettings: React.FC<ProviderSettingsProps> = ({ settings, setSettin
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateApplyModelId = (provider: ApiProvider, modelId: string) => {
|
const updateApplyModelId = (provider: ApiProvider, modelId: string) => {
|
||||||
setSettings({
|
handleSettingsUpdate({
|
||||||
...settings,
|
...settings,
|
||||||
applyModelProvider: provider,
|
applyModelProvider: provider,
|
||||||
applyModelId: modelId
|
applyModelId: modelId
|
||||||
@ -119,7 +125,7 @@ const ProviderSettings: React.FC<ProviderSettingsProps> = ({ settings, setSettin
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateEmbeddingModelId = (provider: ApiProvider, modelId: string) => {
|
const updateEmbeddingModelId = (provider: ApiProvider, modelId: string) => {
|
||||||
setSettings({
|
handleSettingsUpdate({
|
||||||
...settings,
|
...settings,
|
||||||
embeddingModelProvider: provider,
|
embeddingModelProvider: provider,
|
||||||
embeddingModelId: modelId
|
embeddingModelId: modelId
|
||||||
@ -129,28 +135,28 @@ const ProviderSettings: React.FC<ProviderSettingsProps> = ({ settings, setSettin
|
|||||||
return (
|
return (
|
||||||
<div className="infio-llm-setting-provider">
|
<div className="infio-llm-setting-provider">
|
||||||
<DropdownComponent
|
<DropdownComponent
|
||||||
name="API Provider:"
|
name="Api provider:"
|
||||||
value={currProvider}
|
value={currProvider}
|
||||||
options={PROVIDERS}
|
options={providers}
|
||||||
onChange={updateProvider}
|
onChange={updateProvider}
|
||||||
/>
|
/>
|
||||||
<div className="infio-llm-setting-divider"></div>
|
<div className="infio-llm-setting-divider"></div>
|
||||||
<TextComponent
|
<TextComponent
|
||||||
name={currProvider + " API Key:"}
|
name={currProvider + " api key:"}
|
||||||
placeholder="Enter your API key"
|
placeholder="Enter your api key"
|
||||||
value={providerSetting.apiKey || ''}
|
value={providerSetting.apiKey || ''}
|
||||||
onChange={updateProviderApiKey}
|
onChange={updateProviderApiKey}
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
<div className="infio-llm-setting-divider"></div>
|
<div className="infio-llm-setting-divider"></div>
|
||||||
<ToggleComponent
|
<ToggleComponent
|
||||||
name="Use custom base URL"
|
name="Use custom base url"
|
||||||
value={providerSetting.useCustomUrl || false}
|
value={providerSetting.useCustomUrl || false}
|
||||||
onChange={updateProviderUseCustomUrl}
|
onChange={updateProviderUseCustomUrl}
|
||||||
/>
|
/>
|
||||||
{providerSetting.useCustomUrl && (
|
{providerSetting.useCustomUrl && (
|
||||||
<TextComponent
|
<TextComponent
|
||||||
placeholder="Enter your custom API endpoint URL"
|
placeholder="Enter your custom api endpoint url"
|
||||||
value={providerSetting.baseUrl || ''}
|
value={providerSetting.baseUrl || ''}
|
||||||
onChange={updateProviderBaseUrl}
|
onChange={updateProviderBaseUrl}
|
||||||
/>
|
/>
|
||||||
@ -159,21 +165,21 @@ const ProviderSettings: React.FC<ProviderSettingsProps> = ({ settings, setSettin
|
|||||||
<div className="infio-llm-setting-divider"></div>
|
<div className="infio-llm-setting-divider"></div>
|
||||||
<div className="infio-llm-setting-divider"></div>
|
<div className="infio-llm-setting-divider"></div>
|
||||||
<ComboBoxComponent
|
<ComboBoxComponent
|
||||||
name="Chat Model:"
|
name="Chat model:"
|
||||||
provider={settings.chatModelProvider || currProvider}
|
provider={settings.chatModelProvider || currProvider}
|
||||||
modelId={settings.chatModelId}
|
modelId={settings.chatModelId}
|
||||||
updateModel={updateChatModelId}
|
updateModel={updateChatModelId}
|
||||||
/>
|
/>
|
||||||
<div className="infio-llm-setting-divider"></div>
|
<div className="infio-llm-setting-divider"></div>
|
||||||
<ComboBoxComponent
|
<ComboBoxComponent
|
||||||
name="Autocomplete Model:"
|
name="Autocomplete model:"
|
||||||
provider={settings.applyModelProvider || currProvider}
|
provider={settings.applyModelProvider || currProvider}
|
||||||
modelId={settings.applyModelId}
|
modelId={settings.applyModelId}
|
||||||
updateModel={updateApplyModelId}
|
updateModel={updateApplyModelId}
|
||||||
/>
|
/>
|
||||||
<div className="infio-llm-setting-divider"></div>
|
<div className="infio-llm-setting-divider"></div>
|
||||||
<ComboBoxComponent
|
<ComboBoxComponent
|
||||||
name="Embedding Model:"
|
name="Embedding model:"
|
||||||
provider={settings.embeddingModelProvider || ApiProvider.Google}
|
provider={settings.embeddingModelProvider || ApiProvider.Google}
|
||||||
modelId={settings.embeddingModelId}
|
modelId={settings.embeddingModelId}
|
||||||
isEmbedding={true}
|
isEmbedding={true}
|
||||||
@ -185,4 +191,4 @@ const ProviderSettings: React.FC<ProviderSettingsProps> = ({ settings, setSettin
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ProviderSettings;
|
export default CustomProviderSettings;
|
||||||
37
src/settings/components/PostprocessingSettings.tsx
Normal file
37
src/settings/components/PostprocessingSettings.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { InfioSettings } from '../../types/settings';
|
||||||
|
|
||||||
|
import CheckBoxSettingItem from "./CheckBoxSettingItem";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
settings: InfioSettings;
|
||||||
|
updateSettings: (update: Partial<InfioSettings>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PostprocessingSettings({ settings, updateSettings }: Props): React.JSX.Element {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CheckBoxSettingItem
|
||||||
|
name={"Auto remove duplicate mat block indicators"}
|
||||||
|
description={
|
||||||
|
"The AI model might eagerly add a math block indicator ($), even though the cursor is already inside a math block. If this setting is enabled, the plugin will automatically remove these duplicate indicators from the completion."
|
||||||
|
}
|
||||||
|
enabled={settings.removeDuplicateMathBlockIndicator}
|
||||||
|
setEnabled={(value) =>
|
||||||
|
updateSettings({ removeDuplicateMathBlockIndicator: value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<CheckBoxSettingItem
|
||||||
|
name={"Auto remove duplicate code block indicators"}
|
||||||
|
description={
|
||||||
|
"The AI model might eagerly add a code block indicator (`), even though the cursor is already inside a code block. If this setting is enabled, the plugin will automatically remove these duplicate indicators from the completion."
|
||||||
|
}
|
||||||
|
enabled={settings.removeDuplicateCodeBlockIndicator}
|
||||||
|
setEnabled={(value) =>
|
||||||
|
updateSettings({ removeDuplicateCodeBlockIndicator: value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
63
src/settings/components/PreprocessingSettings.tsx
Normal file
63
src/settings/components/PreprocessingSettings.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { InfioSettings } from '../../types/settings';
|
||||||
|
import {
|
||||||
|
MAX_MAX_CHAR_LIMIT,
|
||||||
|
MIN_MAX_CHAR_LIMIT,
|
||||||
|
} from "../versions";
|
||||||
|
|
||||||
|
import CheckBoxSettingItem from "./CheckBoxSettingItem";
|
||||||
|
import SliderSettingsItem from "./SliderSettingsItem";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
settings: InfioSettings;
|
||||||
|
updateSettings: (update: Partial<InfioSettings>) => void;
|
||||||
|
errors: Map<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PreprocessingSettings({ settings, updateSettings, errors }: Props): React.JSX.Element {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CheckBoxSettingItem
|
||||||
|
name={"Don't include Dataview"}
|
||||||
|
description={
|
||||||
|
"Dataview(js) blocks can be quite long while not providing much value to the AI. If this setting is enabled, data view blocks will be removed promptly to reduce the number of tokens. This could save you some money in the long run."
|
||||||
|
}
|
||||||
|
enabled={settings.dontIncludeDataviews}
|
||||||
|
setEnabled={(value) =>
|
||||||
|
updateSettings({ dontIncludeDataviews: value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<SliderSettingsItem
|
||||||
|
name={"Maximum prefix length"}
|
||||||
|
description={
|
||||||
|
"The maximum number of characters that will be included in the prefix. A larger value will increase the context for the completion, but it can also increase the cost or push you over the token limit."
|
||||||
|
}
|
||||||
|
value={settings.maxPrefixCharLimit}
|
||||||
|
errorMessage={errors.get("maxPrefixCharLimit")}
|
||||||
|
setValue={(value: number) =>
|
||||||
|
updateSettings({ maxPrefixCharLimit: value })
|
||||||
|
}
|
||||||
|
min={MIN_MAX_CHAR_LIMIT}
|
||||||
|
max={MAX_MAX_CHAR_LIMIT}
|
||||||
|
step={100}
|
||||||
|
suffix={" chars"}
|
||||||
|
/>
|
||||||
|
<SliderSettingsItem
|
||||||
|
name={"Maximum suffix length"}
|
||||||
|
description={
|
||||||
|
"The maximum number of characters that will be included in the suffix. A larger value will increase the context for the completion, but it can also increase the cost or push you over the token limit."
|
||||||
|
}
|
||||||
|
value={settings.maxSuffixCharLimit}
|
||||||
|
errorMessage={errors.get("maxSuffixCharLimit")}
|
||||||
|
setValue={(value: number) =>
|
||||||
|
updateSettings({ maxSuffixCharLimit: value })
|
||||||
|
}
|
||||||
|
min={MIN_MAX_CHAR_LIMIT}
|
||||||
|
max={MAX_MAX_CHAR_LIMIT}
|
||||||
|
step={100}
|
||||||
|
suffix={" chars"}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
83
src/settings/components/PrivacySettings.tsx
Normal file
83
src/settings/components/PrivacySettings.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { InfioSettings } from '../../types/settings';
|
||||||
|
|
||||||
|
import SettingsItem from "./SettingsItem";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
settings: InfioSettings;
|
||||||
|
updateSettings: (update: Partial<InfioSettings>) => void;
|
||||||
|
errors: Map<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PrivacySettings({ settings, updateSettings, errors }: Props): React.JSX.Element {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SettingsItem
|
||||||
|
name={"Ignored files"}
|
||||||
|
description={
|
||||||
|
<div>
|
||||||
|
<p>This field enables you to specify files and directories that the plugin should ignore. When
|
||||||
|
you open any of these files, the plugin will automatically disable itself and display a
|
||||||
|
'disabled' status in the bottom menu. Enter one pattern per line. These patterns function
|
||||||
|
similar to glob patterns. Here are some frequently used patterns:</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>path/to/folder/**</code>: This pattern ignores all files and sub folders within
|
||||||
|
this folder.
|
||||||
|
</li>
|
||||||
|
<li><code>"**/secret/**"</code>: This pattern ignores any file located inside a 'secret'
|
||||||
|
directory,
|
||||||
|
regardless of its location in the path.
|
||||||
|
</li>
|
||||||
|
<li><code>!path/to/folder/example.md</code>: This pattern explicitly undoes an ignore,
|
||||||
|
making this file noticeable to the plugin.
|
||||||
|
</li>
|
||||||
|
<li><code>**/*Python*.md</code>: This pattern ignores any file with 'Python' in its name,
|
||||||
|
irrespective of its location.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
display={"block"}
|
||||||
|
errorMessage={errors.get("ignoredFilePatterns")}
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
className="infio-autocomplete-setting-item-textarea"
|
||||||
|
rows={10}
|
||||||
|
placeholder="Your file patterns, e.g., **/secret/**"
|
||||||
|
value={settings.ignoredFilePatterns}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateSettings({
|
||||||
|
ignoredFilePatterns: e.target.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
<SettingsItem
|
||||||
|
name={"Ignored tags"}
|
||||||
|
description={
|
||||||
|
<div>
|
||||||
|
<p>Files containing any of these tags will be ignored. When you open a file containing a
|
||||||
|
tag listed here, the plugin will automatically disable itself and display a 'disabled'
|
||||||
|
status in the bottom menu. Enter one tag per line.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
display={"block"}
|
||||||
|
errorMessage={errors.get("ignoredTags")}
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
className="infio-autocomplete-setting-item-textarea"
|
||||||
|
rows={10}
|
||||||
|
placeholder="Your file tags, e.g., secret"
|
||||||
|
value={settings.ignoredTags}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateSettings({
|
||||||
|
ignoredTags: e.target.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -2,9 +2,9 @@ import * as Popover from "@radix-ui/react-popover";
|
|||||||
import Fuse, { FuseResult } from "fuse.js";
|
import Fuse, { FuseResult } from "fuse.js";
|
||||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
import { ApiProvider } from "../types/llm/model";
|
import { ApiProvider } from "../../types/llm/model";
|
||||||
// import { PROVIDERS } from '../constants';
|
// import { PROVIDERS } from '../constants';
|
||||||
import { GetAllProviders, GetEmbeddingProviderModelIds, GetEmbeddingProviders, GetProviderModelIds } from "../utils/api";
|
import { GetAllProviders, GetEmbeddingProviderModelIds, GetEmbeddingProviders, GetProviderModelIds } from "../../utils/api";
|
||||||
|
|
||||||
type TextSegment = {
|
type TextSegment = {
|
||||||
text: string;
|
text: string;
|
||||||
46
src/settings/components/TriggerSettingsSection.tsx
Normal file
46
src/settings/components/TriggerSettingsSection.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { InfioSettings } from '../../types/settings';
|
||||||
|
import {
|
||||||
|
MAX_DELAY,
|
||||||
|
MIN_DELAY,
|
||||||
|
} from "../versions";
|
||||||
|
|
||||||
|
import SliderSettingsItem from "./SliderSettingsItem";
|
||||||
|
import TriggerSettings from "./TriggerSettings";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
settings: InfioSettings;
|
||||||
|
updateSettings: (update: Partial<InfioSettings>) => void;
|
||||||
|
errors: Map<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TriggerSettingsSection({ settings, updateSettings, errors }: Props): React.JSX.Element {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SliderSettingsItem
|
||||||
|
name={"Delay"}
|
||||||
|
description={
|
||||||
|
"Delay in ms between the last character typed and the completion request."
|
||||||
|
}
|
||||||
|
value={settings.delay}
|
||||||
|
errorMessage={errors.get("delay")}
|
||||||
|
setValue={(value: number) => updateSettings({ delay: value })}
|
||||||
|
min={MIN_DELAY}
|
||||||
|
max={MAX_DELAY}
|
||||||
|
step={100}
|
||||||
|
suffix={"ms"}
|
||||||
|
/>
|
||||||
|
<TriggerSettings
|
||||||
|
name={"Trigger words"}
|
||||||
|
description={
|
||||||
|
"Completions will be triggered if the text before the matches any of these words or characters. This can either be a direct string match or a regex match. When using a regex, make sure to include the end of line character ($)."
|
||||||
|
}
|
||||||
|
triggers={settings.triggers}
|
||||||
|
setValues={(triggers) => updateSettings({ triggers })}
|
||||||
|
errorMessage={errors.get("triggerWords")}
|
||||||
|
errorMessages={errors}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -50,8 +50,8 @@ export const modelOptionsSchema = z.object({
|
|||||||
export const fewShotExampleSchema = z.object({
|
export const fewShotExampleSchema = z.object({
|
||||||
// TODO: figure out how to make this compatible with the context enum and its namespace.
|
// TODO: figure out how to make this compatible with the context enum and its namespace.
|
||||||
context: z.enum(["Text", "Heading", "BlockQuotes", "UnorderedList", "NumberedList", "CodeBlock", "MathBlock", "TaskList"]),
|
context: z.enum(["Text", "Heading", "BlockQuotes", "UnorderedList", "NumberedList", "CodeBlock", "MathBlock", "TaskList"]),
|
||||||
input: z.string().min(3, { message: "The Input must be at least 3 characters long" }),
|
input: z.string().min(3, { message: "The input must be at least 3 characters long" }),
|
||||||
answer: z.string().min(3, { message: "The Answer must be at least 3 characters long" }),
|
answer: z.string().min(3, { message: "The answer must be at least 3 characters long" }),
|
||||||
}).strict();
|
}).strict();
|
||||||
|
|
||||||
export type FewShotExample = z.infer<typeof fewShotExampleSchema>;
|
export type FewShotExample = z.infer<typeof fewShotExampleSchema>;
|
||||||
|
|||||||
@ -153,7 +153,7 @@ export function getMentionableName(mentionable: Mentionable): string {
|
|||||||
case 'vault':
|
case 'vault':
|
||||||
return 'Vault'
|
return 'Vault'
|
||||||
case 'current-file':
|
case 'current-file':
|
||||||
return mentionable.file?.name ?? 'Current File'
|
return mentionable.file?.name ?? 'Current file'
|
||||||
case 'block':
|
case 'block':
|
||||||
return `${mentionable.file.name} (${mentionable.startLine}:${mentionable.endLine})`
|
return `${mentionable.file.name} (${mentionable.startLine}:${mentionable.endLine})`
|
||||||
case 'url':
|
case 'url':
|
||||||
|
|||||||
@ -209,7 +209,7 @@ export class PromptGenerator {
|
|||||||
},
|
},
|
||||||
onQueryProgressChange: onQueryProgressChange,
|
onQueryProgressChange: onQueryProgressChange,
|
||||||
})
|
})
|
||||||
filePrompt = `## Potentially Relevant Snippets from the current vault
|
filePrompt = `## Potentially relevant snippets from the current vault
|
||||||
${similaritySearchResults
|
${similaritySearchResults
|
||||||
.map(({ path, content, metadata }) => {
|
.map(({ path, content, metadata }) => {
|
||||||
const contentWithLineNumbers = this.addLineNumbersToContent({
|
const contentWithLineNumbers = this.addLineNumbersToContent({
|
||||||
@ -242,7 +242,7 @@ ${similaritySearchResults
|
|||||||
|
|
||||||
const urlPrompt =
|
const urlPrompt =
|
||||||
urls.length > 0
|
urls.length > 0
|
||||||
? `## Potentially Relevant Websearch Results
|
? `## Potentially relevant web search results
|
||||||
${(
|
${(
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
urls.map(
|
urls.map(
|
||||||
@ -387,7 +387,7 @@ ${customInstruction}
|
|||||||
return {
|
return {
|
||||||
role: 'user',
|
role: 'user',
|
||||||
content: `# Inputs
|
content: `# Inputs
|
||||||
## Current File
|
## Current file
|
||||||
Here is the file I'm looking at.
|
Here is the file I'm looking at.
|
||||||
\`\`\`${currentFile.path}
|
\`\`\`${currentFile.path}
|
||||||
${fileContent}
|
${fileContent}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user