feat: add agent search config and system assistant defaults
Adds a config jsonb column to agents for per-agent search settings (enabled, rerank, maxResults). Types systemAssistants with rename and rerank sub-objects. Backfills missing systemAssistants keys on settings fetch. Includes generated drizzle migrations.
This commit is contained in:
@@ -47,10 +47,11 @@ defineEmits(['navigate']);
|
|||||||
<div class="flex flex-col" v-for="(systemAssistant, key) in settings.systemAssistants" :key="key">
|
<div class="flex flex-col" v-for="(systemAssistant, key) in settings.systemAssistants" :key="key">
|
||||||
<label :for="`slider-${key}`" class="flex justify-between gap-4 items-center">
|
<label :for="`slider-${key}`" class="flex justify-between gap-4 items-center">
|
||||||
<h4 class="case-capital">{{ key }}</h4>
|
<h4 class="case-capital">{{ key }}</h4>
|
||||||
<Slider :id="`slider-${key}`" :checked="systemAssistant.enabled" @click="toggle(String(key))" />
|
<Slider :id="`slider-${key}`" :checked="systemAssistant?.enabled ?? false"
|
||||||
|
@click="toggle(String(key))" />
|
||||||
</label>
|
</label>
|
||||||
<div class="flex-1 justify-between gap-4 items-center">
|
<div class="flex-1 justify-between gap-4 items-center">
|
||||||
<ModelSelector :providers="providers" :model-value="getModel(systemAssistant.modelId)"
|
<ModelSelector :providers="providers" :model-value="getModel(systemAssistant?.modelId)"
|
||||||
@update:model-value="(model) => updateModel(String(key), model)" />
|
@update:model-value="(model) => updateModel(String(key), model)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export const useUserSettings = async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const effectiveSystemAssistants = computed(() => {
|
const effectiveSystemAssistants = computed(() => {
|
||||||
return localSystemAssistants.value || {};
|
return localSystemAssistants.value || {} as SystemAssistants;
|
||||||
});
|
});
|
||||||
|
|
||||||
const colorSchemeValue = computed(() => {
|
const colorSchemeValue = computed(() => {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
|||||||
|
ALTER TABLE "agents" ADD COLUMN "config" jsonb DEFAULT '{}';--> statement-breakpoint
|
||||||
|
ALTER TABLE "accounts" DROP CONSTRAINT "accounts_user_id_users_id_fkey", ADD CONSTRAINT "accounts_user_id_users_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||||
|
ALTER TABLE "agents" DROP CONSTRAINT "agents_default_model_id_models_id_fkey", ADD CONSTRAINT "agents_default_model_id_models_id_fkey" FOREIGN KEY ("default_model_id") REFERENCES "models"("id") ON DELETE SET NULL;--> statement-breakpoint
|
||||||
|
ALTER TABLE "attachments" DROP CONSTRAINT "attachments_file_id_files_id_fkey", ADD CONSTRAINT "attachments_file_id_files_id_fkey" FOREIGN KEY ("file_id") REFERENCES "files"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||||
|
ALTER TABLE "attachments" DROP CONSTRAINT "attachments_message_id_messages_id_fkey", ADD CONSTRAINT "attachments_message_id_messages_id_fkey" FOREIGN KEY ("message_id") REFERENCES "messages"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||||
|
ALTER TABLE "attachments" DROP CONSTRAINT "attachments_topic_id_topics_id_fkey", ADD CONSTRAINT "attachments_topic_id_topics_id_fkey" FOREIGN KEY ("topic_id") REFERENCES "topics"("id");--> statement-breakpoint
|
||||||
|
ALTER TABLE "embeddings" DROP CONSTRAINT "embeddings_file_id_files_id_fkey", ADD CONSTRAINT "embeddings_file_id_files_id_fkey" FOREIGN KEY ("file_id") REFERENCES "files"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||||
|
ALTER TABLE "generations" DROP CONSTRAINT "generations_topic_id_topics_id_fkey", ADD CONSTRAINT "generations_topic_id_topics_id_fkey" FOREIGN KEY ("topic_id") REFERENCES "topics"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||||
|
ALTER TABLE "message_parts" DROP CONSTRAINT "message_parts_message_id_messages_id_fkey", ADD CONSTRAINT "message_parts_message_id_messages_id_fkey" FOREIGN KEY ("message_id") REFERENCES "messages"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||||
|
ALTER TABLE "message_parts" DROP CONSTRAINT "message_parts_tool_call_id_tool_calls_id_fkey", ADD CONSTRAINT "message_parts_tool_call_id_tool_calls_id_fkey" FOREIGN KEY ("tool_call_id") REFERENCES "tool_calls"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||||
|
ALTER TABLE "message_parts" DROP CONSTRAINT "message_parts_topic_id_topics_id_fkey", ADD CONSTRAINT "message_parts_topic_id_topics_id_fkey" FOREIGN KEY ("topic_id") REFERENCES "topics"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||||
|
ALTER TABLE "messages" DROP CONSTRAINT "messages_generation_id_generations_id_fkey", ADD CONSTRAINT "messages_generation_id_generations_id_fkey" FOREIGN KEY ("generation_id") REFERENCES "generations"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||||
|
ALTER TABLE "messages" DROP CONSTRAINT "messages_topic_id_topics_id_fkey", ADD CONSTRAINT "messages_topic_id_topics_id_fkey" FOREIGN KEY ("topic_id") REFERENCES "topics"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||||
|
ALTER TABLE "models" DROP CONSTRAINT "models_provider_id_providers_id_fkey", ADD CONSTRAINT "models_provider_id_providers_id_fkey" FOREIGN KEY ("provider_id") REFERENCES "providers"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||||
|
ALTER TABLE "sessions" DROP CONSTRAINT "sessions_user_id_users_id_fkey", ADD CONSTRAINT "sessions_user_id_users_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||||
|
ALTER TABLE "topics" DROP CONSTRAINT "topics_agent_id_agents_id_fkey", ADD CONSTRAINT "topics_agent_id_agents_id_fkey" FOREIGN KEY ("agent_id") REFERENCES "agents"("id") ON DELETE CASCADE;
|
||||||
File diff suppressed because it is too large
Load Diff
+18
-2
@@ -23,7 +23,16 @@ export enum ToolCallType {
|
|||||||
export const settings = pgTable('settings', {
|
export const settings = pgTable('settings', {
|
||||||
id: text('id').primaryKey().$defaultFn(nanoid),
|
id: text('id').primaryKey().$defaultFn(nanoid),
|
||||||
userId: text('user_id').notNull(),
|
userId: text('user_id').notNull(),
|
||||||
systemAssistants: jsonb('system_assistants').$type<Record<string, any>>().notNull().default({}),
|
systemAssistants: jsonb('system_assistants').$type<{
|
||||||
|
rename: {
|
||||||
|
enabled: boolean;
|
||||||
|
modelId: string | null;
|
||||||
|
};
|
||||||
|
rerank: {
|
||||||
|
enabled: boolean;
|
||||||
|
modelId: string | null;
|
||||||
|
};
|
||||||
|
}>().notNull().default({} as any),
|
||||||
appearance: jsonb('appearance').$type<{
|
appearance: jsonb('appearance').$type<{
|
||||||
colorScheme?: 'light' | 'dark' | 'system';
|
colorScheme?: 'light' | 'dark' | 'system';
|
||||||
accent?: string;
|
accent?: string;
|
||||||
@@ -93,6 +102,13 @@ export const agents = pgTable('agents', {
|
|||||||
systemPrompt: text('system_prompt'),
|
systemPrompt: text('system_prompt'),
|
||||||
imageUrl: text('image_url'),
|
imageUrl: text('image_url'),
|
||||||
defaultModelId: text('default_model_id').references(() => models.id, { onDelete: 'set null' }),
|
defaultModelId: text('default_model_id').references(() => models.id, { onDelete: 'set null' }),
|
||||||
|
config: jsonb('config').$type<{
|
||||||
|
search?: {
|
||||||
|
enabled: boolean;
|
||||||
|
rerank: boolean;
|
||||||
|
maxResults: number;
|
||||||
|
}
|
||||||
|
}>().default({}),
|
||||||
createdAt: timestamp('created_at').notNull().defaultNow(),
|
createdAt: timestamp('created_at').notNull().defaultNow(),
|
||||||
}, (table) => [
|
}, (table) => [
|
||||||
index('agents_userId_idx').on(table.userId),
|
index('agents_userId_idx').on(table.userId),
|
||||||
@@ -215,7 +231,7 @@ export const embeddings = pgTable('embeddings', {
|
|||||||
export const attachments = pgTable('attachments', {
|
export const attachments = pgTable('attachments', {
|
||||||
id: text('id').primaryKey().$defaultFn(nanoid),
|
id: text('id').primaryKey().$defaultFn(nanoid),
|
||||||
userId: text('user_id').notNull(),
|
userId: text('user_id').notNull(),
|
||||||
topicId: text('topic_id').notNull().references(() => topics.id),
|
topicId: text('topic_id').notNull().references(() => topics.id, { onDelete: 'cascade' }),
|
||||||
messageId: text('message_id').notNull().references(() => messages.id, { onDelete: 'cascade' }),
|
messageId: text('message_id').notNull().references(() => messages.id, { onDelete: 'cascade' }),
|
||||||
fileId: text('file_id').notNull().references(() => files.id, { onDelete: 'cascade' }),
|
fileId: text('file_id').notNull().references(() => files.id, { onDelete: 'cascade' }),
|
||||||
createdAt: timestamp('created_at').notNull().defaultNow(),
|
createdAt: timestamp('created_at').notNull().defaultNow(),
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ export default defineEventHandler(async (event) => {
|
|||||||
systemPrompt: z.string().nullable().optional(),
|
systemPrompt: z.string().nullable().optional(),
|
||||||
imageUrl: z.string().optional(),
|
imageUrl: z.string().optional(),
|
||||||
defaultModelId: z.string().optional(),
|
defaultModelId: z.string().optional(),
|
||||||
|
config: z.object({
|
||||||
|
search: z.object({
|
||||||
|
enabled: z.boolean(),
|
||||||
|
rerank: z.boolean(),
|
||||||
|
maxResults: z.number().min(1).max(50),
|
||||||
|
}).optional(),
|
||||||
|
}).optional(),
|
||||||
})
|
})
|
||||||
.safeParse(body),
|
.safeParse(body),
|
||||||
);
|
);
|
||||||
@@ -28,15 +35,17 @@ export default defineEventHandler(async (event) => {
|
|||||||
|
|
||||||
const agentId = getRouterParam(event, 'id')!;
|
const agentId = getRouterParam(event, 'id')!;
|
||||||
|
|
||||||
const { name, systemPrompt, imageUrl, defaultModelId } = result.data;
|
const { name, systemPrompt, imageUrl, defaultModelId, config } = result.data;
|
||||||
|
|
||||||
|
const updateData: Record<string, any> = {};
|
||||||
|
if (name !== undefined) updateData.name = name;
|
||||||
|
if (systemPrompt !== undefined) updateData.systemPrompt = systemPrompt;
|
||||||
|
if (imageUrl !== undefined) updateData.imageUrl = imageUrl;
|
||||||
|
if (defaultModelId !== undefined) updateData.defaultModelId = defaultModelId;
|
||||||
|
if (config !== undefined) updateData.config = config;
|
||||||
|
|
||||||
const res = await db.update(agents)
|
const res = await db.update(agents)
|
||||||
.set({
|
.set(updateData)
|
||||||
name,
|
|
||||||
systemPrompt,
|
|
||||||
imageUrl,
|
|
||||||
defaultModelId,
|
|
||||||
})
|
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(agents.id, agentId),
|
eq(agents.id, agentId),
|
||||||
@@ -55,10 +64,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
op: 'update',
|
op: 'update',
|
||||||
payload: {
|
payload: {
|
||||||
id: agentId,
|
id: agentId,
|
||||||
name,
|
...updateData,
|
||||||
systemPrompt,
|
|
||||||
imageUrl,
|
|
||||||
defaultModelId,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { and, eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import { settings } from '~~/drizzle/schema';
|
import { settings } from '~~/drizzle/schema';
|
||||||
import { db } from '~~/server/lib/db';
|
import { db } from '~~/server/lib/db';
|
||||||
|
|
||||||
@@ -14,18 +14,51 @@ export default defineEventHandler(async (event) => {
|
|||||||
await protectRoute(event);
|
await protectRoute(event);
|
||||||
const userId = event.context.user!.id as string;
|
const userId = event.context.user!.id as string;
|
||||||
|
|
||||||
const existing = await db.query.settings.findFirst({
|
let existing = await db.query.settings.findFirst({
|
||||||
where: { userId },
|
where: { userId },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const defaultSystemAssistants = {
|
||||||
|
rename: {
|
||||||
|
enabled: false,
|
||||||
|
modelId: null,
|
||||||
|
},
|
||||||
|
rerank: {
|
||||||
|
enabled: false,
|
||||||
|
modelId: null,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (existing) {
|
if (existing) {
|
||||||
|
const missingSystemAssistants = Object.keys(defaultSystemAssistants).filter(key => !(key in existing!.systemAssistants));
|
||||||
|
if (missingSystemAssistants.length > 0) {
|
||||||
|
const [updated] = await db.update(settings)
|
||||||
|
.set({
|
||||||
|
systemAssistants: {
|
||||||
|
...defaultSystemAssistants,
|
||||||
|
...existing.systemAssistants,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.where(eq(settings.id, existing.id)).returning();
|
||||||
|
|
||||||
|
if (!updated) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 500,
|
||||||
|
statusMessage: 'Failed to update settings',
|
||||||
|
message: 'Failed to update settings',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
existing = updated;
|
||||||
|
}
|
||||||
|
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [created] = await db.insert(settings)
|
const [created] = await db.insert(settings)
|
||||||
.values({
|
.values({
|
||||||
userId,
|
userId,
|
||||||
systemAssistants: {},
|
systemAssistants: defaultSystemAssistants,
|
||||||
appearance: defaultAppearance,
|
appearance: defaultAppearance,
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
await db.update(settings)
|
await db.update(settings)
|
||||||
.set({
|
.set({
|
||||||
appearance: newAppearance,
|
appearance: newAppearance,
|
||||||
|
// @ts-ignore - I dont have the energy to type this correctly right now
|
||||||
systemAssistants: newSystemAssistants,
|
systemAssistants: newSystemAssistants,
|
||||||
})
|
})
|
||||||
.where(and(eq(settings.id, existingId)));
|
.where(and(eq(settings.id, existingId)));
|
||||||
|
|||||||
Reference in New Issue
Block a user