Add dual-model support with separate chat and agent models
Split the single model setting into `chatModel` and `agentModel` to allow using different LLMs for conversational modes (Ask, Research) versus agentic modes (Edit, Organize, Workflow, auto-organizer). Defaults are `deepseek-v4-flash` for chat and `glm-5.1` for agents. Includes backward compatibility migration from legacy `model` field, updated settings UI, per-mode tool filtering via new `agent-modes.ts` configs, and vault search scoring improvements (exact phrase, recency, filename bonuses).
This commit is contained in:
+53
-21
@@ -67,12 +67,11 @@ export class ChatView extends ItemView {
|
||||
this.listenersAttached = false;
|
||||
this.settings = settings;
|
||||
this.currentAgentMode = settings.agentMode ?? 'ask';
|
||||
this.ollamaClient = new OllamaClient(
|
||||
settings.ollamaUrl,
|
||||
settings.model,
|
||||
undefined,
|
||||
settings.cacheConfig
|
||||
);
|
||||
this.ollamaClient = this.createOllamaClient(settings.chatModel ?? settings.model, settings);
|
||||
this.agentOllamaClient =
|
||||
(settings.agentModel ?? settings.model) === (settings.chatModel ?? settings.model)
|
||||
? this.ollamaClient
|
||||
: this.createOllamaClient(settings.agentModel ?? settings.model, settings);
|
||||
this.vaultIndexer = new VaultIndexer(this.app.vault, undefined, vectorStore);
|
||||
this.toolExecutor = new ToolExecutor(this.app.vault, this.app, telemetryManager);
|
||||
this.actionPreviewBuilder = new ActionPreviewBuilder(this.app.vault, this.app);
|
||||
@@ -84,7 +83,7 @@ export class ChatView extends ItemView {
|
||||
this.app.vault,
|
||||
this.app,
|
||||
settings.ollamaUrl,
|
||||
settings.model,
|
||||
settings.agentModel ?? settings.model,
|
||||
{ cacheConfig: settings.cacheConfig }
|
||||
);
|
||||
}
|
||||
@@ -95,20 +94,22 @@ export class ChatView extends ItemView {
|
||||
if (this.modeSelectorEl) {
|
||||
this.modeSelectorEl.value = this.currentAgentMode;
|
||||
}
|
||||
this.ollamaClient = new OllamaClient(
|
||||
newSettings.ollamaUrl,
|
||||
newSettings.model,
|
||||
undefined,
|
||||
newSettings.cacheConfig
|
||||
this.ollamaClient = this.createOllamaClient(
|
||||
newSettings.chatModel ?? newSettings.model,
|
||||
newSettings
|
||||
);
|
||||
this.agentOllamaClient =
|
||||
(newSettings.agentModel ?? newSettings.model) === (newSettings.chatModel ?? newSettings.model)
|
||||
? this.ollamaClient
|
||||
: this.createOllamaClient(newSettings.agentModel ?? newSettings.model, newSettings);
|
||||
this.workflowEngine = new WorkflowEngine(
|
||||
this.app.vault,
|
||||
this.app,
|
||||
newSettings.ollamaUrl,
|
||||
newSettings.model,
|
||||
newSettings.agentModel ?? newSettings.model,
|
||||
{ cacheConfig: newSettings.cacheConfig }
|
||||
);
|
||||
void this.ollamaClient.initializeCache().catch(() => {
|
||||
void this.initializeClientCaches().catch(() => {
|
||||
new Notice(
|
||||
'Semantic cache unavailable: could not connect to ChromaDB. Check the ChromaDB URL in settings.'
|
||||
);
|
||||
@@ -122,6 +123,9 @@ export class ChatView extends ItemView {
|
||||
|
||||
async clearCache(): Promise<void> {
|
||||
await this.ollamaClient.clearCache();
|
||||
if (this.agentOllamaClient !== this.ollamaClient) {
|
||||
await this.agentOllamaClient.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
getViewType(): string {
|
||||
@@ -138,7 +142,7 @@ export class ChatView extends ItemView {
|
||||
|
||||
async onOpen(): Promise<void> {
|
||||
try {
|
||||
await this.ollamaClient.initializeCache();
|
||||
await this.initializeClientCaches();
|
||||
} catch {
|
||||
new Notice(
|
||||
'Semantic cache unavailable: could not connect to ChromaDB. Check the ChromaDB URL in settings.'
|
||||
@@ -744,7 +748,7 @@ export class ChatView extends ItemView {
|
||||
if (followUpMessages.length > 0) {
|
||||
const finalMessages = [...messages, followUp, ...followUpMessages];
|
||||
const followUpStartTime = Date.now();
|
||||
const response = await this.ollamaClient.chat(finalMessages, tools);
|
||||
const response = await this.getActiveOllamaClient().chat(finalMessages, tools);
|
||||
const followUpDurationMs = Date.now() - followUpStartTime;
|
||||
const finalResponse = response.content || fullResponse;
|
||||
this.updateMessageById(assistantMessageId, {
|
||||
@@ -755,7 +759,7 @@ export class ChatView extends ItemView {
|
||||
|
||||
// Record follow-up LLM call telemetry
|
||||
this.telemetryManager?.recordLlmCall({
|
||||
model: this.settings.model,
|
||||
model: this.getActiveModel(),
|
||||
promptTokens: Math.round(finalMessages.reduce((sum, m) => sum + m.content.length, 0) / 4),
|
||||
completionTokens: Math.round(finalResponse.length / 4),
|
||||
totalTokens: Math.round(
|
||||
@@ -812,7 +816,7 @@ export class ChatView extends ItemView {
|
||||
if (followUpMessages.length > 0) {
|
||||
const finalMessages = [...messages, followUp, ...followUpMessages];
|
||||
const followUpStartTime = Date.now();
|
||||
const response = await this.ollamaClient.chat(finalMessages, tools);
|
||||
const response = await this.getActiveOllamaClient().chat(finalMessages, tools);
|
||||
const followUpDurationMs = Date.now() - followUpStartTime;
|
||||
const finalResponse = response.content || 'Actions applied successfully.';
|
||||
this.updateMessageById(assistantMessageId, {
|
||||
@@ -823,7 +827,7 @@ export class ChatView extends ItemView {
|
||||
|
||||
// Record follow-up LLM call telemetry
|
||||
this.telemetryManager?.recordLlmCall({
|
||||
model: this.settings.model,
|
||||
model: this.getActiveModel(),
|
||||
promptTokens: Math.round(finalMessages.reduce((sum, m) => sum + m.content.length, 0) / 4),
|
||||
completionTokens: Math.round(finalResponse.length / 4),
|
||||
totalTokens: Math.round(
|
||||
@@ -1090,7 +1094,9 @@ export class ChatView extends ItemView {
|
||||
// Prepend structured memory as a system message if available
|
||||
const messagesWithMemory = this.buildMessagesWithMemory(completeMessages);
|
||||
|
||||
const stream = this.ollamaClient.streamChat(messagesWithMemory, tools);
|
||||
const activeClient = this.getActiveOllamaClient();
|
||||
const activeModel = this.getActiveModel();
|
||||
const stream = activeClient.streamChat(messagesWithMemory, tools);
|
||||
|
||||
let fullResponse = '';
|
||||
let toolCalls: OllamaToolCall[] = [];
|
||||
@@ -1131,7 +1137,7 @@ export class ChatView extends ItemView {
|
||||
completionTokens > 0 ? completionTokens : fullResponse.length / 4;
|
||||
|
||||
this.telemetryManager?.recordLlmCall({
|
||||
model: this.settings.model,
|
||||
model: activeModel,
|
||||
promptTokens: Math.round(estimatedPromptTokens),
|
||||
completionTokens: Math.round(estimatedCompletionTokens),
|
||||
totalTokens: Math.round(estimatedPromptTokens + estimatedCompletionTokens),
|
||||
@@ -1232,6 +1238,7 @@ export class ChatView extends ItemView {
|
||||
private listenersAttached: boolean = false;
|
||||
private settings: PluginSettings;
|
||||
private ollamaClient: OllamaClient;
|
||||
private agentOllamaClient: OllamaClient;
|
||||
private vaultIndexer: VaultIndexer;
|
||||
private toolExecutor: ToolExecutor;
|
||||
private actionPreviewBuilder: ActionPreviewBuilder;
|
||||
@@ -1253,6 +1260,31 @@ export class ChatView extends ItemView {
|
||||
tools: OllamaTool[];
|
||||
assistantMessageId: string;
|
||||
} | null = null;
|
||||
|
||||
private createOllamaClient(model: string, settings: PluginSettings): OllamaClient {
|
||||
return new OllamaClient(settings.ollamaUrl, model, undefined, settings.cacheConfig);
|
||||
}
|
||||
|
||||
private async initializeClientCaches(): Promise<void> {
|
||||
await this.ollamaClient.initializeCache();
|
||||
if (this.agentOllamaClient !== this.ollamaClient) {
|
||||
await this.agentOllamaClient.initializeCache();
|
||||
}
|
||||
}
|
||||
|
||||
private getActiveModel(): string {
|
||||
return this.isAgenticMode(this.currentAgentMode)
|
||||
? (this.settings.agentModel ?? this.settings.model)
|
||||
: (this.settings.chatModel ?? this.settings.model);
|
||||
}
|
||||
|
||||
private getActiveOllamaClient(): OllamaClient {
|
||||
return this.isAgenticMode(this.currentAgentMode) ? this.agentOllamaClient : this.ollamaClient;
|
||||
}
|
||||
|
||||
private isAgenticMode(mode: AgentMode): boolean {
|
||||
return mode === 'edit' || mode === 'organize' || mode === 'workflow';
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_TOOL_CALLS = 5;
|
||||
|
||||
Reference in New Issue
Block a user