Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion astrbot/builtin_stars/astrbot/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,9 @@ async def on_message(self, event: AstrMessageEvent):

if not session_curr_cid:
logger.error(
"当前未处于对话状态,无法主动回复,请确保 平台设置->会话隔离(unique_session) 未开启,并使用 /new 创建一个会话。",
"No active conversation is available for proactive replies. "
"Disable Isolate Conversation in Config -> Platforms & Messages "
"-> General, then use /new to create a conversation.",
)
return

Expand Down
92 changes: 52 additions & 40 deletions astrbot/core/config/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -1240,7 +1240,7 @@ def get_local_permission_defaults(system: str | None = None) -> dict:
"id_whitelist": {
"type": "list",
"items": {"type": "string"},
"hint": "只处理填写的 ID 发来的消息事件,为空时不启用。可使用 /sid 指令获取在平台上的会话 ID(类似 abc:GroupMessage:123)。管理员可在 WebUI 的平台设置中管理白名单",
"hint": "只处理填写的 ID 发来的消息事件,为空时不启用。可使用 /sid 指令获取在平台上的会话 ID(类似 abc:GroupMessage:123)。管理员可在 WebUI 的配置文件 -> 访问控制 -> 白名单中管理白名单。",
},
"id_whitelist_log": {
"type": "bool",
Expand Down Expand Up @@ -3284,7 +3284,7 @@ def get_local_permission_defaults(system: str | None = None) -> dict:
"""
CONFIG_METADATA_3 = {
"ai_group": {
"name": "AI 配置",
"name": "AI",
"metadata": {
"agent_runner": {
"description": "Agent 执行方式",
Expand Down Expand Up @@ -3825,7 +3825,7 @@ def get_local_permission_defaults(system: str | None = None) -> dict:
"provider_settings.computer_use_require_admin": {
"description": "沙箱能力需要 AstrBot 管理员权限",
"type": "bool",
"hint": "开启后,需要 AstrBot 管理员权限才能调用远程沙箱能力。在平台配置->管理员中可添加管理员。使用 /sid 指令查看管理员 ID。",
"hint": "开启后,需要 AstrBot 管理员权限才能调用远程沙箱能力。在配置文件 -> 访问控制 -> 管理员 -> 管理员 ID 中可添加管理员。给机器人发送 /sid 指令以获取 ID。",
"condition": {
"provider_settings.computer_use_runtime": "sandbox",
},
Expand Down Expand Up @@ -4280,17 +4280,12 @@ def get_local_permission_defaults(system: str | None = None) -> dict:
},
},
"platform_group": {
"name": "平台配置",
"name": "平台与消息",
"metadata": {
"general": {
"description": "基本",
"type": "object",
"items": {
"admins_id": {
"description": "管理员 ID",
"type": "list",
"items": {"type": "string"},
},
"platform_settings.unique_session": {
"description": "隔离对话",
"type": "bool",
Expand Down Expand Up @@ -4332,36 +4327,6 @@ def get_local_permission_defaults(system: str | None = None) -> dict:
},
},
},
"whitelist": {
"description": "白名单",
"type": "object",
"items": {
"platform_settings.enable_id_white_list": {
"description": "启用白名单",
"type": "bool",
"hint": "启用后,只有在白名单内的会话会被响应。",
},
"platform_settings.id_whitelist": {
"description": "白名单 ID 列表",
"type": "list",
"items": {"type": "string"},
"hint": "使用 /sid 获取 ID。当白名单列表为空时,代表不启用白名单(即所有 ID 都在白名单内)。",
},
"platform_settings.id_whitelist_log": {
"description": "输出日志",
"type": "bool",
"hint": "启用后,当一条消息没通过白名单时,会输出 INFO 级别的日志。",
},
"platform_settings.wl_ignore_admin_on_group": {
"description": "管理员群组消息无视 ID 白名单",
"type": "bool",
},
"platform_settings.wl_ignore_admin_on_friend": {
"description": "管理员私聊消息无视 ID 白名单",
"type": "bool",
},
},
},
"rate_limit": {
"description": "速率限制",
"type": "object",
Expand Down Expand Up @@ -4488,8 +4453,55 @@ def get_local_permission_defaults(system: str | None = None) -> dict:
},
},
},
"access_group": {
"name": "访问控制",
"metadata": {
"admins": {
"description": "管理员",
"type": "object",
"items": {
"admins_id": {
"description": "管理员 ID",
"type": "list",
"items": {"type": "string"},
"hint": "给机器人发送 /sid 指令以获取 ID。",
},
},
},
"whitelist": {
"description": "白名单",
"type": "object",
"items": {
"platform_settings.enable_id_white_list": {
"description": "启用白名单",
"type": "bool",
"hint": "启用后,只有在白名单内的会话会被响应。",
},
"platform_settings.id_whitelist": {
"description": "白名单 ID 列表",
"type": "list",
"items": {"type": "string"},
"hint": "给机器人发送 /sid 指令以获取 ID。为空时代表不启用白名单功能。",
},
"platform_settings.id_whitelist_log": {
"description": "输出拦截日志",
"type": "bool",
"hint": "输出未通过白名单校验的消息的日志。",
},
"platform_settings.wl_ignore_admin_on_group": {
"description": "管理员群组消息无视 ID 白名单",
"type": "bool",
},
"platform_settings.wl_ignore_admin_on_friend": {
"description": "管理员私聊消息无视 ID 白名单",
"type": "bool",
},
},
},
},
},
"plugin_group": {
"name": "插件配置",
"name": "插件",
"metadata": {
"plugin": {
"description": "插件",
Expand Down
3 changes: 2 additions & 1 deletion astrbot/core/pipeline/whitelist_check/stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ async def process(
logger.info(
f"Session ID {event.unified_msg_origin} is not in the session "
"allowlist, so event propagation was stopped. Add this session "
"ID to the allowlist in the configuration file.",
"ID in WebUI: Config -> Access Control -> Whitelist -> "
"Whitelist ID List, using the profile assigned to this bot.",
)
event.stop_event()
4 changes: 3 additions & 1 deletion astrbot/core/tools/computer_tools/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,9 @@ def check_admin_permission(
if require_admin and context.context.event.role != "admin":
return (
f"error: Permission denied. {operation_name} is only allowed for admin users. "
"Tell user to set admins in `AstrBot WebUI -> Config -> General Config` by adding their user ID to the admins list if they need this feature. "
"Tell user to add their user ID under `AstrBot WebUI -> Config -> "
"Access Control -> Administrators -> Administrator IDs` in the "
"profile used by this bot if they need this feature. "
f"User's ID is: {context.context.event.get_sender_id()}. User's ID can be found by using /sid command."
)
return None
Expand Down
5 changes: 5 additions & 0 deletions changelogs/unreleased.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Unreleased

- WebUI: Add model settings shortcuts beside selected models and inside model/fallback menus. Model edits save immediately for all profiles using that model.
- WebUI: Move **Platform → General → Administrator IDs** to **Access Control → Administrators**, and **Platform → Whitelist** to **Access Control → Whitelist**. Rename Platform to Platforms & Messages. Order sections as AI, Platforms & Messages, Access Control, Plugin, and Extensions; remove the “configuration” suffix from section labels.
- WebUI:模型选择器旁及模型、回退模型菜单内新增设置齿轮,模型设置保存后立即影响引用它的所有配置文件。
- WebUI:**平台配置 → 基本 → 管理员 ID** 移至 **访问控制 → 管理员**,**平台配置 → 白名单** 移至 **访问控制 → 白名单**;平台分区改名为平台与消息;分区顺序调整为 AI、平台与消息、访问控制、插件、扩展功能,分区名称去掉“配置”后缀。

- Generate numbered original-path notices in the image preparation stage during main-agent construction, including quoted images. After request hooks, apply the same size and format policy only to newly introduced image references and refresh their notices. Skipped and captioned attachments retain explicit status without taking a visual index; animation labels identify frame montages.
- Raise the model image input cap from 32 MiB to 64 MiB. Larger originals are skipped before reading image bytes, with a model notice retaining their paths and suggesting the file-reading tool or a smaller upload; accepted inputs still produce images strictly below 512 KiB.
- Local Agent input images are always prepared as JPEG/PNG files strictly below 512 KiB. Compliant local images are reused without copying. Transparent previews retain PNG alpha; animations become 3×3 montages. Original attachment paths remain available to tools, and event-owned previews are deleted after use without a shared conversion cache.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Auto-generated MDI subset – 265 icons */
/* Auto-generated MDI subset – 266 icons */
/* Do not edit manually. Run: pnpm run subset-icons */

@font-face {
Expand Down Expand Up @@ -32,6 +32,10 @@
content: "\F0FFB";
}

.mdi-account-lock-outline::before {
content: "\F115F";
}

.mdi-account-voice::before {
content: "\F05CB";
}
Expand Down
Binary file not shown.
Binary file not shown.
3 changes: 2 additions & 1 deletion dashboard/src/components/config/AstrBotCoreConfigWrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,10 @@ import AstrBotConfigV4 from '@/components/shared/AstrBotConfigV4.vue';
import PluginSetSelector from '@/components/shared/PluginSetSelector.vue';
import { useModuleI18n } from '@/i18n/composables';

const SECTION_ORDER = ['ai_group', 'plugin_group', 'platform_group', 'ext_group'];
const SECTION_ORDER = ['ai_group', 'platform_group', 'access_group', 'plugin_group', 'ext_group'];
const SECTION_ICONS = {
ai_group: 'mdi-auto-fix',
access_group: 'mdi-account-lock-outline',
plugin_group: 'mdi-puzzle-outline',
platform_group: 'mdi-robot-outline',
ext_group: 'mdi-tune-variant'
Expand Down
135 changes: 135 additions & 0 deletions dashboard/src/components/shared/ProviderSelectMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,23 @@
</template>
<span>{{ providerTm("models.testButton") }}</span>
</v-tooltip>
<v-tooltip v-if="variant === 'config'" location="top">
<template #activator="{ props: editTooltipProps }">
<v-btn
v-bind="editTooltipProps"
icon="mdi-cog-outline"
size="x-small"
variant="text"
:aria-label="`${providerTm(
'dialogs.config.editTitle',
)} ${provider.id}`"
:loading="editingProviderId === provider.id"
:disabled="Boolean(editingProviderId)"
@click.stop="editProvider(provider)"
/>
</template>
<span>{{ providerTm("dialogs.config.editTitle") }}</span>
</v-tooltip>
<v-icon
v-if="isProviderSelected(provider.id)"
class="provider-selected-icon"
Expand Down Expand Up @@ -345,6 +362,31 @@
</div>
</v-card>
</v-menu>
<v-tooltip
v-if="
variant === 'config' &&
!multiple &&
typeof modelValue === 'string' &&
modelValue
"
location="top"
>
<template #activator="{ props: editTooltipProps }">
<v-btn
v-bind="editTooltipProps"
icon="mdi-cog-outline"
size="x-small"
variant="text"
:aria-label="`${providerTm(
'dialogs.config.editTitle',
)} ${modelValue}`"
:loading="editingProviderId === modelValue"
:disabled="Boolean(editingProviderId)"
@click.stop="editProvider({ id: modelValue })"
/>
</template>
<span>{{ providerTm("dialogs.config.editTitle") }}</span>
</v-tooltip>
</div>

<v-overlay
Expand All @@ -369,12 +411,49 @@
</div>
</v-card>
</v-overlay>

<v-dialog v-model="showProviderEditDialog" width="800">
<v-card>
<v-card-title class="text-h3 pa-4 pb-0 pl-6">
{{ providerEditDialogTitle }}
</v-card-title>
<v-card-text class="py-4">
<AstrBotConfig
v-if="providerEditData"
:iterable="providerEditData"
:metadata="providerModelConfigSchema"
metadata-key="provider"
:is-editing="true"
/>
</v-card-text>
<v-card-actions class="pa-4">
<v-spacer />
<v-btn
variant="text"
:disabled="savingProviders.includes(providerEditData?.id)"
@click="showProviderEditDialog = false"
>
{{ providerTm("dialogs.config.cancel") }}
</v-btn>
<v-btn
color="primary"
variant="tonal"
:loading="savingProviders.includes(providerEditData?.id)"
@click="saveEditedProvider"
>
{{ providerTm("dialogs.config.save") }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>

<script setup lang="ts">
import { computed, nextTick, ref, shallowRef, watch } from "vue";
import { useDisplay } from "vuetify";
import { providerApi } from "@/api/v1";
import AstrBotConfig from "@/components/shared/AstrBotConfig.vue";
import { useProviderModelConfigDialog } from "@/composables/useProviderModelConfigDialog";
import ProviderChatCompletionPanel from "@/components/provider/ProviderChatCompletionPanel.vue";
import ProviderPage from "@/views/ProviderPage.vue";
import { useModuleI18n } from "@/i18n/composables";
Expand Down Expand Up @@ -447,6 +526,25 @@ const providerListRef = ref<{ $el: HTMLElement } | null>(null);
const providerDrawer = ref(false);
const loadingProviders = ref(false);
const providersLoaded = ref(false);
const editingProviderId = ref("");
const configSchema = ref<Record<string, any>>({});
const selectedProviderSource = ref<any | null>(null);
const {
showProviderEditDialog,
providerEditData,
savingProviders,
providerModelConfigSchema,
providerEditDialogTitle,
openProviderEdit,
saveEditedProvider,
} = useProviderModelConfigDialog({
selectedProviderSource,
configSchema,
loadConfig: () => loadProviderConfigs(true),
tm: providerTm,
showMessage: (message, color) =>
color === "error" ? toastError(message) : toastSuccess(message),
});

const selectedProviderIds = computed(() =>
props.multiple && Array.isArray(props.modelValue)
Expand Down Expand Up @@ -703,6 +801,42 @@ async function testProvider(provider: ProviderConfig) {
}
}

async function editProvider(provider: ProviderConfig) {
if (editingProviderId.value) return;
editingProviderId.value = provider.id;
try {
// The selector list may include inherited fields; edit the stored model config.
const response = await providerApi.schema();
if (response.data.status === "error") {
throw new Error(
response.data.message || providerTm("providerSources.saveError"),
);
}
const data = response.data.data;
const storedProvider = data.providers?.find(
(item) => item.id === provider.id,
);
if (!storedProvider) {
throw new Error(providerTm("providerSources.saveError"));
}
configSchema.value = data.config_schema || {};
selectedProviderSource.value =
data.provider_sources?.find(
(source) => source.id === storedProvider.provider_source_id,
) || storedProvider;
openProviderEdit(storedProvider);
menuOpen.value = false;
} catch (error: any) {
toastError(
error.response?.data?.message ||
error.message ||
providerTm("providerSources.saveError"),
);
} finally {
editingProviderId.value = "";
}
}

function handleMenuToggle(isOpen: boolean) {
if (isOpen) {
scrollToSelectionPending = true;
Expand Down Expand Up @@ -780,6 +914,7 @@ defineExpose({ getCurrentSelection });

.provider-select-menu--config {
display: flex;
align-items: center;
width: 100%;
justify-content: flex-end;
}
Expand Down
Loading
Loading