Skip to content

Commit

Permalink
Add logic for settings migrations
Browse files Browse the repository at this point in the history
  • Loading branch information
taichimaeda committed Apr 19, 2024
1 parent f8441a3 commit 55dd630
Show file tree
Hide file tree
Showing 11 changed files with 337 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
MarkpilotSettings,
MarkpilotSettingTab,
} from './settings';
import { SettingsMigrationsRunner } from './settings/runner';

export default class Markpilot extends Plugin {
settings: MarkpilotSettings;
Expand All @@ -35,6 +36,8 @@ export default class Markpilot extends Plugin {

async onload() {
await this.loadSettings();
const runner = new SettingsMigrationsRunner(this);
await runner.apply();
this.addSettingTab(new MarkpilotSettingTab(this.app, this));

const { settings } = this;
Expand Down
1 change: 1 addition & 0 deletions src/settings/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type SettingsMigrator = (settings: object) => object;
33 changes: 33 additions & 0 deletions src/settings/migrators/1.1.0-1.2.0.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { SettingsMigrator } from '..';
import { MarkpilotSettings1_1_0 } from '../versions/1.1.0';
import { MarkpilotSettings1_2_0 } from '../versions/1.2.0';

export const migrateVersion1_1_0_toVersion1_2_0: SettingsMigrator = (
settings: MarkpilotSettings1_1_0,
) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const newSettings: MarkpilotSettings1_2_0 = structuredClone(settings) as any;
newSettings.providers = {
openai: {
apiKey: settings.apiKey,
},
openrouter: {
apiKey: undefined,
},
ollama: {
apiUrl: undefined,
},
};
newSettings.completions.provider = 'openai';
newSettings.completions.ignoredFiles = [];
newSettings.completions.ignoredTags = [];
newSettings.chat.provider = 'openai';
// Update if default models are still selected.
if (settings.completions.model === 'gpt-3.5-turbo-instruct') {
newSettings.completions.model = 'gpt-3.5-turbo';
}
if (settings.chat.model === 'gpt-3.5-turbo-0125') {
newSettings.chat.model = 'gpt-3.5-turbo';
}
return newSettings;
};
29 changes: 29 additions & 0 deletions src/settings/runner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { SettingsMigrator } from '.';
import Markpilot from '../main';
import { migrateVersion1_1_0_toVersion1_2_0 } from './migrators/1.1.0-1.2.0';

export class SettingsMigrationsRunner {
migrators: Record<string, SettingsMigrator> = {
'1.1.0': migrateVersion1_1_0_toVersion1_2_0,
};

constructor(private plugin: Markpilot) {}

async apply() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let settings = this.plugin.settings as any;

while (true) {
// Settings versions and migrations were introduced from version 1.1.0.
const version = settings.version ?? '1.1.0';
const migrator = this.migrators[version];
if (migrator === undefined) {
break;
}
settings = migrator(settings);
}

this.plugin.settings = settings;
await this.plugin.saveSettings();
}
}
30 changes: 30 additions & 0 deletions src/settings/versions/1.1.0/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ChatCompletionsModel, ChatHistory, CompletionsModel } from './openai';

export interface MarkpilotSettings1_1_0 {
apiKey: string | undefined;
completions: {
enabled: boolean;
model: CompletionsModel;
maxTokens: number;
temperature: number;
waitTime: number;
windowSize: number;
acceptKey: string;
rejectKey: string;
};
chat: {
enabled: boolean;
model: ChatCompletionsModel;
maxTokens: number;
temperature: number;
history: ChatHistory;
};
cache: {
enabled: boolean;
};
usage: {
dailyCosts: Record<string, number>; // e.g. '2021-09-01' to 10.0 (USD)
monthlyCosts: Record<string, number>; // e.g. '2021-09' to 100.0 (USD)
monthlyLimit: number;
};
}
95 changes: 95 additions & 0 deletions src/settings/versions/1.1.0/openai.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
export const COMPLETIONS_MODELS = [
'gpt-3.5-turbo-instruct',
'davinci-002',
'babbage-002',
] as const;

