Browse Source

feat: update agent configuration and model handling for global defaults

feat/agent-independence
npmrun 2 months ago
parent
commit
84ebd0d5e9
  1. 8
      app/pages/admin/agent-config.vue
  2. 3
      app/pages/admin/global-config/index.vue
  3. 12
      app/pages/chat/[agentSlug]/index.vue
  4. BIN
      packages/drizzle-pkg/db.sqlite
  5. 4
      server/api/admin/agent-config/index.get.ts
  6. 5
      server/api/agents/[agentSlug]/chat/index.post.ts
  7. 9
      server/api/agents/[agentSlug]/sessions/index.post.ts
  8. 27
      server/api/llm/models/index.get.ts
  9. 5
      server/service/agent/chat-engine.ts
  10. 21
      server/service/llm/index.ts
  11. 5
      server/service/llm/model-resolver.ts

8
app/pages/admin/agent-config.vue

@ -136,7 +136,7 @@ onMounted(async () => {
<!-- System Prompt -->
<section class="config-section">
<h2 class="section-title">系统提示词</h2>
<p class="section-desc">设置 agent 对话的全局系统提示词</p>
<p class="section-desc">设置 Agent 对话的全局系统提示词,引导模型行为</p>
<textarea
v-model="systemPrompt"
class="config-textarea"
@ -148,10 +148,10 @@ onMounted(async () => {
</button>
</section>
<!-- Guest Default Model -->
<!-- Default Model -->
<section class="config-section">
<h2 class="section-title">未登录用户模型</h2>
<p class="section-desc">未登录用户(临时会话)使用的默认模型与标题生成模型</p>
<h2 class="section-title">默认模型</h2>
<p class="section-desc">所有用户的默认模型与标题生成模型。未自行配置模型的用户将自动使用此设置</p>
<div class="model-row">
<div class="model-field">
<label class="field-label">默认模型</label>

3
app/pages/admin/global-config/index.vue

@ -17,9 +17,6 @@ const configMeta: ConfigMeta[] = [
{ key: 'siteName', label: '站点名称', description: '网站标题,显示在浏览器标签和页面上', type: 'string', group: '基本设置' },
{ key: 'siteIcon', label: '站点图标', description: 'favicon 图标,支持上传或填写 URL。推荐 32×32 PNG / 16×16 ICO', type: 'string', group: '基本设置' },
{ key: 'allowRegister', label: '允许注册', description: '是否开放新用户注册功能', type: 'boolean', group: '基本设置' },
{ key: 'agentSystemPrompt', label: 'Agent 系统提示词', description: 'Agent 对话的默认系统提示词,引导模型行为', type: 'string', group: 'Agent 设置' },
{ key: 'agentDefaultModelId', label: 'Agent 未登录默认模型 ID', description: '未登录用户(临时会话)使用的默认 LLM 模型 ID(数字,如 2)。留 0 则不设置', type: 'number', group: 'Agent 设置' },
{ key: 'agentTitleModelId', label: 'Agent 标题生成模型 ID', description: '用于自动生成对话标题的 LLM 模型 ID(数字,如 2)。留 0 则不自动生成标题', type: 'number', group: 'Agent 设置' },
]
const form = reactive<Record<string, any>>(

12
app/pages/chat/[agentSlug]/index.vue

@ -28,7 +28,7 @@ const isCustomPrompt = computed(() =>
!!(sessions.currentSession.value?.systemPrompt?.trim()),
);
const preferredModelId = ref<number | null>(null);
const guestDefaultModelId = ref<number | null>(null);
const globalDefaultModelId = ref<number | null>(null);
interface ModelOption {
id: number;
@ -65,9 +65,9 @@ watch(
currentModelId.value = null;
} else if (mid === null) {
const fallbackSource = auth.loggedIn.value
? (preferredModelId.value && modelList.some((m) => m.id === preferredModelId.value) ? preferredModelId.value : null)
: (guestDefaultModelId.value && modelList.some((m) => m.id === guestDefaultModelId.value) ? guestDefaultModelId.value : null);
const fallback = fallbackSource ?? modelList[0].id;
? (preferredModelId.value && modelList.some((m) => m.id === preferredModelId.value) ? preferredModelId.value : (globalDefaultModelId.value && modelList.some((m) => m.id === globalDefaultModelId.value) ? globalDefaultModelId.value : null))
: (globalDefaultModelId.value && modelList.some((m) => m.id === globalDefaultModelId.value) ? globalDefaultModelId.value : null);
const fallback = fallbackSource ?? modelList[0]?.id ?? null;
currentModelId.value = fallback;
}
},
@ -127,9 +127,7 @@ async function loadModels() {
providerName: m.providerName ?? "—",
supportsTools: m.supportsTools ?? 1,
}));
if (isGuest) {
guestDefaultModelId.value = res.data?.defaultModelId ?? null;
}
globalDefaultModelId.value = res.data?.defaultModelId ?? null;
} catch {
models.value = [];
}

BIN
packages/drizzle-pkg/db.sqlite

Binary file not shown.

4
server/api/admin/agent-config/index.get.ts

@ -2,7 +2,7 @@ import { defineWrappedResponseHandler } from "#server/utils/handler";
import { R } from "#server/utils/response";
import { requireAdmin } from "#server/utils/admin-guard";
import { getConfigGlobal } from "#server/utils/context";
import { listSystemModels } from "#server/service/llm";
import { listAllEnabledModels } from "#server/service/llm";
export default defineWrappedResponseHandler(async (event) => {
await requireAdmin(event);
@ -14,7 +14,7 @@ export default defineWrappedResponseHandler(async (event) => {
getConfigGlobal("agentPublicToolSlugs"),
]);
const systemModels = await listSystemModels();
const systemModels = await listAllEnabledModels();
return R.success({
systemPrompt,

5
server/api/agents/[agentSlug]/chat/index.post.ts

@ -1,5 +1,5 @@
import { defineEventHandler, getRouterParam, readBody, setResponseHeaders } from "h3";
import { getCurrentUser, getConfigUser } from "#server/utils/context";
import { getCurrentUser, getConfigUser, getConfigGlobal } from "#server/utils/context";
import { resolveAgentIdentity } from "#server/service/agent/identity";
import { getSessionByIdAndUser } from "#server/service/agent/session";
import { getAgentBySlug } from "#server/service/agent/agent";
@ -61,6 +61,8 @@ export default defineEventHandler(async (event) => {
preferredModelId = (await getConfigUser(event, "preferredLlmModelId")) ?? null;
}
const globalDefaultModelId = (await getConfigGlobal("agentDefaultModelId")) ?? null;
const user = identity.userId ? await getCurrentUser(event) : null;
const { response } = await executeChat({
@ -81,6 +83,7 @@ export default defineEventHandler(async (event) => {
},
ip,
preferredModelId,
globalDefaultModelId,
requestId: event.context.requestId,
registerAbort: registerAbortController,
unregisterAbort: unregisterAbortController,

9
server/api/agents/[agentSlug]/sessions/index.post.ts

@ -3,7 +3,7 @@ import { R } from "#server/utils/response";
import { resolveAgentIdentity } from "#server/service/agent/identity";
import { createSession } from "#server/service/agent/session";
import { generateTempToken, setTempTokenCookie, getTempTokenTtlMs } from "#server/service/agent/temp-token";
import { getConfigUser } from "#server/utils/context";
import { getConfigUser, getConfigGlobal } from "#server/utils/context";
import { getAgentBySlug } from "#server/service/agent/agent";
export default defineWrappedResponseHandler(async (event) => {
@ -44,6 +44,13 @@ export default defineWrappedResponseHandler(async (event) => {
modelId = agent.defaultModelId;
}
if (!modelId) {
const globalDefault = await getConfigGlobal("agentDefaultModelId");
if (globalDefault && globalDefault > 0) {
modelId = globalDefault;
}
}
const body = await readBody(event).catch(() => ({}));
const systemPrompt: string | null =
typeof body?.systemPrompt === "string" && body.systemPrompt.trim().length > 0

27
server/api/llm/models/index.get.ts

@ -1,10 +1,29 @@
import { defineWrappedResponseHandler } from "#server/utils/handler";
import { R } from "#server/utils/response";
import { getCurrentUser } from "#server/utils/context";
import { listAllEnabledModelsForUser } from "#server/service/llm";
import { getCurrentUser, getConfigGlobal } from "#server/utils/context";
import { listAllEnabledModelsForUser, getModelWithProviderByIdAny } from "#server/service/llm";
export default defineWrappedResponseHandler(async (event) => {
const user = await getCurrentUser(event);
const list = await listAllEnabledModelsForUser(user?.id ?? null);
return R.success({ list });
const [list, globalDefaultModelId] = await Promise.all([
listAllEnabledModelsForUser(user?.id ?? null),
getConfigGlobal("agentDefaultModelId"),
]);
if (globalDefaultModelId && !list.some((m) => m.id === globalDefaultModelId)) {
const row = await getModelWithProviderByIdAny(globalDefaultModelId);
if (row) {
list.unshift({
id: row.model.id,
name: row.model.name,
modelId: row.model.modelId,
type: row.model.type,
providerName: row.provider.name,
providerId: row.provider.id,
supportsTools: row.model.supportsTools,
});
}
}
return R.success({ list, defaultModelId: globalDefaultModelId ?? null });
});

5
server/service/agent/chat-engine.ts

@ -45,6 +45,7 @@ export interface ChatEngineParams {
};
ip: string;
preferredModelId?: number | null;
globalDefaultModelId?: number | null;
requestId?: string;
registerAbort: (sessionId: string, controller: AbortController) => void;
unregisterAbort: (sessionId: string) => void;
@ -222,13 +223,13 @@ export async function executeChat(params: ChatEngineParams): Promise<ChatEngineR
let resolvedModel;
if (user) {
const targetModelId = session.modelId ?? params.preferredModelId ?? agent.defaultModelId;
const targetModelId = session.modelId ?? params.preferredModelId ?? agent.defaultModelId ?? params.globalDefaultModelId;
if (!targetModelId) {
throw createError({ statusCode: 400, statusMessage: "未配置模型,请先在设置中选择模型" });
}
resolvedModel = await resolveModelForUser(targetModelId, user.id);
} else {
const targetModelId = session.modelId ?? agent.defaultModelId;
const targetModelId = session.modelId ?? agent.defaultModelId ?? params.globalDefaultModelId;
if (!targetModelId) {
throw createError({ statusCode: 400, statusMessage: "系统未配置默认模型" });
}

21
server/service/llm/index.ts

@ -299,6 +299,27 @@ export async function listSystemModels(): Promise<LlmModelRow[]> {
.orderBy(desc(llmModels.createdAt));
}
export async function listAllEnabledModels(): Promise<LlmModelRow[]> {
return dbGlobal
.select({
id: llmModels.id,
providerId: llmModels.providerId,
name: llmModels.name,
modelId: llmModels.modelId,
type: llmModels.type,
enabled: llmModels.enabled,
description: llmModels.description,
maxTokens: llmModels.maxTokens,
supportsTools: llmModels.supportsTools,
createdAt: llmModels.createdAt,
updatedAt: llmModels.updatedAt,
})
.from(llmModels)
.innerJoin(llmProviders, eq(llmModels.providerId, llmProviders.id))
.where(eq(llmModels.enabled, 1))
.orderBy(desc(llmModels.createdAt));
}
// ============ 通用 Model 查询(agent 对话核心使用)============
export async function getModelWithProviderById(modelId: number, userId: number | null) {

5
server/service/llm/model-resolver.ts

@ -40,10 +40,13 @@ export async function resolveModelForUser(
modelId: number,
userId: number | null,
): Promise<ResolvedModel | null> {
const modelRow = userId
let modelRow = userId
? await getModelWithProviderById(modelId, userId)
: await getSystemModelWithProviderById(modelId);
if (!modelRow) {
modelRow = await getModelWithProviderByIdAny(modelId);
}
if (!modelRow) {
logger.warn("[MODEL-RESOLVER] modelId=%d not found for userId=%s", modelId, userId);
return null;
}

Loading…
Cancel
Save