mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-16 20:46:08 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 565044f41e | |||
| 18bcfcecb6 | |||
| 82c078efce | |||
| e50f45f64e | |||
| 3e1913b50b | |||
| bd7b58524b | |||
| b836f199a8 | |||
| c775a233a8 | |||
| e4fa7682ae | |||
| f39ba2cfb4 | |||
| 8551a235d5 | |||
| 20497d8f07 | |||
| 9ca78ae292 |
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "المكتبة",
|
||||
"knowledgeBase.uploadGuide": "يمكن عرض الملفات المرفوعة في قسم 'الموارد'.",
|
||||
"knowledgeBase.viewMore": "عرض المزيد",
|
||||
"localSystem.off.desc": "تعطيل الوصول إلى النظام المحلي.",
|
||||
"localSystem.off.title": "إيقاف",
|
||||
"localSystem.on.desc": "السماح بالوصول إلى الملفات والأوامر المحلية.",
|
||||
"localSystem.on.title": "مفعل",
|
||||
"localSystem.title": "النظام المحلي",
|
||||
"memberSelection.addMember": "إضافة عضو",
|
||||
"memberSelection.allMembers": "جميع الأعضاء",
|
||||
"memberSelection.createGroup": "إنشاء مجموعة",
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "Библиотека",
|
||||
"knowledgeBase.uploadGuide": "Качените файлове могат да се видят в секцията 'Ресурси'.",
|
||||
"knowledgeBase.viewMore": "Виж повече",
|
||||
"localSystem.off.desc": "Деактивиране на достъпа до локалната система.",
|
||||
"localSystem.off.title": "Изключено",
|
||||
"localSystem.on.desc": "Разрешаване на достъп до локални файлове и команди.",
|
||||
"localSystem.on.title": "Активирано",
|
||||
"localSystem.title": "Локална система",
|
||||
"memberSelection.addMember": "Добави член",
|
||||
"memberSelection.allMembers": "Всички членове",
|
||||
"memberSelection.createGroup": "Създай група",
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "Bibliothek",
|
||||
"knowledgeBase.uploadGuide": "Hochgeladene Dateien sind im Bereich 'Ressourcen' einsehbar.",
|
||||
"knowledgeBase.viewMore": "Mehr anzeigen",
|
||||
"localSystem.off.desc": "Zugriff auf das lokale System deaktivieren.",
|
||||
"localSystem.off.title": "Aus",
|
||||
"localSystem.on.desc": "Zugriff auf lokale Dateien und Befehle erlauben.",
|
||||
"localSystem.on.title": "Aktiviert",
|
||||
"localSystem.title": "Lokales System",
|
||||
"memberSelection.addMember": "Mitglied hinzufügen",
|
||||
"memberSelection.allMembers": "Alle Mitglieder",
|
||||
"memberSelection.createGroup": "Gruppe erstellen",
|
||||
|
||||
@@ -241,6 +241,14 @@
|
||||
"rag.userQuery.actions.regenerate": "Regenerate Query",
|
||||
"regenerate": "Regenerate",
|
||||
"roleAndArchive": "Agent Profile & History",
|
||||
"runtimeEnv.mode.cloud": "Cloud Sandbox",
|
||||
"runtimeEnv.mode.cloudDesc": "Run in a secure cloud sandbox",
|
||||
"runtimeEnv.mode.local": "Local",
|
||||
"runtimeEnv.mode.localDesc": "Access local files and commands",
|
||||
"runtimeEnv.mode.none": "None",
|
||||
"runtimeEnv.mode.noneDesc": "No runtime environment",
|
||||
"runtimeEnv.selectMode": "Select Runtime Environment",
|
||||
"runtimeEnv.title": "Runtime Environment",
|
||||
"search.grounding.imageSearchQueries": "Image Search Keywords",
|
||||
"search.grounding.imageTitle": "Found {{count}} images",
|
||||
"search.grounding.searchQueries": "Search Keywords",
|
||||
@@ -391,6 +399,7 @@
|
||||
"tokenTag.overload": "Exceeded Limit",
|
||||
"tokenTag.remained": "Remaining",
|
||||
"tokenTag.used": "Used",
|
||||
"tool.intervention.approvalMode": "Approval Mode",
|
||||
"tool.intervention.approve": "Approve",
|
||||
"tool.intervention.approveAndRemember": "Approve and Remember",
|
||||
"tool.intervention.approveOnce": "Approve This Time Only",
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "Biblioteca",
|
||||
"knowledgeBase.uploadGuide": "Los archivos subidos se pueden ver en la sección 'Recursos'.",
|
||||
"knowledgeBase.viewMore": "Ver más",
|
||||
"localSystem.off.desc": "Desactivar el acceso al sistema local.",
|
||||
"localSystem.off.title": "Desactivado",
|
||||
"localSystem.on.desc": "Permitir acceso a archivos y comandos locales.",
|
||||
"localSystem.on.title": "Activado",
|
||||
"localSystem.title": "Sistema Local",
|
||||
"memberSelection.addMember": "Agregar miembro",
|
||||
"memberSelection.allMembers": "Todos los miembros",
|
||||
"memberSelection.createGroup": "Crear grupo",
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "کتابخانه",
|
||||
"knowledgeBase.uploadGuide": "فایلهای بارگذاریشده در بخش «منابع» قابل مشاهده هستند.",
|
||||
"knowledgeBase.viewMore": "مشاهده بیشتر",
|
||||
"localSystem.off.desc": "غیرفعال کردن دسترسی به سیستم محلی.",
|
||||
"localSystem.off.title": "خاموش",
|
||||
"localSystem.on.desc": "اجازه دسترسی به فایلها و دستورات محلی.",
|
||||
"localSystem.on.title": "فعال",
|
||||
"localSystem.title": "سیستم محلی",
|
||||
"memberSelection.addMember": "افزودن عضو",
|
||||
"memberSelection.allMembers": "همه اعضا",
|
||||
"memberSelection.createGroup": "ایجاد گروه",
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "Bibliothèque",
|
||||
"knowledgeBase.uploadGuide": "Les fichiers téléchargés peuvent être consultés dans la section « Ressources ».",
|
||||
"knowledgeBase.viewMore": "Voir plus",
|
||||
"localSystem.off.desc": "Désactiver l'accès au système local.",
|
||||
"localSystem.off.title": "Désactivé",
|
||||
"localSystem.on.desc": "Autoriser l'accès aux fichiers et commandes locaux.",
|
||||
"localSystem.on.title": "Activé",
|
||||
"localSystem.title": "Système Local",
|
||||
"memberSelection.addMember": "Ajouter un membre",
|
||||
"memberSelection.allMembers": "Tous les membres",
|
||||
"memberSelection.createGroup": "Créer un groupe",
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "Libreria",
|
||||
"knowledgeBase.uploadGuide": "I file caricati possono essere visualizzati nella sezione 'Risorse'.",
|
||||
"knowledgeBase.viewMore": "Visualizza Altro",
|
||||
"localSystem.off.desc": "Disabilita l'accesso al sistema locale.",
|
||||
"localSystem.off.title": "Spento",
|
||||
"localSystem.on.desc": "Consenti l'accesso ai file e ai comandi locali.",
|
||||
"localSystem.on.title": "Abilitato",
|
||||
"localSystem.title": "Sistema Locale",
|
||||
"memberSelection.addMember": "Aggiungi membro",
|
||||
"memberSelection.allMembers": "Tutti i membri",
|
||||
"memberSelection.createGroup": "Crea gruppo",
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "ライブラリ",
|
||||
"knowledgeBase.uploadGuide": "アップロードしたファイルは「リソース」で確認できます",
|
||||
"knowledgeBase.viewMore": "さらに表示",
|
||||
"localSystem.off.desc": "ローカルシステムへのアクセスを無効にします。",
|
||||
"localSystem.off.title": "オフ",
|
||||
"localSystem.on.desc": "ローカルファイルやコマンドへのアクセスを許可します。",
|
||||
"localSystem.on.title": "有効",
|
||||
"localSystem.title": "ローカルシステム",
|
||||
"memberSelection.addMember": "メンバーを追加",
|
||||
"memberSelection.allMembers": "全メンバー",
|
||||
"memberSelection.createGroup": "グループを作成",
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "자료실",
|
||||
"knowledgeBase.uploadGuide": "업로드한 파일은 '자료'에서 확인할 수 있습니다",
|
||||
"knowledgeBase.viewMore": "더 보기",
|
||||
"localSystem.off.desc": "로컬 시스템 액세스를 비활성화합니다.",
|
||||
"localSystem.off.title": "꺼짐",
|
||||
"localSystem.on.desc": "로컬 파일 및 명령에 대한 액세스를 허용합니다.",
|
||||
"localSystem.on.title": "사용",
|
||||
"localSystem.title": "로컬 시스템",
|
||||
"memberSelection.addMember": "구성원 추가",
|
||||
"memberSelection.allMembers": "전체 구성원",
|
||||
"memberSelection.createGroup": "그룹 만들기",
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "Bibliotheek",
|
||||
"knowledgeBase.uploadGuide": "Geüploade bestanden zijn te bekijken in het gedeelte 'Bronnen'.",
|
||||
"knowledgeBase.viewMore": "Meer bekijken",
|
||||
"localSystem.off.desc": "Toegang tot het lokale systeem uitschakelen.",
|
||||
"localSystem.off.title": "Uit",
|
||||
"localSystem.on.desc": "Toegang tot lokale bestanden en opdrachten toestaan.",
|
||||
"localSystem.on.title": "Ingeschakeld",
|
||||
"localSystem.title": "Lokaal Systeem",
|
||||
"memberSelection.addMember": "Lid toevoegen",
|
||||
"memberSelection.allMembers": "Alle leden",
|
||||
"memberSelection.createGroup": "Groep aanmaken",
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "Biblioteka",
|
||||
"knowledgeBase.uploadGuide": "Przesłane pliki można przeglądać w sekcji „Zasoby”.",
|
||||
"knowledgeBase.viewMore": "Zobacz więcej",
|
||||
"localSystem.off.desc": "Wyłącz dostęp do systemu lokalnego.",
|
||||
"localSystem.off.title": "Wyłączone",
|
||||
"localSystem.on.desc": "Zezwól na dostęp do lokalnych plików i poleceń.",
|
||||
"localSystem.on.title": "Włączone",
|
||||
"localSystem.title": "System lokalny",
|
||||
"memberSelection.addMember": "Dodaj członka",
|
||||
"memberSelection.allMembers": "Wszyscy członkowie",
|
||||
"memberSelection.createGroup": "Utwórz grupę",
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "Biblioteca",
|
||||
"knowledgeBase.uploadGuide": "Arquivos enviados podem ser visualizados na seção 'Recursos'.",
|
||||
"knowledgeBase.viewMore": "Ver mais",
|
||||
"localSystem.off.desc": "Desativar acesso ao sistema local.",
|
||||
"localSystem.off.title": "Desligado",
|
||||
"localSystem.on.desc": "Permitir acesso a arquivos e comandos locais.",
|
||||
"localSystem.on.title": "Ativado",
|
||||
"localSystem.title": "Sistema Local",
|
||||
"memberSelection.addMember": "Adicionar Membro",
|
||||
"memberSelection.allMembers": "Todos os membros",
|
||||
"memberSelection.createGroup": "Criar Grupo",
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "Библиотека",
|
||||
"knowledgeBase.uploadGuide": "Загруженные файлы можно просмотреть в разделе «Ресурсы».",
|
||||
"knowledgeBase.viewMore": "Показать больше",
|
||||
"localSystem.off.desc": "Отключить доступ к локальной системе.",
|
||||
"localSystem.off.title": "Выключено",
|
||||
"localSystem.on.desc": "Разрешить доступ к локальным файлам и командам.",
|
||||
"localSystem.on.title": "Включено",
|
||||
"localSystem.title": "Локальная система",
|
||||
"memberSelection.addMember": "Добавить участника",
|
||||
"memberSelection.allMembers": "Все участники",
|
||||
"memberSelection.createGroup": "Создать группу",
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "Kütüphane",
|
||||
"knowledgeBase.uploadGuide": "Yüklenen dosyalar 'Kaynaklar' bölümünde görüntülenebilir.",
|
||||
"knowledgeBase.viewMore": "Daha Fazla Görüntüle",
|
||||
"localSystem.off.desc": "Yerel sistem erişimini devre dışı bırak.",
|
||||
"localSystem.off.title": "Kapalı",
|
||||
"localSystem.on.desc": "Yerel dosyalara ve komutlara erişime izin ver.",
|
||||
"localSystem.on.title": "Etkin",
|
||||
"localSystem.title": "Yerel Sistem",
|
||||
"memberSelection.addMember": "Üye Ekle",
|
||||
"memberSelection.allMembers": "Tüm üyeler",
|
||||
"memberSelection.createGroup": "Grup Oluştur",
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "Thư viện",
|
||||
"knowledgeBase.uploadGuide": "Các tệp đã tải lên có thể xem trong phần 'Tài nguyên'.",
|
||||
"knowledgeBase.viewMore": "Xem thêm",
|
||||
"localSystem.off.desc": "Tắt quyền truy cập hệ thống cục bộ.",
|
||||
"localSystem.off.title": "Tắt",
|
||||
"localSystem.on.desc": "Cho phép truy cập vào tệp và lệnh cục bộ.",
|
||||
"localSystem.on.title": "Bật",
|
||||
"localSystem.title": "Hệ thống cục bộ",
|
||||
"memberSelection.addMember": "Thêm thành viên",
|
||||
"memberSelection.allMembers": "Tất cả thành viên",
|
||||
"memberSelection.createGroup": "Tạo nhóm",
|
||||
|
||||
@@ -241,6 +241,14 @@
|
||||
"rag.userQuery.actions.regenerate": "重新生成 Query",
|
||||
"regenerate": "重新生成",
|
||||
"roleAndArchive": "助理档案与记录",
|
||||
"runtimeEnv.mode.cloud": "云端沙箱",
|
||||
"runtimeEnv.mode.cloudDesc": "在安全的云端沙箱中运行",
|
||||
"runtimeEnv.mode.local": "本地",
|
||||
"runtimeEnv.mode.localDesc": "访问本地文件和命令",
|
||||
"runtimeEnv.mode.none": "无",
|
||||
"runtimeEnv.mode.noneDesc": "不使用运行环境",
|
||||
"runtimeEnv.selectMode": "选择运行环境",
|
||||
"runtimeEnv.title": "运行环境",
|
||||
"search.grounding.imageSearchQueries": "图片搜索关键词",
|
||||
"search.grounding.imageTitle": "找到 {{count}} 张图片",
|
||||
"search.grounding.searchQueries": "搜索关键词",
|
||||
@@ -391,6 +399,7 @@
|
||||
"tokenTag.overload": "超出限制",
|
||||
"tokenTag.remained": "剩余",
|
||||
"tokenTag.used": "已使用",
|
||||
"tool.intervention.approvalMode": "审批模式",
|
||||
"tool.intervention.approve": "批准",
|
||||
"tool.intervention.approveAndRemember": "批准并记住",
|
||||
"tool.intervention.approveOnce": "仅本次批准",
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
"knowledgeBase.title": "資源庫",
|
||||
"knowledgeBase.uploadGuide": "上傳的檔案可在「資源」中查看喔",
|
||||
"knowledgeBase.viewMore": "查看更多",
|
||||
"localSystem.off.desc": "停用本機系統存取。",
|
||||
"localSystem.off.title": "關閉",
|
||||
"localSystem.on.desc": "允許存取本機檔案與指令。",
|
||||
"localSystem.on.title": "開啟",
|
||||
"localSystem.title": "本機系統",
|
||||
"memberSelection.addMember": "添加成員",
|
||||
"memberSelection.allMembers": "所有成員",
|
||||
"memberSelection.createGroup": "建立群組",
|
||||
|
||||
@@ -29,6 +29,7 @@ export const defaultToolIds = [
|
||||
WebBrowsingManifest.identifier,
|
||||
KnowledgeBaseManifest.identifier,
|
||||
MemoryManifest.identifier,
|
||||
LocalSystemManifest.identifier,
|
||||
];
|
||||
|
||||
export const builtinTools: LobeBuiltinTool[] = [
|
||||
@@ -55,7 +56,7 @@ export const builtinTools: LobeBuiltinTool[] = [
|
||||
},
|
||||
{
|
||||
discoverable: isDesktop,
|
||||
hidden: !isDesktop,
|
||||
hidden: true,
|
||||
identifier: LocalSystemManifest.identifier,
|
||||
manifest: LocalSystemManifest,
|
||||
type: 'builtin',
|
||||
@@ -73,6 +74,7 @@ export const builtinTools: LobeBuiltinTool[] = [
|
||||
type: 'builtin',
|
||||
},
|
||||
{
|
||||
hidden: true,
|
||||
identifier: CloudSandboxManifest.identifier,
|
||||
manifest: CloudSandboxManifest,
|
||||
type: 'builtin',
|
||||
|
||||
@@ -8,15 +8,23 @@
|
||||
export type AgentMode = 'auto' | 'plan' | 'ask' | 'implement';
|
||||
|
||||
/**
|
||||
* Local System configuration (desktop only)
|
||||
* Runtime environment mode
|
||||
* - local: Access local files and commands (desktop only)
|
||||
* - cloud: Run in cloud sandbox
|
||||
* - none: No runtime environment
|
||||
*/
|
||||
export interface LocalSystemConfig {
|
||||
export type RuntimeEnvMode = 'cloud' | 'local' | 'none';
|
||||
|
||||
/**
|
||||
* Runtime environment configuration (desktop only)
|
||||
*/
|
||||
export interface RuntimeEnvConfig {
|
||||
/**
|
||||
* Local System working directory (desktop only)
|
||||
* Runtime environment mode
|
||||
*/
|
||||
runtimeMode?: RuntimeEnvMode;
|
||||
/**
|
||||
* Working directory (desktop only)
|
||||
*/
|
||||
workingDirectory?: string;
|
||||
|
||||
// Future extensions:
|
||||
// allowedPaths?: string[];
|
||||
// deniedCommands?: string[];
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
||||
|
||||
import { type SearchMode } from '../search';
|
||||
import { type UserMemoryEffort } from '../user/settings/memory';
|
||||
import { type LocalSystemConfig } from './agentConfig';
|
||||
import { type RuntimeEnvConfig } from './agentConfig';
|
||||
|
||||
export interface WorkingModel {
|
||||
model: string;
|
||||
@@ -95,12 +95,12 @@ export interface LobeAgentChatConfig extends AgentMemoryChatConfig {
|
||||
*/
|
||||
imageResolution2?: '512px' | '1K' | '2K' | '4K';
|
||||
inputTemplate?: string;
|
||||
/**
|
||||
* Local System configuration (desktop only)
|
||||
*/
|
||||
localSystem?: LocalSystemConfig;
|
||||
reasoningBudgetToken?: number;
|
||||
reasoningEffort?: 'low' | 'medium' | 'high';
|
||||
/**
|
||||
* Runtime environment configuration (desktop only)
|
||||
*/
|
||||
runtimeEnv?: RuntimeEnvConfig;
|
||||
|
||||
searchFCModel?: WorkingModel;
|
||||
searchMode?: SearchMode;
|
||||
@@ -130,9 +130,10 @@ export interface LobeAgentChatConfig extends AgentMemoryChatConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Zod schema for LocalSystemConfig
|
||||
* Zod schema for RuntimeEnvConfig
|
||||
*/
|
||||
export const LocalSystemConfigSchema = z.object({
|
||||
export const RuntimeEnvConfigSchema = z.object({
|
||||
runtimeMode: z.enum(['local', 'cloud', 'none']).optional(),
|
||||
workingDirectory: z.string().optional(),
|
||||
});
|
||||
|
||||
@@ -173,7 +174,7 @@ export const AgentChatConfigSchema = z
|
||||
imageAspectRatio2: z.string().optional(),
|
||||
imageResolution: z.enum(['1K', '2K', '4K']).optional(),
|
||||
imageResolution2: z.enum(['512px', '1K', '2K', '4K']).optional(),
|
||||
localSystem: LocalSystemConfigSchema.optional(),
|
||||
runtimeEnv: RuntimeEnvConfigSchema.optional(),
|
||||
reasoningBudgetToken: z.number().optional(),
|
||||
reasoningEffort: z.enum(['low', 'medium', 'high']).optional(),
|
||||
searchFCModel: z
|
||||
|
||||
@@ -18,6 +18,7 @@ import { systemStatusSelectors } from '@/store/global/selectors';
|
||||
import { type ActionToolbarProps } from '../ActionBar';
|
||||
import ActionBar from '../ActionBar';
|
||||
import InputEditor from '../InputEditor';
|
||||
import RuntimeConfig from '../RuntimeConfig';
|
||||
import SendArea from '../SendArea';
|
||||
import TypoBar from '../TypoBar';
|
||||
import ContextContainer from './ContextContainer';
|
||||
@@ -59,11 +60,13 @@ interface DesktopChatInputProps extends ActionToolbarProps {
|
||||
leftContent?: ReactNode;
|
||||
sendAreaPrefix?: ReactNode;
|
||||
showFootnote?: boolean;
|
||||
showRuntimeConfig?: boolean;
|
||||
}
|
||||
|
||||
const DesktopChatInput = memo<DesktopChatInputProps>(
|
||||
({
|
||||
showFootnote,
|
||||
showRuntimeConfig = true,
|
||||
inputContainerProps,
|
||||
extentHeaderContent,
|
||||
actionBarStyle,
|
||||
@@ -151,6 +154,7 @@ const DesktopChatInput = memo<DesktopChatInputProps>(
|
||||
>
|
||||
<InputEditor />
|
||||
</ChatInput>
|
||||
{showRuntimeConfig && <RuntimeConfig />}
|
||||
{showFootnote && !expand && (
|
||||
<Center style={{ pointerEvents: 'none', zIndex: 100 }}>
|
||||
<Text className={styles.footnote} type={'secondary'}>
|
||||
|
||||
+30
-15
@@ -1,14 +1,13 @@
|
||||
import { type MenuProps } from '@lobehub/ui';
|
||||
import { Button, Center, DropdownMenu, Icon } from '@lobehub/ui';
|
||||
import { Button, Center, DropdownMenu, Icon, Tooltip } from '@lobehub/ui';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { Check, ChevronDown, Hand, ListChecks, Zap } from 'lucide-react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { toolInterventionSelectors } from '@/store/user/selectors';
|
||||
|
||||
import { type ApprovalMode } from './index';
|
||||
import { type ApprovalMode } from '@/store/user/slices/settings/selectors';
|
||||
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
icon: css`
|
||||
@@ -39,6 +38,7 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
|
||||
const ModeSelector = memo(() => {
|
||||
const { t } = useTranslation('chat');
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const approvalMode = useUserStore(toolInterventionSelectors.approvalMode);
|
||||
const updateHumanIntervention = useUserStore((s) => s.updateHumanIntervention);
|
||||
|
||||
@@ -112,18 +112,33 @@ const ModeSelector = memo(() => {
|
||||
[approvalMode, modeLabels, handleModeChange, styles, t],
|
||||
);
|
||||
|
||||
const button = (
|
||||
<Button
|
||||
className={styles.modeButton}
|
||||
color={'default'}
|
||||
icon={ChevronDown}
|
||||
iconPlacement="end"
|
||||
size="small"
|
||||
variant={'text'}
|
||||
>
|
||||
{modeLabels[approvalMode]}
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<DropdownMenu items={menuItems} placement="bottomLeft">
|
||||
<Button
|
||||
className={styles.modeButton}
|
||||
color={'default'}
|
||||
icon={ChevronDown}
|
||||
iconPlacement="end"
|
||||
size="small"
|
||||
variant={'text'}
|
||||
>
|
||||
{modeLabels[approvalMode]}
|
||||
</Button>
|
||||
<DropdownMenu
|
||||
items={menuItems}
|
||||
open={dropdownOpen}
|
||||
placement="bottomLeft"
|
||||
onOpenChange={setDropdownOpen}
|
||||
>
|
||||
<div>
|
||||
{dropdownOpen ? (
|
||||
button
|
||||
) : (
|
||||
<Tooltip title={t('tool.intervention.approvalMode')}>{button}</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</DropdownMenu>
|
||||
);
|
||||
});
|
||||
+3
-3
@@ -27,7 +27,7 @@ const WorkingDirectoryContent = memo<WorkingDirectoryContentProps>(({ agentId, o
|
||||
const activeTopicId = useChatStore((s) => s.activeTopicId);
|
||||
|
||||
// Actions
|
||||
const updateAgentLocalSystemConfig = useAgentStore((s) => s.updateAgentLocalSystemConfigById);
|
||||
const updateAgentRuntimeEnvConfig = useAgentStore((s) => s.updateAgentRuntimeEnvConfigById);
|
||||
const updateTopicMetadata = useChatStore((s) => s.updateTopicMetadata);
|
||||
|
||||
// Local state for editing
|
||||
@@ -58,7 +58,7 @@ const WorkingDirectoryContent = memo<WorkingDirectoryContentProps>(({ agentId, o
|
||||
setLoading(true);
|
||||
try {
|
||||
// Save agent working directory
|
||||
await updateAgentLocalSystemConfig(agentId, {
|
||||
await updateAgentRuntimeEnvConfig(agentId, {
|
||||
workingDirectory: agentDir || undefined,
|
||||
});
|
||||
|
||||
@@ -85,7 +85,7 @@ const WorkingDirectoryContent = memo<WorkingDirectoryContentProps>(({ agentId, o
|
||||
useTopicOverride,
|
||||
topicDir,
|
||||
topicWorkingDirectory,
|
||||
updateAgentLocalSystemConfig,
|
||||
updateAgentRuntimeEnvConfig,
|
||||
updateTopicMetadata,
|
||||
onClose,
|
||||
]);
|
||||
@@ -0,0 +1,274 @@
|
||||
import { isDesktop } from '@lobechat/const';
|
||||
import { type RuntimeEnvMode } from '@lobechat/types';
|
||||
import { Flexbox, Icon, Popover, Skeleton, Tooltip } from '@lobehub/ui';
|
||||
import { createStaticStyles, cssVar, cx } from 'antd-style';
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
CloudIcon,
|
||||
FolderIcon,
|
||||
LaptopIcon,
|
||||
MonitorOffIcon,
|
||||
SquircleDashed,
|
||||
} from 'lucide-react';
|
||||
import { memo, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useAgentStore } from '@/store/agent';
|
||||
import { agentByIdSelectors, chatConfigByIdSelectors } from '@/store/agent/selectors';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import { topicSelectors } from '@/store/chat/selectors';
|
||||
|
||||
import { useAgentId } from '../hooks/useAgentId';
|
||||
import { useUpdateAgentConfig } from '../hooks/useUpdateAgentConfig';
|
||||
import ApprovalMode from './ApprovalMode';
|
||||
import WorkingDirectory from './WorkingDirectory';
|
||||
|
||||
const MODE_ICONS: Record<RuntimeEnvMode, typeof LaptopIcon> = {
|
||||
cloud: CloudIcon,
|
||||
local: LaptopIcon,
|
||||
none: MonitorOffIcon,
|
||||
};
|
||||
|
||||
const styles = createStaticStyles(({ css }) => ({
|
||||
bar: css`
|
||||
padding-block: 0;
|
||||
padding-inline: 4px;
|
||||
`,
|
||||
button: css`
|
||||
cursor: pointer;
|
||||
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
|
||||
height: 28px;
|
||||
padding-inline: 8px;
|
||||
border-radius: 6px;
|
||||
|
||||
font-size: 12px;
|
||||
color: ${cssVar.colorTextSecondary};
|
||||
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: ${cssVar.colorText};
|
||||
background: ${cssVar.colorFillSecondary};
|
||||
}
|
||||
`,
|
||||
modeDesc: css`
|
||||
font-size: 12px;
|
||||
color: ${cssVar.colorTextTertiary};
|
||||
`,
|
||||
modeOption: css`
|
||||
cursor: pointer;
|
||||
|
||||
width: 100%;
|
||||
padding-block: 8px;
|
||||
padding-inline: 8px;
|
||||
border-radius: ${cssVar.borderRadius};
|
||||
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: ${cssVar.colorFillTertiary};
|
||||
}
|
||||
`,
|
||||
modeOptionActive: css`
|
||||
background: ${cssVar.colorFillTertiary};
|
||||
`,
|
||||
modeOptionDesc: css`
|
||||
font-size: 12px;
|
||||
color: ${cssVar.colorTextDescription};
|
||||
`,
|
||||
modeOptionIcon: css`
|
||||
border: 1px solid ${cssVar.colorFillTertiary};
|
||||
border-radius: ${cssVar.borderRadius};
|
||||
background: ${cssVar.colorBgElevated};
|
||||
`,
|
||||
modeOptionTitle: css`
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: ${cssVar.colorText};
|
||||
`,
|
||||
}));
|
||||
|
||||
const RuntimeConfig = memo(() => {
|
||||
const { t } = useTranslation('chat');
|
||||
const { t: tPlugin } = useTranslation('plugin');
|
||||
const agentId = useAgentId();
|
||||
const { updateAgentChatConfig } = useUpdateAgentConfig();
|
||||
const [dirPopoverOpen, setDirPopoverOpen] = useState(false);
|
||||
const [modePopoverOpen, setModePopoverOpen] = useState(false);
|
||||
|
||||
const [isLoading, runtimeMode] = useAgentStore((s) => [
|
||||
agentByIdSelectors.isAgentConfigLoadingById(agentId)(s),
|
||||
chatConfigByIdSelectors.getRuntimeModeById(agentId)(s),
|
||||
]);
|
||||
|
||||
// Get working directory
|
||||
const topicWorkingDirectory = useChatStore(topicSelectors.currentTopicWorkingDirectory);
|
||||
const agentWorkingDirectory = useAgentStore((s) =>
|
||||
agentId ? agentByIdSelectors.getAgentWorkingDirectoryById(agentId)(s) : undefined,
|
||||
);
|
||||
const effectiveWorkingDirectory = topicWorkingDirectory || agentWorkingDirectory;
|
||||
|
||||
const switchMode = useCallback(
|
||||
async (mode: RuntimeEnvMode) => {
|
||||
if (mode === runtimeMode) return;
|
||||
|
||||
await updateAgentChatConfig({
|
||||
runtimeEnv: { runtimeMode: mode },
|
||||
});
|
||||
},
|
||||
[runtimeMode, updateAgentChatConfig],
|
||||
);
|
||||
|
||||
// Skeleton placeholder to prevent layout jump during loading
|
||||
if (!agentId || isLoading) {
|
||||
return (
|
||||
<Flexbox horizontal align={'center'} className={styles.bar} gap={4}>
|
||||
<Skeleton.Button active size="small" style={{ height: 22, minWidth: 64, width: 64 }} />
|
||||
<Skeleton.Button active size="small" style={{ height: 22, minWidth: 100, width: 100 }} />
|
||||
</Flexbox>
|
||||
);
|
||||
}
|
||||
|
||||
const ModeIcon = MODE_ICONS[runtimeMode];
|
||||
const modeLabel = t(`runtimeEnv.mode.${runtimeMode}`);
|
||||
|
||||
const displayName = effectiveWorkingDirectory
|
||||
? effectiveWorkingDirectory.split('/').findLast(Boolean) || effectiveWorkingDirectory
|
||||
: tPlugin('localSystem.workingDirectory.notSet');
|
||||
|
||||
const modes: { desc: string; icon: typeof LaptopIcon; label: string; mode: RuntimeEnvMode }[] = [
|
||||
// Local mode is desktop-only
|
||||
...(isDesktop
|
||||
? [
|
||||
{
|
||||
desc: t('runtimeEnv.mode.localDesc'),
|
||||
icon: LaptopIcon,
|
||||
label: t('runtimeEnv.mode.local'),
|
||||
mode: 'local' as RuntimeEnvMode,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
desc: t('runtimeEnv.mode.cloudDesc'),
|
||||
icon: CloudIcon,
|
||||
label: t('runtimeEnv.mode.cloud'),
|
||||
mode: 'cloud',
|
||||
},
|
||||
{
|
||||
desc: t('runtimeEnv.mode.noneDesc'),
|
||||
icon: MonitorOffIcon,
|
||||
label: t('runtimeEnv.mode.none'),
|
||||
mode: 'none',
|
||||
},
|
||||
];
|
||||
|
||||
const modeContent = (
|
||||
<Flexbox gap={4} style={{ minWidth: 280 }}>
|
||||
{modes.map(({ mode, icon, label, desc }) => (
|
||||
<Flexbox
|
||||
horizontal
|
||||
align={'flex-start'}
|
||||
className={cx(styles.modeOption, runtimeMode === mode && styles.modeOptionActive)}
|
||||
gap={12}
|
||||
key={mode}
|
||||
onClick={() => switchMode(mode)}
|
||||
>
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
className={styles.modeOptionIcon}
|
||||
flex={'none'}
|
||||
height={32}
|
||||
justify={'center'}
|
||||
width={32}
|
||||
>
|
||||
<Icon icon={icon} />
|
||||
</Flexbox>
|
||||
<Flexbox flex={1}>
|
||||
<div className={styles.modeOptionTitle}>{label}</div>
|
||||
<div className={styles.modeOptionDesc}>{desc}</div>
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
))}
|
||||
</Flexbox>
|
||||
);
|
||||
|
||||
const modeButton = (
|
||||
<div className={styles.button}>
|
||||
<Icon icon={ModeIcon} size={14} />
|
||||
<span>{modeLabel}</span>
|
||||
<Icon icon={ChevronDownIcon} size={12} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const dirButton = (
|
||||
<div className={styles.button}>
|
||||
<Icon icon={effectiveWorkingDirectory ? FolderIcon : SquircleDashed} size={14} />
|
||||
<span>{displayName}</span>
|
||||
<Icon icon={ChevronDownIcon} size={12} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const rightContent = () => {
|
||||
if (runtimeMode === 'local') {
|
||||
return (
|
||||
<Popover
|
||||
content={<WorkingDirectory agentId={agentId} onClose={() => setDirPopoverOpen(false)} />}
|
||||
open={dirPopoverOpen}
|
||||
placement="bottomRight"
|
||||
trigger="click"
|
||||
onOpenChange={setDirPopoverOpen}
|
||||
>
|
||||
<div>
|
||||
{dirPopoverOpen ? (
|
||||
dirButton
|
||||
) : (
|
||||
<Tooltip
|
||||
title={effectiveWorkingDirectory || tPlugin('localSystem.workingDirectory.notSet')}
|
||||
>
|
||||
{dirButton}
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<Flexbox horizontal align={'center'} className={styles.bar} justify={'space-between'}>
|
||||
{/* Left: Runtime env + working directory */}
|
||||
<Flexbox horizontal align={'center'} gap={4}>
|
||||
<Popover
|
||||
content={modeContent}
|
||||
open={modePopoverOpen}
|
||||
placement="top"
|
||||
styles={{ content: { padding: 4 } }}
|
||||
trigger="click"
|
||||
onOpenChange={setModePopoverOpen}
|
||||
>
|
||||
<div>
|
||||
{modePopoverOpen ? (
|
||||
modeButton
|
||||
) : (
|
||||
<Tooltip title={t('runtimeEnv.selectMode')}>{modeButton}</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</Popover>
|
||||
{rightContent()}
|
||||
</Flexbox>
|
||||
|
||||
{/* Right: Permission control */}
|
||||
<ApprovalMode />
|
||||
</Flexbox>
|
||||
);
|
||||
});
|
||||
|
||||
RuntimeConfig.displayName = 'RuntimeConfig';
|
||||
|
||||
export default RuntimeConfig;
|
||||
+1
-3
@@ -11,7 +11,6 @@ import { useConversationStore } from '../../../../../store';
|
||||
import Arguments from '../Arguments';
|
||||
import ApprovalActions from './ApprovalActions';
|
||||
import KeyValueEditor from './KeyValueEditor';
|
||||
import ModeSelector from './ModeSelector';
|
||||
|
||||
interface FallbackInterventionProps {
|
||||
apiName: string;
|
||||
@@ -77,8 +76,7 @@ const FallbackIntervention = memo<FallbackInterventionProps>(
|
||||
}
|
||||
/>
|
||||
|
||||
<Flexbox horizontal justify={'space-between'}>
|
||||
<ModeSelector />
|
||||
<Flexbox horizontal justify={'flex-end'}>
|
||||
<ApprovalActions
|
||||
apiName={apiName}
|
||||
approvalMode={approvalMode}
|
||||
|
||||
+2
-4
@@ -11,10 +11,9 @@ import Arguments from '../Arguments';
|
||||
import ApprovalActions from './ApprovalActions';
|
||||
import Fallback from './Fallback';
|
||||
import KeyValueEditor from './KeyValueEditor';
|
||||
import ModeSelector from './ModeSelector';
|
||||
import SecurityBlacklistWarning from './SecurityBlacklistWarning';
|
||||
|
||||
export type ApprovalMode = 'auto-run' | 'allow-list' | 'manual';
|
||||
export type { ApprovalMode } from '@/store/user/slices/settings/selectors';
|
||||
|
||||
interface InterventionProps {
|
||||
apiName: string;
|
||||
@@ -110,8 +109,7 @@ const Intervention = memo<InterventionProps>(
|
||||
registerBeforeApprove={registerBeforeApprove}
|
||||
onArgsChange={handleArgsChange}
|
||||
/>
|
||||
<Flexbox horizontal justify={'space-between'}>
|
||||
<ModeSelector />
|
||||
<Flexbox horizontal justify={'flex-end'}>
|
||||
<ApprovalActions
|
||||
apiName={apiName}
|
||||
approvalMode={approvalMode}
|
||||
|
||||
@@ -6,7 +6,6 @@ import { memo, Suspense } from 'react';
|
||||
|
||||
import AbortResponse from './AbortResponse';
|
||||
import Intervention from './Intervention';
|
||||
import ModeSelector from './Intervention/ModeSelector';
|
||||
import LoadingPlaceholder from './LoadingPlaceholder';
|
||||
import RejectedResponse from './RejectedResponse';
|
||||
import ToolRender from './Render';
|
||||
@@ -124,11 +123,6 @@ const Render = memo<RenderProps>(
|
||||
type: type as any,
|
||||
}}
|
||||
/>
|
||||
{!disableEditing && (
|
||||
<div>
|
||||
<ModeSelector />
|
||||
</div>
|
||||
)}
|
||||
</Flexbox>
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/**
|
||||
* Tools Engineering - Unified tools processing using ToolsEngine
|
||||
*/
|
||||
import { CloudSandboxManifest } from '@lobechat/builtin-tool-cloud-sandbox';
|
||||
import { KnowledgeBaseManifest } from '@lobechat/builtin-tool-knowledge-base';
|
||||
import { LocalSystemManifest } from '@lobechat/builtin-tool-local-system';
|
||||
import { MemoryManifest } from '@lobechat/builtin-tool-memory';
|
||||
import { WebBrowsingManifest } from '@lobechat/builtin-tool-web-browsing';
|
||||
import { defaultToolIds } from '@lobechat/builtin-tools';
|
||||
@@ -102,7 +104,10 @@ export const createAgentToolsEngine = (workingModel: WorkingModel) => {
|
||||
return undefined; // fall through to rules
|
||||
},
|
||||
rules: {
|
||||
[CloudSandboxManifest.identifier]:
|
||||
agentChatConfigSelectors.isCloudSandboxEnabled(agentState),
|
||||
[KnowledgeBaseManifest.identifier]: agentSelectors.hasEnabledKnowledgeBases(agentState),
|
||||
[LocalSystemManifest.identifier]: agentChatConfigSelectors.isLocalSystemEnabled(agentState),
|
||||
[MemoryManifest.identifier]: agentChatConfigSelectors.isMemoryToolEnabled(agentState),
|
||||
[WebBrowsingManifest.identifier]: searchConfig.useApplicationBuiltinSearchTool,
|
||||
},
|
||||
|
||||
@@ -273,6 +273,14 @@ export default {
|
||||
'memory.title': 'Memory',
|
||||
'search.grounding.imageSearchQueries': 'Image Search Keywords',
|
||||
'search.grounding.imageTitle': 'Found {{count}} images',
|
||||
'runtimeEnv.mode.cloud': 'Cloud Sandbox',
|
||||
'runtimeEnv.mode.cloudDesc': 'Run in a secure cloud sandbox',
|
||||
'runtimeEnv.mode.local': 'Local',
|
||||
'runtimeEnv.mode.localDesc': 'Access local files and commands',
|
||||
'runtimeEnv.mode.none': 'None',
|
||||
'runtimeEnv.mode.noneDesc': 'No runtime environment',
|
||||
'runtimeEnv.selectMode': 'Select Runtime Environment',
|
||||
'runtimeEnv.title': 'Runtime Environment',
|
||||
'search.grounding.searchQueries': 'Search Keywords',
|
||||
'search.grounding.title': 'Found {{count}} results',
|
||||
'search.mode.auto.desc': 'Search the web automatically when needed.',
|
||||
@@ -425,6 +433,7 @@ export default {
|
||||
'tokenTag.overload': 'Exceeded Limit',
|
||||
'tokenTag.remained': 'Remaining',
|
||||
'tokenTag.used': 'Used',
|
||||
'tool.intervention.approvalMode': 'Approval Mode',
|
||||
'tool.intervention.approve': 'Approve',
|
||||
'tool.intervention.approveAndRemember': 'Approve and Remember',
|
||||
'tool.intervention.approveOnce': 'Approve This Time Only',
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
import { LocalSystemManifest } from '@lobechat/builtin-tool-local-system';
|
||||
import { Flexbox, Icon, Popover, Tooltip } from '@lobehub/ui';
|
||||
import { createStaticStyles, cx } from 'antd-style';
|
||||
import { LaptopIcon, SquircleDashed } from 'lucide-react';
|
||||
import { memo, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useAgentStore } from '@/store/agent';
|
||||
import { agentByIdSelectors } from '@/store/agent/selectors';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import { topicSelectors } from '@/store/chat/selectors';
|
||||
|
||||
import WorkingDirectoryContent from './WorkingDirectoryContent';
|
||||
|
||||
const styles = createStaticStyles(({ css, cssVar }) => {
|
||||
return {
|
||||
base: css`
|
||||
border-radius: 6px;
|
||||
color: ${cssVar.colorTextTertiary};
|
||||
background-color: ${cssVar.colorFillTertiary};
|
||||
|
||||
:hover {
|
||||
color: ${cssVar.colorTextSecondary};
|
||||
background-color: ${cssVar.colorFillSecondary};
|
||||
}
|
||||
`,
|
||||
filled: css`
|
||||
font-family: ${cssVar.fontFamilyCode};
|
||||
color: ${cssVar.colorText} !important;
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
||||
const WorkingDirectory = memo(() => {
|
||||
const { t } = useTranslation('plugin');
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const agentId = useAgentStore((s) => s.activeAgentId);
|
||||
|
||||
// Check if local-system plugin is enabled for current agent
|
||||
const plugins = useAgentStore((s) =>
|
||||
agentId ? agentByIdSelectors.getAgentPluginsById(agentId)(s) : [],
|
||||
);
|
||||
const isLocalSystemEnabled = useMemo(
|
||||
() => plugins.includes(LocalSystemManifest.identifier),
|
||||
[plugins],
|
||||
);
|
||||
|
||||
// Get working directory from Topic (higher priority) or Agent (fallback)
|
||||
const topicWorkingDirectory = useChatStore(topicSelectors.currentTopicWorkingDirectory);
|
||||
const agentWorkingDirectory = useAgentStore((s) =>
|
||||
agentId ? agentByIdSelectors.getAgentWorkingDirectoryById(agentId)(s) : undefined,
|
||||
);
|
||||
|
||||
const effectiveWorkingDirectory = topicWorkingDirectory || agentWorkingDirectory;
|
||||
|
||||
// Only show when local-system is enabled and agent exists
|
||||
if (!agentId || !isLocalSystemEnabled) return null;
|
||||
|
||||
// Get last folder name for display
|
||||
const hasWorkingDirectory = !!effectiveWorkingDirectory;
|
||||
|
||||
const displayName = effectiveWorkingDirectory
|
||||
? effectiveWorkingDirectory.split('/').findLast(Boolean) || effectiveWorkingDirectory
|
||||
: t('localSystem.workingDirectory.notSet');
|
||||
|
||||
const content = hasWorkingDirectory ? (
|
||||
<Flexbox
|
||||
horizontal
|
||||
align="center"
|
||||
className={cx(styles.base, styles.filled)}
|
||||
gap={6}
|
||||
style={{ cursor: 'pointer', height: 32, padding: '0 12px' }}
|
||||
>
|
||||
<Icon icon={LaptopIcon} size={18} />
|
||||
<span>{displayName}</span>
|
||||
</Flexbox>
|
||||
) : (
|
||||
<Flexbox
|
||||
horizontal
|
||||
align="center"
|
||||
className={styles.base}
|
||||
gap={6}
|
||||
style={{ cursor: 'pointer', height: 32, padding: '0 12px' }}
|
||||
>
|
||||
<Icon icon={SquircleDashed} size={16} />
|
||||
<span>{t('localSystem.workingDirectory.notSet')}</span>
|
||||
</Flexbox>
|
||||
);
|
||||
return (
|
||||
<Popover
|
||||
content={<WorkingDirectoryContent agentId={agentId} onClose={() => setOpen(false)} />}
|
||||
open={open}
|
||||
placement="bottomRight"
|
||||
trigger="click"
|
||||
onOpenChange={setOpen}
|
||||
>
|
||||
<div>
|
||||
{open ? (
|
||||
content
|
||||
) : (
|
||||
<Tooltip title={effectiveWorkingDirectory || t('localSystem.workingDirectory.notSet')}>
|
||||
{content}
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
|
||||
WorkingDirectory.displayName = 'WorkingDirectory';
|
||||
|
||||
export default WorkingDirectory;
|
||||
@@ -1,6 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import { isDesktop } from '@lobechat/const';
|
||||
import { Flexbox } from '@lobehub/ui';
|
||||
import { cssVar } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
@@ -11,7 +10,6 @@ import HeaderActions from './HeaderActions';
|
||||
import NotebookButton from './NotebookButton';
|
||||
import ShareButton from './ShareButton';
|
||||
import Tags from './Tags';
|
||||
import WorkingDirectory from './WorkingDirectory';
|
||||
|
||||
const Header = memo(() => {
|
||||
return (
|
||||
@@ -23,7 +21,6 @@ const Header = memo(() => {
|
||||
}
|
||||
right={
|
||||
<Flexbox horizontal align={'center'} style={{ backgroundColor: cssVar.colorBgContainer }}>
|
||||
{isDesktop && <WorkingDirectory />}
|
||||
<NotebookButton />
|
||||
<ShareButton />
|
||||
<HeaderActions />
|
||||
|
||||
@@ -112,6 +112,7 @@ const InputArea = () => {
|
||||
dropdownPlacement="bottomLeft"
|
||||
extraActionItems={extraActionItems}
|
||||
inputContainerProps={inputContainerProps}
|
||||
showRuntimeConfig={false}
|
||||
/>
|
||||
</ChatInputProvider>
|
||||
</DragUploadZone>
|
||||
|
||||
@@ -16,7 +16,13 @@ const Home: FC = () => {
|
||||
return (
|
||||
<>
|
||||
{isHomeRoute && <PageTitle title="" />}
|
||||
<NavHeader right={<WideScreenButton />} />
|
||||
<NavHeader
|
||||
right={
|
||||
<Flexbox horizontal align="center">
|
||||
<WideScreenButton />
|
||||
</Flexbox>
|
||||
}
|
||||
/>
|
||||
<Flexbox height={'100%'} style={{ overflowY: 'auto', paddingBottom: '16vh' }} width={'100%'}>
|
||||
<WideScreenContainer>
|
||||
<HomeContent />
|
||||
|
||||
@@ -311,7 +311,7 @@ export class AgentRuntimeService {
|
||||
userMemory,
|
||||
userTimezone,
|
||||
webhookDelivery,
|
||||
workingDirectory: agentConfig?.chatConfig?.localSystem?.workingDirectory,
|
||||
workingDirectory: agentConfig?.chatConfig?.runtimeEnv?.workingDirectory,
|
||||
...appContext,
|
||||
},
|
||||
maxSteps,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { DEFAULT_PROVIDER } from '@lobechat/business-const';
|
||||
import { DEFAULT_MODEL, DEFAUTT_AGENT_TTS_CONFIG } from '@lobechat/const';
|
||||
import { type AgentBuilderContext } from '@lobechat/context-engine';
|
||||
import { type AgentMode, type LobeAgentTTSConfig, type LocalSystemConfig } from '@lobechat/types';
|
||||
import { type AgentMode, type LobeAgentTTSConfig, type RuntimeEnvConfig } from '@lobechat/types';
|
||||
|
||||
import { type AgentStoreState } from '../initialState';
|
||||
import { agentSelectors } from './selectors';
|
||||
@@ -74,13 +74,13 @@ const getAgentEnableModeById =
|
||||
};
|
||||
|
||||
/**
|
||||
* Get local system config by agentId
|
||||
* Now reads from chatConfig.localSystem
|
||||
* Get runtime env config by agentId
|
||||
* Now reads from chatConfig.runtimeEnv
|
||||
*/
|
||||
const getAgentLocalSystemConfigById =
|
||||
const getAgentRuntimeEnvConfigById =
|
||||
(agentId: string) =>
|
||||
(s: AgentStoreState): LocalSystemConfig | undefined =>
|
||||
agentSelectors.getAgentConfigById(agentId)(s)?.chatConfig?.localSystem;
|
||||
(s: AgentStoreState): RuntimeEnvConfig | undefined =>
|
||||
agentSelectors.getAgentConfigById(agentId)(s)?.chatConfig?.runtimeEnv;
|
||||
|
||||
/**
|
||||
* Get working directory by agentId
|
||||
@@ -88,7 +88,7 @@ const getAgentLocalSystemConfigById =
|
||||
const getAgentWorkingDirectoryById =
|
||||
(agentId: string) =>
|
||||
(s: AgentStoreState): string | undefined =>
|
||||
getAgentLocalSystemConfigById(agentId)(s)?.workingDirectory;
|
||||
getAgentRuntimeEnvConfigById(agentId)(s)?.workingDirectory;
|
||||
|
||||
/**
|
||||
* Get agent builder context by agentId
|
||||
@@ -128,7 +128,7 @@ export const agentByIdSelectors = {
|
||||
getAgentEnableModeById,
|
||||
getAgentFilesById,
|
||||
getAgentKnowledgeBasesById,
|
||||
getAgentLocalSystemConfigById,
|
||||
getAgentRuntimeEnvConfigById,
|
||||
getAgentModeById,
|
||||
getAgentModelById,
|
||||
getAgentModelProviderById,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DEFAULT_AGENT_CHAT_CONFIG, DEFAULT_AGENT_SEARCH_FC_MODEL } from '@lobechat/const';
|
||||
import { isContextCachingModel, isThinkingWithToolClaudeModel } from '@lobechat/model-runtime';
|
||||
import { type LobeAgentChatConfig } from '@lobechat/types';
|
||||
import { type LobeAgentChatConfig, type RuntimeEnvMode } from '@lobechat/types';
|
||||
|
||||
import { type AgentStoreState } from '@/store/agent/initialState';
|
||||
|
||||
@@ -63,15 +63,34 @@ const isMemoryToolEnabledById = (agentId: string) => (s: AgentStoreState) =>
|
||||
const getMemoryToolEffortById = (agentId: string) => (s: AgentStoreState) =>
|
||||
getChatConfigById(agentId)(s).memory?.effort ?? 'medium';
|
||||
|
||||
const getRuntimeEnvConfigById = (agentId: string) => (s: AgentStoreState) =>
|
||||
getChatConfigById(agentId)(s).runtimeEnv;
|
||||
|
||||
const isLocalSystemEnabledById = (agentId: string) => (s: AgentStoreState) =>
|
||||
getRuntimeModeById(agentId)(s) === 'local';
|
||||
|
||||
/**
|
||||
* Get runtime environment mode by agent ID.
|
||||
* Defaults to 'local' on desktop if not set.
|
||||
*/
|
||||
const getRuntimeModeById =
|
||||
(agentId: string) =>
|
||||
(s: AgentStoreState): RuntimeEnvMode => {
|
||||
return getChatConfigById(agentId)(s).runtimeEnv?.runtimeMode ?? 'local';
|
||||
};
|
||||
|
||||
export const chatConfigByIdSelectors = {
|
||||
getChatConfigById,
|
||||
getEnableHistoryCountById,
|
||||
getHistoryCountById,
|
||||
getRuntimeEnvConfigById,
|
||||
getMemoryToolConfigById,
|
||||
getMemoryToolEffortById,
|
||||
getRuntimeModeById,
|
||||
getSearchFCModelById,
|
||||
getSearchModeById,
|
||||
getUseModelBuiltinSearchById,
|
||||
isEnableSearchById,
|
||||
isLocalSystemEnabledById,
|
||||
isMemoryToolEnabledById,
|
||||
};
|
||||
|
||||
@@ -30,6 +30,12 @@ const historyCount = (s: AgentStoreState): number =>
|
||||
const isMemoryToolEnabled = (s: AgentStoreState) =>
|
||||
chatConfigByIdSelectors.isMemoryToolEnabledById(s.activeAgentId || '')(s);
|
||||
|
||||
const isLocalSystemEnabled = (s: AgentStoreState) =>
|
||||
chatConfigByIdSelectors.isLocalSystemEnabledById(s.activeAgentId || '')(s);
|
||||
|
||||
const isCloudSandboxEnabled = (s: AgentStoreState) =>
|
||||
chatConfigByIdSelectors.getRuntimeModeById(s.activeAgentId || '')(s) === 'cloud';
|
||||
|
||||
const enableHistoryDivider =
|
||||
(historyLength: number, currentIndex: number) => (s: AgentStoreState) => {
|
||||
const config = currentChatConfig(s);
|
||||
@@ -48,6 +54,8 @@ export const agentChatConfigSelectors = {
|
||||
enableHistoryDivider,
|
||||
historyCount,
|
||||
isAgentEnableSearch,
|
||||
isCloudSandboxEnabled,
|
||||
isLocalSystemEnabled,
|
||||
isMemoryToolEnabled,
|
||||
searchFCModel,
|
||||
useModelBuiltinSearch,
|
||||
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
type KnowledgeItem,
|
||||
type LobeAgentConfig,
|
||||
type LobeAgentTTSConfig,
|
||||
type LocalSystemConfig,
|
||||
type MetaData,
|
||||
type RuntimeEnvConfig,
|
||||
} from '@lobechat/types';
|
||||
import { KnowledgeType } from '@lobechat/types';
|
||||
import { VoiceList } from '@lobehub/tts';
|
||||
@@ -248,17 +248,17 @@ const currentAgentMode = (s: AgentStoreState): AgentMode | undefined => {
|
||||
const isAgentModeEnabled = (s: AgentStoreState): boolean => currentAgentMode(s) !== undefined;
|
||||
|
||||
/**
|
||||
* Get current agent's local system config
|
||||
* Now reads from chatConfig.localSystem
|
||||
* Get current agent's runtime env config
|
||||
* Now reads from chatConfig.runtimeEnv
|
||||
*/
|
||||
const currentAgentLocalSystemConfig = (s: AgentStoreState): LocalSystemConfig | undefined =>
|
||||
currentAgentConfig(s)?.chatConfig?.localSystem;
|
||||
const currentAgentRuntimeEnvConfig = (s: AgentStoreState): RuntimeEnvConfig | undefined =>
|
||||
currentAgentConfig(s)?.chatConfig?.runtimeEnv;
|
||||
|
||||
/**
|
||||
* Get current agent's working directory
|
||||
*/
|
||||
const currentAgentWorkingDirectory = (s: AgentStoreState): string | undefined =>
|
||||
currentAgentLocalSystemConfig(s)?.workingDirectory;
|
||||
currentAgentRuntimeEnvConfig(s)?.workingDirectory;
|
||||
|
||||
const isCurrentAgentExternal = (s: AgentStoreState): boolean => !currentAgentData(s)?.virtual;
|
||||
|
||||
@@ -269,7 +269,7 @@ export const agentSelectors = {
|
||||
currentAgentDescription,
|
||||
currentAgentFiles,
|
||||
currentAgentKnowledgeBases,
|
||||
currentAgentLocalSystemConfig,
|
||||
currentAgentRuntimeEnvConfig,
|
||||
currentAgentMeta,
|
||||
currentAgentMode,
|
||||
currentAgentModel,
|
||||
|
||||
@@ -15,7 +15,7 @@ import { userProfileSelectors } from '@/store/user/selectors';
|
||||
import {
|
||||
type LobeAgentChatConfig,
|
||||
type LobeAgentConfig,
|
||||
type LocalSystemConfig,
|
||||
type RuntimeEnvConfig,
|
||||
} from '@/types/agent';
|
||||
import { type MetaData } from '@/types/meta';
|
||||
import { merge } from '@/utils/merge';
|
||||
@@ -191,13 +191,13 @@ export class AgentSliceActionImpl {
|
||||
await this.#get().optimisticUpdateAgentConfig(agentId, config, controller.signal);
|
||||
};
|
||||
|
||||
updateAgentLocalSystemConfigById = async (
|
||||
updateAgentRuntimeEnvConfigById = async (
|
||||
agentId: string,
|
||||
config: Partial<LocalSystemConfig>,
|
||||
config: Partial<RuntimeEnvConfig>,
|
||||
): Promise<void> => {
|
||||
if (!agentId) return;
|
||||
|
||||
await this.#get().updateAgentChatConfigById(agentId, { localSystem: config });
|
||||
await this.#get().updateAgentChatConfigById(agentId, { runtimeEnv: config });
|
||||
};
|
||||
|
||||
updateAgentMeta = async (meta: Partial<MetaData>): Promise<void> => {
|
||||
|
||||
@@ -2,4 +2,4 @@ export { userGeneralSettingsSelectors } from './general';
|
||||
export { keyVaultsConfigSelectors } from './keyVaults';
|
||||
export { settingsSelectors } from './settings';
|
||||
export { systemAgentSelectors } from './systemAgent';
|
||||
export { toolInterventionSelectors } from './toolIntervention';
|
||||
export { type ApprovalMode, toolInterventionSelectors } from './toolIntervention';
|
||||
|
||||
@@ -5,11 +5,11 @@ import { currentSettings } from './settings';
|
||||
/**
|
||||
* User-selectable approval modes (excludes 'headless' which is for backend async tasks only)
|
||||
*/
|
||||
type UserApprovalMode = 'auto-run' | 'allow-list' | 'manual';
|
||||
export type ApprovalMode = 'auto-run' | 'allow-list' | 'manual';
|
||||
|
||||
const humanInterventionConfig = (s: UserStore) => currentSettings(s).tool?.humanIntervention || {};
|
||||
|
||||
const interventionApprovalMode = (s: UserStore): UserApprovalMode => {
|
||||
const interventionApprovalMode = (s: UserStore): ApprovalMode => {
|
||||
const mode = currentSettings(s).tool?.humanIntervention?.approvalMode;
|
||||
// Filter out 'headless' mode as it's not user-selectable (fallback to auto-run as similar behavior)
|
||||
if (mode === 'headless') return 'auto-run';
|
||||
|
||||
Reference in New Issue
Block a user