export const CHAT_COMPLETIONS_MODELS = [
'gpt-4-0125-preview',
'gpt-4-turbo-preview',
'gpt-4-1106-preview',
'gpt-4-vision-preview',
'gpt-4',
'gpt-4-0314',
'gpt-4-0613',
'gpt-4-32k',
'gpt-4-32k-0314',
'gpt-4-32k-0613',
'gpt-3.5-turbo',
'gpt-3.5-turbo-16k',
'gpt-3.5-turbo-0301',
'gpt-3.5-turbo-0613',
'gpt-3.5-turbo-1106',
'gpt-3.5-turbo-0125',
'gpt-3.5-turbo-16k-0613',
] as const;

export const MODEL_INPUT_COSTS: Record<
| (typeof COMPLETIONS_MODELS)[number]
| (typeof CHAT_COMPLETIONS_MODELS)[number],
number
> = {
'gpt-3.5-turbo-instruct': 1.5,
'davinci-002': 12.0,
'babbage-002': 1.6,
'gpt-4-0125-preview': 10.0,
'gpt-4-turbo-preview': 10.0,
'gpt-4-1106-preview': 10.0,
'gpt-4-vision-preview': 10.0,
'gpt-4': 30.0,
'gpt-4-0314': 30.0,
'gpt-4-0613': 30.0,
'gpt-4-32k': 60.0,
'gpt-4-32k-0314': 60.0,
'gpt-4-32k-0613': 60.0,
'gpt-3.5-turbo': 0.5,
'gpt-3.5-turbo-16k': 0.5,
'gpt-3.5-turbo-0301': 0.5,
'gpt-3.5-turbo-0613': 0.5,
'gpt-3.5-turbo-1106': 0.5,
'gpt-3.5-turbo-0125': 0.5,
'gpt-3.5-turbo-16k-0613': 0.5,
} as const;

export const MODEL_OUTPUT_COSTS: Record<
| (typeof COMPLETIONS_MODELS)[number]
| (typeof CHAT_COMPLETIONS_MODELS)[number],
number
> = {
'gpt-3.5-turbo-instruct': 2.0,
'davinci-002': 12.0,
'babbage-002': 1.6,
'gpt-4-0125-preview': 30,
'gpt-4-turbo-preview': 30,
'gpt-4-1106-preview': 30,
'gpt-4-vision-preview': 30,
'gpt-4': 60,
'gpt-4-0314': 60,
'gpt-4-0613': 60,
'gpt-4-32k': 120,
'gpt-4-32k-0314': 120,
'gpt-4-32k-0613': 120,
'gpt-3.5-turbo': 1.5,
'gpt-3.5-turbo-16k': 1.5,
'gpt-3.5-turbo-0301': 1.5,
'gpt-3.5-turbo-0613': 1.5,
'gpt-3.5-turbo-1106': 1.5,
'gpt-3.5-turbo-0125': 1.5,
'gpt-3.5-turbo-16k-0613': 1.5,
};

export type CompletionsModel = (typeof COMPLETIONS_MODELS)[number];

export type ChatCompletionsModel = (typeof CHAT_COMPLETIONS_MODELS)[number];

export type ChatRole = 'system' | 'assistant' | 'user';

export interface ChatMessage {
role: ChatRole;
content: string;
}

export interface ChatHistory {
messages: ChatMessage[];
response: string;
}
55 changes: 55 additions & 0 deletions src/settings/versions/1.2.0/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { MarkpilotSettings } from 'src/settings';
import { Equal, Expect } from 'src/utils';
import { Model } from './models';
import { Provider } from './provider';
import { ChatHistory } from './types';

