mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-16 12:27:53 +08:00
feat(agent): re-enable per-Agent model override
This commit is contained in:
parent
b0f5dadbe6
commit
92bd8e9b6e
@ -155,9 +155,13 @@ public class AgentGraphBuilder {
|
|||||||
// the agent's bound-skill requires-model. Falls back to the
|
// the agent's bound-skill requires-model. Falls back to the
|
||||||
// global default when no preferred provider satisfies, so the
|
// global default when no preferred provider satisfies, so the
|
||||||
// existing "no default model" error path stays intact.
|
// existing "no default model" error path stays intact.
|
||||||
|
// RFC-03 Lane G1 — honor per-Agent model override when set.
|
||||||
|
// resolveModel() looks up entity.modelName in enabled-only models;
|
||||||
|
// null / blank / unmatched silently fall back to getDefaultModel(),
|
||||||
|
// preserving the legacy behavior for Agents without an override.
|
||||||
ModelConfigEntity globalDefault;
|
ModelConfigEntity globalDefault;
|
||||||
try {
|
try {
|
||||||
globalDefault = modelConfigService.getDefaultModel();
|
globalDefault = modelConfigService.resolveModel(entity.getModelName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new MateClawException("err.agent.no_default_model", "无法构建 Agent:请先在「设置 → 模型」中配置并启用默认模型");
|
throw new MateClawException("err.agent.no_default_model", "无法构建 Agent:请先在「设置 → 模型」中配置并启用默认模型");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,10 +31,22 @@ public class AgentEntity {
|
|||||||
private String systemPrompt;
|
private String systemPrompt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保留但不再生效:运行时统一使用全局默认模型(ModelConfigService.getDefaultModel())。
|
* Per-Agent model override.
|
||||||
* 该字段为历史残留,仅保留以避免数据库迁移。
|
*
|
||||||
|
* <p>When non-blank, the runtime resolves this value via
|
||||||
|
* {@code ModelConfigService.resolveModel(...)} — a case-sensitive,
|
||||||
|
* enabled-only lookup against {@code mate_model_config.model_name}.
|
||||||
|
* On match, the resolved entity is used as the primary model in
|
||||||
|
* place of {@code getDefaultModel()}.
|
||||||
|
*
|
||||||
|
* <p>Null / blank → fall back to the global default (preserves the
|
||||||
|
* original behavior). Stale rows whose named model has been removed
|
||||||
|
* or disabled also fall back, since {@code resolveModel} returns the
|
||||||
|
* default when no enabled match is found.
|
||||||
|
*
|
||||||
|
* <p>RFC-03 Lane G1 — re-enables this field after it was silently
|
||||||
|
* deprecated in earlier work; the database column is unchanged.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
private String modelName;
|
private String modelName;
|
||||||
|
|
||||||
/** 最大迭代次数 */
|
/** 最大迭代次数 */
|
||||||
|
|||||||
@ -233,6 +233,7 @@ err.llm.pkce_failed=PKCE \u751f\u6210\u5931\u8d25
|
|||||||
err.llm.chatgpt_stream_failed=ChatGPT \u6d41\u5f0f\u8c03\u7528\u5931\u8d25
|
err.llm.chatgpt_stream_failed=ChatGPT \u6d41\u5f0f\u8c03\u7528\u5931\u8d25
|
||||||
err.llm.chatgpt_error=ChatGPT \u8fd4\u56de\u9519\u8bef
|
err.llm.chatgpt_error=ChatGPT \u8fd4\u56de\u9519\u8bef
|
||||||
err.llm.chatgpt_account_missing=chatgpt-account-id \u7f3a\u5931
|
err.llm.chatgpt_account_missing=chatgpt-account-id \u7f3a\u5931
|
||||||
|
err.llm.model_not_supported=\u6a21\u578b ID \u4e0d\u652f\u6301\u5728 DashScope \u539f\u751f\u534f\u8bae\u4e2d\u4f7f\u7528\u3002\u70b9\u7248\u672c\u683c\u5f0f\u7684\u7cfb\u5217\uff08\u4f8b\u5982 qwen3.5-*\u3001qwen3.6-*\uff09\u53ea\u80fd\u901a\u8fc7\u517c\u5bb9\u6a21\u5f0f\u4f7f\u7528\u3002\u8bf7\u4f7f\u7528\u5141\u8bb8\u7684 ID\uff0c\u5982 qwen-max / qwen-plus / qwen3-max\u3002
|
||||||
# datasource
|
# datasource
|
||||||
err.datasource.not_found=\u6570\u636e\u6e90\u4e0d\u5b58\u5728
|
err.datasource.not_found=\u6570\u636e\u6e90\u4e0d\u5b58\u5728
|
||||||
err.datasource.sql_empty=SQL \u4e0d\u80fd\u4e3a\u7a7a
|
err.datasource.sql_empty=SQL \u4e0d\u80fd\u4e3a\u7a7a
|
||||||
|
|||||||
@ -773,6 +773,9 @@ export default {
|
|||||||
systemPrompt: 'System Prompt',
|
systemPrompt: 'System Prompt',
|
||||||
maxIterations: 'Max Iterations',
|
maxIterations: 'Max Iterations',
|
||||||
defaultThinkingLevel: 'Default Thinking Level',
|
defaultThinkingLevel: 'Default Thinking Level',
|
||||||
|
modelName: 'Model',
|
||||||
|
modelGlobalDefault: 'Use global default',
|
||||||
|
modelHint: 'Override the global default model for this Agent. Leave blank to follow Settings → Models.',
|
||||||
tags: 'Tags',
|
tags: 'Tags',
|
||||||
enabled: 'Enabled',
|
enabled: 'Enabled',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -775,6 +775,9 @@ export default {
|
|||||||
systemPrompt: '系统提示词',
|
systemPrompt: '系统提示词',
|
||||||
maxIterations: '最大迭代次数',
|
maxIterations: '最大迭代次数',
|
||||||
defaultThinkingLevel: '默认思考深度',
|
defaultThinkingLevel: '默认思考深度',
|
||||||
|
modelName: '模型',
|
||||||
|
modelGlobalDefault: '使用全局默认模型',
|
||||||
|
modelHint: '为此 Agent 单独指定模型,留空则跟随「设置 → 模型」中的全局默认。',
|
||||||
tags: '标签',
|
tags: '标签',
|
||||||
enabled: '启用',
|
enabled: '启用',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -204,6 +204,18 @@
|
|||||||
<label class="form-label">{{ t('agents.fields.maxIterations') }}</label>
|
<label class="form-label">{{ t('agents.fields.maxIterations') }}</label>
|
||||||
<input v-model.number="form.maxIterations" type="number" min="1" max="50" class="form-input" />
|
<input v-model.number="form.maxIterations" type="number" min="1" max="50" class="form-input" />
|
||||||
</div>
|
</div>
|
||||||
|
<!-- RFC-03 Lane G1: per-Agent model override. Empty value falls
|
||||||
|
back to the global default in ModelConfigService.resolveModel. -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ t('agents.fields.modelName') }}</label>
|
||||||
|
<select v-model="form.modelName" class="form-input">
|
||||||
|
<option value="">{{ t('agents.fields.modelGlobalDefault') }}</option>
|
||||||
|
<option v-for="m in availableModels" :key="m.id" :value="m.modelName">
|
||||||
|
{{ m.name }} ({{ m.provider }}/{{ m.modelName }})
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<p class="form-hint">{{ t('agents.fields.modelHint') }}</p>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ t('agents.fields.defaultThinkingLevel') }}</label>
|
<label class="form-label">{{ t('agents.fields.defaultThinkingLevel') }}</label>
|
||||||
<select v-model="form.defaultThinkingLevel" class="form-input">
|
<select v-model="form.defaultThinkingLevel" class="form-input">
|
||||||
@ -368,6 +380,9 @@ const selectedToolNames = ref<string[]>([])
|
|||||||
// RFC-009 PR-3: per-agent provider preference order
|
// RFC-009 PR-3: per-agent provider preference order
|
||||||
const availableProviders = ref<{ id: string; name: string }[]>([])
|
const availableProviders = ref<{ id: string; name: string }[]>([])
|
||||||
const selectedProviderIds = ref<string[]>([])
|
const selectedProviderIds = ref<string[]>([])
|
||||||
|
// RFC-03 Lane G1: per-Agent model override picker — populated from the
|
||||||
|
// global enabled-models list, blank value means "fall back to default".
|
||||||
|
const availableModels = ref<Array<{ id: number; name: string; provider: string; modelName: string }>>([])
|
||||||
|
|
||||||
// Template selector state
|
// Template selector state
|
||||||
const showTemplateSelector = ref(false)
|
const showTemplateSelector = ref(false)
|
||||||
@ -387,6 +402,7 @@ const defaultForm = (): Partial<Agent> & { name: string; defaultThinkingLevel: s
|
|||||||
description: '',
|
description: '',
|
||||||
agentType: 'react',
|
agentType: 'react',
|
||||||
systemPrompt: '',
|
systemPrompt: '',
|
||||||
|
modelName: '', // RFC-03 G1 — empty means "use global default"
|
||||||
maxIterations: 10,
|
maxIterations: 10,
|
||||||
// Empty so SkillIcon's fallback (🤖) shows instead of pinning a literal
|
// Empty so SkillIcon's fallback (🤖) shows instead of pinning a literal
|
||||||
// emoji into form.icon — otherwise the picker thinks the user picked
|
// emoji into form.icon — otherwise the picker thinks the user picked
|
||||||
@ -419,6 +435,9 @@ const filteredAgents = computed(() => {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadAgents()
|
loadAgents()
|
||||||
|
// RFC-03 G1: load models once for the per-Agent override dropdown.
|
||||||
|
// Failure is non-fatal — the dropdown just shows only "global default".
|
||||||
|
loadAvailableModels()
|
||||||
})
|
})
|
||||||
|
|
||||||
async function loadAgents() {
|
async function loadAgents() {
|
||||||
@ -430,6 +449,20 @@ async function loadAgents() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadAvailableModels() {
|
||||||
|
try {
|
||||||
|
const res: any = await modelApi.listEnabled()
|
||||||
|
availableModels.value = (res.data || []).map((m: any) => ({
|
||||||
|
id: m.id,
|
||||||
|
name: m.name,
|
||||||
|
provider: m.provider,
|
||||||
|
modelName: m.modelName,
|
||||||
|
}))
|
||||||
|
} catch {
|
||||||
|
// Silent — the picker still works (empty list = only "default" option).
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function parseTags(tags: string): string[] {
|
function parseTags(tags: string): string[] {
|
||||||
return tags.split(',').map(s => s.trim()).filter(Boolean)
|
return tags.split(',').map(s => s.trim()).filter(Boolean)
|
||||||
}
|
}
|
||||||
@ -515,6 +548,7 @@ async function openEditModal(agent: Agent) {
|
|||||||
description: agent.description || '',
|
description: agent.description || '',
|
||||||
agentType: agent.agentType,
|
agentType: agent.agentType,
|
||||||
systemPrompt: agent.systemPrompt || '',
|
systemPrompt: agent.systemPrompt || '',
|
||||||
|
modelName: agent.modelName || '',
|
||||||
maxIterations: agent.maxIterations,
|
maxIterations: agent.maxIterations,
|
||||||
icon: agent.icon || '',
|
icon: agent.icon || '',
|
||||||
tags: agent.tags || '',
|
tags: agent.tags || '',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user