feat(agents): 创建专家页面加模型选择
NewAgentPage 在两处加 Select 下拉: - 左侧聊天框上方 "当前对话模型":透传给 useThreadStream 的 context.model_name,控制当前 bootstrap 对话调用的 LLM - 右侧表单 "模型":写入 agent.model,控制专家保存后被使用时调用 的 LLM 两个下拉互相独立,复用 useModels() 拉 /api/models。页面加载完 模型列表后自动 seed 到列表第一个(与 config.yaml 全局默认对齐), 用户可随时切换。 杂项:.gitignore 忽略 skills/ 与 hermes-agent-main/ 运行时目录。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
4cc8b3198b
commit
4e764350a4
2 files changed
+77
-1
No files matched your search
@@ -64,3 +64,7 @@ offline-backend-20260512/backend/.deer-flow/*
|
||||
offline-backend-20260512/start_backend.sh
|
||||
offline-backend-20260512/stop_backend.sh
|
||||
offline-backend-20260512/check_backend.sh
|
||||
|
||||
skills/
|
||||
hermes-agent-main/
|
||||
.gitignore
|
||||
@@ -17,6 +17,13 @@ import {
|
||||
} from "@/components/ai-elements/prompt-input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { ArtifactsProvider } from "@/components/workspace/artifacts";
|
||||
import {
|
||||
@@ -33,6 +40,7 @@ import {
|
||||
updateAgent,
|
||||
} from "@/core/agents/api";
|
||||
import { useI18n } from "@/core/i18n/hooks";
|
||||
import { useModels } from "@/core/models/hooks";
|
||||
import type { Skill } from "@/core/skills";
|
||||
import { useSkills } from "@/core/skills/hooks";
|
||||
import { useThreadStream } from "@/core/threads/hooks";
|
||||
@@ -63,6 +71,8 @@ export default function NewAgentPage() {
|
||||
const [nameInput, setNameInput] = useState("");
|
||||
const [descriptionInput, setDescriptionInput] = useState("");
|
||||
const [soulInput, setSoulInput] = useState("");
|
||||
const [modelInput, setModelInput] = useState<string>("");
|
||||
const [chatModelInput, setChatModelInput] = useState<string>("");
|
||||
const [formError, setFormError] = useState("");
|
||||
const [agentId, setAgentId] = useState("");
|
||||
const [agent, setAgent] = useState<Agent | null>(null);
|
||||
@@ -72,6 +82,7 @@ export default function NewAgentPage() {
|
||||
const [isSkillSelectionTouched, setIsSkillSelectionTouched] = useState(false);
|
||||
const [threadPrepared, setThreadPrepared] = useState(false);
|
||||
const { skills, isLoading: isSkillsLoading } = useSkills();
|
||||
const { models, isLoading: isModelsLoading } = useModels();
|
||||
|
||||
const enabledSkills = useMemo(
|
||||
() => skills.filter((skill) => skill.enabled),
|
||||
@@ -89,12 +100,24 @@ export default function NewAgentPage() {
|
||||
);
|
||||
}, [enabledSkills, isSkillSelectionTouched]);
|
||||
|
||||
// Seed both model selectors with the first available model the first time
|
||||
// models load. The user can still change either independently afterwards.
|
||||
useEffect(() => {
|
||||
if (models.length === 0) {
|
||||
return;
|
||||
}
|
||||
const first = models[0]!.name;
|
||||
setModelInput((prev) => prev || first);
|
||||
setChatModelInput((prev) => prev || first);
|
||||
}, [models]);
|
||||
|
||||
const [thread, sendMessage] = useThreadStream({
|
||||
threadId,
|
||||
assistantId: agentId || undefined,
|
||||
context: {
|
||||
mode: "flash",
|
||||
is_bootstrap: true,
|
||||
model_name: chatModelInput || undefined,
|
||||
},
|
||||
onToolEnd() {
|
||||
// setup_agent never actually executes in this flow — it gets
|
||||
@@ -140,6 +163,8 @@ export default function NewAgentPage() {
|
||||
name = `未命名专家-${Date.now().toString(36)}`;
|
||||
}
|
||||
|
||||
const modelOverride = modelInput.trim() || null;
|
||||
|
||||
if (agentId) {
|
||||
setIsSavingDraft(true);
|
||||
try {
|
||||
@@ -148,6 +173,7 @@ export default function NewAgentPage() {
|
||||
description: descriptionInput,
|
||||
soul: soulInput,
|
||||
skills: selectedSkills,
|
||||
model: modelOverride,
|
||||
});
|
||||
setAgent(updated);
|
||||
return updated;
|
||||
@@ -167,6 +193,7 @@ export default function NewAgentPage() {
|
||||
description: descriptionInput,
|
||||
soul: soulInput,
|
||||
skills: selectedSkills,
|
||||
model: modelOverride,
|
||||
});
|
||||
setAgentId(created.id);
|
||||
setAgent(created);
|
||||
@@ -193,6 +220,7 @@ export default function NewAgentPage() {
|
||||
}, [
|
||||
agentId,
|
||||
descriptionInput,
|
||||
modelInput,
|
||||
selectedSkills,
|
||||
soulInput,
|
||||
t.agents.nameStepCheckError,
|
||||
@@ -305,7 +333,28 @@ export default function NewAgentPage() {
|
||||
onSetupAgentReject={handleRejectSetupAgent}
|
||||
/>
|
||||
</div>
|
||||
<div className="bg-background shrink-0 border-t px-4 py-4">
|
||||
<div className="bg-background shrink-0 space-y-2 border-t px-4 py-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-muted-foreground text-xs">
|
||||
当前对话模型
|
||||
</span>
|
||||
<Select
|
||||
value={chatModelInput}
|
||||
onValueChange={setChatModelInput}
|
||||
disabled={isModelsLoading || thread.isLoading}
|
||||
>
|
||||
<SelectTrigger className="h-7 w-auto min-w-[10rem] gap-2 px-2 text-xs">
|
||||
<SelectValue placeholder="选择模型" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{models.map((model) => (
|
||||
<SelectItem key={model.name} value={model.name}>
|
||||
{model.display_name || model.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<PromptInput
|
||||
onSubmit={({ text }) => void handleChatSubmit(text)}
|
||||
>
|
||||
@@ -354,6 +403,29 @@ export default function NewAgentPage() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">模型</label>
|
||||
<Select
|
||||
value={modelInput}
|
||||
onValueChange={setModelInput}
|
||||
disabled={isModelsLoading}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="选择模型" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{models.map((model) => (
|
||||
<SelectItem key={model.name} value={model.name}>
|
||||
{model.display_name || model.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
该专家被使用时调用的模型。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">SOUL.md</label>
|
||||
<Textarea
|
||||
|
||||
Reference in new issue
Block a user