mirror of
https://github.com/vrtmrz/ring-a-secretary.git
synced 2026-07-22 07:40:33 +00:00
- Fix styles
- Add showing consumed tokens - Model can be configurable.
This commit is contained in:
parent
cd9b506a1f
commit
a8b333af07
6 changed files with 122 additions and 24 deletions
|
|
@ -21,5 +21,6 @@ Note: We need the [API key of ChatGPT](https://platform.openai.com/account/api-k
|
|||
2. That is all. Now, it is time to chat!
|
||||
|
||||
## Memo
|
||||
- The dialogue is saved as a code block. We can continue the conversation anytime we want.
|
||||
- The dialogue is saved as a code block. We can resume the conversation anytime we want.
|
||||
- Also, they are searchable.
|
||||
- Long consultation consumes many tokens. If the dialogue gets longer, starting a brand-new one is recommended.
|
||||
|
|
|
|||
57
main.ts
57
main.ts
|
|
@ -5,11 +5,15 @@ import axios from "axios";
|
|||
interface RingASecretarySettings {
|
||||
token: string;
|
||||
defaultSystem: string;
|
||||
model: string;
|
||||
showConsumedTokens: boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: RingASecretarySettings = {
|
||||
token: "",
|
||||
defaultSystem: "",
|
||||
model: 'gpt-3.5-turbo-0301',
|
||||
showConsumedTokens: false,
|
||||
}
|
||||
|
||||
const roleSystem = "**SYSTEM**";
|
||||
|
|
@ -23,7 +27,7 @@ const MarkToRole = {
|
|||
"**USER**": "user",
|
||||
"**ASSISTANT**": "assistant",
|
||||
} as Record<DIALOGUE_ROLE, API_ROLE>;
|
||||
const WAIT_MARK = " WAITING FOR RESPONSE...";
|
||||
const WAIT_MARK = " <span class='ofx-thinking'></span>Please, bear with me.";
|
||||
|
||||
export default class RingASecretaryPlugin extends Plugin {
|
||||
settings: RingASecretarySettings;
|
||||
|
|
@ -58,18 +62,20 @@ export default class RingASecretaryPlugin extends Plugin {
|
|||
|
||||
const openai = new OpenAIApi(this.configuration);
|
||||
const response = await openai.createChatCompletion({
|
||||
|
||||
|
||||
model: "gpt-3.5-turbo-0301",
|
||||
model: this.settings.model,
|
||||
messages
|
||||
// temperature: 0,
|
||||
// max_tokens: 7,
|
||||
});
|
||||
const responseContent = response.data.choices[0].message?.content;
|
||||
// const responseRole = response.data.choices[0].message?.role;
|
||||
this.writeResponseToFile(responseContent ?? "");
|
||||
if (this.settings.showConsumedTokens && response.data.usage) {
|
||||
new Notice(`Consumed: ${response.data.usage.total_tokens} tokens`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Write response to processing file by replacing placeholder. */
|
||||
async writeResponseToFile(response: string) {
|
||||
if (!this.processingFile) return;
|
||||
const file = this.processingFile;
|
||||
|
|
@ -90,7 +96,7 @@ export default class RingASecretaryPlugin extends Plugin {
|
|||
const fx = el.createDiv({ text: "", cls: ["obsidian-fx"] });
|
||||
MarkdownRenderer.renderMarkdown(source, fx, sourcePath, this)
|
||||
const ops = el.createDiv({ text: "", cls: ["obsidian-fx-buttons"] });
|
||||
const span = ops.createSpan({ text: "USER:" });
|
||||
const span = ops.createEl("span", { text: "USER:", cls: "label" });
|
||||
const input = ops.createEl("textarea");
|
||||
const submit = ops.createEl("button", { text: "🤵" });
|
||||
const secInfo = ctx.getSectionInfo(el);
|
||||
|
|
@ -103,21 +109,25 @@ export default class RingASecretaryPlugin extends Plugin {
|
|||
const c = new AbortController();
|
||||
const submitFunc = async () => {
|
||||
if (source.contains(WAIT_MARK)) {
|
||||
new Notice("Some question is already in progress...", 5000)//TODO:MESSAGE
|
||||
new Notice("Some question is already in progress... If not, please modify the code block directly.", 5000);
|
||||
return
|
||||
}
|
||||
const f = app.vault.getAbstractFileByPath(sourcePath);
|
||||
if (!f) {
|
||||
new Notice("Could not edit the file", 3000);
|
||||
new Notice("Could not find the file", 3000);
|
||||
return;
|
||||
}
|
||||
if (!(f instanceof TFile)) {
|
||||
new Notice("Could not edit the file", 3000);
|
||||
new Notice("Could not find the file", 3000);
|
||||
return;
|
||||
}
|
||||
const dataMain = source;
|
||||
const text = input.value;
|
||||
//TODO:ESCAPE MARKDOWN?
|
||||
if (text.trim() == "") {
|
||||
new Notice("Request is empty");
|
||||
return;
|
||||
}
|
||||
//Note: ESCAPE MARKDOWN?
|
||||
const newBody = `${dataMain}\n${roleUser}:${text} \n${roleAssistant}: ${WAIT_MARK}`;
|
||||
|
||||
await app.vault.process(f, (data) => {
|
||||
|
|
@ -131,14 +141,19 @@ export default class RingASecretaryPlugin extends Plugin {
|
|||
this.processingFile = f;
|
||||
setTimeout(() => {
|
||||
this.askToAI(newBody).catch(err => {
|
||||
this.writeResponseToFile("Something has been occurred (PLUGIN)")
|
||||
this.writeResponseToFile(`Something has been occurred: ${err?.message}`);
|
||||
new Notice(`Something has been occurred: ${err?.message}`);
|
||||
});
|
||||
}, 20);
|
||||
app.vault.trigger("modify", f);
|
||||
}
|
||||
submit.addEventListener("click", submitFunc, { signal: c.signal });
|
||||
input.addEventListener("keydown", e => {
|
||||
console.dir(e);
|
||||
setTimeout(() => {
|
||||
if (input.clientHeight < input.scrollHeight) {
|
||||
input.style.height = `${input.scrollHeight + 4}px`;
|
||||
}
|
||||
}, 10);
|
||||
if (e.key == "Enter" && (e.shiftKey) && !e.isComposing) {
|
||||
e.preventDefault();
|
||||
submitFunc();
|
||||
|
|
@ -202,6 +217,16 @@ class RingASecretarySettingTab extends PluginSettingTab {
|
|||
this.plugin.settings.token = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
new Setting(containerEl)
|
||||
.setName('Model')
|
||||
.setDesc('Model')
|
||||
.addText(text => text
|
||||
.setPlaceholder('gpt-3.5-turbo-0301')
|
||||
.setValue(this.plugin.settings.model)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.model = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
new Setting(containerEl)
|
||||
.setName('Initial prompt')
|
||||
.setDesc('Initial prompt; i.e., instructions and prerequisites presented to the AI.')
|
||||
|
|
@ -212,5 +237,13 @@ class RingASecretarySettingTab extends PluginSettingTab {
|
|||
this.plugin.settings.defaultSystem = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
new Setting(containerEl)
|
||||
.setName('Show consumed tokens')
|
||||
.setDesc('When enabled, consumed tokens will be notified after each turn.')
|
||||
.addToggle(toggle => toggle.setValue(this.plugin.settings.showConsumedTokens)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.showConsumedTokens = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"id": "ring-a-secretary",
|
||||
"name": "Ring a secretary",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "ChatGPT powered secretary",
|
||||
"description": "Yet another ChatGPT-powered digital secretary",
|
||||
"author": "vorotamoroz",
|
||||
"authorUrl": "https://github.com/vrtmrz",
|
||||
"isDesktopOnly": false
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "ring-a-secretary",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ring-a-secretary",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.6",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "ring-a-secretary",
|
||||
"version": "0.0.1",
|
||||
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
|
||||
"version": "0.0.2",
|
||||
"description": "Yet another ChatGPT-powered digital secretary",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"dev": "node esbuild.config.mjs",
|
||||
|
|
|
|||
74
styles.css
74
styles.css
|
|
@ -16,18 +16,82 @@ If your plugin does not need CSS, delete this file.
|
|||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: auto;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.obsidian-fx-buttons button {
|
||||
flex-grow: 0;
|
||||
padding: 4px 8px 4px 8px;
|
||||
margin: 4px 0px 4px 4px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.obsidian-fx-buttons .label {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.is-phone .obsidian-fx-buttons .label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.obsidian-fx * {
|
||||
margin: auto 2px;
|
||||
margin: auto auto;
|
||||
}
|
||||
|
||||
.obsidian-fx textarea {
|
||||
flex-grow: 1;
|
||||
min-height: 3ex;
|
||||
resize: vertical;
|
||||
-webkit-appearance: none;
|
||||
outline: none;
|
||||
outline-style: none;
|
||||
box-shadow: none;
|
||||
border: 0px;
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
padding: 2px 4px;
|
||||
font-size: var(--font-adaptive-normal);
|
||||
font-weight: var(--normal-weight);
|
||||
line-height: var(--line-height);
|
||||
font-family: var(--font-editor);
|
||||
}
|
||||
|
||||
.obsidian-fx textarea:focus {
|
||||
-webkit-appearance: none;
|
||||
outline: none;
|
||||
outline-style: none;
|
||||
}
|
||||
|
||||
.ofx-thinking {
|
||||
animation: ofx-thinking-animation 1s infinite;
|
||||
}
|
||||
|
||||
.ofx-thinking::after {
|
||||
content: " ......";
|
||||
animation: ofx-thinking-animation 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes ofx-thinking-animation {
|
||||
0% {
|
||||
content: ". .....";
|
||||
}
|
||||
|
||||
20% {
|
||||
content: ".. ....";
|
||||
}
|
||||
|
||||
40% {
|
||||
content: "... ...";
|
||||
}
|
||||
|
||||
60% {
|
||||
content: ".... ..";
|
||||
}
|
||||
|
||||
80% {
|
||||
content: "..... .";
|
||||
}
|
||||
|
||||
100% {
|
||||
content: " ...... ";
|
||||
}
|
||||
}
|
||||
/*
|
||||
.obsidian-fx textarea .obsidian-fx input,
|
||||
.obsidian-fx button {} */
|
||||
|
|
|
|||
Loading…
Reference in a new issue