mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-15 06:16:10 +00:00
fix(config): remove ignored session scoping controls (#1405)
Co-authored-by: Collective Developer <man@collective.dev>
This commit is contained in:
1 parent
ba621391c2
commit
fb28b72b27
7 files changed
+116
-125
No files matched your search
@@ -205,8 +205,6 @@
|
||||
"behavior.pendingCompactionInfo": "When a group chat exceeds the threshold, the LLM automatically summarizes older messages into a single compact entry, keeping the most recent ones intact for context.",
|
||||
"behavior.rateLimitTitle": "Rate Limiting",
|
||||
"behavior.rateLimitDescription": "Inbound message size limits and per-user request throttling",
|
||||
"behavior.sessionsTitle": "Session Scoping",
|
||||
"behavior.sessionsDescription": "Controls how conversation context is partitioned across users and channels",
|
||||
"behavior.securityTitle": "Input Security",
|
||||
"behavior.securityDescription": "Prompt injection detection and credential scrubbing to protect the gateway",
|
||||
"behavior.injectionActionHint": "What to do when a prompt injection attempt is detected in an inbound message.",
|
||||
@@ -215,10 +213,6 @@
|
||||
"behavior.scrubCredentialsHint": "Automatically redact API keys, tokens, and passwords found in tool output before returning to the agent.",
|
||||
"behavior.scrubCredentialsInfo": "Credential patterns in tool responses are masked before the agent sees them.",
|
||||
"behavior.scrubCredentialsOffInfo": "Credential scrubbing is disabled. Secrets in tool output may be exposed to the agent.",
|
||||
"sessions.scope": "Session Scope",
|
||||
"sessions.scopeTip": "Determines how sessions are partitioned. 'Per Sender' gives each user a separate session; 'Global' shares one session across all users.",
|
||||
"sessions.dmScope": "DM Scope",
|
||||
"sessions.dmScopeTip": "How direct-message sessions are keyed. 'Per Channel Peer' is the recommended default.",
|
||||
"quota.title": "Usage Quota",
|
||||
"quota.description": "Per-user message quota limits with optional per-provider, channel, and group overrides",
|
||||
"quota.enabled": "Enable Quotas",
|
||||
|
||||
@@ -171,8 +171,6 @@
|
||||
"behavior.pendingCompactionInfo": "그룹 채팅이 임계값을 초과하면 LLM이 오래된 메시지를 단일 압축 항목으로 자동 요약하여 컨텍스트를 위해 가장 최근 메시지를 유지합니다.",
|
||||
"behavior.rateLimitTitle": "속도 제한",
|
||||
"behavior.rateLimitDescription": "인바운드 메시지 크기 제한 및 사용자당 요청 쓰로틀링",
|
||||
"behavior.sessionsTitle": "세션 범위",
|
||||
"behavior.sessionsDescription": "사용자와 채널 간에 대화 컨텍스트가 분할되는 방식을 제어합니다",
|
||||
"behavior.securityTitle": "입력 보안",
|
||||
"behavior.securityDescription": "게이트웨이를 보호하기 위한 프롬프트 주입 감지 및 자격증명 스크러빙",
|
||||
"behavior.injectionActionHint": "인바운드 메시지에서 프롬프트 주입 시도가 감지될 때 수행할 작업입니다.",
|
||||
@@ -181,10 +179,6 @@
|
||||
"behavior.scrubCredentialsHint": "에이전트에 반환하기 전에 도구 출력에서 발견된 API 키, 토큰 및 비밀번호를 자동으로 수정합니다.",
|
||||
"behavior.scrubCredentialsInfo": "도구 응답의 자격증명 패턴은 에이전트가 보기 전에 마스킹됩니다.",
|
||||
"behavior.scrubCredentialsOffInfo": "자격증명 스크러빙이 비활성화되어 있습니다. 도구 출력의 시크릿이 에이전트에 노출될 수 있습니다.",
|
||||
"sessions.scope": "세션 범위",
|
||||
"sessions.scopeTip": "세션 분할 방법을 결정합니다. '발신자별'은 각 사용자에게 별도 세션을 제공하고, '전역'은 모든 사용자가 하나의 세션을 공유합니다.",
|
||||
"sessions.dmScope": "DM 범위",
|
||||
"sessions.dmScopeTip": "DM 세션이 키로 구분되는 방법입니다. '채널 피어별'이 권장 기본값입니다.",
|
||||
"quota.title": "사용 할당량",
|
||||
"quota.description": "선택적 공급자, 채널 및 그룹별 재정의가 있는 사용자별 메시지 할당량 제한",
|
||||
"quota.enabled": "할당량 활성화",
|
||||
|
||||
@@ -204,8 +204,6 @@
|
||||
"behavior.pendingCompactionInfo": "Khi nhóm chat vượt ngưỡng, LLM tự động tóm tắt các tin nhắn cũ thành một mục rút gọn, giữ nguyên các tin gần đây nhất làm ngữ cảnh.",
|
||||
"behavior.rateLimitTitle": "Giới hạn tốc độ",
|
||||
"behavior.rateLimitDescription": "Giới hạn kích thước tin nhắn đến và kiểm soát tốc độ yêu cầu mỗi người dùng",
|
||||
"behavior.sessionsTitle": "Phạm vi phiên",
|
||||
"behavior.sessionsDescription": "Kiểm soát cách phân vùng ngữ cảnh cuộc trò chuyện theo người dùng và kênh",
|
||||
"behavior.securityTitle": "Bảo mật đầu vào",
|
||||
"behavior.securityDescription": "Phát hiện injection prompt và ẩn thông tin xác thực để bảo vệ gateway",
|
||||
"behavior.injectionActionHint": "Hành động thực hiện khi phát hiện nỗ lực injection prompt trong tin nhắn đến.",
|
||||
@@ -214,10 +212,6 @@
|
||||
"behavior.scrubCredentialsHint": "Tự động ẩn khóa API, token và mật khẩu tìm thấy trong đầu ra công cụ trước khi trả về agent.",
|
||||
"behavior.scrubCredentialsInfo": "Các mẫu thông tin xác thực trong phản hồi công cụ được che đi trước khi agent nhìn thấy.",
|
||||
"behavior.scrubCredentialsOffInfo": "Tính năng ẩn thông tin xác thực bị tắt. Bí mật trong đầu ra công cụ có thể bị lộ với agent.",
|
||||
"sessions.scope": "Phạm vi phiên",
|
||||
"sessions.scopeTip": "Xác định cách phân vùng phiên. 'Theo người gửi' cho mỗi người dùng một phiên riêng; 'Toàn cục' chia sẻ một phiên cho tất cả người dùng.",
|
||||
"sessions.dmScope": "Phạm vi tin nhắn riêng",
|
||||
"sessions.dmScopeTip": "Cách đặt khóa phiên tin nhắn riêng. 'Theo kênh và đối tác' là mặc định được khuyến nghị.",
|
||||
"quota.title": "Hạn mức sử dụng",
|
||||
"quota.description": "Giới hạn hạn mức tin nhắn mỗi người dùng với tùy chọn ghi đè theo provider, kênh và nhóm",
|
||||
"quota.enabled": "Bật hạn mức",
|
||||
|
||||
@@ -204,8 +204,6 @@
|
||||
"behavior.pendingCompactionInfo": "当群聊超过阈值时,LLM 自动将旧消息总结为一条精简条目,保留最近的消息作为上下文。",
|
||||
"behavior.rateLimitTitle": "速率限制",
|
||||
"behavior.rateLimitDescription": "入站消息大小限制和每用户请求限流",
|
||||
"behavior.sessionsTitle": "会话范围",
|
||||
"behavior.sessionsDescription": "控制跨用户和频道的对话上下文分区方式",
|
||||
"behavior.securityTitle": "输入安全",
|
||||
"behavior.securityDescription": "提示词注入检测和凭证擦除,以保护网关",
|
||||
"behavior.injectionActionHint": "当入站消息中检测到提示词注入尝试时的处理动作。",
|
||||
@@ -214,10 +212,6 @@
|
||||
"behavior.scrubCredentialsHint": "在返回给 Agent 之前,自动从工具输出中删除 API 密钥、令牌和密码。",
|
||||
"behavior.scrubCredentialsInfo": "工具响应中的凭证模式在 Agent 看到之前被屏蔽。",
|
||||
"behavior.scrubCredentialsOffInfo": "凭证擦除已禁用,工具输出中的密钥可能暴露给 Agent。",
|
||||
"sessions.scope": "会话范围",
|
||||
"sessions.scopeTip": "确定会话分区方式。「按发送者」为每个用户提供单独的会话;「全局」在所有用户之间共享一个会话。",
|
||||
"sessions.dmScope": "私信范围",
|
||||
"sessions.dmScopeTip": "私信会话的键控方式。推荐默认使用「按频道对等方」。",
|
||||
"quota.title": "使用配额",
|
||||
"quota.description": "每用户消息配额限制,可选按提供商、频道和群组覆盖",
|
||||
"quota.enabled": "启用配额",
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildBehaviorPatch } from "./behavior-section";
|
||||
|
||||
describe("buildBehaviorPatch", () => {
|
||||
it("does not include deprecated session scoping config", () => {
|
||||
const patch = buildBehaviorPatch({
|
||||
rate: {
|
||||
max_message_chars: 12000,
|
||||
rate_limit_rpm: 30,
|
||||
inbound_debounce_ms: 250,
|
||||
},
|
||||
security: {
|
||||
injection_action: "warn",
|
||||
scrub_credentials: true,
|
||||
},
|
||||
ux: {
|
||||
intent_classify: true,
|
||||
team_work_classify: false,
|
||||
},
|
||||
pendingCompaction: {
|
||||
threshold: 200,
|
||||
keep_recent: 40,
|
||||
},
|
||||
chatBehavior: {
|
||||
enabled: true,
|
||||
quick_ack: {
|
||||
enabled: true,
|
||||
mode: "sidecar_generated",
|
||||
min_delay_ms: 1000,
|
||||
provider: "",
|
||||
model: "",
|
||||
timeout_ms: 2500,
|
||||
max_tokens: 40,
|
||||
max_chars: 120,
|
||||
templates: ["Got it. Working on it..."],
|
||||
},
|
||||
intermediate_replies: {
|
||||
enabled: false,
|
||||
mode: "sidecar_generated",
|
||||
provider: "",
|
||||
model: "",
|
||||
timeout_ms: 2500,
|
||||
max_tokens: 60,
|
||||
max_chars: 180,
|
||||
},
|
||||
final_split: {
|
||||
enabled: false,
|
||||
min_chars: 1200,
|
||||
max_messages: 3,
|
||||
delay_ms: 500,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(Object.prototype.hasOwnProperty.call(patch, "sessions")).toBe(false);
|
||||
expect(patch).toMatchObject({
|
||||
gateway: {
|
||||
max_message_chars: 12000,
|
||||
rate_limit_rpm: 30,
|
||||
inbound_debounce_ms: 250,
|
||||
injection_action: "warn",
|
||||
team_work_classify: false,
|
||||
},
|
||||
agents: {
|
||||
defaults: { intent_classify: true },
|
||||
},
|
||||
tools: { scrub_credentials: true },
|
||||
channels: {
|
||||
pending_compaction: {
|
||||
threshold: 200,
|
||||
keep_recent: 40,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4,27 +4,23 @@ import { useTranslation } from "react-i18next";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { BehaviorUxCard } from "./behavior-ux-card";
|
||||
import { BehaviorRateCard } from "./behavior-rate-card";
|
||||
import { BehaviorSessionsCard } from "./behavior-sessions-card";
|
||||
import { BehaviorSecurityCard } from "./behavior-security-card";
|
||||
import { BehaviorPendingCompactionCard, type PendingCompactionValues } from "./behavior-pending-compaction-card";
|
||||
import { BehaviorChatCard, type ChatBehaviorValues } from "./behavior-chat-card";
|
||||
import { BehaviorPassiveMemoryCard } from "./behavior-passive-memory-card";
|
||||
|
||||
|
||||
|
||||
interface Props {
|
||||
config: Record<string, any>;
|
||||
onPatch: (updates: Record<string, unknown>) => Promise<void>;
|
||||
saving: boolean;
|
||||
}
|
||||
|
||||
/** State container for Behavior tab — composes 4 sub-cards, patches multiple config keys. */
|
||||
/** State container for Behavior tab cards and their patch payload. */
|
||||
export function BehaviorSection({ config, onPatch, saving }: Props) {
|
||||
const { t } = useTranslation("config");
|
||||
const gw = config.gateway ?? {};
|
||||
const ag = config.agents?.defaults ?? {};
|
||||
const tl = config.tools ?? {};
|
||||
const ss = config.sessions ?? {};
|
||||
const ch = config.channels ?? {};
|
||||
|
||||
// UX toggles (from gateway + agents.defaults)
|
||||
@@ -40,12 +36,6 @@ export function BehaviorSection({ config, onPatch, saving }: Props) {
|
||||
inbound_debounce_ms: gw.inbound_debounce_ms,
|
||||
});
|
||||
|
||||
// Sessions
|
||||
const [sessions, setSessions] = useState<{ scope?: string; dm_scope?: string }>({
|
||||
scope: ss.scope,
|
||||
dm_scope: ss.dm_scope,
|
||||
});
|
||||
|
||||
// Security (from gateway + tools)
|
||||
const [security, setSecurity] = useState<{ injection_action?: string; scrub_credentials?: boolean }>({
|
||||
injection_action: gw.injection_action,
|
||||
@@ -71,7 +61,6 @@ export function BehaviorSection({ config, onPatch, saving }: Props) {
|
||||
rate_limit_rpm: gw.rate_limit_rpm,
|
||||
inbound_debounce_ms: gw.inbound_debounce_ms,
|
||||
});
|
||||
setSessions({ scope: ss.scope, dm_scope: ss.dm_scope });
|
||||
setSecurity({
|
||||
injection_action: gw.injection_action,
|
||||
scrub_credentials: tl.scrub_credentials,
|
||||
@@ -85,22 +74,13 @@ export function BehaviorSection({ config, onPatch, saving }: Props) {
|
||||
(v: T) => { setter(v); setDirty(true); };
|
||||
|
||||
const handleSave = () => {
|
||||
onPatch({
|
||||
gateway: {
|
||||
max_message_chars: rate.max_message_chars,
|
||||
rate_limit_rpm: rate.rate_limit_rpm,
|
||||
inbound_debounce_ms: rate.inbound_debounce_ms,
|
||||
injection_action: security.injection_action,
|
||||
chat_behavior: chatBehavior,
|
||||
team_work_classify: ux.team_work_classify,
|
||||
},
|
||||
agents: {
|
||||
defaults: { intent_classify: ux.intent_classify },
|
||||
},
|
||||
tools: { scrub_credentials: security.scrub_credentials },
|
||||
sessions,
|
||||
channels: { pending_compaction: pendingCompaction },
|
||||
}).catch(() => {});
|
||||
onPatch(buildBehaviorPatch({
|
||||
rate,
|
||||
security,
|
||||
chatBehavior,
|
||||
ux,
|
||||
pendingCompaction,
|
||||
})).catch(() => {});
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -108,7 +88,6 @@ export function BehaviorSection({ config, onPatch, saving }: Props) {
|
||||
<BehaviorUxCard value={ux} onChange={markDirty(setUx)} />
|
||||
<BehaviorChatCard value={chatBehavior} onChange={markDirty(setChatBehavior)} />
|
||||
<BehaviorRateCard value={rate} onChange={markDirty(setRate)} />
|
||||
<BehaviorSessionsCard value={sessions} onChange={markDirty(setSessions)} />
|
||||
<BehaviorSecurityCard value={security} onChange={markDirty(setSecurity)} />
|
||||
<BehaviorPendingCompactionCard value={pendingCompaction} onChange={markDirty(setPendingCompaction)} />
|
||||
<BehaviorPassiveMemoryCard />
|
||||
@@ -124,6 +103,38 @@ export function BehaviorSection({ config, onPatch, saving }: Props) {
|
||||
);
|
||||
}
|
||||
|
||||
export interface BehaviorPatchInput {
|
||||
rate: { max_message_chars?: number; rate_limit_rpm?: number; inbound_debounce_ms?: number };
|
||||
security: { injection_action?: string; scrub_credentials?: boolean };
|
||||
chatBehavior: ChatBehaviorValues;
|
||||
ux: { intent_classify: boolean; team_work_classify: boolean };
|
||||
pendingCompaction: PendingCompactionValues;
|
||||
}
|
||||
|
||||
export function buildBehaviorPatch({
|
||||
rate,
|
||||
security,
|
||||
chatBehavior,
|
||||
ux,
|
||||
pendingCompaction,
|
||||
}: BehaviorPatchInput): Record<string, unknown> {
|
||||
return {
|
||||
gateway: {
|
||||
max_message_chars: rate.max_message_chars,
|
||||
rate_limit_rpm: rate.rate_limit_rpm,
|
||||
inbound_debounce_ms: rate.inbound_debounce_ms,
|
||||
injection_action: security.injection_action,
|
||||
chat_behavior: chatBehavior,
|
||||
team_work_classify: ux.team_work_classify,
|
||||
},
|
||||
agents: {
|
||||
defaults: { intent_classify: ux.intent_classify },
|
||||
},
|
||||
tools: { scrub_credentials: security.scrub_credentials },
|
||||
channels: { pending_compaction: pendingCompaction },
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeChatBehavior(value: any): ChatBehaviorValues {
|
||||
return {
|
||||
enabled: value?.enabled ?? false,
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { InfoLabel } from "@/components/shared/info-label";
|
||||
|
||||
interface SessionsValues {
|
||||
scope?: string;
|
||||
dm_scope?: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
value: SessionsValues;
|
||||
onChange: (v: SessionsValues) => void;
|
||||
}
|
||||
|
||||
/** Session scoping settings (scope + DM scope). */
|
||||
export function BehaviorSessionsCard({ value, onChange }: Props) {
|
||||
const { t } = useTranslation("config");
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-base">{t("behavior.sessionsTitle")}</CardTitle>
|
||||
<CardDescription>{t("behavior.sessionsDescription")}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<div className="grid gap-1.5">
|
||||
<InfoLabel tip={t("sessions.scopeTip")}>{t("sessions.scope")}</InfoLabel>
|
||||
<Select
|
||||
value={value.scope ?? "per-sender"}
|
||||
onValueChange={(v) => onChange({ ...value, scope: v })}
|
||||
>
|
||||
<SelectTrigger><SelectValue /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="per-sender">Per Sender</SelectItem>
|
||||
<SelectItem value="global">Global</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="grid gap-1.5">
|
||||
<InfoLabel tip={t("sessions.dmScopeTip")}>{t("sessions.dmScope")}</InfoLabel>
|
||||
<Select
|
||||
value={value.dm_scope ?? "per-channel-peer"}
|
||||
onValueChange={(v) => onChange({ ...value, dm_scope: v })}
|
||||
>
|
||||
<SelectTrigger><SelectValue /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="main">Main</SelectItem>
|
||||
<SelectItem value="per-peer">Per Peer</SelectItem>
|
||||
<SelectItem value="per-channel-peer">Per Channel Peer</SelectItem>
|
||||
<SelectItem value="per-account-channel-peer">Per Account Channel Peer</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user