Fix CORS by removing outdated anthropic-beta header
The anthropic-beta: messages-2023-12-15 header triggered CORS preflight failures. Removed it (streaming is stable API) and switched non-streaming chat() to use Obsidian's requestUrl which bypasses CORS entirely. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,14 +19,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|||||||
// src/main.ts
|
// src/main.ts
|
||||||
var main_exports = {};
|
var main_exports = {};
|
||||||
__export(main_exports, {
|
__export(main_exports, {
|
||||||
default: () => VaultChatPlugin
|
default: () => MemexChatPlugin
|
||||||
});
|
});
|
||||||
module.exports = __toCommonJS(main_exports);
|
module.exports = __toCommonJS(main_exports);
|
||||||
var import_obsidian3 = require("obsidian");
|
var import_obsidian4 = require("obsidian");
|
||||||
|
|
||||||
// src/ChatView.ts
|
// src/ChatView.ts
|
||||||
var import_obsidian = require("obsidian");
|
var import_obsidian = require("obsidian");
|
||||||
var VIEW_TYPE_VAULT_CHAT = "vault-chat-view";
|
var VIEW_TYPE_MEMEX_CHAT = "memex-chat-view";
|
||||||
var ChatView = class extends import_obsidian.ItemView {
|
var ChatView = class extends import_obsidian.ItemView {
|
||||||
constructor(leaf, plugin) {
|
constructor(leaf, plugin) {
|
||||||
super(leaf);
|
super(leaf);
|
||||||
@@ -39,10 +39,10 @@ var ChatView = class extends import_obsidian.ItemView {
|
|||||||
this.renderComponent = new import_obsidian.Component();
|
this.renderComponent = new import_obsidian.Component();
|
||||||
}
|
}
|
||||||
getViewType() {
|
getViewType() {
|
||||||
return VIEW_TYPE_VAULT_CHAT;
|
return VIEW_TYPE_MEMEX_CHAT;
|
||||||
}
|
}
|
||||||
getDisplayText() {
|
getDisplayText() {
|
||||||
return "Vault Chat";
|
return "Memex Chat";
|
||||||
}
|
}
|
||||||
getIcon() {
|
getIcon() {
|
||||||
return "message-circle";
|
return "message-circle";
|
||||||
@@ -67,7 +67,7 @@ var ChatView = class extends import_obsidian.ItemView {
|
|||||||
root.empty();
|
root.empty();
|
||||||
root.addClass("vc-root");
|
root.addClass("vc-root");
|
||||||
const header = root.createDiv("vc-header");
|
const header = root.createDiv("vc-header");
|
||||||
header.createEl("span", { text: "Vault Chat", cls: "vc-header-title" });
|
header.createEl("span", { text: "Memex Chat", cls: "vc-header-title" });
|
||||||
const headerActions = header.createDiv("vc-header-actions");
|
const headerActions = header.createDiv("vc-header-actions");
|
||||||
const newThreadBtn = headerActions.createEl("button", { cls: "vc-icon-btn", title: "Neuer Thread" });
|
const newThreadBtn = headerActions.createEl("button", { cls: "vc-icon-btn", title: "Neuer Thread" });
|
||||||
newThreadBtn.innerHTML = `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" fill="none"><path d="M12 5v14M5 12h14" stroke-width="2" stroke-linecap="round"/></svg>`;
|
newThreadBtn.innerHTML = `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" fill="none"><path d="M12 5v14M5 12h14" stroke-width="2" stroke-linecap="round"/></svg>`;
|
||||||
@@ -645,21 +645,24 @@ var VaultSearch = class {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// src/ClaudeClient.ts
|
// src/ClaudeClient.ts
|
||||||
|
var import_obsidian2 = require("obsidian");
|
||||||
var ClaudeClient = class {
|
var ClaudeClient = class {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.baseUrl = "https://api.anthropic.com/v1/messages";
|
this.baseUrl = "https://api.anthropic.com/v1/messages";
|
||||||
}
|
}
|
||||||
|
headers(apiKey) {
|
||||||
|
return {
|
||||||
|
"content-type": "application/json",
|
||||||
|
"x-api-key": apiKey,
|
||||||
|
"anthropic-version": "2023-06-01"
|
||||||
|
};
|
||||||
|
}
|
||||||
/** Stream a chat completion, yielding text chunks */
|
/** Stream a chat completion, yielding text chunks */
|
||||||
async *streamChat(messages, options) {
|
async *streamChat(messages, options) {
|
||||||
var _a, _b, _c, _d, _e, _f;
|
var _a, _b, _c, _d, _e, _f;
|
||||||
const response = await fetch(this.baseUrl, {
|
const response = await fetch(this.baseUrl, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: this.headers(options.apiKey),
|
||||||
"content-type": "application/json",
|
|
||||||
"x-api-key": options.apiKey,
|
|
||||||
"anthropic-version": "2023-06-01",
|
|
||||||
"anthropic-beta": "messages-2023-12-15"
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model: options.model,
|
model: options.model,
|
||||||
max_tokens: (_a = options.maxTokens) != null ? _a : 2048,
|
max_tokens: (_a = options.maxTokens) != null ? _a : 2048,
|
||||||
@@ -712,34 +715,30 @@ var ClaudeClient = class {
|
|||||||
}
|
}
|
||||||
yield { type: "done" };
|
yield { type: "done" };
|
||||||
}
|
}
|
||||||
/** Non-streaming version for simpler use cases */
|
/** Non-streaming version — uses Obsidian's requestUrl to bypass CORS */
|
||||||
async chat(messages, options) {
|
async chat(messages, options) {
|
||||||
var _a, _b, _c, _d;
|
var _a, _b, _c, _d;
|
||||||
const response = await fetch(this.baseUrl, {
|
const response = await (0, import_obsidian2.requestUrl)({
|
||||||
|
url: this.baseUrl,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: this.headers(options.apiKey),
|
||||||
"content-type": "application/json",
|
|
||||||
"x-api-key": options.apiKey,
|
|
||||||
"anthropic-version": "2023-06-01"
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model: options.model,
|
model: options.model,
|
||||||
max_tokens: (_a = options.maxTokens) != null ? _a : 2048,
|
max_tokens: (_a = options.maxTokens) != null ? _a : 2048,
|
||||||
system: options.systemPrompt,
|
system: options.systemPrompt,
|
||||||
messages
|
messages
|
||||||
})
|
}),
|
||||||
|
throw: false
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (response.status >= 400) {
|
||||||
const err = await response.text();
|
throw new Error(`API Error ${response.status}: ${response.text}`);
|
||||||
throw new Error(`API Error ${response.status}: ${err}`);
|
|
||||||
}
|
}
|
||||||
const data = await response.json();
|
return (_d = (_c = (_b = response.json.content) == null ? void 0 : _b[0]) == null ? void 0 : _c.text) != null ? _d : "";
|
||||||
return (_d = (_c = (_b = data.content) == null ? void 0 : _b[0]) == null ? void 0 : _c.text) != null ? _d : "";
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// src/SettingsTab.ts
|
// src/SettingsTab.ts
|
||||||
var import_obsidian2 = require("obsidian");
|
var import_obsidian3 = require("obsidian");
|
||||||
var DEFAULT_SETTINGS = {
|
var DEFAULT_SETTINGS = {
|
||||||
apiKey: "",
|
apiKey: "",
|
||||||
model: "claude-opus-4-5-20251101",
|
model: "claude-opus-4-5-20251101",
|
||||||
@@ -764,7 +763,7 @@ var MODELS = [
|
|||||||
{ id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5 (Schnell)" },
|
{ id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5 (Schnell)" },
|
||||||
{ id: "claude-opus-4-5-20251101", name: "Claude Opus 4.5" }
|
{ id: "claude-opus-4-5-20251101", name: "Claude Opus 4.5" }
|
||||||
];
|
];
|
||||||
var VaultChatSettingsTab = class extends import_obsidian2.PluginSettingTab {
|
var MemexChatSettingsTab = class extends import_obsidian3.PluginSettingTab {
|
||||||
constructor(app, plugin) {
|
constructor(app, plugin) {
|
||||||
super(app, plugin);
|
super(app, plugin);
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
@@ -772,15 +771,15 @@ var VaultChatSettingsTab = class extends import_obsidian2.PluginSettingTab {
|
|||||||
display() {
|
display() {
|
||||||
const { containerEl } = this;
|
const { containerEl } = this;
|
||||||
containerEl.empty();
|
containerEl.empty();
|
||||||
containerEl.createEl("h2", { text: "Vault Chat Einstellungen" });
|
containerEl.createEl("h2", { text: "Memex Chat Einstellungen" });
|
||||||
containerEl.createEl("h3", { text: "Claude API" });
|
containerEl.createEl("h3", { text: "Claude API" });
|
||||||
new import_obsidian2.Setting(containerEl).setName("API Key").setDesc("Dein Anthropic API Key (sk-ant-...)").addText(
|
new import_obsidian3.Setting(containerEl).setName("API Key").setDesc("Dein Anthropic API Key (sk-ant-...)").addText(
|
||||||
(text) => text.setPlaceholder("sk-ant-api03-...").setValue(this.plugin.settings.apiKey).onChange(async (value) => {
|
(text) => text.setPlaceholder("sk-ant-api03-...").setValue(this.plugin.settings.apiKey).onChange(async (value) => {
|
||||||
this.plugin.settings.apiKey = value.trim();
|
this.plugin.settings.apiKey = value.trim();
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
new import_obsidian2.Setting(containerEl).setName("Modell").setDesc("Welches Claude-Modell verwenden?").addDropdown((drop) => {
|
new import_obsidian3.Setting(containerEl).setName("Modell").setDesc("Welches Claude-Modell verwenden?").addDropdown((drop) => {
|
||||||
for (const m of MODELS)
|
for (const m of MODELS)
|
||||||
drop.addOption(m.id, m.name);
|
drop.addOption(m.id, m.name);
|
||||||
drop.setValue(this.plugin.settings.model).onChange(async (value) => {
|
drop.setValue(this.plugin.settings.model).onChange(async (value) => {
|
||||||
@@ -789,45 +788,45 @@ var VaultChatSettingsTab = class extends import_obsidian2.PluginSettingTab {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
containerEl.createEl("h3", { text: "Kontext-Einstellungen" });
|
containerEl.createEl("h3", { text: "Kontext-Einstellungen" });
|
||||||
new import_obsidian2.Setting(containerEl).setName("Max. Kontext-Notizen").setDesc("Wie viele Notizen werden automatisch als Kontext hinzugef\xFCgt? (1\u201315)").addSlider(
|
new import_obsidian3.Setting(containerEl).setName("Max. Kontext-Notizen").setDesc("Wie viele Notizen werden automatisch als Kontext hinzugef\xFCgt? (1\u201315)").addSlider(
|
||||||
(slider) => slider.setLimits(1, 15, 1).setValue(this.plugin.settings.maxContextNotes).setDynamicTooltip().onChange(async (value) => {
|
(slider) => slider.setLimits(1, 15, 1).setValue(this.plugin.settings.maxContextNotes).setDynamicTooltip().onChange(async (value) => {
|
||||||
this.plugin.settings.maxContextNotes = value;
|
this.plugin.settings.maxContextNotes = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
new import_obsidian2.Setting(containerEl).setName("Max. Zeichen pro Notiz").setDesc("Wie viele Zeichen einer Notiz in den Kontext einbezogen werden (1000\u20138000)").addSlider(
|
new import_obsidian3.Setting(containerEl).setName("Max. Zeichen pro Notiz").setDesc("Wie viele Zeichen einer Notiz in den Kontext einbezogen werden (1000\u20138000)").addSlider(
|
||||||
(slider) => slider.setLimits(1e3, 8e3, 500).setValue(this.plugin.settings.maxCharsPerNote).setDynamicTooltip().onChange(async (value) => {
|
(slider) => slider.setLimits(1e3, 8e3, 500).setValue(this.plugin.settings.maxCharsPerNote).setDynamicTooltip().onChange(async (value) => {
|
||||||
this.plugin.settings.maxCharsPerNote = value;
|
this.plugin.settings.maxCharsPerNote = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
new import_obsidian2.Setting(containerEl).setName("Automatischer Kontext-Abruf").setDesc("Beim Senden automatisch relevante Notizen suchen und einbinden").addToggle(
|
new import_obsidian3.Setting(containerEl).setName("Automatischer Kontext-Abruf").setDesc("Beim Senden automatisch relevante Notizen suchen und einbinden").addToggle(
|
||||||
(toggle) => toggle.setValue(this.plugin.settings.autoRetrieveContext).onChange(async (value) => {
|
(toggle) => toggle.setValue(this.plugin.settings.autoRetrieveContext).onChange(async (value) => {
|
||||||
this.plugin.settings.autoRetrieveContext = value;
|
this.plugin.settings.autoRetrieveContext = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
new import_obsidian2.Setting(containerEl).setName("Kontext-Vorschau anzeigen").setDesc("Vor dem Senden zeigen, welche Notizen als Kontext verwendet werden").addToggle(
|
new import_obsidian3.Setting(containerEl).setName("Kontext-Vorschau anzeigen").setDesc("Vor dem Senden zeigen, welche Notizen als Kontext verwendet werden").addToggle(
|
||||||
(toggle) => toggle.setValue(this.plugin.settings.showContextPreview).onChange(async (value) => {
|
(toggle) => toggle.setValue(this.plugin.settings.showContextPreview).onChange(async (value) => {
|
||||||
this.plugin.settings.showContextPreview = value;
|
this.plugin.settings.showContextPreview = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
containerEl.createEl("h3", { text: "Thread-History" });
|
containerEl.createEl("h3", { text: "Thread-History" });
|
||||||
new import_obsidian2.Setting(containerEl).setName("Threads im Vault speichern").setDesc("Chat-Threads als Markdown-Notizen im Vault ablegen").addToggle(
|
new import_obsidian3.Setting(containerEl).setName("Threads im Vault speichern").setDesc("Chat-Threads als Markdown-Notizen im Vault ablegen").addToggle(
|
||||||
(toggle) => toggle.setValue(this.plugin.settings.saveThreadsToVault).onChange(async (value) => {
|
(toggle) => toggle.setValue(this.plugin.settings.saveThreadsToVault).onChange(async (value) => {
|
||||||
this.plugin.settings.saveThreadsToVault = value;
|
this.plugin.settings.saveThreadsToVault = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
new import_obsidian2.Setting(containerEl).setName("Threads-Ordner").setDesc("Pfad im Vault, wo Chat-Threads gespeichert werden").addText(
|
new import_obsidian3.Setting(containerEl).setName("Threads-Ordner").setDesc("Pfad im Vault, wo Chat-Threads gespeichert werden").addText(
|
||||||
(text) => text.setPlaceholder("Calendar/Chat").setValue(this.plugin.settings.threadsFolder).onChange(async (value) => {
|
(text) => text.setPlaceholder("Calendar/Chat").setValue(this.plugin.settings.threadsFolder).onChange(async (value) => {
|
||||||
this.plugin.settings.threadsFolder = value;
|
this.plugin.settings.threadsFolder = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
containerEl.createEl("h3", { text: "System Prompt" });
|
containerEl.createEl("h3", { text: "System Prompt" });
|
||||||
new import_obsidian2.Setting(containerEl).setName("System Prompt").setDesc("Instruktionen f\xFCr Claude (wie soll er sich verhalten?)").addTextArea((textarea) => {
|
new import_obsidian3.Setting(containerEl).setName("System Prompt").setDesc("Instruktionen f\xFCr Claude (wie soll er sich verhalten?)").addTextArea((textarea) => {
|
||||||
textarea.setValue(this.plugin.settings.systemPrompt).onChange(async (value) => {
|
textarea.setValue(this.plugin.settings.systemPrompt).onChange(async (value) => {
|
||||||
this.plugin.settings.systemPrompt = value;
|
this.plugin.settings.systemPrompt = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
@@ -838,7 +837,7 @@ var VaultChatSettingsTab = class extends import_obsidian2.PluginSettingTab {
|
|||||||
textarea.inputEl.style.fontSize = "12px";
|
textarea.inputEl.style.fontSize = "12px";
|
||||||
});
|
});
|
||||||
containerEl.createEl("h3", { text: "Aktionen" });
|
containerEl.createEl("h3", { text: "Aktionen" });
|
||||||
new import_obsidian2.Setting(containerEl).setName("Index neu aufbauen").setDesc("Vault-Index f\xFCr die Suche neu aufbauen (dauert je nach Vault-Gr\xF6\xDFe einige Sekunden)").addButton(
|
new import_obsidian3.Setting(containerEl).setName("Index neu aufbauen").setDesc("Vault-Index f\xFCr die Suche neu aufbauen (dauert je nach Vault-Gr\xF6\xDFe einige Sekunden)").addButton(
|
||||||
(btn) => btn.setButtonText("Index neu aufbauen").setCta().onClick(async () => {
|
(btn) => btn.setButtonText("Index neu aufbauen").setCta().onClick(async () => {
|
||||||
btn.setButtonText("Indiziere\u2026");
|
btn.setButtonText("Indiziere\u2026");
|
||||||
btn.setDisabled(true);
|
btn.setDisabled(true);
|
||||||
@@ -854,7 +853,7 @@ var VaultChatSettingsTab = class extends import_obsidian2.PluginSettingTab {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// src/main.ts
|
// src/main.ts
|
||||||
var VaultChatPlugin = class extends import_obsidian3.Plugin {
|
var MemexChatPlugin = class extends import_obsidian4.Plugin {
|
||||||
async onload() {
|
async onload() {
|
||||||
var _a, _b;
|
var _a, _b;
|
||||||
const loaded = await this.loadData();
|
const loaded = await this.loadData();
|
||||||
@@ -865,28 +864,28 @@ var VaultChatPlugin = class extends import_obsidian3.Plugin {
|
|||||||
this.settings = this.data.settings;
|
this.settings = this.data.settings;
|
||||||
this.search = new VaultSearch(this.app);
|
this.search = new VaultSearch(this.app);
|
||||||
this.claude = new ClaudeClient();
|
this.claude = new ClaudeClient();
|
||||||
this.registerView(VIEW_TYPE_VAULT_CHAT, (leaf) => new ChatView(leaf, this));
|
this.registerView(VIEW_TYPE_MEMEX_CHAT, (leaf) => new ChatView(leaf, this));
|
||||||
this.addRibbonIcon("message-circle", "Vault Chat \xF6ffnen", () => {
|
this.addRibbonIcon("message-circle", "Memex Chat \xF6ffnen", () => {
|
||||||
this.activateView();
|
this.activateView();
|
||||||
});
|
});
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "open-vault-chat",
|
id: "open-memex-chat",
|
||||||
name: "Vault Chat \xF6ffnen",
|
name: "Memex Chat \xF6ffnen",
|
||||||
callback: () => this.activateView()
|
callback: () => this.activateView()
|
||||||
});
|
});
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "vault-chat-rebuild-index",
|
id: "memex-chat-rebuild-index",
|
||||||
name: "Vault Chat: Index neu aufbauen",
|
name: "Memex Chat: Index neu aufbauen",
|
||||||
callback: () => this.rebuildIndex()
|
callback: () => this.rebuildIndex()
|
||||||
});
|
});
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "vault-chat-active-note",
|
id: "memex-chat-active-note",
|
||||||
name: "Vault Chat: Aktive Notiz als Kontext",
|
name: "Memex Chat: Aktive Notiz als Kontext",
|
||||||
callback: () => {
|
callback: () => {
|
||||||
const file = this.app.workspace.getActiveFile();
|
const file = this.app.workspace.getActiveFile();
|
||||||
if (file) {
|
if (file) {
|
||||||
this.activateView().then(() => {
|
this.activateView().then(() => {
|
||||||
const leaf = this.app.workspace.getLeavesOfType(VIEW_TYPE_VAULT_CHAT)[0];
|
const leaf = this.app.workspace.getLeavesOfType(VIEW_TYPE_MEMEX_CHAT)[0];
|
||||||
if (leaf) {
|
if (leaf) {
|
||||||
const view = leaf.view;
|
const view = leaf.view;
|
||||||
view.inputEl.value = `Erkl\xE4re und verkn\xFCpfe [[${file.basename}]] mit anderen Konzepten im Vault.`;
|
view.inputEl.value = `Erkl\xE4re und verkn\xFCpfe [[${file.basename}]] mit anderen Konzepten im Vault.`;
|
||||||
@@ -896,19 +895,19 @@ var VaultChatPlugin = class extends import_obsidian3.Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.addSettingTab(new VaultChatSettingsTab(this.app, this));
|
this.addSettingTab(new MemexChatSettingsTab(this.app, this));
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!this.search.isIndexed()) {
|
if (!this.search.isIndexed()) {
|
||||||
this.search.buildIndex().catch(console.error);
|
this.search.buildIndex().catch(console.error);
|
||||||
}
|
}
|
||||||
}, 3e3);
|
}, 3e3);
|
||||||
console.log("[Vault Chat] Plugin geladen");
|
console.log("[Memex Chat] Plugin geladen");
|
||||||
}
|
}
|
||||||
onunload() {
|
onunload() {
|
||||||
this.app.workspace.detachLeavesOfType(VIEW_TYPE_VAULT_CHAT);
|
this.app.workspace.detachLeavesOfType(VIEW_TYPE_MEMEX_CHAT);
|
||||||
}
|
}
|
||||||
async activateView() {
|
async activateView() {
|
||||||
const existing = this.app.workspace.getLeavesOfType(VIEW_TYPE_VAULT_CHAT);
|
const existing = this.app.workspace.getLeavesOfType(VIEW_TYPE_MEMEX_CHAT);
|
||||||
if (existing.length > 0) {
|
if (existing.length > 0) {
|
||||||
this.app.workspace.revealLeaf(existing[0]);
|
this.app.workspace.revealLeaf(existing[0]);
|
||||||
return;
|
return;
|
||||||
@@ -916,12 +915,12 @@ var VaultChatPlugin = class extends import_obsidian3.Plugin {
|
|||||||
const leaf = this.app.workspace.getRightLeaf(false);
|
const leaf = this.app.workspace.getRightLeaf(false);
|
||||||
if (!leaf)
|
if (!leaf)
|
||||||
return;
|
return;
|
||||||
await leaf.setViewState({ type: VIEW_TYPE_VAULT_CHAT, active: true });
|
await leaf.setViewState({ type: VIEW_TYPE_MEMEX_CHAT, active: true });
|
||||||
this.app.workspace.revealLeaf(leaf);
|
this.app.workspace.revealLeaf(leaf);
|
||||||
}
|
}
|
||||||
async rebuildIndex() {
|
async rebuildIndex() {
|
||||||
var _a;
|
var _a;
|
||||||
const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_VAULT_CHAT);
|
const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_MEMEX_CHAT);
|
||||||
const view = (_a = leaves[0]) == null ? void 0 : _a.view;
|
const view = (_a = leaves[0]) == null ? void 0 : _a.view;
|
||||||
this.search.onProgress = (done, total) => {
|
this.search.onProgress = (done, total) => {
|
||||||
if (view && done % 200 === 0) {
|
if (view && done % 200 === 0) {
|
||||||
|
|||||||
+21
-18
@@ -1,3 +1,5 @@
|
|||||||
|
import { requestUrl } from "obsidian";
|
||||||
|
|
||||||
export interface ClaudeMessage {
|
export interface ClaudeMessage {
|
||||||
role: "user" | "assistant";
|
role: "user" | "assistant";
|
||||||
content: string;
|
content: string;
|
||||||
@@ -20,19 +22,24 @@ export interface ClaudeStreamChunk {
|
|||||||
export class ClaudeClient {
|
export class ClaudeClient {
|
||||||
private baseUrl = "https://api.anthropic.com/v1/messages";
|
private baseUrl = "https://api.anthropic.com/v1/messages";
|
||||||
|
|
||||||
|
private headers(apiKey: string): Record<string, string> {
|
||||||
|
return {
|
||||||
|
"content-type": "application/json",
|
||||||
|
"x-api-key": apiKey,
|
||||||
|
"anthropic-version": "2023-06-01",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/** Stream a chat completion, yielding text chunks */
|
/** Stream a chat completion, yielding text chunks */
|
||||||
async *streamChat(
|
async *streamChat(
|
||||||
messages: ClaudeMessage[],
|
messages: ClaudeMessage[],
|
||||||
options: ClaudeOptions
|
options: ClaudeOptions
|
||||||
): AsyncGenerator<ClaudeStreamChunk> {
|
): AsyncGenerator<ClaudeStreamChunk> {
|
||||||
|
// Use native fetch for streaming (requestUrl doesn't support streaming).
|
||||||
|
// The outdated anthropic-beta header is omitted — streaming is stable API.
|
||||||
const response = await fetch(this.baseUrl, {
|
const response = await fetch(this.baseUrl, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: this.headers(options.apiKey),
|
||||||
"content-type": "application/json",
|
|
||||||
"x-api-key": options.apiKey,
|
|
||||||
"anthropic-version": "2023-06-01",
|
|
||||||
"anthropic-beta": "messages-2023-12-15",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model: options.model,
|
model: options.model,
|
||||||
max_tokens: options.maxTokens ?? 2048,
|
max_tokens: options.maxTokens ?? 2048,
|
||||||
@@ -90,29 +97,25 @@ export class ClaudeClient {
|
|||||||
yield { type: "done" };
|
yield { type: "done" };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Non-streaming version for simpler use cases */
|
/** Non-streaming version — uses Obsidian's requestUrl to bypass CORS */
|
||||||
async chat(messages: ClaudeMessage[], options: ClaudeOptions): Promise<string> {
|
async chat(messages: ClaudeMessage[], options: ClaudeOptions): Promise<string> {
|
||||||
const response = await fetch(this.baseUrl, {
|
const response = await requestUrl({
|
||||||
|
url: this.baseUrl,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: this.headers(options.apiKey),
|
||||||
"content-type": "application/json",
|
|
||||||
"x-api-key": options.apiKey,
|
|
||||||
"anthropic-version": "2023-06-01",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model: options.model,
|
model: options.model,
|
||||||
max_tokens: options.maxTokens ?? 2048,
|
max_tokens: options.maxTokens ?? 2048,
|
||||||
system: options.systemPrompt,
|
system: options.systemPrompt,
|
||||||
messages,
|
messages,
|
||||||
}),
|
}),
|
||||||
|
throw: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (response.status >= 400) {
|
||||||
const err = await response.text();
|
throw new Error(`API Error ${response.status}: ${response.text}`);
|
||||||
throw new Error(`API Error ${response.status}: ${err}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
return response.json.content?.[0]?.text ?? "";
|
||||||
return data.content?.[0]?.text ?? "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user