Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions app/apps/components/NodePropertyPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { X } from "lucide-react";
import { WorkflowNode } from "@/types/workflow";
import { Button } from "@/components/ui/button";
import { useTranslations } from "next-intl";
import axios from "@/lib/axios";
import { DEFAULT_LLM_MODEL_CODE, type LlmModelOption } from "@/lib/llmModels";

interface NodePropertyPanelProps {
selectedNode: WorkflowNode | null;
Expand Down Expand Up @@ -58,6 +60,7 @@ export default function NodePropertyPanel({
}: NodePropertyPanelProps) {
const t = useTranslations("workflow");
const [localData, setLocalData] = useState<any>(null);
const [llmModels, setLlmModels] = useState<LlmModelOption[]>([]);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);

// 当选中节点变化时,更新本地数据
Expand All @@ -67,6 +70,21 @@ export default function NodePropertyPanel({
}
}, [selectedNode]);

useEffect(() => {
let cancelled = false;
axios
.get("/api/v1/llm-models")
.then((response) => {
if (!cancelled) setLlmModels(response.data?.items || []);
})
.catch(() => {
if (!cancelled) setLlmModels([]);
});
return () => {
cancelled = true;
};
}, []);

// 清理定时器
useEffect(() => {
return () => {
Expand Down Expand Up @@ -182,16 +200,22 @@ export default function NodePropertyPanel({
<div className="space-y-2">
<Label className="text-xs">{t("aiModel")}</Label>
<Select
value={localData.aiModel || "deepseek"}
value={localData.aiModel || DEFAULT_LLM_MODEL_CODE}
onValueChange={(value) => handleDataChange("aiModel", value)}
>
<SelectTrigger className="text-sm">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="deepseek">Deepseek</SelectItem>
<SelectItem value="qwen">Qwen</SelectItem>
<SelectItem value="openai">OpenAI</SelectItem>
{llmModels.length === 0 ? (
<SelectItem value={DEFAULT_LLM_MODEL_CODE}>DeepSeek Flash</SelectItem>
) : (
llmModels.map((m) => (
<SelectItem key={m.code} value={m.code}>
{m.display_name}
</SelectItem>
))
)}
</SelectContent>
</Select>
</div>
Expand Down
3 changes: 2 additions & 1 deletion app/apps/components/WorkflowEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import NodePalette from "./NodePalette";
import NodePropertyPanel from "./NodePropertyPanel";

import { WorkflowConfig, WorkflowNode, AppWithWorkflow } from "@/types/workflow";
import { DEFAULT_LLM_MODEL_CODE } from "@/lib/llmModels";
import {
generateDefaultWorkflow,
generateNodeId,
Expand Down Expand Up @@ -372,7 +373,7 @@ function getDefaultNodeData(type: string, t: (key: string) => string): Record<st
},
aiNode: {
name: t("chatAgent"),
aiModel: "deepseek",
aiModel: DEFAULT_LLM_MODEL_CODE,
temperature: 0.7,
maxTokens: 2000,
},
Expand Down
34 changes: 26 additions & 8 deletions app/apps/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import { checkSuperAdmin } from "@/lib/clientPermissions";
import WorkflowEditor from "./components/WorkflowEditor";
import { syncFormToWorkflow } from "@/lib/workflowUtils";
import { WorkflowConfig } from "@/types/workflow";
import { DEFAULT_LLM_MODEL_CODE, type LlmModelOption } from "@/lib/llmModels";

// 类型定义
interface App {
Expand Down Expand Up @@ -230,12 +231,13 @@ export default function AppsPage() {
app_type: "Chat",
platform: "Web",
avatar_url: null,
ai_model: "deepseek",
ai_model: DEFAULT_LLM_MODEL_CODE,
dataset_ids: [],
email: "",
settings: {},
is_default: false,
});
const [llmModels, setLlmModels] = useState<LlmModelOption[]>([]);

// 定义加载函数(必须在 useEffect 之前)
const loadApps = useCallback(async () => {
Expand All @@ -259,6 +261,14 @@ export default function AppsPage() {
}
}, []);

const loadLlmModels = useCallback(async () => {
try {
const response = await axios.get("/api/v1/llm-models");
setLlmModels(response.data?.items || []);
} catch {
setLlmModels([]);
}
}, []);

const loadWechatAgents = useCallback(async () => {
try {
Expand All @@ -280,7 +290,7 @@ export default function AppsPage() {
dataLoadedRef.current = true;

// 并行加载所有数据
Promise.all([loadApps(), loadDatasets()]).catch(() => {
Promise.all([loadApps(), loadDatasets(), loadLlmModels()]).catch(() => {
dataLoadedRef.current = false;
});

Expand Down Expand Up @@ -309,7 +319,7 @@ export default function AppsPage() {
app_type: "Chat",
platform: "Web",
avatar_url: null,
ai_model: "deepseek",
ai_model: DEFAULT_LLM_MODEL_CODE,
dataset_ids: [],
email: "",
settings: {},
Expand All @@ -335,7 +345,7 @@ export default function AppsPage() {
description: t("qualityAgentDesc"),
app_type: "Chat" as const,
platform: "Web" as const,
ai_model: "deepseek",
ai_model: DEFAULT_LLM_MODEL_CODE,
dataset_ids: ["quality_knowledge_base"], // 使用质量知识库
settings: {
workflow: {
Expand Down Expand Up @@ -377,7 +387,7 @@ export default function AppsPage() {
data: {
name: t("templateQualityClassification"),
type: "ai",
aiModel: "deepseek",
aiModel: DEFAULT_LLM_MODEL_CODE,
agentType: "quality_classify",
knowledgeBase: t("templateQualityKnowledgeBase"),
},
Expand Down Expand Up @@ -1206,9 +1216,17 @@ export default function AppsPage() {
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="deepseek">Deepseek</SelectItem>
<SelectItem value="qwen">Qwen</SelectItem>
<SelectItem value="openai">OpenAI</SelectItem>
{llmModels.length === 0 ? (
<SelectItem value={DEFAULT_LLM_MODEL_CODE}>
DeepSeek Flash
</SelectItem>
) : (
llmModels.map((m) => (
<SelectItem key={m.code} value={m.code}>
{m.display_name}
</SelectItem>
))
)}
</SelectContent>
</Select>
</div>
Expand Down
34 changes: 0 additions & 34 deletions app/system-settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Checkbox } from "@/components/ui/checkbox";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea";
import { Settings, Mail, Building2, Upload, Eye, Palette, Check } from "lucide-react";
import { useState, useEffect } from "react";
Expand Down Expand Up @@ -110,7 +103,6 @@ interface SmtpSettings {
}

interface SystemSettings {
llm_model?: string;
platform_name?: string;
platform_logo?: string;
platform_subtitle?: string;
Expand Down Expand Up @@ -143,7 +135,6 @@ export default function SystemSettingsPage() {

// 系统设置状态(平台名称、Logo等)
const [systemSettings, setSystemSettings] = useState<SystemSettings>({
llm_model: "",
platform_name: "",
platform_logo: "",
platform_subtitle: "",
Expand Down Expand Up @@ -177,7 +168,6 @@ export default function SystemSettingsPage() {
});
if (response.data) {
setSystemSettings({
llm_model: response.data.llm_model || "",
platform_name: response.data.platform_name || "",
platform_logo: response.data.platform_logo || "",
platform_subtitle: response.data.platform_subtitle || "",
Expand Down Expand Up @@ -277,7 +267,6 @@ export default function SystemSettingsPage() {

try {
await axios.put("/api/system", {
llm_model: systemSettings.llm_model,
platform_name: systemSettings.platform_name,
platform_logo: systemSettings.platform_logo,
platform_subtitle: systemSettings.platform_subtitle,
Expand Down Expand Up @@ -506,29 +495,6 @@ export default function SystemSettingsPage() {
<p className="text-xs text-muted-foreground">{t("subtitleTip")}</p>
</div>

<div className="space-y-2">
<Label htmlFor="llm_model" className="text-xs">
{t("chatModel")}
</Label>
<Select
value={systemSettings.llm_model || "deepseek"}
onValueChange={(value) =>
setSystemSettings({ ...systemSettings, llm_model: value })
}
>
<SelectTrigger className="h-8 text-sm">
<SelectValue placeholder={t("selectChatModel")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="deepseek">{t("deepseekRemote")}</SelectItem>
<SelectItem value="local">{t("localModel")}</SelectItem>
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">
{systemSettings.llm_model === "local" ? t("localModelTip") : t("remoteModelTip")}
</p>
</div>

{systemError && (
<div className="text-red-600 text-xs bg-red-50 p-2 rounded">{systemError}</div>
)}
Expand Down
8 changes: 8 additions & 0 deletions lib/llmModels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/** 对话模型目录(与 ragent-service llm_models.code 对齐) */

export const DEFAULT_LLM_MODEL_CODE = "deepseek-flash";

export interface LlmModelOption {
code: string;
display_name: string;
}
3 changes: 2 additions & 1 deletion lib/workflowUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

import { WorkflowConfig, WorkflowNode, WorkflowEdge, AppWithWorkflow } from "@/types/workflow";
import { DEFAULT_LLM_MODEL_CODE } from "@/lib/llmModels";

/**
* 从App配置生成默认工作流
Expand Down Expand Up @@ -32,7 +33,7 @@ export const generateDefaultWorkflow = (app: Partial<AppWithWorkflow>): Workflow
position: { x: 250, y: 150 },
data: {
name: "会话智能体",
aiModel: app.ai_model || "deepseek",
aiModel: app.ai_model || DEFAULT_LLM_MODEL_CODE,
temperature: 0.7,
maxTokens: 2000,
},
Expand Down
6 changes: 0 additions & 6 deletions messages/en/systemSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,6 @@
"upload": "Upload",
"uploading": "Uploading...",
"logoPreview": "Logo Preview:",
"chatModel": "Chat Model",
"selectChatModel": "Select Chat Model",
"deepseekRemote": "Deepseek (Remote)",
"localModel": "Local Model",
"localModelTip": "Use local model for faster response, suitable for offline environments",
"remoteModelTip": "Use remote model for more powerful features and capabilities",
"configSaved": "Configuration saved",
"saveConfig": "Save Configuration",
"loginLeftPanelHtml": "Login Page Left Panel Custom HTML",
Expand Down
8 changes: 1 addition & 7 deletions messages/zh-CN/systemSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"noPermission": "您需要向管理员申请权限",
"platformSettingsTitle": "平台设置",
"platformSettings": "平台设置",
"platformSettingsDesc": "配置平台名称、Logo 和 AI 模型",
"platformSettingsDesc": "配置平台名称、Logo 和登录页外观",
"platformName": "平台名称",
"platformNamePlaceholder": "请输入平台名称",
"platformSubtitle": "平台副标题",
Expand All @@ -47,12 +47,6 @@
"upload": "上传",
"uploading": "上传中...",
"logoPreview": "Logo 预览:",
"chatModel": "会话模型",
"selectChatModel": "选择会话模型",
"deepseekRemote": "Deepseek(远程)",
"localModel": "本地模型",
"localModelTip": "使用本地模型,响应速度快,适合离线环境",
"remoteModelTip": "使用远程模型,功能更强大,支持更多功能",
"configSaved": "配置已保存",
"saveConfig": "保存配置",
"loginLeftPanelHtml": "登录页左侧面板 HTML",
Expand Down
8 changes: 1 addition & 7 deletions pages/api/system/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const hasThemePrimaryColor = existingColumns.includes("theme_primary_color");
const hasThemeSecondaryColor = existingColumns.includes("theme_secondary_color");
// 根据字段是否存在构建查询
let selectFields = "platform_name, platform_logo, platform_subtitle, llm_model";
let selectFields = "platform_name, platform_logo, platform_subtitle";
if (hasLoginPanelHtml) selectFields += ", login_left_panel_html";
if (hasThemePrimaryColor) selectFields += ", theme_primary_color";
if (hasThemeSecondaryColor) selectFields += ", theme_secondary_color";
Expand All @@ -72,10 +72,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
? row.theme_secondary_color || null
: null,
};
// 如果请求了 full_data,也返回 llm_model
if (req.query.full_data) {
publicData.llm_model = row.llm_model || null;
}
return res.status(200).json(publicData);
} else {
// 没有系统设置记录,返回空数据
Expand All @@ -86,7 +82,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
login_left_panel_html: null,
theme_primary_color: null,
theme_secondary_color: null,
...(req.query.full_data ? { llm_model: null } : {}),
});
}
} finally {
Expand All @@ -102,7 +97,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
login_left_panel_html: null,
theme_primary_color: null,
theme_secondary_color: null,
...(req.query.full_data ? { llm_model: null } : {}),
});
}
}
Expand Down
10 changes: 10 additions & 0 deletions pages/api/v1/llm-models/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { proxySkillsApi } from "@/lib/skillsProxy";

/** GET /api/v1/llm-models → 后端对话模型目录(应用配置下拉) */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
return proxySkillsApi(req, res, {
path: "/api/v1/llm-models/",
allow: ["GET"],
});
}
Loading