```
feat(engine): 添加技能查看工具并优化异步任务管理 - 添加SkillViewTool到引擎加载器中,增强技能管理功能 - 在AgentLoop中引入_active_direct_task来跟踪活跃任务 - 实现直接任务执行时的同步处理逻辑 - 更新工具实例化方式以支持依赖注入 feat(config): 增加智能体运行时参数配置支持 - 扩展AgentDefaultsConfig添加max_tokens和temperature字段 - 实现配置解析函数_first_config_value处理多个配置源 - 支持通过Web API动态更新智能体运行时参数 - 添加前端页面配置表单和验证逻辑 refactor(provider): 统一最大令牌数参数类型为可选整型 - 将所有LLM提供者的max_tokens参数改为int | None类型 - 为AnthropicProvider实现模型特定的最大令牌数默认值 - 调整参数传递逻辑,优先级:调用参数 > 配置文件 > 模型默认值 - 移除硬编码的默认值,改用条件判断 feat(process): 增强事件投影功能 - 添加工具调用开始/结束事件的映射逻辑 - 实现技能激活事件的识别和展示 - 添加辅助函数处理工具调用名称和参数提取 - 优化运行记录关联逻辑,提升事件匹配准确性 fix(web): 更新网络请求客户端信任环境设置 - 将WebFetchTool和WebSearchTool的trust_env参数设为True - 确保HTTP客户端能够正确使用系统代理配置 - 修复可能的网络连接问题 test: 添加配置加载和事件投影相关测试 - 新增智能体默认参数配置测试用例 - 实现API配置持久化和重载测试 - 添加技能卡片和工具事件的投影测试 ```
This commit is contained in:
@ -15,7 +15,7 @@ import {
|
||||
Settings2,
|
||||
ScrollText,
|
||||
} from 'lucide-react';
|
||||
import { getStatus, updateProviderConfig } from '@/lib/api';
|
||||
import { getStatus, updateAgentConfig, updateProviderConfig } from '@/lib/api';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@ -42,6 +42,12 @@ type ProviderFormState = {
|
||||
requestTimeoutSeconds: string;
|
||||
};
|
||||
|
||||
type AgentFormState = {
|
||||
maxTokens: string;
|
||||
temperature: string;
|
||||
maxToolIterations: string;
|
||||
};
|
||||
|
||||
export default function StatusPage() {
|
||||
const { locale } = useAppI18n();
|
||||
const [status, setStatus] = useState<SystemStatus | null>(null);
|
||||
@ -57,6 +63,13 @@ export default function StatusPage() {
|
||||
}));
|
||||
const [savingProvider, setSavingProvider] = useState(false);
|
||||
const [providerError, setProviderError] = useState<string | null>(null);
|
||||
const [agentForm, setAgentForm] = useState<AgentFormState>(() => ({
|
||||
maxTokens: '',
|
||||
temperature: '0.2',
|
||||
maxToolIterations: '30',
|
||||
}));
|
||||
const [savingAgent, setSavingAgent] = useState(false);
|
||||
const [agentError, setAgentError] = useState<string | null>(null);
|
||||
|
||||
const loadStatus = async () => {
|
||||
setLoading(true);
|
||||
@ -64,6 +77,11 @@ export default function StatusPage() {
|
||||
try {
|
||||
const data = await getStatus();
|
||||
setStatus(data);
|
||||
setAgentForm({
|
||||
maxTokens: data.max_tokens == null ? '' : String(data.max_tokens),
|
||||
temperature: String(data.temperature),
|
||||
maxToolIterations: String(data.max_tool_iterations),
|
||||
});
|
||||
} catch (err: any) {
|
||||
setError(err.message || pickAppText(locale, '连接后端失败', 'Failed to connect to the backend'));
|
||||
} finally {
|
||||
@ -115,6 +133,39 @@ export default function StatusPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveAgentConfig = async () => {
|
||||
setSavingAgent(true);
|
||||
setAgentError(null);
|
||||
try {
|
||||
const maxTokensText = agentForm.maxTokens.trim();
|
||||
const maxTokens = maxTokensText ? Number(maxTokensText) : null;
|
||||
const temperature = Number(agentForm.temperature.trim());
|
||||
const maxToolIterations = Number(agentForm.maxToolIterations.trim());
|
||||
if (
|
||||
maxTokens !== null &&
|
||||
(!Number.isInteger(maxTokens) || maxTokens <= 0)
|
||||
) {
|
||||
throw new Error(pickAppText(locale, '最大令牌数必须为空或正整数', 'Max tokens must be blank or a positive integer'));
|
||||
}
|
||||
if (!Number.isFinite(temperature) || temperature < 0 || temperature > 2) {
|
||||
throw new Error(pickAppText(locale, '温度必须在 0 到 2 之间', 'Temperature must be between 0 and 2'));
|
||||
}
|
||||
if (!Number.isInteger(maxToolIterations) || maxToolIterations < 0) {
|
||||
throw new Error(pickAppText(locale, '最大工具迭代次数必须是非负整数', 'Max tool iterations must be a non-negative integer'));
|
||||
}
|
||||
await updateAgentConfig({
|
||||
max_tokens: maxTokens,
|
||||
temperature,
|
||||
max_tool_iterations: maxToolIterations,
|
||||
});
|
||||
await loadStatus();
|
||||
} catch (err: any) {
|
||||
setAgentError(err.message || pickAppText(locale, '保存智能体配置失败', 'Failed to save agent configuration'));
|
||||
} finally {
|
||||
setSavingAgent(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center py-20">
|
||||
@ -207,14 +258,47 @@ export default function StatusPage() {
|
||||
{pickAppText(locale, '智能体配置', 'Agent configuration')}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
<CardContent className="space-y-5">
|
||||
<InfoRow label={pickAppText(locale, '模型', 'Model')} value={status.model} />
|
||||
<InfoRow label={pickAppText(locale, '最大令牌数', 'Max tokens')} value={String(status.max_tokens)} />
|
||||
<InfoRow label={pickAppText(locale, '温度', 'Temperature')} value={String(status.temperature)} />
|
||||
<InfoRow
|
||||
label={pickAppText(locale, '最大工具迭代次数', 'Max tool iterations')}
|
||||
value={String(status.max_tool_iterations)}
|
||||
/>
|
||||
<div className="grid gap-4 border-t pt-5 md:grid-cols-3">
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="agent-max-tokens">{pickAppText(locale, '最大令牌数', 'Max tokens')}</Label>
|
||||
<Input
|
||||
id="agent-max-tokens"
|
||||
inputMode="numeric"
|
||||
value={agentForm.maxTokens}
|
||||
onChange={(event) => setAgentForm((prev) => ({ ...prev, maxTokens: event.target.value }))}
|
||||
placeholder={pickAppText(locale, '模型默认', 'Model default')}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="agent-temperature">{pickAppText(locale, '温度', 'Temperature')}</Label>
|
||||
<Input
|
||||
id="agent-temperature"
|
||||
inputMode="decimal"
|
||||
value={agentForm.temperature}
|
||||
onChange={(event) => setAgentForm((prev) => ({ ...prev, temperature: event.target.value }))}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="agent-max-tool-iterations">
|
||||
{pickAppText(locale, '最大工具迭代次数', 'Max tool iterations')}
|
||||
</Label>
|
||||
<Input
|
||||
id="agent-max-tool-iterations"
|
||||
inputMode="numeric"
|
||||
value={agentForm.maxToolIterations}
|
||||
onChange={(event) => setAgentForm((prev) => ({ ...prev, maxToolIterations: event.target.value }))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div className="text-sm text-destructive">{agentError || ''}</div>
|
||||
<Button onClick={handleSaveAgentConfig} disabled={savingAgent} className="sm:self-end">
|
||||
{savingAgent ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : null}
|
||||
{pickAppText(locale, '保存智能体配置', 'Save agent config')}
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user