mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-26 18:35:38 +08:00
feat(operation-trace): collapsible retention policy and assistant trace tool
- Collapse the retention policy section by default, showing a one-line summary when collapsed. - Add the owner/admin-only list_operation_logs assistant tool backed by audit.view. - Enforce per-tool permissions on both tool listing and invocation. - Add a substring search filter (search) to query_logs and the tool so callers can ask narrow questions (plugin name, account, verb) instead of dumping the full history. - Project records to a compact shape so a page stays under the assistant result budget. - Localize the new assistant tool label and retention summary across all locales.
This commit is contained in:
@@ -45,7 +45,9 @@ export default function AssistantToolResult({
|
||||
? result
|
||||
: Array.isArray(data.items)
|
||||
? data.items
|
||||
: null;
|
||||
: Array.isArray(data.records)
|
||||
? data.records
|
||||
: null;
|
||||
const total = typeof data.total === 'number' ? data.total : items?.length;
|
||||
const kind = tool?.arguments.kind;
|
||||
const label =
|
||||
@@ -142,7 +144,13 @@ export default function AssistantToolResult({
|
||||
: {};
|
||||
return (
|
||||
<li key={index} className="truncate">
|
||||
{String(entry.name || entry.uuid || '—')}
|
||||
{String(
|
||||
entry.name ||
|
||||
entry.summary ||
|
||||
entry.action ||
|
||||
entry.uuid ||
|
||||
'—',
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -15,6 +15,11 @@ import { toast } from 'sonner';
|
||||
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from '@/components/ui/collapsible';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Item, ItemContent, ItemMedia, ItemTitle } from '@/components/ui/item';
|
||||
import {
|
||||
@@ -341,6 +346,9 @@ export default function OperationTracePanel({
|
||||
const [retentionDays, setRetentionDays] = useState<number>(30);
|
||||
const [maxRows, setMaxRows] = useState<number>(20000);
|
||||
const [dedupeSeconds, setDedupeSeconds] = useState<number>(60);
|
||||
// Retention is an advanced, rarely-touched policy: it collapses by default so
|
||||
// the capture level and the records list stay the focus of the panel.
|
||||
const [retentionOpen, setRetentionOpen] = useState(false);
|
||||
// Only one record shows its full change detail at a time, so the list stays
|
||||
// scannable: the payload diff is long and is opt-in per row.
|
||||
const [expandedId, setExpandedId] = useState<number | null>(null);
|
||||
@@ -631,66 +639,85 @@ export default function OperationTracePanel({
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="space-y-3">
|
||||
<h3 className="text-sm font-semibold">
|
||||
{t('operationTrace.retention')}
|
||||
</h3>
|
||||
<div className="flex flex-wrap items-end gap-2">
|
||||
<label className="flex flex-col gap-1 text-xs">
|
||||
<span className="text-muted-foreground">
|
||||
{t('operationTrace.retentionDays')}
|
||||
</span>
|
||||
<Input
|
||||
type="number"
|
||||
className="w-28"
|
||||
min={governance?.limits.min_retention_days ?? 1}
|
||||
max={governance?.limits.max_retention_days ?? 3650}
|
||||
value={retentionDays}
|
||||
disabled={!canConfigure}
|
||||
onChange={(event) =>
|
||||
setRetentionDays(Number(event.target.value))
|
||||
}
|
||||
<Collapsible open={retentionOpen} onOpenChange={setRetentionOpen}>
|
||||
<section className="space-y-3">
|
||||
<CollapsibleTrigger className="flex w-full items-center gap-2 text-left">
|
||||
<ChevronDown
|
||||
className={`size-4 shrink-0 text-muted-foreground transition-transform ${
|
||||
retentionOpen ? '' : '-rotate-90'
|
||||
}`}
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-1 text-xs">
|
||||
<span className="text-muted-foreground">
|
||||
{t('operationTrace.maxRows')}
|
||||
</span>
|
||||
<Input
|
||||
type="number"
|
||||
className="w-32"
|
||||
min={governance?.limits.min_max_rows ?? 100}
|
||||
max={governance?.limits.max_max_rows ?? 500000}
|
||||
value={maxRows}
|
||||
disabled={!canConfigure}
|
||||
onChange={(event) => setMaxRows(Number(event.target.value))}
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-1 text-xs">
|
||||
<span className="text-muted-foreground">
|
||||
{t('operationTrace.dedupeWindow')}
|
||||
</span>
|
||||
<Input
|
||||
type="number"
|
||||
className="w-28"
|
||||
min={governance?.limits.min_dedupe_window_seconds ?? 0}
|
||||
max={governance?.limits.max_dedupe_window_seconds ?? 3600}
|
||||
value={dedupeSeconds}
|
||||
disabled={!canConfigure}
|
||||
onChange={(event) =>
|
||||
setDedupeSeconds(Number(event.target.value))
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => void saveRetention()}
|
||||
disabled={!canConfigure || saving}
|
||||
>
|
||||
{t('operationTrace.save')}
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
<h3 className="text-sm font-semibold">
|
||||
{t('operationTrace.retention')}
|
||||
</h3>
|
||||
{!retentionOpen && (
|
||||
<span className="ml-auto truncate text-xs text-muted-foreground">
|
||||
{t('operationTrace.retentionSummary', {
|
||||
days: retentionDays,
|
||||
rows: maxRows,
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<div className="flex flex-wrap items-end gap-2 pt-3">
|
||||
<label className="flex flex-col gap-1 text-xs">
|
||||
<span className="text-muted-foreground">
|
||||
{t('operationTrace.retentionDays')}
|
||||
</span>
|
||||
<Input
|
||||
type="number"
|
||||
className="w-28"
|
||||
min={governance?.limits.min_retention_days ?? 1}
|
||||
max={governance?.limits.max_retention_days ?? 3650}
|
||||
value={retentionDays}
|
||||
disabled={!canConfigure}
|
||||
onChange={(event) =>
|
||||
setRetentionDays(Number(event.target.value))
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-1 text-xs">
|
||||
<span className="text-muted-foreground">
|
||||
{t('operationTrace.maxRows')}
|
||||
</span>
|
||||
<Input
|
||||
type="number"
|
||||
className="w-32"
|
||||
min={governance?.limits.min_max_rows ?? 100}
|
||||
max={governance?.limits.max_max_rows ?? 500000}
|
||||
value={maxRows}
|
||||
disabled={!canConfigure}
|
||||
onChange={(event) => setMaxRows(Number(event.target.value))}
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-1 text-xs">
|
||||
<span className="text-muted-foreground">
|
||||
{t('operationTrace.dedupeWindow')}
|
||||
</span>
|
||||
<Input
|
||||
type="number"
|
||||
className="w-28"
|
||||
min={governance?.limits.min_dedupe_window_seconds ?? 0}
|
||||
max={governance?.limits.max_dedupe_window_seconds ?? 3600}
|
||||
value={dedupeSeconds}
|
||||
disabled={!canConfigure}
|
||||
onChange={(event) =>
|
||||
setDedupeSeconds(Number(event.target.value))
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => void saveRetention()}
|
||||
disabled={!canConfigure || saving}
|
||||
>
|
||||
{t('operationTrace.save')}
|
||||
</Button>
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</section>
|
||||
</Collapsible>
|
||||
|
||||
<section className="space-y-3">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
|
||||
@@ -21,6 +21,7 @@ const enUS = {
|
||||
create_knowledge_base: 'Create knowledge base',
|
||||
get_pipeline: 'Read Pipeline',
|
||||
get_knowledge_schema: 'Read knowledge engine schema',
|
||||
list_operation_logs: 'Read operation trace',
|
||||
},
|
||||
resources: {
|
||||
models: 'Find chat models',
|
||||
@@ -2724,6 +2725,7 @@ const enUS = {
|
||||
description: 'Trace admin and owner edits and views, and what changed.',
|
||||
captureLevel: 'Capture level',
|
||||
retention: 'Retention',
|
||||
retentionSummary: 'Keep {{days}} days · up to {{rows}} records',
|
||||
retentionDays: 'Retention days',
|
||||
maxRows: 'Max records',
|
||||
dedupeWindow: 'Dedupe window (s)',
|
||||
|
||||
@@ -21,6 +21,7 @@ const esES = {
|
||||
create_knowledge_base: 'Crear base de conocimiento',
|
||||
get_pipeline: 'Consultar Pipeline',
|
||||
get_knowledge_schema: 'Consultar el esquema del motor de conocimiento',
|
||||
list_operation_logs: 'Consultar la trazabilidad de operaciones',
|
||||
},
|
||||
resources: {
|
||||
models: 'Buscar modelos de chat',
|
||||
@@ -2779,6 +2780,7 @@ const esES = {
|
||||
description: 'Rastrea cambios y consultas de los administradores',
|
||||
captureLevel: 'Nivel de captura',
|
||||
retention: 'Retención',
|
||||
retentionSummary: 'Conservar {{days}} días · hasta {{rows}} registros',
|
||||
retentionDays: 'Días de retención',
|
||||
maxRows: 'Registros máximos',
|
||||
dedupeWindow: 'Ventana de deduplicación (s)',
|
||||
|
||||
@@ -21,6 +21,7 @@ const jaJP = {
|
||||
create_knowledge_base: 'ナレッジベース作成',
|
||||
get_pipeline: 'Pipeline 参照',
|
||||
get_knowledge_schema: 'エンジン設定の参照',
|
||||
list_operation_logs: '操作トレーサビリティの参照',
|
||||
},
|
||||
resources: {
|
||||
models: 'チャットモデル検索',
|
||||
@@ -2742,6 +2743,7 @@ const jaJP = {
|
||||
description: '管理者とオーナーの操作を追跡します。',
|
||||
captureLevel: 'キャプチャレベル',
|
||||
retention: '保持ポリシー',
|
||||
retentionSummary: '{{days}} 日間保持 · 最大 {{rows}} 件',
|
||||
retentionDays: '保持日数',
|
||||
maxRows: '最大レコード数',
|
||||
dedupeWindow: '重複排除ウィンドウ(秒)',
|
||||
|
||||
@@ -21,6 +21,7 @@ const ruRU = {
|
||||
create_knowledge_base: 'Создать базу знаний',
|
||||
get_pipeline: 'Посмотреть Pipeline',
|
||||
get_knowledge_schema: 'Посмотреть схему движка знаний',
|
||||
list_operation_logs: 'Просмотр трассировки операций',
|
||||
},
|
||||
resources: {
|
||||
models: 'Найти чат-модели',
|
||||
@@ -2749,6 +2750,7 @@ const ruRU = {
|
||||
description: 'Отслеживание изменений и просмотров администраторов',
|
||||
captureLevel: 'Уровень записи',
|
||||
retention: 'Хранение',
|
||||
retentionSummary: 'Хранить {{days}} дн. · до {{rows}} записей',
|
||||
retentionDays: 'Дней хранения',
|
||||
maxRows: 'Максимум записей',
|
||||
dedupeWindow: 'Окно дедупликации (с)',
|
||||
|
||||
@@ -21,6 +21,7 @@ const thTH = {
|
||||
create_knowledge_base: 'สร้างฐานความรู้',
|
||||
get_pipeline: 'ดู Pipeline',
|
||||
get_knowledge_schema: 'ดูสคีมาของ knowledge engine',
|
||||
list_operation_logs: 'ดูการติดตามการดำเนินการ',
|
||||
},
|
||||
resources: {
|
||||
models: 'ค้นหาโมเดลแชต',
|
||||
@@ -2676,6 +2677,7 @@ const thTH = {
|
||||
description: 'ติดตามการแก้ไขและการดูของผู้ดูแลและเจ้าของ',
|
||||
captureLevel: 'ระดับการบันทึก',
|
||||
retention: 'นโยบายการเก็บรักษา',
|
||||
retentionSummary: 'เก็บ {{days}} วัน · สูงสุด {{rows}} รายการ',
|
||||
retentionDays: 'จำนวนวันเก็บรักษา',
|
||||
maxRows: 'จำนวนบันทึกสูงสุด',
|
||||
dedupeWindow: 'ช่วงเวลาลบรายการซ้ำ (วินาที)',
|
||||
|
||||
@@ -22,6 +22,7 @@ const viVN = {
|
||||
create_knowledge_base: 'Tạo cơ sở tri thức',
|
||||
get_pipeline: 'Xem Pipeline',
|
||||
get_knowledge_schema: 'Xem lược đồ knowledge engine',
|
||||
list_operation_logs: 'Truy vấn truy vết thao tác',
|
||||
},
|
||||
resources: {
|
||||
models: 'Tìm mô hình trò chuyện',
|
||||
@@ -2711,6 +2712,7 @@ const viVN = {
|
||||
description: 'Theo dõi thao tác sửa và xem của quản trị viên',
|
||||
captureLevel: 'Mức ghi nhận',
|
||||
retention: 'Chính sách lưu trữ',
|
||||
retentionSummary: 'Lưu {{days}} ngày · tối đa {{rows}} bản ghi',
|
||||
retentionDays: 'Số ngày lưu',
|
||||
maxRows: 'Số bản ghi tối đa',
|
||||
dedupeWindow: 'Khoảng khử trùng lặp (giây)',
|
||||
|
||||
@@ -19,6 +19,7 @@ const zhHans = {
|
||||
create_knowledge_base: '创建知识库',
|
||||
get_pipeline: '查看 Pipeline',
|
||||
get_knowledge_schema: '查看知识引擎配置',
|
||||
list_operation_logs: '查询操作溯源',
|
||||
},
|
||||
resources: {
|
||||
models: '查询聊天模型',
|
||||
@@ -2574,6 +2575,7 @@ const zhHans = {
|
||||
description: '追踪管理员与拥有者的修改和查看操作,记录“什么改成了什么”。',
|
||||
captureLevel: '操作等级',
|
||||
retention: '保留策略',
|
||||
retentionSummary: '保留 {{days}} 天 · 最多 {{rows}} 条',
|
||||
retentionDays: '保留天数',
|
||||
maxRows: '最大记录数',
|
||||
dedupeWindow: '去重窗口(秒)',
|
||||
|
||||
@@ -19,6 +19,7 @@ const zhHant = {
|
||||
create_knowledge_base: '建立知識庫',
|
||||
get_pipeline: '查看 Pipeline',
|
||||
get_knowledge_schema: '查看知識引擎設定',
|
||||
list_operation_logs: '查詢操作溯源',
|
||||
},
|
||||
resources: {
|
||||
models: '查詢聊天模型',
|
||||
@@ -2575,6 +2576,7 @@ const zhHant = {
|
||||
description: '追蹤管理員與擁有者的修改與查看操作,記錄「什麼改成了什麼」。',
|
||||
captureLevel: '操作等級',
|
||||
retention: '保留策略',
|
||||
retentionSummary: '保留 {{days}} 天 · 最多 {{rows}} 條',
|
||||
retentionDays: '保留天數',
|
||||
maxRows: '最大記錄數',
|
||||
dedupeWindow: '去重視窗(秒)',
|
||||
|
||||
Reference in New Issue
Block a user