export interface MarkpilotSettings1_2_0 {
version: string;
providers: {
openai: {
apiKey: string | undefined;
};
openrouter: {
apiKey: string | undefined;
};
ollama: {
apiUrl: string | undefined;
};
};
completions: {
enabled: boolean;
provider: Provider;
model: Model;
maxTokens: number;
temperature: number;
waitTime: number;
windowSize: number;
acceptKey: string;
rejectKey: string;
ignoredFiles: string[];
ignoredTags: string[];
};
chat: {
enabled: boolean;
provider: Provider;
model: Model;
maxTokens: number;
temperature: number;
history: ChatHistory;
};
cache: {
enabled: boolean;
};
usage: {
dailyCosts: Record<string, number>; // e.g. '2021-09-01' to 10.0 (USD)
monthlyCosts: Record<string, number>; // e.g. '2021-09' to 100.0 (USD)
monthlyLimit: number;
};
}

// Check the settings type in this version matches the current settings type.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
type AssertEqualCurrentSettings = Expect<
Equal<MarkpilotSettings1_2_0, MarkpilotSettings>
>;
60 changes: 60 additions & 0 deletions src/settings/versions/1.2.0/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Provider } from './provider';

export type OpenAIModel = (typeof OPENAI_MODELS)[number];

export type OpenRouterModel = (typeof OPENROUTER_MODELS)[number];

export type OllamaModel = (typeof OLLAMA_MODELS)[number];

export type Model = OpenAIModel | OpenRouterModel | OllamaModel;

export const OPENAI_MODELS = [
'gpt-3.5-turbo-instruct',
'davinci-002',
'babbage-002',
'gpt-4-0125-preview',
'gpt-4-turbo-preview',
'gpt-4-1106-preview',
'gpt-4-vision-preview',
'gpt-4',
'gpt-4-0314',
'gpt-4-0613',
'gpt-4-32k',
'gpt-4-32k-0314',
'gpt-4-32k-0613',
'gpt-3.5-turbo',
'gpt-3.5-turbo-16k',
'gpt-3.5-turbo-0301',
'gpt-3.5-turbo-0613',
'gpt-3.5-turbo-1106',
'gpt-3.5-turbo-0125',
'gpt-3.5-turbo-16k-0613',
] as const;

// TODO:
// This is a placeholder.
export const OPENROUTER_MODELS = [
'openai/gpt-3.5-turbo',
'openai/gpt-4-turbo',
] as const;

// TODO:
// This is a placeholder.
export const OLLAMA_MODELS = [
'llama2',
'llama3',
'codellama',
'phind-codellama',
] as const;

export const MODELS = {
openai: OPENAI_MODELS,
openrouter: OPENROUTER_MODELS,
ollama: OLLAMA_MODELS,
};

export const DEFAULT_MODELS: Record<Provider, Model> = {
openai: 'gpt-3.5-turbo',
openrouter: 'openai/gpt-3.5-turbo',
ollama: 'llama2',
};
13 changes: 13 additions & 0 deletions src/settings/versions/1.2.0/provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export type OnlineProvider = (typeof ONLINE_PROVIDERS)[number];

export type OfflineProvider = (typeof OFFLINE_PROVIDERS)[number];

export type Provider = OnlineProvider | OfflineProvider;

export const ONLINE_PROVIDERS = ['openai', 'openrouter'] as const;

export const OFFLINE_PROVIDERS = ['ollama'] as const;

export const PROVIDERS = [...ONLINE_PROVIDERS, ...OFFLINE_PROVIDERS] as const;

export const DEFAULT_PROVIDER = 'openai' as Provider;
11 changes: 11 additions & 0 deletions src/settings/versions/1.2.0/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type ChatRole = 'system' | 'assistant' | 'user';

export interface ChatMessage {
role: ChatRole;
content: string;
}

export interface ChatHistory {
messages: ChatMessage[];
response: string;
}
7 changes: 7 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,10 @@ export function getDaysInCurrentMonth(): Date[] {
}
return dates;
}

// Utility types used for settings migration.
export type Expect<T extends true> = T;
export type Equal<X, Y> =
(<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
? true
: false;

0 comments on commit 55dd630

Please sign in to comment.