From e22bea7f1e73ec59c861cd46b80cdba24ade80da Mon Sep 17 00:00:00 2001 From: Hyu Date: Fri, 9 Oct 2026 17:03:15 +0800 Subject: [PATCH] fix(acceptance): improve bot setup and execution log presentation --- .../service/monitoring_execution_details.py | 16 +-- .../platform/adapters/aiocqhttp/manifest.yaml | 1 + .../platform/adapters/dingtalk/manifest.yaml | 1 + src/langbot/pkg/platform/botmgr.py | 19 +-- .../pkg/platform/sources/aiocqhttp.yaml | 3 +- .../pkg/platform/sources/dingtalk.yaml | 1 + .../pkg/platform/sources/http_bot.yaml | 12 +- src/langbot/pkg/platform/sources/line.yaml | 3 +- .../pkg/platform/sources/mattermost.yaml | 1 - .../pkg/platform/sources/openclaw_weixin.py | 1 + .../agents/components/AgentMonitoringTab.tsx | 112 ++++++------------ .../agents/components/AgentRunTimeline.tsx | 73 ++++++++++++ .../components/PluginProcessorTrace.tsx | 2 +- .../agents/components/ProcessorRunList.tsx | 1 + .../home/agents/components/tool-summary.ts | 61 ++++++++++ web/src/app/home/bots/BotDetailContent.tsx | 4 +- .../home/bots/components/bot-form/BotForm.tsx | 67 +++++++++-- .../home/components/WorkspaceAssistant.tsx | 16 ++- .../executions/ExecutionDetailSheet.tsx | 80 +++++++++---- .../components/executions/ExecutionTable.tsx | 4 +- web/src/components/ui/badge.tsx | 19 ++- web/src/i18n/locales/en-US.ts | 13 ++ web/src/i18n/locales/es-ES.ts | 13 ++ web/src/i18n/locales/ja-JP.ts | 13 ++ web/src/i18n/locales/ru-RU.ts | 13 ++ web/src/i18n/locales/th-TH.ts | 13 ++ web/src/i18n/locales/vi-VN.ts | 13 ++ web/src/i18n/locales/zh-Hans.ts | 13 ++ web/src/i18n/locales/zh-Hant.ts | 13 ++ 29 files changed, 450 insertions(+), 151 deletions(-) create mode 100644 web/src/app/home/agents/components/AgentRunTimeline.tsx create mode 100644 web/src/app/home/agents/components/tool-summary.ts diff --git a/src/langbot/pkg/api/http/service/monitoring_execution_details.py b/src/langbot/pkg/api/http/service/monitoring_execution_details.py index 1b5fa94f9..d8496facc 100644 --- a/src/langbot/pkg/api/http/service/monitoring_execution_details.py +++ b/src/langbot/pkg/api/http/service/monitoring_execution_details.py @@ -4,6 +4,7 @@ from __future__ import annotations import datetime import json +from langbot_plugin.api.entities.builtin.platform.events import event_summary import sqlalchemy as sa from sqlalchemy.ext.asyncio import AsyncSession @@ -32,19 +33,8 @@ def event_content(content, event_data): """Keep event-specific properties alongside the normalized message input.""" if not isinstance(event_data, dict) or not event_data: return content - if not isinstance(content, dict): - return event_data if not content else {**event_data, 'input': content} - merged = dict(event_data) - conflicts = {} - for key, value in content.items(): - if key not in merged: - merged[key] = value - elif merged[key] != value and value is not None and value != '' and value != [] and value != {}: - conflicts[key] = value - if conflicts: - # Never silently overwrite a custom field that shares an input name. - return {'event': merged, 'input': conflicts} - return merged + # Keep the original event intact; text is the existing normalized input field. + return {'text': event_summary(event_data), 'event': event_data, 'input': content} def event_item(row): diff --git a/src/langbot/pkg/platform/adapters/aiocqhttp/manifest.yaml b/src/langbot/pkg/platform/adapters/aiocqhttp/manifest.yaml index 6911881c9..e14581cfb 100644 --- a/src/langbot/pkg/platform/adapters/aiocqhttp/manifest.yaml +++ b/src/langbot/pkg/platform/adapters/aiocqhttp/manifest.yaml @@ -15,6 +15,7 @@ metadata: spec: categories: + - popular - protocol help_links: zh: https://langbot.app/docs/zh/usage/platforms/qq/aiocqhttp/napcat diff --git a/src/langbot/pkg/platform/adapters/dingtalk/manifest.yaml b/src/langbot/pkg/platform/adapters/dingtalk/manifest.yaml index 8e74f21d1..93e6da6de 100644 --- a/src/langbot/pkg/platform/adapters/dingtalk/manifest.yaml +++ b/src/langbot/pkg/platform/adapters/dingtalk/manifest.yaml @@ -15,6 +15,7 @@ metadata: spec: categories: + - popular - china help_links: zh: https://langbot.app/docs/zh/usage/platforms/dingtalk diff --git a/src/langbot/pkg/platform/botmgr.py b/src/langbot/pkg/platform/botmgr.py index 291acd1db..fa80a5c6b 100644 --- a/src/langbot/pkg/platform/botmgr.py +++ b/src/langbot/pkg/platform/botmgr.py @@ -704,33 +704,20 @@ class RuntimeBot: @classmethod def _build_agent_input(cls, event: platform_events.EBAEvent) -> AgentInput: - text = None + text = platform_events.event_summary(event) contents: list[dict[str, typing.Any]] = [] message_chain = getattr(event, 'message_chain', None) if message_chain: - text_parts: list[str] = [] try: for component in message_chain: - if isinstance(component, platform_message.Plain): - text_parts.append(component.text) - elif isinstance(component, platform_message.Image): + if isinstance(component, platform_message.Image): if component.url: contents.append({'type': 'image_url', 'image_url': {'url': component.url}}) elif component.base64: contents.append({'type': 'image_base64', 'image_base64': component.base64}) except TypeError: - text_parts.append(str(message_chain)) - text = ''.join(text_parts) or str(message_chain) - - if text is None: - feedback_content = getattr(event, 'feedback_content', None) - if feedback_content: - text = str(feedback_content) - elif getattr(event, 'action', None): - text = str(getattr(event, 'action')) - else: - text = str(getattr(event, 'type', 'event')) + pass if text: contents.insert(0, {'type': 'text', 'text': text}) diff --git a/src/langbot/pkg/platform/sources/aiocqhttp.yaml b/src/langbot/pkg/platform/sources/aiocqhttp.yaml index d9880bcfe..fdabf4969 100644 --- a/src/langbot/pkg/platform/sources/aiocqhttp.yaml +++ b/src/langbot/pkg/platform/sources/aiocqhttp.yaml @@ -14,6 +14,7 @@ metadata: spec: legacy: true categories: + - popular - protocol help_links: zh: https://langbot.app/docs/zh/usage/platforms/qq/aiocqhttp/napcat @@ -59,4 +60,4 @@ spec: execution: python: path: ./aiocqhttp.py - attr: AiocqhttpAdapter \ No newline at end of file + attr: AiocqhttpAdapter diff --git a/src/langbot/pkg/platform/sources/dingtalk.yaml b/src/langbot/pkg/platform/sources/dingtalk.yaml index f8a7e6395..dcde99126 100644 --- a/src/langbot/pkg/platform/sources/dingtalk.yaml +++ b/src/langbot/pkg/platform/sources/dingtalk.yaml @@ -14,6 +14,7 @@ metadata: spec: legacy: true categories: + - popular - china help_links: zh: https://langbot.app/docs/zh/usage/platforms/dingtalk diff --git a/src/langbot/pkg/platform/sources/http_bot.yaml b/src/langbot/pkg/platform/sources/http_bot.yaml index 97392a444..5e13f437e 100644 --- a/src/langbot/pkg/platform/sources/http_bot.yaml +++ b/src/langbot/pkg/platform/sources/http_bot.yaml @@ -8,10 +8,14 @@ metadata: zh_Hant: HTTP 通用接入 ja_JP: HTTP ボット description: - en_US: Integrate any backend over plain HTTP. Push messages in via a signed webhook, receive replies on a callback URL. Server-to-server, no long-lived connection. Preserves message aggregation (N->1) and multi-part replies (1->M). - zh_Hans: 通过 HTTP 接入任意后端系统。以签名 Webhook 推入消息,在回调地址接收回复。面向服务间集成,无需长连接。完整保留消息聚合(多条合一)与多段回复(一条问、多条回)能力。 - zh_Hant: 透過 HTTP 接入任意後端系統。以簽名 Webhook 推入訊息,在回調地址接收回覆。面向服務間整合,無需長連線。完整保留訊息聚合(多條合一)與多段回覆(一條問、多條回)能力。 - ja_JP: 任意のバックエンドを HTTP で接続。署名付き Webhook でメッセージを送信し、コールバック URL で返信を受信します。サーバー間連携、長時間接続不要。メッセージ集約(N→1)とマルチパート返信(1→M)に対応。 + en_US: Connect backend systems via HTTP. Send messages through signed webhooks and receive replies via callbacks. + zh_Hans: 通过 HTTP 接入后端系统,以签名 Webhook 发送消息,通过回调接收回复。 + zh_Hant: 透過 HTTP 接入後端系統,以簽名 Webhook 傳送訊息,透過回呼接收回覆。 + ja_JP: HTTP でバックエンドに接続。署名付き Webhook でメッセージを送信し、コールバックで返信を受信します。 + es_ES: Conecta sistemas mediante HTTP. Envía mensajes con webhooks firmados y recibe respuestas mediante callbacks. + ru_RU: Подключайте серверные системы по HTTP. Отправляйте сообщения через подписанные вебхуки и получайте ответы через обратные вызовы. + th_TH: เชื่อมต่อระบบผ่าน HTTP ส่งข้อความด้วย Webhook ที่ลงลายเซ็นและรับคำตอบผ่าน callback + vi_VN: Kết nối hệ thống qua HTTP. Gửi tin nhắn bằng webhook có chữ ký và nhận phản hồi qua callback. icon: http_bot.svg spec: categories: diff --git a/src/langbot/pkg/platform/sources/line.yaml b/src/langbot/pkg/platform/sources/line.yaml index 83f520286..b99ab5b2c 100644 --- a/src/langbot/pkg/platform/sources/line.yaml +++ b/src/langbot/pkg/platform/sources/line.yaml @@ -20,6 +20,7 @@ metadata: icon: line.png spec: categories: + - popular - global help_links: zh: https://langbot.app/docs/zh/usage/platforms/line @@ -81,4 +82,4 @@ spec: execution: python: path: ./line.py - attr: LINEAdapter \ No newline at end of file + attr: LINEAdapter diff --git a/src/langbot/pkg/platform/sources/mattermost.yaml b/src/langbot/pkg/platform/sources/mattermost.yaml index f46b125bb..6c958e42d 100644 --- a/src/langbot/pkg/platform/sources/mattermost.yaml +++ b/src/langbot/pkg/platform/sources/mattermost.yaml @@ -22,7 +22,6 @@ metadata: spec: categories: - global - - popular config: - name: server_url label: diff --git a/src/langbot/pkg/platform/sources/openclaw_weixin.py b/src/langbot/pkg/platform/sources/openclaw_weixin.py index 4726888b1..3991f5555 100644 --- a/src/langbot/pkg/platform/sources/openclaw_weixin.py +++ b/src/langbot/pkg/platform/sources/openclaw_weixin.py @@ -385,6 +385,7 @@ class OpenClawWeixinAdapter(abstract_platform_adapter.AbstractMessagePlatformAda await self.logger.error( f'Failed to send component {type(component).__name__}: {traceback.format_exc()}' ) + raise async def reply_message( self, diff --git a/web/src/app/home/agents/components/AgentMonitoringTab.tsx b/web/src/app/home/agents/components/AgentMonitoringTab.tsx index 34c712240..a5cff9e97 100644 --- a/web/src/app/home/agents/components/AgentMonitoringTab.tsx +++ b/web/src/app/home/agents/components/AgentMonitoringTab.tsx @@ -1,6 +1,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Brain, ChevronRight, RefreshCw, Wrench } from 'lucide-react'; +import { ChevronRight, RefreshCw } from 'lucide-react'; +import AgentRunTimeline, { isReplyStep, isMockStep } from './AgentRunTimeline'; import { httpClient } from '@/app/infra/http/HttpClient'; import type { AgentPlatformTool, @@ -206,7 +207,12 @@ export default function AgentMonitoringTab({ const labels = Object.fromEntries( platformTools.map((tool) => [tool.name, extractI18nObject(tool.label)]), ); - const steps = executionSteps(events); + const steps = executionSteps(events).filter( + (step) => step.kind === 'tool' || step.text.trim() || step.reasoning.trim(), + ); + const toolSteps = steps.filter((step) => step.kind === 'tool'); + const sentCount = toolSteps.filter((step) => isReplyStep(step) && !isMockStep(step) && step.status === 'completed').length; + const sendFailed = toolSteps.some((step) => isReplyStep(step) && step.status === 'failed'); const duration = selected ? processorRunDuration(selected) : null; const failureReason = selected?.status_reason || @@ -261,7 +267,7 @@ export default function AgentMonitoringTab({ {t('monitoring.loadError')} )} -
+
setSelectedId(run.run_id)} - className="h-auto w-full flex-col items-stretch gap-2 rounded-none border-b p-3 text-left font-normal last:border-b-0 aria-pressed:bg-accent" + className="h-auto w-full flex-col items-stretch gap-2 rounded-none border-b border-l-2 border-l-transparent p-4 text-left font-normal last:border-b-0 aria-pressed:border-l-primary aria-pressed:bg-primary/5" > {eventPatternLabel(run.metadata.event_type ?? '', t)} {debugRun(run) && ( - {t('agents.debugTab')} + {t('agents.debugTab')} )} {processorRunDuration(run) !== null && ( {formatRunDuration(processorRunDuration(run)!)} @@ -343,6 +350,7 @@ export default function AgentMonitoringTab({ {eventPatternLabel(selected.metadata.event_type ?? '', t)} {debugRun(selected) && ( - {t('agents.debugTab')} + {t('agents.debugTab')} )}
@@ -373,9 +381,22 @@ export default function AgentMonitoringTab({ )}
-

- {selected.runner_id} -

+
+
+
+

{t('agents.monitoring.runStatus')}

+

{stateLabel(selected)}

+
+
+

{t('agents.monitoring.toolCalls')}

+

{toolSteps.length}

+
+
+

{t('agents.monitoring.delivery')}

+

+ {t(`agents.monitoring.${sendFailed ? 'sendFailed' : sentCount ? 'sendCount' : 'noSend'}`, { count: sentCount })} +

+
{failedStatuses.has(selected.status) && failureReason && ( @@ -418,72 +439,14 @@ export default function AgentMonitoringTab({

{t('agents.monitoring.execution')} + {steps.length}

- {steps.map((step, index) => ( - - - - {step.kind === 'tool' ? ( - <> - - - {labels[step.name] || step.name} - - - {t( - `agents.debugTool${step.status === 'running' ? (activeStatuses.has(selected.status) || eventCursor !== null ? 'Running' : 'Interrupted') : step.result && typeof step.result === 'object' && 'mock' in step.result && step.result.mock === true ? (step.status === 'failed' ? 'MockFailed' : 'Simulated') : step.status === 'failed' ? 'Failed' : 'Completed'}`, - )} - - - ) : ( - <> - - {t('agents.debugTextOutput')} - - )} - - - - {step.kind === 'message' ? ( - <> - {step.text && ( -

{step.text}

- )} - {step.reasoning && ( - - )} - - ) : ( - <> - {step.error && ( -

- {step.error} -

- )} - - {step.result !== undefined && ( - - )} - - )} -
-
- ))} + {!steps.length &&

{t('agents.monitoring.noSteps')}

} + {events .filter((event) => event.type === 'processor.log') .map((event) => ( @@ -520,6 +483,7 @@ export default function AgentMonitoringTab({ > {json({ run_id: selected.run_id, + runner_id: selected.runner_id, usage: selected.usage, events, })} diff --git a/web/src/app/home/agents/components/AgentRunTimeline.tsx b/web/src/app/home/agents/components/AgentRunTimeline.tsx new file mode 100644 index 000000000..88857a0d1 --- /dev/null +++ b/web/src/app/home/agents/components/AgentRunTimeline.tsx @@ -0,0 +1,73 @@ +import { useTranslation } from 'react-i18next'; +import { MessageSquare, Send, Wrench } from 'lucide-react'; +import { Badge } from '@/components/ui/badge'; +import { ProcessorPayload } from './PluginProcessorTrace'; +import type { ExecutionStep } from './debug-execution'; +import { toolDisplay, toolSummary } from './tool-summary'; + +export function isReplyStep(step: ExecutionStep) { + return step.kind === 'tool' && + toolDisplay(step.name)?.kind === 'reply'; +} + +export function isMockStep(step: ExecutionStep) { + return step.kind === 'tool' && !!step.result && + typeof step.result === 'object' && 'mock' in step.result && step.result.mock === true; +} + +export default function AgentRunTimeline({ steps, labels, active }: { + steps: ExecutionStep[]; + labels: Record; + active: boolean; +}) { + const { t } = useTranslation(); + return ( +
    + {steps.map((step, index) => { + const reply = isReplyStep(step); + const mock = isMockStep(step); + const params = step.kind === 'tool' && step.parameters && typeof step.parameters === 'object' + ? step.parameters as Record : {}; + const summary = toolSummary(step.kind === 'tool' ? step.name : '', params); + const replyText = reply ? summary.text : ''; + const actionText = reply ? '' : summary.text; + const Icon = step.kind === 'message' ? MessageSquare : reply ? Send : Wrench; + const status = step.kind === 'tool' ? step.status : null; + const statusKey = status === 'running' ? (active ? 'Running' : 'Interrupted') + : mock ? (status === 'failed' ? 'MockFailed' : 'Simulated') + : status === 'failed' ? 'Failed' : 'Completed'; + return ( +
  1. + {index < steps.length - 1 &&
  2. + ); + })} +
+ ); +} diff --git a/web/src/app/home/agents/components/PluginProcessorTrace.tsx b/web/src/app/home/agents/components/PluginProcessorTrace.tsx index 40eac2329..e37a247ad 100644 --- a/web/src/app/home/agents/components/PluginProcessorTrace.tsx +++ b/web/src/app/home/agents/components/PluginProcessorTrace.tsx @@ -55,7 +55,7 @@ export default function PluginProcessorTrace({ variant={event.data.level === 'error' ? 'destructive' : 'default'} > - {String(event.data.level)} + {String(event.data.level)} {String(event.data.text)} diff --git a/web/src/app/home/agents/components/ProcessorRunList.tsx b/web/src/app/home/agents/components/ProcessorRunList.tsx index c9abec543..0963943a9 100644 --- a/web/src/app/home/agents/components/ProcessorRunList.tsx +++ b/web/src/app/home/agents/components/ProcessorRunList.tsx @@ -54,6 +54,7 @@ export default function ProcessorRunList({ > = { + exec: { fields: ['command'] }, + read: { fields: ['path'] }, + write: { fields: ['path'] }, + edit: { fields: ['path'] }, + glob: { fields: ['pattern', 'path'] }, + grep: { fields: ['pattern', 'path'] }, + event_reply: { fields: ['text'], kind: 'reply' }, + platform_send_message: { fields: ['text'], kind: 'reply', recipient: 'target_id' }, + event_delete_message: { fields: [] }, + event_get_actor: { fields: [] }, + event_get_group: { fields: [] }, + event_get_group_member: { fields: [] }, + event_mute_member: { fields: ['duration'] }, + event_unmute_member: { fields: [] }, + event_kick_member: { fields: [] }, + event_respond_friend_request: { fields: ['approve', 'remark'] }, + event_respond_group_invite: { fields: ['approve'] }, + platform_get_message: { fields: ['chat_id', 'message_id'] }, + platform_delete_message: { fields: ['chat_id', 'message_id'] }, + platform_get_group_info: { fields: ['group_id'] }, + platform_get_group_list: { fields: [] }, + platform_get_group_member_list: { fields: ['group_id'] }, + platform_get_group_member_info: { fields: ['group_id', 'user_id'] }, + platform_set_group_name: { fields: ['group_id', 'name'] }, + platform_mute_member: { fields: ['group_id', 'user_id', 'duration'] }, + platform_unmute_member: { fields: ['group_id', 'user_id'] }, + platform_kick_member: { fields: ['group_id', 'user_id'] }, + platform_leave_group: { fields: ['group_id'] }, + platform_get_user_info: { fields: ['user_id'] }, + platform_get_friend_list: { fields: [] }, +}; + +export function toolDisplay(name: string): ToolDisplay | undefined { + return Object.prototype.hasOwnProperty.call(TOOL_DISPLAYS, name) + ? TOOL_DISPLAYS[name] : undefined; +} + +export function toolSummary(name: string, parameters: Record) { + const display = toolDisplay(name); + const fields = (display?.fields ?? []).flatMap((field) => { + const value = parameters[field]; + if (!['string', 'number', 'boolean'].includes(typeof value) || value === '') return []; + return [{ field, text: String(value) }]; + }); + return { + text: fields.map(({ field, text }) => + display?.kind === 'reply' || display?.fields.length === 1 ? text : `${field}: ${text}`, + ).join('\n'), + recipient: display?.recipient && typeof parameters[display.recipient] === 'string' + ? String(parameters[display.recipient]) : '', + }; +} diff --git a/web/src/app/home/bots/BotDetailContent.tsx b/web/src/app/home/bots/BotDetailContent.tsx index 6ed4ca775..43f8a69f2 100644 --- a/web/src/app/home/bots/BotDetailContent.tsx +++ b/web/src/app/home/bots/BotDetailContent.tsx @@ -235,11 +235,11 @@ export default function BotDetailContent({ id }: { id: string }) { - {showLegacyAdapters && ( + {(showLegacyAdapters || !!adapterQuery) && (
- {legacyAdapters.map((item) => ( + {galleryLegacyAdapters.map((item) => ( @@ -169,6 +186,11 @@ export default function ExecutionDetailSheet({ 'deliveries', 'conversation', ].includes(section); + const body = section === 'inputs' + ? typeof item.content === 'string' ? item.content + : item.content && typeof item.content === 'object' && 'text' in item.content + ? item.content.text : null + : messageBody(item.content); const title = content ? [ item.actor_name || item.actor_id, @@ -188,7 +210,7 @@ export default function ExecutionDetailSheet({ item.id, ); return ( -
+
{title} @@ -200,19 +222,17 @@ export default function ExecutionDetailSheet({ {item.status && ( - {item.status} + {t(`monitoring.execution.status.${['success', 'delivered'].includes(item.status) ? 'completed' : item.status}`, { defaultValue: item.status })} )}
{content ? ( <> - +
+ {hasExecutionValue(body) ? :

{t('monitoring.unified.noRecords')}

} +
{item.origin === 'generated' && section === 'conversation' && ( {t('monitoring.unified.outputs')} @@ -222,9 +242,12 @@ export default function ExecutionDetailSheet({ {String(item.delivery.delivery_error)}

) : null} - {Array.isArray(item.metadata?.routes) && ( - - )} +
+ {t('monitoring.unified.payload')} +
+ +
+
) : ( <> @@ -291,12 +314,15 @@ export default function ExecutionDetailSheet({ ); const title = `${t(`monitoring.unified.${section}`)} (${page.items.length}${page.has_more ? '+' : ''})`; return primary ? ( -
-

{title}

+
+

+ {mainSections.indexOf(section) + 1} + {title} +

{body}
) : ( -
+
{title} @@ -355,14 +381,14 @@ export default function ExecutionDetailSheet({ {record && ( <>
- + {executionStatusLabel(record, t)} {executionKindLabel(record, t)} {record.debug && ( - + {t('monitoring.execution.debug')} )} @@ -383,7 +409,21 @@ export default function ExecutionDetailSheet({ .filter(Boolean) .join(' · ')}

- {record.status_reason && ( +
+
+

{t('monitoring.execution.detail.duration')}

+

{record.duration_ms != null ? formatRunDuration(record.duration_ms) : '—'}

+
+
+

{t('monitoring.llmCalls.totalTokens')}

+

{record.usage?.total_tokens?.toLocaleString() ?? '—'}

+
+
+

{t('monitoring.unified.deliveries')}

+

{visible?.pages.deliveries?.items.length ?? 0}{visible?.pages.deliveries?.has_more ? '+' : ''}

+
+
+ {record.status_reason && !['stop', 'completed', 'success'].includes(record.status_reason) && (

{record.status_reason}

diff --git a/web/src/app/home/monitoring/components/executions/ExecutionTable.tsx b/web/src/app/home/monitoring/components/executions/ExecutionTable.tsx index 7a447475a..824f788f2 100644 --- a/web/src/app/home/monitoring/components/executions/ExecutionTable.tsx +++ b/web/src/app/home/monitoring/components/executions/ExecutionTable.tsx @@ -161,11 +161,11 @@ export default function ExecutionTable({
- + {executionStatusLabel(row, t)} {row.debug && ( - + {t('monitoring.execution.debug')} )} diff --git a/web/src/components/ui/badge.tsx b/web/src/components/ui/badge.tsx index b0c1fc933..0c4850d76 100644 --- a/web/src/components/ui/badge.tsx +++ b/web/src/components/ui/badge.tsx @@ -28,19 +28,34 @@ const badgeVariants = cva( function Badge({ className, variant, + status, asChild = false, ...props }: React.ComponentProps<'span'> & - VariantProps & { asChild?: boolean }) { + VariantProps & { asChild?: boolean; status?: string }) { const Comp = asChild ? Slot : 'span'; return ( ); } +function statusBadgeClass(status: string): string { + if (['completed', 'success', 'succeeded', 'delivered', 'ready', 'enabled', 'connected'].includes(status)) + return 'border-emerald-500/25 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300'; + if (['failed', 'error', 'timeout', 'disconnected'].includes(status)) + return 'border-red-500/25 bg-red-500/10 text-red-700 dark:text-red-300'; + if (['running', 'active', 'connecting'].includes(status)) + return 'border-blue-500/25 bg-blue-500/10 text-blue-700 dark:text-blue-300'; + if (['queued', 'pending', 'created', 'claimed', 'waiting', 'warning'].includes(status)) + return 'border-amber-500/25 bg-amber-500/10 text-amber-800 dark:text-amber-300'; + if (['mock', 'simulated', 'debug'].includes(status)) + return 'border-violet-500/25 bg-violet-500/10 text-violet-700 dark:text-violet-300'; + return 'border-border bg-muted text-muted-foreground'; +} + export { Badge, badgeVariants }; diff --git a/web/src/i18n/locales/en-US.ts b/web/src/i18n/locales/en-US.ts index e712f633e..3597bc875 100644 --- a/web/src/i18n/locales/en-US.ts +++ b/web/src/i18n/locales/en-US.ts @@ -781,6 +781,9 @@ const enUS = { deleteConfirmation: 'Are you sure you want to delete this bot?', platformAdapter: 'Platform/Adapter Selection', selectAdapter: 'Select Adapter', + saveAndEnable: 'Save and enable', + searchAdapters: 'Search adapters', + noMatchingAdapters: 'No matching adapters found', legacyAdapters: 'Legacy adapters', legacyAdapterBadge: 'Legacy', legacyAdaptersHint: @@ -1126,6 +1129,16 @@ const enUS = { }, agents: { monitoring: { + runStatus: "Run status", + toolCalls: "Tool calls", + delivery: "Message delivery", + sendFailed: "Send failed", + sendCount: "{{count}} send calls succeeded", + noSend: "No send recorded", + noSteps: "No execution steps recorded yet.", + recipient: "Recipient", + actionCompleted: "Tool completed successfully.", + simulatedAction: "Simulated action; no real message was sent.", description: 'Follow each task from its triggering event through model output and tool execution.', empty: diff --git a/web/src/i18n/locales/es-ES.ts b/web/src/i18n/locales/es-ES.ts index 485270053..6d94ab533 100644 --- a/web/src/i18n/locales/es-ES.ts +++ b/web/src/i18n/locales/es-ES.ts @@ -797,6 +797,9 @@ const esES = { deleteConfirmation: '¿Estás seguro de que deseas eliminar este Bot?', platformAdapter: 'Selección de plataforma/adaptador', selectAdapter: 'Seleccionar adaptador', + saveAndEnable: 'Guardar y activar', + searchAdapters: 'Buscar adaptadores', + noMatchingAdapters: 'No se encontraron adaptadores coincidentes', legacyAdapters: 'Adaptadores heredados', legacyAdapterBadge: 'Heredado', legacyAdaptersHint: @@ -1148,6 +1151,16 @@ const esES = { }, agents: { monitoring: { + runStatus: "Estado de ejecución", + toolCalls: "Llamadas a herramientas", + delivery: "Envío de mensajes", + sendFailed: "Error de envío", + sendCount: "{{count}} llamadas de envío correctas", + noSend: "Sin envíos registrados", + noSteps: "Aún no hay pasos registrados.", + recipient: "Destinatario", + actionCompleted: "Herramienta ejecutada correctamente.", + simulatedAction: "Acción simulada; no se envió ningún mensaje real.", description: 'Consulta el evento, la salida del modelo y las herramientas de cada tarea.', empty: diff --git a/web/src/i18n/locales/ja-JP.ts b/web/src/i18n/locales/ja-JP.ts index 8091523c1..32e497b34 100644 --- a/web/src/i18n/locales/ja-JP.ts +++ b/web/src/i18n/locales/ja-JP.ts @@ -784,6 +784,9 @@ const jaJP = { deleteConfirmation: '本当にこのボットを削除しますか?', platformAdapter: 'プラットフォーム/アダプター選択', selectAdapter: 'アダプターを選択', + saveAndEnable: '保存して有効化', + searchAdapters: 'アダプターを検索', + noMatchingAdapters: '一致するアダプターが見つかりません', legacyAdapters: '旧式アダプター', legacyAdapterBadge: '旧式', legacyAdaptersHint: @@ -1136,6 +1139,16 @@ const jaJP = { }, agents: { monitoring: { + runStatus: "実行状態", + toolCalls: "ツール呼び出し", + delivery: "メッセージ送信", + sendFailed: "送信失敗", + sendCount: "送信 API 呼び出し成功:{{count}} 回", + noSend: "送信記録なし", + noSteps: "実行ステップはまだありません。", + recipient: "送信先", + actionCompleted: "ツールの実行が完了しました。", + simulatedAction: "シミュレーションです。実際のメッセージは送信されていません。", description: '各タスクのトリガーイベント、モデル出力、ツール実行を確認します。', empty: diff --git a/web/src/i18n/locales/ru-RU.ts b/web/src/i18n/locales/ru-RU.ts index c479ef5e8..51b9e1569 100644 --- a/web/src/i18n/locales/ru-RU.ts +++ b/web/src/i18n/locales/ru-RU.ts @@ -792,6 +792,9 @@ const ruRU = { deleteConfirmation: 'Вы уверены, что хотите удалить этого бота?', platformAdapter: 'Выбор платформы/адаптера', selectAdapter: 'Выберите адаптер', + saveAndEnable: 'Сохранить и включить', + searchAdapters: 'Поиск адаптеров', + noMatchingAdapters: 'Подходящие адаптеры не найдены', legacyAdapters: 'Устаревшие адаптеры', legacyAdapterBadge: 'Устаревший', legacyAdaptersHint: @@ -1146,6 +1149,16 @@ const ruRU = { }, agents: { monitoring: { + runStatus: "Статус выполнения", + toolCalls: "Вызовы инструментов", + delivery: "Отправка сообщений", + sendFailed: "Ошибка отправки", + sendCount: "Успешных вызовов отправки: {{count}}", + noSend: "Отправки не зарегистрированы", + noSteps: "Шаги выполнения ещё не зарегистрированы.", + recipient: "Получатель", + actionCompleted: "Инструмент выполнен успешно.", + simulatedAction: "Действие смоделировано; реальное сообщение не отправлено.", description: 'Просмотр события, ответа модели и вызовов инструментов для каждой задачи.', empty: diff --git a/web/src/i18n/locales/th-TH.ts b/web/src/i18n/locales/th-TH.ts index b5978a76b..34194f1b7 100644 --- a/web/src/i18n/locales/th-TH.ts +++ b/web/src/i18n/locales/th-TH.ts @@ -768,6 +768,9 @@ const thTH = { deleteConfirmation: 'คุณแน่ใจหรือไม่ว่าต้องการลบ Bot นี้?', platformAdapter: 'การเลือกแพลตฟอร์ม/อะแดปเตอร์', selectAdapter: 'เลือกอะแดปเตอร์', + saveAndEnable: 'บันทึกและเปิดใช้งาน', + searchAdapters: 'ค้นหาอะแดปเตอร์', + noMatchingAdapters: 'ไม่พบอะแดปเตอร์ที่ตรงกัน', legacyAdapters: 'อะแดปเตอร์รุ่นเก่า', legacyAdapterBadge: 'เก่า', legacyAdaptersHint: @@ -1106,6 +1109,16 @@ const thTH = { }, agents: { monitoring: { + runStatus: "สถานะการทำงาน", + toolCalls: "การเรียกเครื่องมือ", + delivery: "การส่งข้อความ", + sendFailed: "ส่งไม่สำเร็จ", + sendCount: "เรียกส่งสำเร็จ {{count}} ครั้ง", + noSend: "ไม่มีบันทึกการส่ง", + noSteps: "ยังไม่มีบันทึกขั้นตอนการทำงาน", + recipient: "ผู้รับ", + actionCompleted: "เครื่องมือทำงานสำเร็จ", + simulatedAction: "การจำลอง ไม่ได้ส่งข้อความจริง", description: 'ดูเหตุการณ์เริ่มต้น ผลลัพธ์โมเดล และการเรียกเครื่องมือของแต่ละงาน', empty: 'ยังไม่มีการทำงาน เริ่มเหตุการณ์หรือทดสอบการดีบักเพื่อดูบันทึก', diff --git a/web/src/i18n/locales/vi-VN.ts b/web/src/i18n/locales/vi-VN.ts index cefa3d4ce..03ce31f32 100644 --- a/web/src/i18n/locales/vi-VN.ts +++ b/web/src/i18n/locales/vi-VN.ts @@ -782,6 +782,9 @@ const viVN = { deleteConfirmation: 'Bạn có chắc chắn muốn xóa Bot này không?', platformAdapter: 'Nền tảng/Lựa chọn Adapter', selectAdapter: 'Chọn Adapter', + saveAndEnable: 'Lưu và bật', + searchAdapters: 'Tìm kiếm adapter', + noMatchingAdapters: 'Không tìm thấy adapter phù hợp', legacyAdapters: 'Adapter cũ', legacyAdapterBadge: 'Cũ', legacyAdaptersHint: @@ -1124,6 +1127,16 @@ const viVN = { }, agents: { monitoring: { + runStatus: "Trạng thái thực thi", + toolCalls: "Lượt gọi công cụ", + delivery: "Gửi tin nhắn", + sendFailed: "Gửi thất bại", + sendCount: "{{count}} lượt gọi gửi thành công", + noSend: "Chưa ghi nhận lượt gửi", + noSteps: "Chưa ghi nhận bước thực thi.", + recipient: "Người nhận", + actionCompleted: "Công cụ đã thực thi thành công.", + simulatedAction: "Thao tác mô phỏng; không gửi tin nhắn thật.", description: 'Xem sự kiện kích hoạt, đầu ra mô hình và quá trình gọi công cụ của mỗi tác vụ.', empty: 'Chưa có lượt chạy. Kích hoạt sự kiện hoặc chạy thử gỡ lỗi.', diff --git a/web/src/i18n/locales/zh-Hans.ts b/web/src/i18n/locales/zh-Hans.ts index 7eb045d19..87414a319 100644 --- a/web/src/i18n/locales/zh-Hans.ts +++ b/web/src/i18n/locales/zh-Hans.ts @@ -734,6 +734,9 @@ const zhHans = { deleteConfirmation: '你确定要删除这个机器人吗?', platformAdapter: '平台/适配器选择', selectAdapter: '选择适配器', + saveAndEnable: '保存并启用', + searchAdapters: '搜索适配器', + noMatchingAdapters: '未找到匹配的适配器', legacyAdapters: '旧版适配器', legacyAdapterBadge: '过时', legacyAdaptersHint: @@ -1060,6 +1063,16 @@ const zhHans = { }, agents: { monitoring: { + runStatus: "执行状态", + toolCalls: "工具调用", + delivery: "消息发送", + sendFailed: "发送失败", + sendCount: "{{count}} 次发送调用成功", + noSend: "暂无发送记录", + noSteps: "暂未记录执行步骤。", + recipient: "接收目标", + actionCompleted: "工具已执行完成。", + simulatedAction: "模拟执行,未发送真实消息。", description: '查看每次任务的触发事件、模型输出和工具执行过程。', empty: '暂无运行记录。触发平台事件或运行调试后,可在这里查看。', input: '触发输入', diff --git a/web/src/i18n/locales/zh-Hant.ts b/web/src/i18n/locales/zh-Hant.ts index 121154728..5ca09f35a 100644 --- a/web/src/i18n/locales/zh-Hant.ts +++ b/web/src/i18n/locales/zh-Hant.ts @@ -732,6 +732,9 @@ const zhHant = { deleteConfirmation: '您確定要刪除這個機器人嗎?', platformAdapter: '平台/適配器選擇', selectAdapter: '選擇適配器', + saveAndEnable: '儲存並啟用', + searchAdapters: '搜尋適配器', + noMatchingAdapters: '找不到符合條件的適配器', legacyAdapters: '舊版適配器', legacyAdapterBadge: '過時', legacyAdaptersHint: @@ -1059,6 +1062,16 @@ const zhHant = { }, agents: { monitoring: { + runStatus: "執行狀態", + toolCalls: "工具呼叫", + delivery: "訊息傳送", + sendFailed: "傳送失敗", + sendCount: "{{count}} 次傳送呼叫成功", + noSend: "暫無傳送記錄", + noSteps: "尚未記錄執行步驟。", + recipient: "接收目標", + actionCompleted: "工具已執行完成。", + simulatedAction: "模擬執行,未傳送真實訊息。", description: '查看每次任務的觸發事件、模型輸出和工具執行過程。', empty: '尚無執行紀錄。觸發平台事件或執行除錯後,可在此查看。', input: '觸發輸入',