Add CalDAV calendar integration, LLM-suggested tags, and settings refinements
- CalDAV integration: per-user calendar config, create/list/search events via caldav library, LLM tools for calendar operations from chat - LLM-suggested tags: new tag_suggestions service prompts LLM with existing tags and note content to suggest 3-5 relevant tags; exposed via API endpoints (suggest-tags, append-tag); integrated into editor views (suggest button + clickable pills) and chat tool calls (pills in ToolCallCard with one-click apply) - Settings/model UI refinements, generation task improvements Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,9 +13,7 @@ import type {
|
||||
ConversationDetail,
|
||||
ContextMeta,
|
||||
Message,
|
||||
OllamaModel,
|
||||
OllamaStatus,
|
||||
RunningModel,
|
||||
SendMessageResponse,
|
||||
ToolCallRecord,
|
||||
} from "@/types/chat";
|
||||
@@ -29,8 +27,6 @@ export const useChatStore = defineStore("chat", () => {
|
||||
const streamingContent = ref("");
|
||||
const streamingToolCalls = ref<ToolCallRecord[]>([]);
|
||||
const lastContextMeta = ref<ContextMeta | null>(null);
|
||||
const models = ref<OllamaModel[]>([]);
|
||||
const runningModels = ref<RunningModel[]>([]);
|
||||
const ollamaStatus = ref<"checking" | "available" | "unavailable">("checking");
|
||||
const modelStatus = ref<"checking" | "ready" | "not_found">("checking");
|
||||
const defaultModel = ref("");
|
||||
@@ -56,14 +52,10 @@ export const useChatStore = defineStore("chat", () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function createConversation(
|
||||
title = "",
|
||||
model = ""
|
||||
): Promise<Conversation> {
|
||||
async function createConversation(title = ""): Promise<Conversation> {
|
||||
try {
|
||||
const conv = await apiPost<Conversation>("/api/chat/conversations", {
|
||||
title,
|
||||
model,
|
||||
});
|
||||
conversations.value.unshift(conv);
|
||||
return conv;
|
||||
@@ -319,24 +311,6 @@ export const useChatStore = defineStore("chat", () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchModels() {
|
||||
try {
|
||||
const data = await apiGet<{ models: OllamaModel[] }>("/api/chat/models");
|
||||
models.value = data.models;
|
||||
} catch {
|
||||
models.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchRunningModels() {
|
||||
try {
|
||||
const data = await apiGet<{ models: RunningModel[] }>("/api/chat/ps");
|
||||
runningModels.value = data.models;
|
||||
} catch {
|
||||
runningModels.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
async function warmModel(model: string) {
|
||||
try {
|
||||
await apiPost("/api/chat/warm", { model });
|
||||
@@ -345,25 +319,6 @@ export const useChatStore = defineStore("chat", () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function updateConversationModel(id: number, model: string) {
|
||||
try {
|
||||
const updated = await apiPatch<Conversation>(
|
||||
`/api/chat/conversations/${id}`,
|
||||
{ model }
|
||||
);
|
||||
const idx = conversations.value.findIndex((c) => c.id === id);
|
||||
if (idx !== -1) {
|
||||
conversations.value[idx] = { ...conversations.value[idx], ...updated };
|
||||
}
|
||||
if (currentConversation.value?.id === id) {
|
||||
currentConversation.value.model = updated.model;
|
||||
}
|
||||
} catch (e) {
|
||||
useToastStore().show("Failed to update model", "error");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
conversations,
|
||||
currentConversation,
|
||||
@@ -373,8 +328,6 @@ export const useChatStore = defineStore("chat", () => {
|
||||
streamingContent,
|
||||
streamingToolCalls,
|
||||
lastContextMeta,
|
||||
models,
|
||||
runningModels,
|
||||
ollamaStatus,
|
||||
modelStatus,
|
||||
defaultModel,
|
||||
@@ -388,10 +341,7 @@ export const useChatStore = defineStore("chat", () => {
|
||||
cancelGeneration,
|
||||
saveMessageAsNote,
|
||||
summarizeAsNote,
|
||||
fetchModels,
|
||||
fetchRunningModels,
|
||||
warmModel,
|
||||
updateConversationModel,
|
||||
fetchStatus,
|
||||
startStatusPolling,
|
||||
stopStatusPolling,
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { ref, computed } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
import { apiGet, apiPut, apiPost, apiStreamPost } from "@/api/client";
|
||||
import { apiGet, apiPut } from "@/api/client";
|
||||
import { useToastStore } from "@/stores/toast";
|
||||
import type { AppSettings } from "@/types/settings";
|
||||
|
||||
export const useSettingsStore = defineStore("settings", () => {
|
||||
const settings = ref<AppSettings>({});
|
||||
const loading = ref(false);
|
||||
const installedModels = ref<string[]>([]);
|
||||
|
||||
const assistantName = computed(
|
||||
() => settings.value.assistant_name || "Fable"
|
||||
@@ -40,44 +39,12 @@ export const useSettingsStore = defineStore("settings", () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchInstalledModels() {
|
||||
try {
|
||||
const data = await apiGet<{ models: { name: string }[] }>("/api/chat/models");
|
||||
installedModels.value = data.models.map((m) => m.name);
|
||||
} catch {
|
||||
installedModels.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
async function pullModel(
|
||||
model: string,
|
||||
onProgress?: (data: Record<string, unknown>) => void,
|
||||
) {
|
||||
await apiStreamPost("/api/chat/models/pull", { model }, (data) => {
|
||||
if (onProgress) onProgress(data);
|
||||
});
|
||||
}
|
||||
|
||||
async function deleteModel(model: string) {
|
||||
try {
|
||||
await apiPost("/api/chat/models/delete", { model });
|
||||
await fetchInstalledModels();
|
||||
} catch (e) {
|
||||
useToastStore().show("Failed to delete model", "error");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
settings,
|
||||
loading,
|
||||
installedModels,
|
||||
assistantName,
|
||||
defaultModel,
|
||||
fetchSettings,
|
||||
updateSettings,
|
||||
fetchInstalledModels,
|
||||
pullModel,
|
||||
deleteModel,
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user