Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8faaa4b2be | |||
| 7f5bd942b3 | |||
| e254caf82d | |||
| 7efcd242d6 | |||
| 5d811d3949 | |||
| 8e6aaee10c |
@@ -234,7 +234,8 @@ pre-commit install
|
|||||||
- Group 7: 743746109
|
- Group 7: 743746109
|
||||||
- Group 8: 1030353265
|
- Group 8: 1030353265
|
||||||
|
|
||||||
- Developer Group: 975206796
|
- Developer Group(Chit-chat): 975206796
|
||||||
|
- Developer Group(Formal): 1039761811
|
||||||
|
|
||||||
### Discord Server
|
### Discord Server
|
||||||
|
|
||||||
|
|||||||
@@ -222,6 +222,7 @@ pre-commit install
|
|||||||
- Groupe 5 : 822130018
|
- Groupe 5 : 822130018
|
||||||
- Groupe 6 : 753075035
|
- Groupe 6 : 753075035
|
||||||
- Groupe développeurs : 975206796
|
- Groupe développeurs : 975206796
|
||||||
|
- Groupe développeurs (officiel) : 1039761811
|
||||||
|
|
||||||
### Serveur Discord
|
### Serveur Discord
|
||||||
|
|
||||||
|
|||||||
@@ -223,6 +223,7 @@ pre-commit install
|
|||||||
- 5群: 822130018
|
- 5群: 822130018
|
||||||
- 6群: 753075035
|
- 6群: 753075035
|
||||||
- 開発者群: 975206796
|
- 開発者群: 975206796
|
||||||
|
- 開発者群(正式): 1039761811
|
||||||
|
|
||||||
### Discord サーバー
|
### Discord サーバー
|
||||||
|
|
||||||
|
|||||||
@@ -222,6 +222,7 @@ pre-commit install
|
|||||||
- Группа 5: 822130018
|
- Группа 5: 822130018
|
||||||
- Группа 6: 753075035
|
- Группа 6: 753075035
|
||||||
- Группа разработчиков: 975206796
|
- Группа разработчиков: 975206796
|
||||||
|
- Группа разработчиков (официальная): 1039761811
|
||||||
|
|
||||||
### Сервер Discord
|
### Сервер Discord
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -225,7 +225,8 @@ pre-commit install
|
|||||||
- 6 群:753075035
|
- 6 群:753075035
|
||||||
- 7 群:743746109
|
- 7 群:743746109
|
||||||
- 8 群:1030353265
|
- 8 群:1030353265
|
||||||
- 開發者群:975206796
|
- 開發者群(闲聊吹水):975206796
|
||||||
|
- 開發者群(正式):1039761811
|
||||||
|
|
||||||
### Discord 群組
|
### Discord 群組
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -226,7 +226,8 @@ pre-commit install
|
|||||||
- 6 群:753075035
|
- 6 群:753075035
|
||||||
- 7 群:743746109
|
- 7 群:743746109
|
||||||
- 8 群:1030353265
|
- 8 群:1030353265
|
||||||
- 开发者群:975206796
|
- 开发者群(偏闲聊吹水):975206796
|
||||||
|
- 开发者群(正式):1039761811
|
||||||
|
|
||||||
### Discord 频道
|
### Discord 频道
|
||||||
|
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ class SendMessageToUserTool(FunctionTool[AstrAgentContext]):
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": (
|
"description": (
|
||||||
"Component type. One of: "
|
"Component type. One of: "
|
||||||
"plain, image, record, file, mention_user"
|
"plain, image, record, video, file, mention_user. Record is voice message."
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
"text": {
|
"text": {
|
||||||
@@ -320,6 +320,19 @@ class SendMessageToUserTool(FunctionTool[AstrAgentContext]):
|
|||||||
components.append(Comp.Record.fromURL(url=url))
|
components.append(Comp.Record.fromURL(url=url))
|
||||||
else:
|
else:
|
||||||
return f"error: messages[{idx}] must include path or url for record component."
|
return f"error: messages[{idx}] must include path or url for record component."
|
||||||
|
elif msg_type == "video":
|
||||||
|
path = msg.get("path")
|
||||||
|
url = msg.get("url")
|
||||||
|
if path:
|
||||||
|
(
|
||||||
|
local_path,
|
||||||
|
file_from_sandbox,
|
||||||
|
) = await self._resolve_path_from_sandbox(context, path)
|
||||||
|
components.append(Comp.Video.fromFileSystem(path=local_path))
|
||||||
|
elif url:
|
||||||
|
components.append(Comp.Video.fromURL(url=url))
|
||||||
|
else:
|
||||||
|
return f"error: messages[{idx}] must include path or url for video component."
|
||||||
elif msg_type == "file":
|
elif msg_type == "file":
|
||||||
path = msg.get("path")
|
path = msg.get("path")
|
||||||
url = msg.get("url")
|
url = msg.get("url")
|
||||||
|
|||||||
@@ -422,6 +422,12 @@ async def get_booter(
|
|||||||
) -> ComputerBooter:
|
) -> ComputerBooter:
|
||||||
config = context.get_config(umo=session_id)
|
config = context.get_config(umo=session_id)
|
||||||
|
|
||||||
|
runtime = config.get("provider_settings", {}).get("computer_use_runtime", "local")
|
||||||
|
if runtime == "local":
|
||||||
|
return get_local_booter()
|
||||||
|
elif runtime == "none":
|
||||||
|
raise RuntimeError("Sandbox runtime is disabled by configuration.")
|
||||||
|
|
||||||
sandbox_cfg = config.get("provider_settings", {}).get("sandbox", {})
|
sandbox_cfg = config.get("provider_settings", {}).get("sandbox", {})
|
||||||
booter_type = sandbox_cfg.get("booter", "shipyard_neo")
|
booter_type = sandbox_cfg.get("booter", "shipyard_neo")
|
||||||
|
|
||||||
|
|||||||
@@ -219,6 +219,9 @@ DEFAULT_CONFIG = {
|
|||||||
"telegram": {
|
"telegram": {
|
||||||
"pre_ack_emoji": {"enable": False, "emojis": ["✍️"]},
|
"pre_ack_emoji": {"enable": False, "emojis": ["✍️"]},
|
||||||
},
|
},
|
||||||
|
"discord": {
|
||||||
|
"pre_ack_emoji": {"enable": False, "emojis": ["🤔"]},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"wake_prefix": ["/"],
|
"wake_prefix": ["/"],
|
||||||
"log_level": "INFO",
|
"log_level": "INFO",
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ class SessionManagementRoute(Route):
|
|||||||
"/session/group/create": ("POST", self.create_group),
|
"/session/group/create": ("POST", self.create_group),
|
||||||
"/session/group/update": ("POST", self.update_group),
|
"/session/group/update": ("POST", self.update_group),
|
||||||
"/session/group/delete": ("POST", self.delete_group),
|
"/session/group/delete": ("POST", self.delete_group),
|
||||||
"/session/group/update-config": ("POST", self.update_group_config),
|
|
||||||
}
|
}
|
||||||
self.conv_mgr = core_lifecycle.conversation_manager
|
self.conv_mgr = core_lifecycle.conversation_manager
|
||||||
self.core_lifecycle = core_lifecycle
|
self.core_lifecycle = core_lifecycle
|
||||||
@@ -146,20 +145,9 @@ class SessionManagementRoute(Route):
|
|||||||
page=page, page_size=page_size, search=search
|
page=page, page_size=page_size, search=search
|
||||||
)
|
)
|
||||||
|
|
||||||
# 收集属于有配置分组的 UMO,避免重复显示
|
# 构建规则列表
|
||||||
grouped_umos = set()
|
|
||||||
groups = self._get_groups()
|
|
||||||
for group_data in groups.values():
|
|
||||||
if group_data.get("config"):
|
|
||||||
grouped_umos.update(group_data.get("umos", []))
|
|
||||||
|
|
||||||
# 构建规则列表(排除已被分组管理的 UMO)
|
|
||||||
rules_list = []
|
rules_list = []
|
||||||
filtered_count = 0
|
|
||||||
for umo, rules in umo_rules.items():
|
for umo, rules in umo_rules.items():
|
||||||
if umo in grouped_umos:
|
|
||||||
filtered_count += 1
|
|
||||||
continue
|
|
||||||
rule_info = {
|
rule_info = {
|
||||||
"umo": umo,
|
"umo": umo,
|
||||||
"rules": rules,
|
"rules": rules,
|
||||||
@@ -171,7 +159,6 @@ class SessionManagementRoute(Route):
|
|||||||
rule_info["message_type"] = parts[1]
|
rule_info["message_type"] = parts[1]
|
||||||
rule_info["session_id"] = parts[2]
|
rule_info["session_id"] = parts[2]
|
||||||
rules_list.append(rule_info)
|
rules_list.append(rule_info)
|
||||||
total -= filtered_count
|
|
||||||
|
|
||||||
# 获取可用的 providers 和 personas
|
# 获取可用的 providers 和 personas
|
||||||
provider_manager = self.core_lifecycle.provider_manager
|
provider_manager = self.core_lifecycle.provider_manager
|
||||||
@@ -253,7 +240,6 @@ class SessionManagementRoute(Route):
|
|||||||
"available_plugins": available_plugins,
|
"available_plugins": available_plugins,
|
||||||
"available_kbs": available_kbs,
|
"available_kbs": available_kbs,
|
||||||
"available_rule_keys": AVAILABLE_SESSION_RULE_KEYS,
|
"available_rule_keys": AVAILABLE_SESSION_RULE_KEYS,
|
||||||
"group_rules": self._get_group_rules(),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.__dict__
|
.__dict__
|
||||||
@@ -807,51 +793,6 @@ class SessionManagementRoute(Route):
|
|||||||
"""保存分组"""
|
"""保存分组"""
|
||||||
sp.put("session_groups", groups)
|
sp.put("session_groups", groups)
|
||||||
|
|
||||||
def _get_group_rules(self) -> list:
|
|
||||||
"""获取有配置的分组列表,用于在规则列表中显示"""
|
|
||||||
groups = self._get_groups()
|
|
||||||
group_rules = []
|
|
||||||
for group_id, group_data in groups.items():
|
|
||||||
config = group_data.get("config", {})
|
|
||||||
if config: # 只返回有配置的分组
|
|
||||||
group_rules.append(
|
|
||||||
{
|
|
||||||
"group_id": group_id,
|
|
||||||
"name": group_data.get("name", ""),
|
|
||||||
"umo_count": len(group_data.get("umos", [])),
|
|
||||||
"config": config,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return group_rules
|
|
||||||
|
|
||||||
async def _sync_group_config_to_umos(
|
|
||||||
self, config: dict, umos: list[str]
|
|
||||||
) -> tuple[int, list[str]]:
|
|
||||||
"""将分组配置同步到指定的 UMO 列表
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
(success_count, failed_umos)
|
|
||||||
"""
|
|
||||||
success_count = 0
|
|
||||||
failed_umos = []
|
|
||||||
for umo in umos:
|
|
||||||
try:
|
|
||||||
for rule_key, rule_value in config.items():
|
|
||||||
if rule_key not in AVAILABLE_SESSION_RULE_KEYS:
|
|
||||||
continue
|
|
||||||
if rule_value is None:
|
|
||||||
continue
|
|
||||||
if rule_key == "session_plugin_config":
|
|
||||||
# session_plugin_config 需要包裹 umo key
|
|
||||||
await sp.session_put(umo, rule_key, {umo: rule_value})
|
|
||||||
else:
|
|
||||||
await sp.session_put(umo, rule_key, rule_value)
|
|
||||||
success_count += 1
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"同步配置到 {umo} 失败: {e!s}")
|
|
||||||
failed_umos.append(umo)
|
|
||||||
return success_count, failed_umos
|
|
||||||
|
|
||||||
async def list_groups(self):
|
async def list_groups(self):
|
||||||
"""获取所有分组列表"""
|
"""获取所有分组列表"""
|
||||||
try:
|
try:
|
||||||
@@ -865,7 +806,6 @@ class SessionManagementRoute(Route):
|
|||||||
"name": group_data.get("name", ""),
|
"name": group_data.get("name", ""),
|
||||||
"umos": group_data.get("umos", []),
|
"umos": group_data.get("umos", []),
|
||||||
"umo_count": len(group_data.get("umos", [])),
|
"umo_count": len(group_data.get("umos", [])),
|
||||||
"config": group_data.get("config", {}),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return Response().ok({"groups": groups_list}).__dict__
|
return Response().ok({"groups": groups_list}).__dict__
|
||||||
@@ -935,7 +875,6 @@ class SessionManagementRoute(Route):
|
|||||||
return Response().error(f"分组 '{group_id}' 不存在").__dict__
|
return Response().error(f"分组 '{group_id}' 不存在").__dict__
|
||||||
|
|
||||||
group = groups[group_id]
|
group = groups[group_id]
|
||||||
old_umos = set(group.get("umos", []))
|
|
||||||
|
|
||||||
# 更新名称
|
# 更新名称
|
||||||
if name is not None:
|
if name is not None:
|
||||||
@@ -944,7 +883,6 @@ class SessionManagementRoute(Route):
|
|||||||
# 直接设置 umos 列表
|
# 直接设置 umos 列表
|
||||||
if umos is not None:
|
if umos is not None:
|
||||||
group["umos"] = umos
|
group["umos"] = umos
|
||||||
new_umos = set(umos)
|
|
||||||
else:
|
else:
|
||||||
# 增量更新
|
# 增量更新
|
||||||
current_umos = set(group.get("umos", []))
|
current_umos = set(group.get("umos", []))
|
||||||
@@ -953,21 +891,9 @@ class SessionManagementRoute(Route):
|
|||||||
if remove_umos:
|
if remove_umos:
|
||||||
current_umos.difference_update(remove_umos)
|
current_umos.difference_update(remove_umos)
|
||||||
group["umos"] = list(current_umos)
|
group["umos"] = list(current_umos)
|
||||||
new_umos = current_umos
|
|
||||||
|
|
||||||
self._save_groups(groups)
|
self._save_groups(groups)
|
||||||
|
|
||||||
# 自动同步分组配置给新加入的成员
|
|
||||||
group_config = group.get("config", {})
|
|
||||||
newly_added = new_umos - old_umos
|
|
||||||
if group_config and newly_added:
|
|
||||||
sync_count, _ = await self._sync_group_config_to_umos(
|
|
||||||
group_config, list(newly_added)
|
|
||||||
)
|
|
||||||
logger.info(
|
|
||||||
f"自动同步分组 '{group['name']}' 配置到 {sync_count} 个新成员"
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
Response()
|
Response()
|
||||||
.ok(
|
.ok(
|
||||||
@@ -1010,81 +936,3 @@ class SessionManagementRoute(Route):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"删除分组失败: {e!s}")
|
logger.error(f"删除分组失败: {e!s}")
|
||||||
return Response().error(f"删除分组失败: {e!s}").__dict__
|
return Response().error(f"删除分组失败: {e!s}").__dict__
|
||||||
|
|
||||||
async def update_group_config(self):
|
|
||||||
"""更新分组的配置,并同步到所有成员 UMO
|
|
||||||
|
|
||||||
请求体:
|
|
||||||
{
|
|
||||||
"group_id": "分组ID",
|
|
||||||
"config": {
|
|
||||||
"session_service_config": {...},
|
|
||||||
"session_plugin_config": {...},
|
|
||||||
"kb_config": {...},
|
|
||||||
"provider_perf_chat_completion": ...,
|
|
||||||
"provider_perf_speech_to_text": ...,
|
|
||||||
"provider_perf_text_to_speech": ...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
data = await request.get_json()
|
|
||||||
group_id = data.get("group_id")
|
|
||||||
config = data.get("config", {})
|
|
||||||
|
|
||||||
if not group_id:
|
|
||||||
return Response().error("缺少必要参数: group_id").__dict__
|
|
||||||
|
|
||||||
groups = self._get_groups()
|
|
||||||
|
|
||||||
if group_id not in groups:
|
|
||||||
return Response().error(f"分组 '{group_id}' 不存在").__dict__
|
|
||||||
|
|
||||||
group = groups[group_id]
|
|
||||||
|
|
||||||
# 保存配置到分组
|
|
||||||
group["config"] = config
|
|
||||||
self._save_groups(groups)
|
|
||||||
|
|
||||||
# 同步到所有成员 UMO
|
|
||||||
umos = group.get("umos", [])
|
|
||||||
|
|
||||||
if not config:
|
|
||||||
# 空配置 → 清除成员上的所有分组下发规则
|
|
||||||
success_count = 0
|
|
||||||
failed_umos = []
|
|
||||||
for umo in umos:
|
|
||||||
try:
|
|
||||||
for rule_key in AVAILABLE_SESSION_RULE_KEYS:
|
|
||||||
try:
|
|
||||||
await sp.session_remove(umo, rule_key)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
success_count += 1
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"清除 {umo} 规则失败: {e!s}")
|
|
||||||
failed_umos.append(umo)
|
|
||||||
else:
|
|
||||||
success_count, failed_umos = await self._sync_group_config_to_umos(
|
|
||||||
config, umos
|
|
||||||
)
|
|
||||||
|
|
||||||
msg = f"分组 '{group['name']}' 配置已保存并同步到 {success_count}/{len(umos)} 个会话"
|
|
||||||
if failed_umos:
|
|
||||||
msg += f",{len(failed_umos)} 个失败"
|
|
||||||
|
|
||||||
return (
|
|
||||||
Response()
|
|
||||||
.ok(
|
|
||||||
{
|
|
||||||
"message": msg,
|
|
||||||
"success_count": success_count,
|
|
||||||
"failed_count": len(failed_umos),
|
|
||||||
"failed_umos": failed_umos,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.__dict__
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"更新分组配置失败: {e!s}")
|
|
||||||
return Response().error(f"更新分组配置失败: {e!s}").__dict__
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useModuleI18n } from '@/i18n/composables';
|
import { useModuleI18n } from '@/i18n/composables';
|
||||||
|
import { normalizeTextInput } from '@/utils/inputValue';
|
||||||
|
|
||||||
const { tm } = useModuleI18n('features/command');
|
const { tm } = useModuleI18n('features/command');
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ const statusItems = [
|
|||||||
{ title: tm('filters.disabled'), value: 'disabled' },
|
{ title: tm('filters.disabled'), value: 'disabled' },
|
||||||
{ title: tm('filters.conflict'), value: 'conflict' }
|
{ title: tm('filters.conflict'), value: 'conflict' }
|
||||||
];
|
];
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -108,10 +110,11 @@ const statusItems = [
|
|||||||
<div style="min-width: 200px; max-width: 350px; flex: 1; border: 1px solid #B9B9B9; border-radius: 16px;">
|
<div style="min-width: 200px; max-width: 350px; flex: 1; border: 1px solid #B9B9B9; border-radius: 16px;">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
:model-value="searchQuery"
|
:model-value="searchQuery"
|
||||||
@update:model-value="emit('update:searchQuery', $event)"
|
@update:model-value="emit('update:searchQuery', normalizeTextInput($event))"
|
||||||
density="compact"
|
density="compact"
|
||||||
:label="tm('search.placeholder')"
|
:label="tm('search.placeholder')"
|
||||||
prepend-inner-icon="mdi-magnify"
|
prepend-inner-icon="mdi-magnify"
|
||||||
|
clearable
|
||||||
variant="solo-filled"
|
variant="solo-filled"
|
||||||
flat
|
flat
|
||||||
hide-details
|
hide-details
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
import { ref, computed, type Ref } from 'vue';
|
import { ref, computed, type Ref } from 'vue';
|
||||||
import type { CommandItem, FilterState } from '../types';
|
import type { CommandItem, FilterState } from '../types';
|
||||||
|
import { normalizeTextInput } from '@/utils/inputValue';
|
||||||
|
|
||||||
export function useCommandFilters(commands: Ref<CommandItem[]>) {
|
export function useCommandFilters(commands: Ref<CommandItem[]>) {
|
||||||
// 过滤状态
|
// 过滤状态
|
||||||
@@ -95,7 +96,7 @@ export function useCommandFilters(commands: Ref<CommandItem[]>) {
|
|||||||
* 过滤后的指令列表(支持层级结构)
|
* 过滤后的指令列表(支持层级结构)
|
||||||
*/
|
*/
|
||||||
const filteredCommands = computed(() => {
|
const filteredCommands = computed(() => {
|
||||||
const query = searchQuery.value.toLowerCase();
|
const query = normalizeTextInput(searchQuery.value).toLowerCase();
|
||||||
const conflictCmds: CommandItem[] = [];
|
const conflictCmds: CommandItem[] = [];
|
||||||
const normalCmds: CommandItem[] = [];
|
const normalCmds: CommandItem[] = [];
|
||||||
|
|
||||||
@@ -184,4 +185,3 @@ export function useCommandFilters(commands: Ref<CommandItem[]>) {
|
|||||||
isGroupExpanded
|
isGroupExpanded
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
import { computed, onActivated, onMounted, ref, watch} from 'vue';
|
import { computed, onActivated, onMounted, ref, watch} from 'vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useModuleI18n } from '@/i18n/composables';
|
import { useModuleI18n } from '@/i18n/composables';
|
||||||
|
import { normalizeTextInput } from '@/utils/inputValue';
|
||||||
|
|
||||||
// Composables
|
// Composables
|
||||||
import { useComponentData } from './composables/useComponentData';
|
import { useComponentData } from './composables/useComponentData';
|
||||||
@@ -83,7 +84,7 @@ const {
|
|||||||
} = useCommandActions(toast, () => fetchCommands(tm('messages.loadFailed')));
|
} = useCommandActions(toast, () => fetchCommands(tm('messages.loadFailed')));
|
||||||
|
|
||||||
const filteredTools = computed(() => {
|
const filteredTools = computed(() => {
|
||||||
const query = toolSearch.value.trim().toLowerCase();
|
const query = normalizeTextInput(toolSearch.value).trim().toLowerCase();
|
||||||
if (!query) return tools.value;
|
if (!query) return tools.value;
|
||||||
return tools.value.filter(tool =>
|
return tools.value.filter(tool =>
|
||||||
tool.name?.toLowerCase().includes(query) ||
|
tool.name?.toLowerCase().includes(query) ||
|
||||||
@@ -253,7 +254,8 @@ watch(viewMode, async (mode) => {
|
|||||||
<div class="d-flex flex-wrap align-center ga-3 mb-4">
|
<div class="d-flex flex-wrap align-center ga-3 mb-4">
|
||||||
<div style="min-width: 240px; max-width: 380px; flex: 1;">
|
<div style="min-width: 240px; max-width: 380px; flex: 1;">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="toolSearch"
|
:model-value="toolSearch"
|
||||||
|
@update:model-value="toolSearch = normalizeTextInput($event)"
|
||||||
prepend-inner-icon="mdi-magnify"
|
prepend-inner-icon="mdi-magnify"
|
||||||
:label="tmTool('functionTools.search')"
|
:label="tmTool('functionTools.search')"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
v-model="modelSearchProxy"
|
v-model="modelSearchProxy"
|
||||||
density="compact"
|
density="compact"
|
||||||
prepend-inner-icon="mdi-magnify"
|
prepend-inner-icon="mdi-magnify"
|
||||||
|
clearable
|
||||||
hide-details
|
hide-details
|
||||||
variant="solo-filled"
|
variant="solo-filled"
|
||||||
flat
|
flat
|
||||||
@@ -161,6 +162,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
import { normalizeTextInput } from '@/utils/inputValue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
entries: {
|
entries: {
|
||||||
@@ -222,7 +224,7 @@ const emit = defineEmits([
|
|||||||
|
|
||||||
const modelSearchProxy = computed({
|
const modelSearchProxy = computed({
|
||||||
get: () => props.modelSearch,
|
get: () => props.modelSearch,
|
||||||
set: (val) => emit('update:modelSearch', val)
|
set: (val) => emit('update:modelSearch', normalizeTextInput(val))
|
||||||
})
|
})
|
||||||
|
|
||||||
const isProviderTesting = (providerId) => props.testingProviders.includes(providerId)
|
const isProviderTesting = (providerId) => props.testingProviders.includes(providerId)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { ref, computed, onMounted, nextTick, watch } from 'vue'
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { getProviderIcon } from '@/utils/providerUtils'
|
import { getProviderIcon } from '@/utils/providerUtils'
|
||||||
import { askForConfirmation as askForConfirmationDialog, useConfirmDialog } from '@/utils/confirmDialog'
|
import { askForConfirmation as askForConfirmationDialog, useConfirmDialog } from '@/utils/confirmDialog'
|
||||||
|
import { normalizeTextInput } from '@/utils/inputValue'
|
||||||
|
|
||||||
export interface UseProviderSourcesOptions {
|
export interface UseProviderSourcesOptions {
|
||||||
defaultTab?: string
|
defaultTab?: string
|
||||||
@@ -157,7 +158,7 @@ export function useProviderSources(options: UseProviderSourcesOptions) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const filteredMergedModelEntries = computed(() => {
|
const filteredMergedModelEntries = computed(() => {
|
||||||
const term = modelSearch.value.trim().toLowerCase()
|
const term = normalizeTextInput(modelSearch.value).trim().toLowerCase()
|
||||||
if (!term) return mergedModelEntries.value
|
if (!term) return mergedModelEntries.value
|
||||||
|
|
||||||
return mergedModelEntries.value.filter((entry: any) => {
|
return mergedModelEntries.value.filter((entry: any) => {
|
||||||
|
|||||||
@@ -873,7 +873,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"regex": {
|
"regex": {
|
||||||
"description": "Segmentation Regular Expression"
|
"description": "Segmentation Regular Expression",
|
||||||
|
"hint": "Used to identify split points with a regular expression. Prefer patterns that match separators."
|
||||||
},
|
},
|
||||||
"split_words": {
|
"split_words": {
|
||||||
"description": "Split Word List",
|
"description": "Split Word List",
|
||||||
|
|||||||
@@ -876,7 +876,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"regex": {
|
"regex": {
|
||||||
"description": "分段正则表达式"
|
"description": "分段正则表达式",
|
||||||
|
"hint": "用于按正则规则识别分段点。建议使用能匹配分隔符的表达式。"
|
||||||
},
|
},
|
||||||
"split_words": {
|
"split_words": {
|
||||||
"description": "分段词列表",
|
"description": "分段词列表",
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export const normalizeTextInput = (value: unknown): string =>
|
||||||
|
typeof value === 'string' ? value : '';
|
||||||
@@ -13,9 +13,11 @@
|
|||||||
</v-select>
|
</v-select>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
class="config-search-input"
|
class="config-search-input"
|
||||||
v-model="configSearchKeyword"
|
:model-value="configSearchKeyword"
|
||||||
|
@update:model-value="onConfigSearchInput"
|
||||||
prepend-inner-icon="mdi-magnify"
|
prepend-inner-icon="mdi-magnify"
|
||||||
:label="tm('search.placeholder')"
|
:label="tm('search.placeholder')"
|
||||||
|
clearable
|
||||||
hide-details
|
hide-details
|
||||||
density="compact"
|
density="compact"
|
||||||
rounded="md"
|
rounded="md"
|
||||||
@@ -211,6 +213,7 @@ import {
|
|||||||
useConfirmDialog
|
useConfirmDialog
|
||||||
} from '@/utils/confirmDialog';
|
} from '@/utils/confirmDialog';
|
||||||
import UnsavedChangesConfirmDialog from '@/components/config/UnsavedChangesConfirmDialog.vue';
|
import UnsavedChangesConfirmDialog from '@/components/config/UnsavedChangesConfirmDialog.vue';
|
||||||
|
import { normalizeTextInput } from '@/utils/inputValue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ConfigPage',
|
name: 'ConfigPage',
|
||||||
@@ -419,6 +422,9 @@ export default {
|
|||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
onConfigSearchInput(value) {
|
||||||
|
this.configSearchKeyword = normalizeTextInput(value);
|
||||||
|
},
|
||||||
extractConfigTypeFromHash(hash) {
|
extractConfigTypeFromHash(hash) {
|
||||||
const rawHash = String(hash || '');
|
const rawHash = String(hash || '');
|
||||||
const lastHashIndex = rawHash.lastIndexOf('#');
|
const lastHashIndex = rawHash.lastIndexOf('#');
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="session-management-page">
|
<div class="session-management-page">
|
||||||
<v-container fluid class="pa-0">
|
<v-container fluid class="pa-0">
|
||||||
<v-card flat>
|
<v-card flat>
|
||||||
@@ -35,16 +35,7 @@
|
|||||||
<!-- UMO 信息 -->
|
<!-- UMO 信息 -->
|
||||||
<template v-slot:item.umo_info="{ item }">
|
<template v-slot:item.umo_info="{ item }">
|
||||||
<div>
|
<div>
|
||||||
<div class="d-flex align-center" v-if="item.isGroup">
|
<div class="d-flex align-center">
|
||||||
<v-chip size="x-small" color="deep-purple" variant="flat" class="mr-2">
|
|
||||||
分组
|
|
||||||
</v-chip>
|
|
||||||
<span class="font-weight-medium">{{ item.groupName }}</span>
|
|
||||||
<v-chip size="x-small" variant="outlined" class="ml-2">
|
|
||||||
{{ item.umo_count }} 个会话
|
|
||||||
</v-chip>
|
|
||||||
</div>
|
|
||||||
<div class="d-flex align-center" v-else>
|
|
||||||
<v-chip size="x-small" :color="getPlatformColor(item.platform)" class="mr-2">
|
<v-chip size="x-small" :color="getPlatformColor(item.platform)" class="mr-2">
|
||||||
{{ item.platform || 'unknown' }}
|
{{ item.platform || 'unknown' }}
|
||||||
</v-chip>
|
</v-chip>
|
||||||
@@ -291,24 +282,14 @@
|
|||||||
{{ tm('addRule.description') }}
|
{{ tm('addRule.description') }}
|
||||||
</v-alert>
|
</v-alert>
|
||||||
|
|
||||||
<v-radio-group v-model="addRuleTargetType" inline hide-details class="mb-4">
|
<v-autocomplete v-model="selectedNewUmo" :items="availableUmos" :loading="loadingUmos"
|
||||||
<v-radio label="单个会话" value="session"></v-radio>
|
|
||||||
<v-radio label="分组" value="group" :disabled="groups.length === 0"></v-radio>
|
|
||||||
</v-radio-group>
|
|
||||||
|
|
||||||
<v-autocomplete v-if="addRuleTargetType === 'session'" v-model="selectedNewUmo" :items="availableUmos" :loading="loadingUmos"
|
|
||||||
:label="tm('addRule.selectUmo')" variant="outlined" clearable :no-data-text="tm('addRule.noUmos')" />
|
:label="tm('addRule.selectUmo')" variant="outlined" clearable :no-data-text="tm('addRule.noUmos')" />
|
||||||
|
|
||||||
<v-select v-if="addRuleTargetType === 'group'" v-model="selectedGroup" :items="groupSelectOptions"
|
|
||||||
item-title="label" item-value="value" return-object
|
|
||||||
label="选择分组" variant="outlined" clearable
|
|
||||||
:no-data-text="'暂无分组,请先创建分组'" />
|
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
<v-card-actions class="px-4 pb-4">
|
<v-card-actions class="px-4 pb-4">
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn variant="text" @click="addRuleDialog = false">{{ tm('buttons.cancel') }}</v-btn>
|
<v-btn variant="text" @click="addRuleDialog = false">{{ tm('buttons.cancel') }}</v-btn>
|
||||||
<v-btn color="primary" variant="tonal" @click="createNewRule" :disabled="addRuleTargetType === 'session' ? !selectedNewUmo : !selectedGroup">
|
<v-btn color="primary" variant="tonal" @click="createNewRule" :disabled="!selectedNewUmo">
|
||||||
{{ tm('buttons.next') }}
|
{{ tm('buttons.next') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
@@ -353,7 +334,12 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
|
<div class="d-flex justify-end mt-4">
|
||||||
|
<v-btn color="primary" variant="tonal" size="small" @click="saveServiceConfig" :loading="saving"
|
||||||
|
prepend-icon="mdi-content-save">
|
||||||
|
{{ tm('buttons.save') }}
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Provider Config Section -->
|
<!-- Provider Config Section -->
|
||||||
<div class="d-flex align-center mb-4 mt-4">
|
<div class="d-flex align-center mb-4 mt-4">
|
||||||
@@ -378,7 +364,12 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
|
<div class="d-flex justify-end mt-4">
|
||||||
|
<v-btn color="primary" variant="tonal" size="small" @click="saveProviderConfig" :loading="saving"
|
||||||
|
prepend-icon="mdi-content-save">
|
||||||
|
{{ tm('buttons.save') }}
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Persona Config Section -->
|
<!-- Persona Config Section -->
|
||||||
<div class="d-flex align-center mb-4 mt-4">
|
<div class="d-flex align-center mb-4 mt-4">
|
||||||
@@ -398,7 +389,12 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
|
<div class="d-flex justify-end mt-4">
|
||||||
|
<v-btn color="primary" variant="tonal" size="small" @click="saveServiceConfig" :loading="saving"
|
||||||
|
prepend-icon="mdi-content-save">
|
||||||
|
{{ tm('buttons.save') }}
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Plugin Config Section -->
|
<!-- Plugin Config Section -->
|
||||||
<div class="d-flex align-center mb-4 mt-4">
|
<div class="d-flex align-center mb-4 mt-4">
|
||||||
@@ -418,7 +414,12 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
|
<div class="d-flex justify-end mt-4">
|
||||||
|
<v-btn color="primary" variant="tonal" size="small" @click="savePluginConfig" :loading="saving"
|
||||||
|
prepend-icon="mdi-content-save">
|
||||||
|
{{ tm('buttons.save') }}
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- KB Config Section -->
|
<!-- KB Config Section -->
|
||||||
<div class="d-flex align-center mb-4 mt-4">
|
<div class="d-flex align-center mb-4 mt-4">
|
||||||
@@ -441,17 +442,14 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
|
<div class="d-flex justify-end mt-4">
|
||||||
|
<v-btn color="primary" variant="tonal" size="small" @click="saveKbConfig" :loading="saving"
|
||||||
|
prepend-icon="mdi-content-save">
|
||||||
|
{{ tm('buttons.save') }}
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-actions class="px-6 pb-4">
|
|
||||||
<v-spacer></v-spacer>
|
|
||||||
<v-btn variant="text" @click="closeRuleEditor">{{ tm('buttons.cancel') }}</v-btn>
|
|
||||||
<v-btn color="primary" variant="tonal" @click="saveAllConfigs" :loading="saving"
|
|
||||||
prepend-icon="mdi-content-save">
|
|
||||||
{{ tm('buttons.save') }}
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
|
|
||||||
@@ -569,8 +567,6 @@ export default {
|
|||||||
addRuleDialog: false,
|
addRuleDialog: false,
|
||||||
availableUmos: [],
|
availableUmos: [],
|
||||||
selectedNewUmo: null,
|
selectedNewUmo: null,
|
||||||
addRuleTargetType: 'session',
|
|
||||||
selectedGroup: null,
|
|
||||||
|
|
||||||
// 规则编辑
|
// 规则编辑
|
||||||
ruleDialog: false,
|
ruleDialog: false,
|
||||||
@@ -733,13 +729,6 @@ export default {
|
|||||||
return options
|
return options
|
||||||
},
|
},
|
||||||
|
|
||||||
groupSelectOptions() {
|
|
||||||
return this.groups.map(g => ({
|
|
||||||
label: `${g.name} (${g.umo_count} 个会话)`,
|
|
||||||
value: g,
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
|
|
||||||
groupOptions() {
|
groupOptions() {
|
||||||
return this.groups.map(g => ({
|
return this.groups.map(g => ({
|
||||||
label: `${g.name} (${g.umo_count} 个会话)`,
|
label: `${g.name} (${g.umo_count} 个会话)`,
|
||||||
@@ -822,7 +811,7 @@ export default {
|
|||||||
})
|
})
|
||||||
if (response.data.status === 'ok') {
|
if (response.data.status === 'ok') {
|
||||||
const data = response.data.data
|
const data = response.data.data
|
||||||
this.rulesList = data.rules || []
|
this.rulesList = data.rules
|
||||||
this.totalItems = data.total
|
this.totalItems = data.total
|
||||||
this.availablePersonas = data.available_personas
|
this.availablePersonas = data.available_personas
|
||||||
this.availableChatProviders = data.available_chat_providers
|
this.availableChatProviders = data.available_chat_providers
|
||||||
@@ -830,20 +819,6 @@ export default {
|
|||||||
this.availableTtsProviders = data.available_tts_providers
|
this.availableTtsProviders = data.available_tts_providers
|
||||||
this.availablePlugins = data.available_plugins || []
|
this.availablePlugins = data.available_plugins || []
|
||||||
this.availableKbs = data.available_kbs || []
|
this.availableKbs = data.available_kbs || []
|
||||||
|
|
||||||
// 合并分组规则到列表中
|
|
||||||
const groupRules = data.group_rules || []
|
|
||||||
for (const gr of groupRules) {
|
|
||||||
this.rulesList.unshift({
|
|
||||||
umo: `[\u5206\u7ec4] ${gr.name}`,
|
|
||||||
isGroup: true,
|
|
||||||
groupId: gr.group_id,
|
|
||||||
groupName: gr.name,
|
|
||||||
umo_count: gr.umo_count,
|
|
||||||
rules: gr.config || {},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.totalItems += groupRules.length
|
|
||||||
} else {
|
} else {
|
||||||
this.showError(response.data.message || this.tm('messages.loadError'))
|
this.showError(response.data.message || this.tm('messages.loadError'))
|
||||||
}
|
}
|
||||||
@@ -897,89 +872,10 @@ export default {
|
|||||||
async openAddRuleDialog() {
|
async openAddRuleDialog() {
|
||||||
this.addRuleDialog = true
|
this.addRuleDialog = true
|
||||||
this.selectedNewUmo = null
|
this.selectedNewUmo = null
|
||||||
this.addRuleTargetType = 'session'
|
|
||||||
this.selectedGroup = null
|
|
||||||
await this.loadUmos()
|
await this.loadUmos()
|
||||||
},
|
},
|
||||||
|
|
||||||
async saveAllConfigs() {
|
|
||||||
if (!this.selectedUmo) return
|
|
||||||
|
|
||||||
// 分组模式:调用分组配置 API
|
|
||||||
if (this.selectedUmo.isGroup) {
|
|
||||||
this.saving = true
|
|
||||||
try {
|
|
||||||
const config = {
|
|
||||||
session_service_config: { ...this.serviceConfig },
|
|
||||||
provider_perf_chat_completion: this.providerConfig.chat_completion || null,
|
|
||||||
provider_perf_speech_to_text: this.providerConfig.speech_to_text || null,
|
|
||||||
provider_perf_text_to_speech: this.providerConfig.text_to_speech || null,
|
|
||||||
session_plugin_config: { ...this.pluginConfig },
|
|
||||||
kb_config: { ...this.kbConfig },
|
|
||||||
}
|
|
||||||
// 清理空值
|
|
||||||
if (!config.session_service_config.custom_name) delete config.session_service_config.custom_name
|
|
||||||
if (config.session_service_config.persona_id === null) delete config.session_service_config.persona_id
|
|
||||||
|
|
||||||
const response = await axios.post('/api/session/group/update-config', {
|
|
||||||
group_id: this.selectedUmo.groupId,
|
|
||||||
config: config
|
|
||||||
})
|
|
||||||
if (response.data.status === 'ok') {
|
|
||||||
this.showSuccess(response.data.data?.message || '分组配置已保存并同步')
|
|
||||||
await this.loadData()
|
|
||||||
} else {
|
|
||||||
this.showError(response.data.message || this.tm('messages.saveError'))
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.showError(error.response?.data?.message || this.tm('messages.saveError'))
|
|
||||||
} finally {
|
|
||||||
this.saving = false
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 单个会话模式
|
|
||||||
this.saving = true
|
|
||||||
this._batchSaving = true
|
|
||||||
try {
|
|
||||||
await this.saveServiceConfig()
|
|
||||||
await this.saveProviderConfig()
|
|
||||||
await this.savePluginConfig()
|
|
||||||
await this.saveKbConfig()
|
|
||||||
this.showSuccess(this.tm('messages.saveSuccess'))
|
|
||||||
} catch (error) {
|
|
||||||
this.showError(error.response?.data?.message || this.tm('messages.saveError'))
|
|
||||||
} finally {
|
|
||||||
this._batchSaving = false
|
|
||||||
this.saving = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
createNewRule() {
|
createNewRule() {
|
||||||
if (this.addRuleTargetType === 'group') {
|
|
||||||
// 分组模式
|
|
||||||
if (!this.selectedGroup) return
|
|
||||||
const group = this.selectedGroup.value || this.selectedGroup
|
|
||||||
if (!group.umos || group.umos.length === 0) {
|
|
||||||
this.showError('该分组没有成员会话')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 创建一个特殊的规则项,标记为分组
|
|
||||||
const newItem = {
|
|
||||||
umo: `[分组] ${group.name}`,
|
|
||||||
isGroup: true,
|
|
||||||
groupId: group.id,
|
|
||||||
groupName: group.name,
|
|
||||||
groupUmos: group.umos,
|
|
||||||
rules: {},
|
|
||||||
}
|
|
||||||
this.addRuleDialog = false
|
|
||||||
this.openRuleEditor(newItem)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 单个会话模式(原逻辑)
|
|
||||||
if (!this.selectedNewUmo) return
|
if (!this.selectedNewUmo) return
|
||||||
|
|
||||||
// 创建一个新的规则项并打开编辑器
|
// 创建一个新的规则项并打开编辑器
|
||||||
@@ -1047,37 +943,13 @@ export default {
|
|||||||
async saveServiceConfig() {
|
async saveServiceConfig() {
|
||||||
if (!this.selectedUmo) return
|
if (!this.selectedUmo) return
|
||||||
|
|
||||||
if (!this._batchSaving) this.saving = true
|
this.saving = true
|
||||||
try {
|
try {
|
||||||
const config = { ...this.serviceConfig }
|
const config = { ...this.serviceConfig }
|
||||||
// 清理空值
|
// 清理空值
|
||||||
if (!config.custom_name) delete config.custom_name
|
if (!config.custom_name) delete config.custom_name
|
||||||
if (config.persona_id === null) delete config.persona_id
|
if (config.persona_id === null) delete config.persona_id
|
||||||
|
|
||||||
// 分组模式:批量下发给所有成员
|
|
||||||
if (this.selectedUmo.isGroup) {
|
|
||||||
const umos = this.selectedUmo.groupUmos
|
|
||||||
let successCount = 0
|
|
||||||
for (const umo of umos) {
|
|
||||||
try {
|
|
||||||
await axios.post('/api/session/update-rule', {
|
|
||||||
umo: umo,
|
|
||||||
rule_key: 'session_service_config',
|
|
||||||
rule_value: config
|
|
||||||
})
|
|
||||||
successCount++
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`更新 ${umo} 失败:`, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!this._batchSaving) {
|
|
||||||
this.showSuccess(`已更新 ${successCount}/${umos.length} 个会话的服务配置`)
|
|
||||||
await this.loadData()
|
|
||||||
this.saving = false
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await axios.post('/api/session/update-rule', {
|
const response = await axios.post('/api/session/update-rule', {
|
||||||
umo: this.selectedUmo.umo,
|
umo: this.selectedUmo.umo,
|
||||||
rule_key: 'session_service_config',
|
rule_key: 'session_service_config',
|
||||||
@@ -1085,7 +957,7 @@ export default {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (response.data.status === 'ok') {
|
if (response.data.status === 'ok') {
|
||||||
if (!this._batchSaving) this.showSuccess(this.tm('messages.saveSuccess'))
|
this.showSuccess(this.tm('messages.saveSuccess'))
|
||||||
this.editingRules.session_service_config = config
|
this.editingRules.session_service_config = config
|
||||||
|
|
||||||
// 更新或添加到列表
|
// 更新或添加到列表
|
||||||
@@ -1108,45 +980,17 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.showError(error.response?.data?.message || this.tm('messages.saveError'))
|
this.showError(error.response?.data?.message || this.tm('messages.saveError'))
|
||||||
}
|
}
|
||||||
if (!this._batchSaving) this.saving = false
|
this.saving = false
|
||||||
},
|
},
|
||||||
|
|
||||||
async saveProviderConfig() {
|
async saveProviderConfig() {
|
||||||
if (!this.selectedUmo) return
|
if (!this.selectedUmo) return
|
||||||
|
|
||||||
if (!this._batchSaving) this.saving = true
|
this.saving = true
|
||||||
try {
|
try {
|
||||||
const providerTypes = ['chat_completion', 'speech_to_text', 'text_to_speech']
|
|
||||||
|
|
||||||
// 分组模式:批量下发给所有成员
|
|
||||||
if (this.selectedUmo.isGroup) {
|
|
||||||
const umos = this.selectedUmo.groupUmos
|
|
||||||
let successCount = 0
|
|
||||||
for (const umo of umos) {
|
|
||||||
try {
|
|
||||||
const tasks = []
|
|
||||||
for (const type of providerTypes) {
|
|
||||||
const value = this.providerConfig[type]
|
|
||||||
if (value) {
|
|
||||||
tasks.push(axios.post('/api/session/update-rule', { umo, rule_key: `provider_perf_${type}`, rule_value: value }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tasks.length > 0) await Promise.all(tasks)
|
|
||||||
successCount++
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`更新 ${umo} Provider 失败:`, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!this._batchSaving) {
|
|
||||||
this.showSuccess(`已更新 ${successCount}/${umos.length} 个会话的 Provider 配置`)
|
|
||||||
await this.loadData()
|
|
||||||
this.saving = false
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateTasks = []
|
const updateTasks = []
|
||||||
const deleteTasks = []
|
const deleteTasks = []
|
||||||
|
const providerTypes = ['chat_completion', 'speech_to_text', 'text_to_speech']
|
||||||
|
|
||||||
for (const type of providerTypes) {
|
for (const type of providerTypes) {
|
||||||
const value = this.providerConfig[type]
|
const value = this.providerConfig[type]
|
||||||
@@ -1173,7 +1017,7 @@ export default {
|
|||||||
const allTasks = [...updateTasks, ...deleteTasks]
|
const allTasks = [...updateTasks, ...deleteTasks]
|
||||||
if (allTasks.length > 0) {
|
if (allTasks.length > 0) {
|
||||||
await Promise.all(allTasks)
|
await Promise.all(allTasks)
|
||||||
if (!this._batchSaving) this.showSuccess(this.tm('messages.saveSuccess'))
|
this.showSuccess(this.tm('messages.saveSuccess'))
|
||||||
|
|
||||||
// 更新或添加到列表
|
// 更新或添加到列表
|
||||||
let item = this.rulesList.find(u => u.umo === this.selectedUmo.umo)
|
let item = this.rulesList.find(u => u.umo === this.selectedUmo.umo)
|
||||||
@@ -1198,48 +1042,24 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!this._batchSaving) this.showSuccess(this.tm('messages.noChanges'))
|
this.showSuccess(this.tm('messages.noChanges'))
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.showError(error.response?.data?.message || this.tm('messages.saveError'))
|
this.showError(error.response?.data?.message || this.tm('messages.saveError'))
|
||||||
}
|
}
|
||||||
if (!this._batchSaving) this.saving = false
|
this.saving = false
|
||||||
},
|
},
|
||||||
|
|
||||||
async savePluginConfig() {
|
async savePluginConfig() {
|
||||||
if (!this.selectedUmo) return
|
if (!this.selectedUmo) return
|
||||||
|
|
||||||
if (!this._batchSaving) this.saving = true
|
this.saving = true
|
||||||
try {
|
try {
|
||||||
const config = {
|
const config = {
|
||||||
enabled_plugins: this.pluginConfig.enabled_plugins,
|
enabled_plugins: this.pluginConfig.enabled_plugins,
|
||||||
disabled_plugins: this.pluginConfig.disabled_plugins,
|
disabled_plugins: this.pluginConfig.disabled_plugins,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分组模式:批量下发给所有成员
|
|
||||||
if (this.selectedUmo.isGroup) {
|
|
||||||
const umos = this.selectedUmo.groupUmos
|
|
||||||
let successCount = 0
|
|
||||||
for (const umo of umos) {
|
|
||||||
try {
|
|
||||||
if (config.enabled_plugins.length === 0 && config.disabled_plugins.length === 0) {
|
|
||||||
await axios.post('/api/session/delete-rule', { umo, rule_key: 'session_plugin_config' })
|
|
||||||
} else {
|
|
||||||
await axios.post('/api/session/update-rule', { umo, rule_key: 'session_plugin_config', rule_value: config })
|
|
||||||
}
|
|
||||||
successCount++
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`更新 ${umo} 插件配置失败:`, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!this._batchSaving) {
|
|
||||||
this.showSuccess(`已更新 ${successCount}/${umos.length} 个会话的插件配置`)
|
|
||||||
await this.loadData()
|
|
||||||
this.saving = false
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果两个列表都为空,删除配置
|
// 如果两个列表都为空,删除配置
|
||||||
if (config.enabled_plugins.length === 0 && config.disabled_plugins.length === 0) {
|
if (config.enabled_plugins.length === 0 && config.disabled_plugins.length === 0) {
|
||||||
if (this.editingRules.session_plugin_config) {
|
if (this.editingRules.session_plugin_config) {
|
||||||
@@ -1251,7 +1071,7 @@ export default {
|
|||||||
let item = this.rulesList.find(u => u.umo === this.selectedUmo.umo)
|
let item = this.rulesList.find(u => u.umo === this.selectedUmo.umo)
|
||||||
if (item) delete item.rules.session_plugin_config
|
if (item) delete item.rules.session_plugin_config
|
||||||
}
|
}
|
||||||
if (!this._batchSaving) this.showSuccess(this.tm('messages.saveSuccess'))
|
this.showSuccess(this.tm('messages.saveSuccess'))
|
||||||
} else {
|
} else {
|
||||||
const response = await axios.post('/api/session/update-rule', {
|
const response = await axios.post('/api/session/update-rule', {
|
||||||
umo: this.selectedUmo.umo,
|
umo: this.selectedUmo.umo,
|
||||||
@@ -1260,7 +1080,7 @@ export default {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (response.data.status === 'ok') {
|
if (response.data.status === 'ok') {
|
||||||
if (!this._batchSaving) this.showSuccess(this.tm('messages.saveSuccess'))
|
this.showSuccess(this.tm('messages.saveSuccess'))
|
||||||
this.editingRules.session_plugin_config = config
|
this.editingRules.session_plugin_config = config
|
||||||
|
|
||||||
let item = this.rulesList.find(u => u.umo === this.selectedUmo.umo)
|
let item = this.rulesList.find(u => u.umo === this.selectedUmo.umo)
|
||||||
@@ -1282,13 +1102,13 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.showError(error.response?.data?.message || this.tm('messages.saveError'))
|
this.showError(error.response?.data?.message || this.tm('messages.saveError'))
|
||||||
}
|
}
|
||||||
if (!this._batchSaving) this.saving = false
|
this.saving = false
|
||||||
},
|
},
|
||||||
|
|
||||||
async saveKbConfig() {
|
async saveKbConfig() {
|
||||||
if (!this.selectedUmo) return
|
if (!this.selectedUmo) return
|
||||||
|
|
||||||
if (!this._batchSaving) this.saving = true
|
this.saving = true
|
||||||
try {
|
try {
|
||||||
const config = {
|
const config = {
|
||||||
kb_ids: this.kbConfig.kb_ids,
|
kb_ids: this.kbConfig.kb_ids,
|
||||||
@@ -1296,30 +1116,6 @@ export default {
|
|||||||
enable_rerank: this.kbConfig.enable_rerank,
|
enable_rerank: this.kbConfig.enable_rerank,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分组模式:批量下发给所有成员
|
|
||||||
if (this.selectedUmo.isGroup) {
|
|
||||||
const umos = this.selectedUmo.groupUmos
|
|
||||||
let successCount = 0
|
|
||||||
for (const umo of umos) {
|
|
||||||
try {
|
|
||||||
if (config.kb_ids.length === 0) {
|
|
||||||
await axios.post('/api/session/delete-rule', { umo, rule_key: 'kb_config' })
|
|
||||||
} else {
|
|
||||||
await axios.post('/api/session/update-rule', { umo, rule_key: 'kb_config', rule_value: config })
|
|
||||||
}
|
|
||||||
successCount++
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`更新 ${umo} 知识库配置失败:`, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!this._batchSaving) {
|
|
||||||
this.showSuccess(`已更新 ${successCount}/${umos.length} 个会话的知识库配置`)
|
|
||||||
await this.loadData()
|
|
||||||
this.saving = false
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果 kb_ids 为空,删除配置
|
// 如果 kb_ids 为空,删除配置
|
||||||
if (config.kb_ids.length === 0) {
|
if (config.kb_ids.length === 0) {
|
||||||
if (this.editingRules.kb_config) {
|
if (this.editingRules.kb_config) {
|
||||||
@@ -1331,7 +1127,7 @@ export default {
|
|||||||
let item = this.rulesList.find(u => u.umo === this.selectedUmo.umo)
|
let item = this.rulesList.find(u => u.umo === this.selectedUmo.umo)
|
||||||
if (item) delete item.rules.kb_config
|
if (item) delete item.rules.kb_config
|
||||||
}
|
}
|
||||||
if (!this._batchSaving) this.showSuccess(this.tm('messages.saveSuccess'))
|
this.showSuccess(this.tm('messages.saveSuccess'))
|
||||||
} else {
|
} else {
|
||||||
const response = await axios.post('/api/session/update-rule', {
|
const response = await axios.post('/api/session/update-rule', {
|
||||||
umo: this.selectedUmo.umo,
|
umo: this.selectedUmo.umo,
|
||||||
@@ -1340,7 +1136,7 @@ export default {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (response.data.status === 'ok') {
|
if (response.data.status === 'ok') {
|
||||||
if (!this._batchSaving) this.showSuccess(this.tm('messages.saveSuccess'))
|
this.showSuccess(this.tm('messages.saveSuccess'))
|
||||||
this.editingRules.kb_config = config
|
this.editingRules.kb_config = config
|
||||||
|
|
||||||
let item = this.rulesList.find(u => u.umo === this.selectedUmo.umo)
|
let item = this.rulesList.find(u => u.umo === this.selectedUmo.umo)
|
||||||
@@ -1362,7 +1158,7 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.showError(error.response?.data?.message || this.tm('messages.saveError'))
|
this.showError(error.response?.data?.message || this.tm('messages.saveError'))
|
||||||
}
|
}
|
||||||
if (!this._batchSaving) this.saving = false
|
this.saving = false
|
||||||
},
|
},
|
||||||
|
|
||||||
confirmDeleteRules(item) {
|
confirmDeleteRules(item) {
|
||||||
@@ -1375,24 +1171,6 @@ export default {
|
|||||||
|
|
||||||
this.deleting = true
|
this.deleting = true
|
||||||
try {
|
try {
|
||||||
// 分组规则:清空分组配置
|
|
||||||
if (this.deleteTarget.isGroup) {
|
|
||||||
const response = await axios.post('/api/session/group/update-config', {
|
|
||||||
group_id: this.deleteTarget.groupId,
|
|
||||||
config: {}
|
|
||||||
})
|
|
||||||
if (response.data.status === 'ok') {
|
|
||||||
this.showSuccess('分组配置已清除')
|
|
||||||
this.deleteDialog = false
|
|
||||||
this.deleteTarget = null
|
|
||||||
await this.loadData()
|
|
||||||
} else {
|
|
||||||
this.showError(response.data.message || this.tm('messages.deleteError'))
|
|
||||||
}
|
|
||||||
this.deleting = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await axios.post('/api/session/delete-rule', {
|
const response = await axios.post('/api/session/delete-rule', {
|
||||||
umo: this.deleteTarget.umo
|
umo: this.deleteTarget.umo
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -353,10 +353,11 @@
|
|||||||
<v-window-item value="search">
|
<v-window-item value="search">
|
||||||
<div class="search-container pa-4">
|
<div class="search-container pa-4">
|
||||||
<v-form @submit.prevent="searchKnowledgeBase" class="d-flex align-center">
|
<v-form @submit.prevent="searchKnowledgeBase" class="d-flex align-center">
|
||||||
<v-text-field v-model="searchQuery" :label="tm('search.queryLabel')"
|
<v-text-field :model-value="searchQuery"
|
||||||
|
@update:model-value="onSearchQueryInput" :label="tm('search.queryLabel')"
|
||||||
append-icon="mdi-magnify" variant="outlined" class="flex-grow-1 me-2"
|
append-icon="mdi-magnify" variant="outlined" class="flex-grow-1 me-2"
|
||||||
@click:append="searchKnowledgeBase" @keyup.enter="searchKnowledgeBase"
|
@click:append="searchKnowledgeBase" @keyup.enter="searchKnowledgeBase"
|
||||||
:placeholder="tm('search.queryPlaceholder')" hide-details></v-text-field>
|
:placeholder="tm('search.queryPlaceholder')" hide-details clearable></v-text-field>
|
||||||
|
|
||||||
<v-select v-model="topK" :items="[3, 5, 10, 20]"
|
<v-select v-model="topK" :items="[3, 5, 10, 20]"
|
||||||
:label="tm('search.resultCountLabel')" variant="outlined"
|
:label="tm('search.resultCountLabel')" variant="outlined"
|
||||||
@@ -434,6 +435,7 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import ConsoleDisplayer from '@/components/shared/ConsoleDisplayer.vue';
|
import ConsoleDisplayer from '@/components/shared/ConsoleDisplayer.vue';
|
||||||
import { useModuleI18n } from '@/i18n/composables';
|
import { useModuleI18n } from '@/i18n/composables';
|
||||||
|
import { normalizeTextInput } from '@/utils/inputValue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'KnowledgeBase',
|
name: 'KnowledgeBase',
|
||||||
@@ -580,6 +582,9 @@ export default {
|
|||||||
this.getProviderList();
|
this.getProviderList();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
onSearchQueryInput(value) {
|
||||||
|
this.searchQuery = normalizeTextInput(value);
|
||||||
|
},
|
||||||
getSelectedGitHubProxy() {
|
getSelectedGitHubProxy() {
|
||||||
if (typeof window === "undefined" || !window.localStorage) return "";
|
if (typeof window === "undefined" || !window.localStorage) return "";
|
||||||
return localStorage.getItem("githubProxyRadioValue") === "1"
|
return localStorage.getItem("githubProxyRadioValue") === "1"
|
||||||
@@ -903,7 +908,8 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
searchKnowledgeBase() {
|
searchKnowledgeBase() {
|
||||||
if (!this.searchQuery.trim()) {
|
const query = normalizeTextInput(this.searchQuery).trim();
|
||||||
|
if (!query) {
|
||||||
this.showSnackbar(this.tm('messages.pleaseEnterSearchContent'), 'warning');
|
this.showSnackbar(this.tm('messages.pleaseEnterSearchContent'), 'warning');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -914,7 +920,7 @@ export default {
|
|||||||
axios.get(`/api/plug/alkaid/kb/collection/search`, {
|
axios.get(`/api/plug/alkaid/kb/collection/search`, {
|
||||||
params: {
|
params: {
|
||||||
collection_name: this.currentKB.collection_name,
|
collection_name: this.currentKB.collection_name,
|
||||||
query: this.searchQuery,
|
query,
|
||||||
top_k: this.topK
|
top_k: this.topK
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -37,10 +37,12 @@
|
|||||||
<h3>{{ tm('search.title') }}</h3>
|
<h3>{{ tm('search.title') }}</h3>
|
||||||
<v-card variant="outlined" class="mt-2 pa-3">
|
<v-card variant="outlined" class="mt-2 pa-3">
|
||||||
<div>
|
<div>
|
||||||
<v-text-field v-model="searchMemoryUserId" :label="tm('search.userIdLabel')" variant="outlined" density="compact" hide-details
|
<v-text-field :model-value="searchMemoryUserId"
|
||||||
class="mb-2"></v-text-field>
|
@update:model-value="onSearchMemoryUserIdInput" :label="tm('search.userIdLabel')" variant="outlined" density="compact" hide-details
|
||||||
<v-text-field v-model="searchQuery" :label="tm('search.queryLabel')" variant="outlined" density="compact" hide-details
|
class="mb-2" clearable></v-text-field>
|
||||||
@keyup.enter="searchMemory" class="mb-2"></v-text-field>
|
<v-text-field :model-value="searchQuery"
|
||||||
|
@update:model-value="onSearchQueryInput" :label="tm('search.queryLabel')" variant="outlined" density="compact" hide-details
|
||||||
|
@keyup.enter="searchMemory" class="mb-2" clearable></v-text-field>
|
||||||
<v-btn color="info" @click="searchMemory" :loading="isSearching" variant="tonal">
|
<v-btn color="info" @click="searchMemory" :loading="isSearching" variant="tonal">
|
||||||
<v-icon start>mdi-text-search</v-icon>
|
<v-icon start>mdi-text-search</v-icon>
|
||||||
{{ tm('search.searchButton') }}
|
{{ tm('search.searchButton') }}
|
||||||
@@ -254,6 +256,7 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
// import * as d3 from "d3"; // npm install d3
|
// import * as d3 from "d3"; // npm install d3
|
||||||
import { useModuleI18n } from '@/i18n/composables';
|
import { useModuleI18n } from '@/i18n/composables';
|
||||||
|
import { normalizeTextInput } from '@/utils/inputValue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'LongTermMemory',
|
name: 'LongTermMemory',
|
||||||
@@ -336,9 +339,16 @@ export default {
|
|||||||
this.searchResults = [];
|
this.searchResults = [];
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
onSearchMemoryUserIdInput(value) {
|
||||||
|
this.searchMemoryUserId = normalizeTextInput(value);
|
||||||
|
},
|
||||||
|
onSearchQueryInput(value) {
|
||||||
|
this.searchQuery = normalizeTextInput(value);
|
||||||
|
},
|
||||||
// 添加搜索记忆方法
|
// 添加搜索记忆方法
|
||||||
searchMemory() {
|
searchMemory() {
|
||||||
if (!this.searchQuery.trim()) {
|
const query = normalizeTextInput(this.searchQuery).trim();
|
||||||
|
if (!query) {
|
||||||
this.$toast.warning(this.tm('messages.searchQueryRequired'));
|
this.$toast.warning(this.tm('messages.searchQueryRequired'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -349,12 +359,13 @@ export default {
|
|||||||
|
|
||||||
// 构建查询参数
|
// 构建查询参数
|
||||||
const params = {
|
const params = {
|
||||||
query: this.searchQuery
|
query
|
||||||
};
|
};
|
||||||
|
|
||||||
// 如果有选择用户ID,也加入查询参数
|
// 如果有选择用户ID,也加入查询参数
|
||||||
if (this.searchMemoryUserId) {
|
const normalizedUserId = normalizeTextInput(this.searchMemoryUserId).trim();
|
||||||
params.user_id = this.searchMemoryUserId;
|
if (normalizedUserId) {
|
||||||
|
params.user_id = normalizedUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
axios.get('/api/plug/alkaid/ltm/graph/search', { params })
|
axios.get('/api/plug/alkaid/ltm/graph/search', { params })
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import PluginSortControl from "@/components/extension/PluginSortControl.vue";
|
|||||||
import ExtensionCard from "@/components/shared/ExtensionCard.vue";
|
import ExtensionCard from "@/components/shared/ExtensionCard.vue";
|
||||||
import StyledMenu from "@/components/shared/StyledMenu.vue";
|
import StyledMenu from "@/components/shared/StyledMenu.vue";
|
||||||
import defaultPluginIcon from "@/assets/images/plugin_icon.png";
|
import defaultPluginIcon from "@/assets/images/plugin_icon.png";
|
||||||
|
import { normalizeTextInput } from "@/utils/inputValue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
state: {
|
state: {
|
||||||
@@ -164,10 +165,12 @@ const {
|
|||||||
|
|
||||||
<div class="d-flex align-center flex-wrap ml-auto" style="gap: 8px">
|
<div class="d-flex align-center flex-wrap ml-auto" style="gap: 8px">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="pluginSearch"
|
:model-value="pluginSearch"
|
||||||
|
@update:model-value="pluginSearch = normalizeTextInput($event)"
|
||||||
density="compact"
|
density="compact"
|
||||||
:label="tm('search.placeholder')"
|
:label="tm('search.placeholder')"
|
||||||
prepend-inner-icon="mdi-magnify"
|
prepend-inner-icon="mdi-magnify"
|
||||||
|
clearable
|
||||||
variant="solo-filled"
|
variant="solo-filled"
|
||||||
flat
|
flat
|
||||||
hide-details
|
hide-details
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import MarketPluginCard from "@/components/extension/MarketPluginCard.vue";
|
|||||||
import PluginSortControl from "@/components/extension/PluginSortControl.vue";
|
import PluginSortControl from "@/components/extension/PluginSortControl.vue";
|
||||||
import defaultPluginIcon from "@/assets/images/plugin_icon.png";
|
import defaultPluginIcon from "@/assets/images/plugin_icon.png";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
import { normalizeTextInput } from "@/utils/inputValue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
state: {
|
state: {
|
||||||
@@ -212,11 +213,13 @@ const marketSortItems = computed(() => [
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="marketSearch"
|
:model-value="marketSearch"
|
||||||
|
@update:model-value="marketSearch = normalizeTextInput($event)"
|
||||||
class="ml-auto"
|
class="ml-auto"
|
||||||
density="compact"
|
density="compact"
|
||||||
:label="tm('search.marketPlaceholder')"
|
:label="tm('search.marketPlaceholder')"
|
||||||
prepend-inner-icon="mdi-magnify"
|
prepend-inner-icon="mdi-magnify"
|
||||||
|
clearable
|
||||||
variant="solo-filled"
|
variant="solo-filled"
|
||||||
flat
|
flat
|
||||||
hide-details
|
hide-details
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ export default defineConfig({
|
|||||||
next: '下一篇'
|
next: '下一篇'
|
||||||
},
|
},
|
||||||
editLink: {
|
editLink: {
|
||||||
pattern: 'https://github.com/AstrBotdevs/AstrBot-docs/edit/v4/:path',
|
pattern: 'https://github.com/AstrBotdevs/AstrBot/edit/master/docs/:path',
|
||||||
text: '发现文档有问题?在 GitHub 上编辑此页',
|
text: '发现文档有问题?在 GitHub 上编辑此页',
|
||||||
},
|
},
|
||||||
logo: '/logo_prod.png',
|
logo: '/logo_prod.png',
|
||||||
@@ -484,7 +484,7 @@ export default defineConfig({
|
|||||||
next: 'Next'
|
next: 'Next'
|
||||||
},
|
},
|
||||||
editLink: {
|
editLink: {
|
||||||
pattern: 'https://github.com/AstrBotdevs/AstrBot-docs/edit/v4/:path',
|
pattern: 'https://github.com/AstrBotdevs/AstrBot/edit/master/docs/:path',
|
||||||
text: 'Edit this page on GitHub',
|
text: 'Edit this page on GitHub',
|
||||||
},
|
},
|
||||||
logo: '/logo_prod.png',
|
logo: '/logo_prod.png',
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ Welcome to submit Issues or Pull Requests:
|
|||||||
|
|
||||||
- [AstrBotDevs/AstrBot](https://github.com/AstrBotDevs/AstrBot)
|
- [AstrBotDevs/AstrBot](https://github.com/AstrBotDevs/AstrBot)
|
||||||
|
|
||||||
- [AstrBotDevs/AstrBot-Docs](https://github.com/AstrBotDevs/AstrBot-docs)
|
|
||||||
|
|
||||||
### Tencent QQ Groups
|
### Tencent QQ Groups
|
||||||
|
|
||||||
> - All groups are available to join. If you find that the group size is below the limit, please feel free to join.
|
> - All groups are available to join. If you find that the group size is below the limit, please feel free to join.
|
||||||
|
|||||||
@@ -128,6 +128,9 @@ The default AstrBot configuration is as follows:
|
|||||||
"telegram": {
|
"telegram": {
|
||||||
"pre_ack_emoji": {"enable": False, "emojis": ["✍️"]},
|
"pre_ack_emoji": {"enable": False, "emojis": ["✍️"]},
|
||||||
},
|
},
|
||||||
|
"discord": {
|
||||||
|
"pre_ack_emoji": {"enable": False, "emojis": ["🤔"]},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"wake_prefix": ["/"],
|
"wake_prefix": ["/"],
|
||||||
"log_level": "INFO",
|
"log_level": "INFO",
|
||||||
@@ -511,6 +514,11 @@ When enabled, AstrBot sends a pre-reply emoji before requesting the LLM to infor
|
|||||||
- `enable`: Whether to enable pre-reply emojis for Telegram messages. Default is `false`.
|
- `enable`: Whether to enable pre-reply emojis for Telegram messages. Default is `false`.
|
||||||
- `emojis`: List of pre-reply emojis. Default is `["✍️"]`. Telegram only supports a fixed set of reactions; refer to [reactions.txt](https://gist.github.com/Soulter/3f22c8e5f9c7e152e967e8bc28c97fc9).
|
- `emojis`: List of pre-reply emojis. Default is `["✍️"]`. Telegram only supports a fixed set of reactions; refer to [reactions.txt](https://gist.github.com/Soulter/3f22c8e5f9c7e152e967e8bc28c97fc9).
|
||||||
|
|
||||||
|
##### discord
|
||||||
|
|
||||||
|
- `enable`: Whether to enable pre-reply emojis for Discord messages. Default is `false`.
|
||||||
|
- `emojis`: List of pre-reply emojis. Default is `["🤔"]`. Refer to [Discord Reaction FAQ](https://support.discord.com/hc/en-us/articles/12102061808663-Reactions-and-Super-Reactions-FAQ).
|
||||||
|
|
||||||
### `wake_prefix`
|
### `wake_prefix`
|
||||||
|
|
||||||
Wake prefix. Default is `/`. When a message starts with `/`, AstrBot is awakened.
|
Wake prefix. Default is `/`. When a message starts with `/`, AstrBot is awakened.
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ https://discord.gg/PxgzhmxJ
|
|||||||
|
|
||||||
- [AstrBotDevs/AstrBot](https://github.com/AstrBotDevs/AstrBot)
|
- [AstrBotDevs/AstrBot](https://github.com/AstrBotDevs/AstrBot)
|
||||||
|
|
||||||
- [AstrBotDevs/AstrBot-Docs](https://github.com/AstrBotDevs/AstrBot-docs)
|
|
||||||
|
|
||||||
## 成为 AstrBot 组织成员
|
## 成为 AstrBot 组织成员
|
||||||
|
|
||||||
欢迎加入我们!
|
欢迎加入我们!
|
||||||
|
|||||||
@@ -128,6 +128,9 @@ AstrBot 默认配置如下:
|
|||||||
"telegram": {
|
"telegram": {
|
||||||
"pre_ack_emoji": {"enable": False, "emojis": ["✍️"]},
|
"pre_ack_emoji": {"enable": False, "emojis": ["✍️"]},
|
||||||
},
|
},
|
||||||
|
"discord": {
|
||||||
|
"pre_ack_emoji": {"enable": False, "emojis": ["🤔"]},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"wake_prefix": ["/"],
|
"wake_prefix": ["/"],
|
||||||
"log_level": "INFO",
|
"log_level": "INFO",
|
||||||
@@ -506,11 +509,16 @@ AstrBot WebUI 配置。
|
|||||||
- `enable`: 是否启用飞书消息预回复表情。默认为 `false`。
|
- `enable`: 是否启用飞书消息预回复表情。默认为 `false`。
|
||||||
- `emojis`: 预回复的表情列表。默认为 `["Typing"]`。表情枚举名参考:[表情文案说明](https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce)
|
- `emojis`: 预回复的表情列表。默认为 `["Typing"]`。表情枚举名参考:[表情文案说明](https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce)
|
||||||
|
|
||||||
#### telegram
|
##### telegram
|
||||||
|
|
||||||
- `enable`: 是否启用 Telegram 消息预回复表情。默认为 `false`。
|
- `enable`: 是否启用 Telegram 消息预回复表情。默认为 `false`。
|
||||||
- `emojis`: 预回复的表情列表。默认为 `["✍️"]`。Telegram 仅支持固定反应集合,参考:[reactions.txt](https://gist.github.com/Soulter/3f22c8e5f9c7e152e967e8bc28c97fc9)
|
- `emojis`: 预回复的表情列表。默认为 `["✍️"]`。Telegram 仅支持固定反应集合,参考:[reactions.txt](https://gist.github.com/Soulter/3f22c8e5f9c7e152e967e8bc28c97fc9)
|
||||||
|
|
||||||
|
##### discord
|
||||||
|
|
||||||
|
- `enable`: 是否启用 Discord 消息预回复表情。默认为 `false`。
|
||||||
|
- `emojis`: 预回复的表情列表。默认为 `["🤔"]`。Discord反应支持参考:[Discord Reaction FAQ](https://support.discord.com/hc/en-us/articles/12102061808663-Reactions-and-Super-Reactions-FAQ)
|
||||||
|
|
||||||
### `wake_prefix`
|
### `wake_prefix`
|
||||||
|
|
||||||
唤醒前缀。默认为 `/`。当消息以 `/` 开头时,AstrBot 会被唤醒。
|
唤醒前缀。默认为 `/`。当消息以 `/` 开头时,AstrBot 会被唤醒。
|
||||||
|
|||||||
Reference in New Issue
Block a user