mirror of
https://github.com/logancyang/obsidian-copilot.git
synced 2026-07-22 07:50:24 +00:00
Only include composer prompt for @composer (#1515)
* Only include composer prompt for @composer * Better handle JSON * Update src/components/Chat.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
ca3b75c2b8
commit
65c0cbef7b
6 changed files with 50 additions and 27 deletions
|
|
@ -21,7 +21,12 @@ import ChainManager from "@/LLMProviders/chainManager";
|
|||
import CopilotPlugin from "@/main";
|
||||
import { Mention } from "@/mentions/Mention";
|
||||
import { useIsPlusUser } from "@/plusUtils";
|
||||
import { getSettings, updateSetting, useSettingsValue } from "@/settings/model";
|
||||
import {
|
||||
getComposerOutputPrompt,
|
||||
getSettings,
|
||||
updateSetting,
|
||||
useSettingsValue,
|
||||
} from "@/settings/model";
|
||||
import SharedState, { ChatMessage, useSharedState } from "@/sharedState";
|
||||
import { FileParserManager } from "@/tools/FileParserManager";
|
||||
import { err2String, formatDateTime } from "@/utils";
|
||||
|
|
@ -160,11 +165,18 @@ const Chat: React.FC<ChatProps> = ({
|
|||
setLoading(true);
|
||||
setLoadingMessage(LOADING_MESSAGES.DEFAULT);
|
||||
|
||||
// First, process the original user message for custom prompts
|
||||
// First, add composer instruction if necessary
|
||||
let processedInputMessage = inputMessage;
|
||||
const composerPrompt = await getComposerOutputPrompt();
|
||||
if (inputMessage.includes("@composer") && composerPrompt !== "") {
|
||||
processedInputMessage =
|
||||
inputMessage + "\n\n<output_format>\n" + composerPrompt + "\n</output_format>";
|
||||
}
|
||||
// process the original user message for custom prompts
|
||||
const customPromptProcessor = CustomPromptProcessor.getInstance(app.vault);
|
||||
const { processedPrompt: processedUserMessage, includedFiles } =
|
||||
await customPromptProcessor.processCustomPrompt(
|
||||
inputMessage || "",
|
||||
processedInputMessage || "",
|
||||
"",
|
||||
app.workspace.getActiveFile() as TFile | undefined
|
||||
);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export const DEFAULT_SYSTEM_PROMPT = `You are Obsidian Copilot, a helpful assist
|
|||
12. Do NOT mention the additional context provided such as getCurrentTime and getTimeRangeMs if it's irrelevant to the user message.
|
||||
13. If the user mentions "tags", it most likely means tags in Obsidian note properties.`;
|
||||
|
||||
export const COMPOSER_INSTRUCTIONS = `If the user explicitly requests to update or create markdown notes or canvases OR mentions "@composer", always return the new note content in a special JSON format.
|
||||
export const COMPOSER_OUTPUT_INSTRUCTIONS = `Return the new note content or canvas JSON in a special JSON format.
|
||||
|
||||
# JSON Format
|
||||
Provide the content in JSON format and wrap it in a code block with the following structure:
|
||||
|
|
|
|||
|
|
@ -104,10 +104,10 @@ export class ContextProcessor {
|
|||
content = await this.processEmbeddedPDFs(content, vault, fileParserManager);
|
||||
}
|
||||
|
||||
additionalContext += `\n\nTitle: [[${note.basename}]]\nPath: ${note.path}\n\n${content}`;
|
||||
additionalContext += `\n\n <note_in_context> \n Title: [[${note.basename}]]\nPath: ${note.path}\n\n${content}\n</note_in_context>`;
|
||||
} catch (error) {
|
||||
console.error(`Error processing file ${note.path}:`, error);
|
||||
additionalContext += `\n\nTitle: [[${note.basename}]]\nPath: ${note.path}\n\n[Error: Could not process file]`;
|
||||
additionalContext += `\n\n <note_in_context_error> \n Title: [[${note.basename}]]\nPath: ${note.path}\n\n[Error: Could not process file]\n</note_in_context_error>`;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -116,9 +116,14 @@ export class ContextProcessor {
|
|||
await processNote(activeNote);
|
||||
}
|
||||
|
||||
const includedFilePaths = new Set<string>();
|
||||
// Process context notes
|
||||
for (const note of contextNotes) {
|
||||
if (includedFilePaths.has(note.path)) {
|
||||
continue;
|
||||
}
|
||||
await processNote(note);
|
||||
includedFilePaths.add(note.path);
|
||||
}
|
||||
|
||||
return additionalContext;
|
||||
|
|
|
|||
|
|
@ -91,7 +91,11 @@ async function extractVariablesFromPrompt(
|
|||
variablesMap.set(variableName, variableResult.content);
|
||||
variableResult.files.forEach((file) => includedFiles.add(file));
|
||||
} else if (variableName.toLowerCase() !== "activenote") {
|
||||
console.warn(`No notes found for variable: ${variableName}`);
|
||||
if (variableName.startsWith('"')) {
|
||||
// DO NOTHING as the user probably wants to write a JSON object
|
||||
} else {
|
||||
console.warn(`No notes found for variable: ${variableName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { DEFAULT_SYSTEM_PROMPT, COMPOSER_INSTRUCTIONS } from "../constants";
|
||||
import { DEFAULT_SYSTEM_PROMPT, COMPOSER_OUTPUT_INSTRUCTIONS } from "../constants";
|
||||
import * as dotenv from "dotenv";
|
||||
import { jest } from "@jest/globals";
|
||||
import {
|
||||
|
|
@ -55,7 +55,7 @@ describe("Composer Instructions - Integration Tests", () => {
|
|||
temperature: 0.1,
|
||||
maxOutputTokens: 1000,
|
||||
},
|
||||
systemInstruction: `${DEFAULT_SYSTEM_PROMPT}\n\n${COMPOSER_INSTRUCTIONS}`,
|
||||
systemInstruction: DEFAULT_SYSTEM_PROMPT,
|
||||
safetySettings: [
|
||||
{
|
||||
category: HarmCategory.HARM_CATEGORY_HARASSMENT,
|
||||
|
|
@ -87,7 +87,9 @@ describe("Composer Instructions - Integration Tests", () => {
|
|||
try {
|
||||
// Create chat session and send message
|
||||
const chat = model.startChat();
|
||||
const result = await chat.sendMessage(userPrompt);
|
||||
const result = await chat.sendMessage(
|
||||
userPrompt + "\n\n<output_format>\n" + COMPOSER_OUTPUT_INSTRUCTIONS + "\n</output_format>"
|
||||
);
|
||||
const content = result.response.text();
|
||||
if (expectedBlocks == 0) {
|
||||
expect(content).not.toContain('"type": "composer"');
|
||||
|
|
@ -137,7 +139,10 @@ describe("Composer Instructions - Integration Tests", () => {
|
|||
};
|
||||
|
||||
// Run tests with different prompts
|
||||
testComposerResponse("Composer: create a new note", "Create a new note about climate change?");
|
||||
testComposerResponse(
|
||||
"Composer: create a new note",
|
||||
"@composer Create a new note about climate change?"
|
||||
);
|
||||
|
||||
testComposerResponse(
|
||||
"Composer: add content to a note",
|
||||
|
|
@ -150,7 +155,7 @@ describe("Composer Instructions - Integration Tests", () => {
|
|||
|
||||
testComposerResponse(
|
||||
"Composer: rewrite a note",
|
||||
`Rewrite the note [[atom]] to be more concise.
|
||||
`@composer Rewrite the note [[atom]] to be more concise.
|
||||
|
||||
Title: [[atom]]
|
||||
Path: atom.md
|
||||
|
|
@ -159,7 +164,7 @@ describe("Composer Instructions - Integration Tests", () => {
|
|||
|
||||
testComposerResponse(
|
||||
"Composer: remove content from a note",
|
||||
`Remove the second paragraph.
|
||||
`@composer Remove the second paragraph.
|
||||
|
||||
Title: [[atom]]
|
||||
Path: atom.md
|
||||
|
|
@ -168,15 +173,19 @@ describe("Composer Instructions - Integration Tests", () => {
|
|||
|
||||
testComposerResponse(
|
||||
"Composer: update multiple notes",
|
||||
"Create two notes on the topic of Earth and Mars separately",
|
||||
"@composer Create two notes on the topic of Earth and Mars separately",
|
||||
2
|
||||
);
|
||||
|
||||
testComposerResponse("Composer: create a canvas", "Create a canvas about water cycle", 1);
|
||||
testComposerResponse(
|
||||
"Composer: create a canvas",
|
||||
"@composer Create a canvas about water cycle",
|
||||
1
|
||||
);
|
||||
|
||||
testComposerResponse(
|
||||
"Composer: not triggered on summarization",
|
||||
"Summarize the note [[atom]]",
|
||||
"@composer Summarize the note [[atom]]",
|
||||
0
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { type ChainType } from "@/chainFactory";
|
|||
import {
|
||||
BUILTIN_CHAT_MODELS,
|
||||
BUILTIN_EMBEDDING_MODELS,
|
||||
COMPOSER_INSTRUCTIONS,
|
||||
COMPOSER_OUTPUT_INSTRUCTIONS,
|
||||
DEFAULT_OPEN_AREA,
|
||||
DEFAULT_SETTINGS,
|
||||
DEFAULT_SYSTEM_PROMPT,
|
||||
|
|
@ -250,22 +250,15 @@ export function sanitizeSettings(settings: CopilotSettings): CopilotSettings {
|
|||
return sanitizedSettings;
|
||||
}
|
||||
|
||||
export function getComposerPrompt(): string {
|
||||
export function getComposerOutputPrompt(): string {
|
||||
const isPlusUser = getSettings().isPlusUser;
|
||||
|
||||
if (isPlusUser) {
|
||||
return COMPOSER_INSTRUCTIONS;
|
||||
}
|
||||
|
||||
return "";
|
||||
return isPlusUser ? COMPOSER_OUTPUT_INSTRUCTIONS : "";
|
||||
}
|
||||
|
||||
export function getSystemPrompt(): string {
|
||||
const userPrompt = getSettings().userSystemPrompt;
|
||||
let basePrompt = DEFAULT_SYSTEM_PROMPT;
|
||||
|
||||
// Add composer instructions for plus users
|
||||
basePrompt = `${basePrompt}\n\n${getComposerPrompt()}`;
|
||||
const basePrompt = DEFAULT_SYSTEM_PROMPT;
|
||||
|
||||
if (userPrompt) {
|
||||
return `${basePrompt}
|
||||
|
|
|
|||
Loading…
Reference in a new issue