-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.ts
187 lines (171 loc) · 5.16 KB
/
main.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
import {
Plugin,
MarkdownView,
Notice,
RequestUrlParam,
requestUrl,
PluginSettingTab,
Setting,
App,
} from "obsidian";
interface PluginSettings {
api_key: string;
status_notices: boolean;
}
const DEFAULT_SETTINGS: PluginSettings = {
status_notices: true,
api_key: "",
};
export default class AutoCorrecter extends Plugin {
settings: PluginSettings;
async onload() {
await this.loadSettings();
this.onKeyDown = this.onKeyDown.bind(this);
this.registerDomEvent(document, "keydown", this.onKeyDown, true);
this.addSettingTab(new SettingTab(this.app, this));
if (!this.settings.api_key) {
new Notice(
"Please enter your Together.ai API key in the settings tab to use Obsidian AutoCorrect."
);
}
}
onunload() {}
async loadSettings() {
this.settings = Object.assign(
{},
DEFAULT_SETTINGS,
await this.loadData()
);
}
async saveSettings() {
await this.saveData(this.settings);
}
stripLeadingWhitespace(input: string): [string, string] {
const match = input.match(/^[\t ]*/);
const leadingWhitespace = match ? match[0] : "";
const parsedText = input.replace(/^[\t ]*/, "");
return [leadingWhitespace, parsedText];
}
async onKeyDown(event: KeyboardEvent) {
if (event.key === "Enter") {
const view = this.app.workspace.getActiveViewOfType(MarkdownView);
// Make sure the user is editing a Markdown file.
if (view) {
const cursor = view.editor.getCursor();
const text = view.editor.getLine(cursor.line);
// LLM has a very hard time reproducing the leading tabs in markdown bullet points
const [leadingWhitespace, parsedText] =
this.stripLeadingWhitespace(text);
console.log(cursor);
console.log(text);
let status: Notice | null = null;
if (this.settings.status_notices) {
status = new Notice(
"Correcting spelling on line " +
(cursor.line + 1) +
"...",
0
);
}
const response: any = await this.getLLMResponse(parsedText);
console.log(response);
if (response.corrected_spelling) {
const correctedText =
leadingWhitespace + response.corrected_spelling;
view.editor.setLine(cursor.line, correctedText);
if (status) {
status.hide();
}
} else {
if (status) {
status.hide();
}
new Notice(
"Error correcting spelling. Make sure API key is correct and Internet is working."
);
}
}
}
}
async getLLMResponse(text: string) {
const url = "https://api.together.xyz/v1/chat/completions";
const apiKey = this.settings.api_key;
const system_content =
"You will receive user input containing text with potential spelling errors. Your task is to correct these errors while preserving the original meaning. The original text may contain Markdown, which should be maintained. You should ONLY correct spelling errors; grammar and punctuation should remain EXACTLY the same. The output should be a corrected version of the input text. Additionally, the model should leave correct words verbatim, even if they may be offensive, slang, abbreviations, brand names, or other non-standard language intentionally input by the user. This system will be utilized for real-time autocorrection, so refrain from altering a word unless you understand the user's intended meaning.";
const user_content = '{"user_text": "' + text + '"}';
const headers: Record<string, string> = {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
};
const data = {
model: "mistralai/Mistral-7B-Instruct-v0.1",
messages: [
{ role: "system", content: system_content },
{ role: "user", content: user_content },
],
response_format: {
type: "json_object",
schema: {
type: "object",
properties: {
corrected_spelling: { type: "string" },
},
required: ["corrected_spelling"],
},
},
temperature: 0.7,
};
const params: RequestUrlParam = {
method: "POST",
headers,
body: JSON.stringify(data),
url: url,
};
try {
const response = await requestUrl(params);
const json_response = JSON.parse(
response.json.choices[0].message.content
);
return json_response;
} catch (error) {
console.error(error);
return error;
}
}
}
class SettingTab extends PluginSettingTab {
plugin: AutoCorrecter;
constructor(app: App, plugin: AutoCorrecter) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const { containerEl } = this;
containerEl.empty();
new Setting(containerEl)
.setName("Together.ai API Key")
.setDesc(
"Create a account at https://www.together.ai/ and enter your API key for the plugin to work."
)
.addText((text) =>
text
.setPlaceholder("Enter your key")
.setValue(this.plugin.settings.api_key)
.onChange(async (value) => {
this.plugin.settings.api_key = value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName("Status Notices")
.setDesc("Show status notice popups when correcting spelling.")
.addToggle((toggle) => {
toggle
.setValue(this.plugin.settings.status_notices)
.onChange(async (value) => {
this.plugin.settings.status_notices = value;
await this.plugin.saveSettings();
});
});
}
}