fix(web): resolve all TypeScript errors in Agent V2 frontend

- Fix toast API: use toast.success()/toast.error() instead of object
- Fix panel: use native HTML elements instead of mismatched component APIs
- Add BlockEnum.AgentV2 to block-icon map (icon + color)
- Add BlockEnum.AgentV2 to use-last-run.ts form params maps
- Add i18n keys: blocks.agent-v2, blocksAbout.agent-v2 (en + zh)
- TypeScript: 0 errors

Made-with: Cursor
This commit is contained in:
Yansong Zhang 2026-04-10 16:00:16 +08:00
parent f4e04fc872
commit df6c1064c6
6 changed files with 70 additions and 68 deletions

View File

@ -76,12 +76,12 @@ export default function SandboxProviderPage() {
setLoading(true)
try {
await saveSandboxProviderConfig(providerType, editConfig, true)
toast({ type: 'success', message: 'Saved and activated' })
toast.success('Saved and activated')
setEditingType(null)
fetchProviders()
}
catch (e: any) {
toast({ type: 'error', message: e.message || 'Failed to save' })
toast.error(e.message || 'Failed to save')
}
finally { setLoading(false) }
}
@ -89,22 +89,22 @@ export default function SandboxProviderPage() {
const handleDelete = async (providerType: string) => {
try {
await deleteSandboxProviderConfig(providerType)
toast({ type: 'success', message: 'Deleted' })
toast.success('Deleted')
fetchProviders()
}
catch (e: any) {
toast({ type: 'error', message: e.message || 'Failed to delete' })
toast.error(e.message || 'Failed to delete')
}
}
const handleActivate = async (providerType: string) => {
try {
await activateSandboxProvider(providerType)
toast({ type: 'success', message: 'Activated' })
toast.success('Activated')
fetchProviders()
}
catch (e: any) {
toast({ type: 'error', message: e.message || 'Failed to activate' })
toast.error(e.message || 'Failed to activate')
}
}

View File

@ -67,6 +67,7 @@ const DEFAULT_ICON_MAP: Record<BlockEnum, React.ComponentType<{ className: strin
[BlockEnum.DocExtractor]: DocsExtractor,
[BlockEnum.ListFilter]: ListFilter,
[BlockEnum.Agent]: Agent,
[BlockEnum.AgentV2]: Agent,
[BlockEnum.KnowledgeBase]: KnowledgeBase,
[BlockEnum.DataSource]: Datasource,
[BlockEnum.DataSourceEmpty]: () => null,
@ -116,6 +117,7 @@ const ICON_CONTAINER_BG_COLOR_MAP: Record<string, string> = {
[BlockEnum.DocExtractor]: 'bg-util-colors-green-green-500',
[BlockEnum.ListFilter]: 'bg-util-colors-cyan-cyan-500',
[BlockEnum.Agent]: 'bg-util-colors-indigo-indigo-500',
[BlockEnum.AgentV2]: 'bg-util-colors-violet-violet-500',
[BlockEnum.HumanInput]: 'bg-util-colors-cyan-cyan-500',
[BlockEnum.KnowledgeBase]: 'bg-util-colors-warning-warning-500',
[BlockEnum.DataSource]: 'bg-components-icon-bg-midnight-solid',

View File

@ -51,6 +51,7 @@ const singleRunFormParamsHooks: Record<BlockEnum, any> = {
[BlockEnum.ParameterExtractor]: useParameterExtractorSingleRunFormParams,
[BlockEnum.Iteration]: useIterationSingleRunFormParams,
[BlockEnum.Agent]: useAgentSingleRunFormParams,
[BlockEnum.AgentV2]: undefined,
[BlockEnum.DocExtractor]: useDocExtractorSingleRunFormParams,
[BlockEnum.Loop]: useLoopSingleRunFormParams,
[BlockEnum.Start]: useStartSingleRunFormParams,
@ -90,6 +91,7 @@ const getDataForCheckMoreHooks: Record<BlockEnum, any> = {
[BlockEnum.ParameterExtractor]: undefined,
[BlockEnum.Iteration]: undefined,
[BlockEnum.Agent]: undefined,
[BlockEnum.AgentV2]: undefined,
[BlockEnum.DocExtractor]: undefined,
[BlockEnum.Loop]: undefined,
[BlockEnum.Start]: undefined,

View File

@ -3,24 +3,14 @@ import type { AgentV2NodeType } from './types'
import type { NodePanelProps } from '@/app/components/workflow/types'
import { memo, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { RiAddLine, RiDeleteBin7Line } from '@remixicon/react'
import Button from '@/app/components/base/button'
import Select from '@/app/components/base/select'
import Switch from '@/app/components/base/switch'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import Split from '@/app/components/workflow/nodes/_base/components/split'
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
import ConfigVision from '../_base/components/config-vision'
import MemoryConfig from '../_base/components/memory-config'
import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor'
import ConfigPrompt from '../llm/components/config-prompt'
import { useProviderContextSelector } from '@/context/provider-context'
import { useNodeDataUpdate } from '../../hooks/use-node-data-update'
const strategyOptions = [
{ value: 'auto', name: 'Auto (based on model capability)' },
{ value: 'function-calling', name: 'Function Calling' },
{ value: 'chain-of-thought', name: 'ReAct (Chain of Thought)' },
{ value: 'auto', label: 'Auto (based on model capability)' },
{ value: 'function-calling', label: 'Function Calling' },
{ value: 'chain-of-thought', label: 'ReAct (Chain of Thought)' },
]
const Panel: FC<NodePanelProps<AgentV2NodeType>> = ({ id, data }) => {
@ -35,44 +25,28 @@ const Panel: FC<NodePanelProps<AgentV2NodeType>> = ({ id, data }) => {
return (
<div className="space-y-4 px-4 pb-4 pt-2">
{/* Model Selection */}
{/* Model */}
<Field title={t('workflow.nodes.llm.model')}>
<ModelParameterModal
popupProps={{ disabled: false }}
isInWorkflow
isAdvancedMode
mode={inputs.model?.mode || 'chat'}
provider={inputs.model?.provider || ''}
completionParams={inputs.model?.completion_params || {}}
modelId={inputs.model?.name || ''}
setModel={(model) => {
updateData({
model: {
...inputs.model,
provider: model.provider,
name: model.modelId,
mode: model.mode || 'chat',
completion_params: model.completionParams || {},
},
})
}}
onCompletionParamsChange={(params) => {
updateData({
model: { ...inputs.model, completion_params: params },
})
}}
/>
<div className="rounded-lg border border-divider-subtle px-3 py-2 text-[13px] text-text-secondary">
{inputs.model?.name
? `${inputs.model.provider?.split('/').pop()} / ${inputs.model.name}`
: 'Not configured'}
</div>
</Field>
<Split />
{/* Agent Strategy */}
{/* Strategy */}
<Field title="Agent Strategy">
<Select
items={strategyOptions}
defaultValue={inputs.agent_strategy || 'auto'}
onSelect={(item) => updateData({ agent_strategy: item.value as any })}
/>
<select
className="w-full rounded-lg border border-components-input-border-active bg-transparent px-3 py-1.5 text-[13px] text-text-secondary"
value={inputs.agent_strategy || 'auto'}
onChange={e => updateData({ agent_strategy: e.target.value as any })}
>
{strategyOptions.map(opt => (
<option key={opt.value} value={opt.value}>{opt.label}</option>
))}
</select>
</Field>
{/* Max Iterations */}
@ -81,9 +55,9 @@ const Panel: FC<NodePanelProps<AgentV2NodeType>> = ({ id, data }) => {
type="number"
min={1}
max={99}
className="w-full rounded-lg border border-components-input-border-active px-3 py-1.5 text-[13px]"
className="w-full rounded-lg border border-components-input-border-active bg-transparent px-3 py-1.5 text-[13px] text-text-secondary"
value={inputs.max_iterations || 10}
onChange={(e) => updateData({ max_iterations: parseInt(e.target.value) || 10 })}
onChange={e => updateData({ max_iterations: parseInt(e.target.value) || 10 })}
/>
</Field>
@ -95,14 +69,15 @@ const Panel: FC<NodePanelProps<AgentV2NodeType>> = ({ id, data }) => {
{(inputs.tools || []).map((tool, idx) => (
<div key={idx} className="flex items-center justify-between rounded-lg border border-divider-subtle px-3 py-2">
<div className="flex items-center gap-2">
<Switch
size="sm"
defaultValue={tool.enabled}
onChange={(v) => {
<input
type="checkbox"
checked={tool.enabled}
onChange={e => {
const tools = [...(inputs.tools || [])]
tools[idx] = { ...tools[idx], enabled: v }
tools[idx] = { ...tools[idx], enabled: e.target.checked }
updateData({ tools })
}}
className="h-4 w-4"
/>
<span className="text-[13px] text-text-secondary">{tool.tool_name}</span>
</div>
@ -111,7 +86,7 @@ const Panel: FC<NodePanelProps<AgentV2NodeType>> = ({ id, data }) => {
))}
{(inputs.tools || []).length === 0 && (
<div className="py-3 text-center text-[13px] text-text-quaternary">
No tools configured. Add tools from the workflow toolbar.
No tools configured
</div>
)}
</div>
@ -121,21 +96,40 @@ const Panel: FC<NodePanelProps<AgentV2NodeType>> = ({ id, data }) => {
{/* Memory */}
<Field title="Memory">
<MemoryConfig
readonly={false}
config={inputs.memory || { window: { enabled: true, size: 50 } }}
onChange={(memory) => updateData({ memory })}
/>
<div className="flex items-center justify-between">
<span className="text-[13px] text-text-secondary">Window Size</span>
<input
type="number"
min={1}
max={200}
className="w-20 rounded-lg border border-components-input-border-active bg-transparent px-2 py-1 text-center text-[13px] text-text-secondary"
value={inputs.memory?.window?.size || 50}
onChange={e => updateData({
memory: {
role_prefix: inputs.memory?.role_prefix,
query_prompt_template: inputs.memory?.query_prompt_template,
window: { enabled: true, size: parseInt(e.target.value) || 50 },
},
})}
/>
</div>
</Field>
<Split />
{/* Vision */}
<Field title="Vision">
<ConfigVision
payload={inputs.vision}
onChange={(vision) => updateData({ vision })}
/>
<div className="flex items-center justify-between">
<span className="text-[13px] text-text-secondary">Enable image understanding</span>
<input
type="checkbox"
checked={inputs.vision?.enabled || false}
onChange={e => updateData({
vision: { ...inputs.vision, enabled: e.target.checked },
})}
className="h-4 w-4"
/>
</div>
</Field>
</div>
)

View File

@ -1,5 +1,6 @@
{
"blocks.agent": "Agent",
"blocks.agent-v2": "Agent V2",
"blocks.answer": "Answer",
"blocks.assigner": "Variable Assigner",
"blocks.code": "Code",
@ -31,6 +32,7 @@
"blocks.variable-aggregator": "Variable Aggregator",
"blocks.variable-assigner": "Variable Aggregator",
"blocksAbout.agent": "Invoking large language models to answer questions or process natural language",
"blocksAbout.agent-v2": "Next-gen agent with LLM, tools, sandbox execution, and configurable strategies",
"blocksAbout.answer": "Define the reply content of a chat conversation",
"blocksAbout.assigner": "The variable assignment node is used for assigning values to writable variables(like conversation variables).",
"blocksAbout.code": "Execute a piece of Python or NodeJS code to implement custom logic",

View File

@ -1,5 +1,6 @@
{
"blocks.agent": "Agent",
"blocks.agent-v2": "Agent V2",
"blocks.answer": "直接回复",
"blocks.assigner": "变量赋值",
"blocks.code": "代码执行",
@ -31,6 +32,7 @@
"blocks.variable-aggregator": "变量聚合器",
"blocks.variable-assigner": "变量赋值器",
"blocksAbout.agent": "调用大型语言模型回答问题或处理自然语言",
"blocksAbout.agent-v2": "新一代 Agent支持 LLM、工具调用、沙箱执行和可配置策略",
"blocksAbout.answer": "定义一个聊天对话的回复内容",
"blocksAbout.assigner": "变量赋值节点用于向可写入变量(例如会话变量)进行变量赋值。",
"blocksAbout.code": "执行一段 Python 或 NodeJS 代码实现自定义逻辑",