diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index 2b2ad3959..81f1212b9 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -969,6 +969,7 @@ class ChatProviderTemplate(TypedDict): }, "gm_thinking_config": {"budget": 0, "level": "HIGH"}, "proxy": "", + "custom_headers": {}, }, "Anthropic": { "id": "anthropic", @@ -980,6 +981,7 @@ class ChatProviderTemplate(TypedDict): "api_base": "https://api.anthropic.com/v1", "timeout": 120, "proxy": "", + "custom_headers": {}, "anth_thinking_config": {"type": "", "budget": 0, "effort": ""}, }, "Moonshot": { @@ -1212,6 +1214,7 @@ class ChatProviderTemplate(TypedDict): "variables": {}, "timeout": 60, "proxy": "", + "custom_headers": {}, }, "Coze": { "id": "coze", @@ -1224,6 +1227,7 @@ class ChatProviderTemplate(TypedDict): "coze_api_base": "https://api.coze.cn", "timeout": 60, "proxy": "", + "custom_headers": {}, # "auto_save_history": True, }, "阿里云百炼应用": { @@ -1243,6 +1247,7 @@ class ChatProviderTemplate(TypedDict): "variables": {}, "timeout": 60, "proxy": "", + "custom_headers": {}, }, "FastGPT": { "id": "fastgpt", @@ -1267,6 +1272,7 @@ class ChatProviderTemplate(TypedDict): "api_base": "", "model": "whisper-1", "proxy": "", + "custom_headers": {}, }, "Whisper(Local)": { "provider": "openai", @@ -1275,6 +1281,7 @@ class ChatProviderTemplate(TypedDict): "enable": False, "id": "whisper_selfhost", "model": "tiny", + "custom_headers": {}, }, "SenseVoice(Local)": { "type": "sensevoice_stt_selfhost", @@ -1284,6 +1291,7 @@ class ChatProviderTemplate(TypedDict): "id": "sensevoice", "stt_model": "iic/SenseVoiceSmall", "is_emotion": False, + "custom_headers": {}, }, "OpenAI TTS(API)": { "id": "openai_tts", @@ -1297,6 +1305,7 @@ class ChatProviderTemplate(TypedDict): "openai-tts-voice": "alloy", "timeout": "20", "proxy": "", + "custom_headers": {}, }, "Genie TTS": { "id": "genie_tts", @@ -1310,6 +1319,7 @@ class ChatProviderTemplate(TypedDict): "genie_refer_audio_path": "", "genie_refer_text": "", "timeout": 20, + "custom_headers": {}, }, "Edge TTS": { "id": "edge_tts", @@ -1322,6 +1332,7 @@ class ChatProviderTemplate(TypedDict): "volume": "+0%", "pitch": "+0Hz", "timeout": 20, + "custom_headers": {}, }, "GSV TTS(Local)": { "id": "gsv_tts", @@ -1333,6 +1344,7 @@ class ChatProviderTemplate(TypedDict): "gpt_weights_path": "", "sovits_weights_path": "", "timeout": 60, + "custom_headers": {}, "gsv_default_parms": { "gsv_ref_audio_path": "", "gsv_prompt_text": "", @@ -1365,6 +1377,7 @@ class ChatProviderTemplate(TypedDict): "emotion": "default", "enable": False, "timeout": 20, + "custom_headers": {}, }, "FishAudio TTS(API)": { "id": "fishaudio_tts", @@ -1378,6 +1391,7 @@ class ChatProviderTemplate(TypedDict): "fishaudio-tts-reference-id": "", "timeout": "20", "proxy": "", + "custom_headers": {}, }, "阿里云百炼 TTS(API)": { "hint": "API Key 从 https://bailian.console.aliyun.com/?tab=model#/api-key 获取。模型和音色的选择文档请参考: 阿里云百炼语音合成音色名称。具体可参考 https://help.aliyun.com/zh/model-studio/speech-synthesis-and-speech-recognition", @@ -1390,6 +1404,7 @@ class ChatProviderTemplate(TypedDict): "model": "cosyvoice-v1", "dashscope_tts_voice": "loongstella", "timeout": "20", + "custom_headers": {}, }, "Azure TTS": { "id": "azure_tts", @@ -1405,6 +1420,7 @@ class ChatProviderTemplate(TypedDict): "azure_tts_subscription_key": "", "azure_tts_region": "eastus", "proxy": "", + "custom_headers": {}, }, "MiniMax TTS(API)": { "id": "minimax_tts", @@ -1428,6 +1444,7 @@ class ChatProviderTemplate(TypedDict): "minimax-voice-english-normalization": False, "timeout": 20, "proxy": "", + "custom_headers": {}, }, "火山引擎_TTS(API)": { "id": "volcengine_tts", @@ -1443,6 +1460,7 @@ class ChatProviderTemplate(TypedDict): "api_base": "https://openspeech.bytedance.com/api/v1/tts", "timeout": 20, "proxy": "", + "custom_headers": {}, }, "Gemini TTS": { "id": "gemini_tts", @@ -1457,6 +1475,7 @@ class ChatProviderTemplate(TypedDict): "gemini_tts_prefix": "", "gemini_tts_voice_name": "Leda", "proxy": "", + "custom_headers": {}, }, "OpenAI Embedding": { "id": "openai_embedding", @@ -1470,6 +1489,7 @@ class ChatProviderTemplate(TypedDict): "embedding_dimensions": 1024, "timeout": 20, "proxy": "", + "custom_headers": {}, }, "Gemini Embedding": { "id": "gemini_embedding", @@ -1483,6 +1503,7 @@ class ChatProviderTemplate(TypedDict): "embedding_dimensions": 768, "timeout": 20, "proxy": "", + "custom_headers": {}, }, "vLLM Rerank": { "id": "vllm_rerank", @@ -1494,6 +1515,7 @@ class ChatProviderTemplate(TypedDict): "rerank_api_base": "http://127.0.0.1:8000", "rerank_model": "BAAI/bge-reranker-base", "timeout": 20, + "custom_headers": {}, }, "Xinference Rerank": { "id": "xinference_rerank", @@ -1506,6 +1528,7 @@ class ChatProviderTemplate(TypedDict): "rerank_model": "BAAI/bge-reranker-base", "timeout": 20, "launch_model_if_not_running": False, + "custom_headers": {}, }, "阿里云百炼重排序": { "id": "bailian_rerank", @@ -1519,6 +1542,7 @@ class ChatProviderTemplate(TypedDict): "timeout": 30, "return_documents": False, "instruct": "", + "custom_headers": {}, }, "Xinference STT": { "id": "xinference_stt", @@ -1531,6 +1555,7 @@ class ChatProviderTemplate(TypedDict): "model": "whisper-large-v3", "timeout": 180, "launch_model_if_not_running": False, + "custom_headers": {}, }, }, "items": { @@ -1597,6 +1622,29 @@ class ChatProviderTemplate(TypedDict): "type": "dict", "items": {}, "hint": "此处添加的键值对将被合并到 OpenAI SDK 的 default_headers 中,用于自定义 HTTP 请求头。值必须为字符串。", + "template_schema": { + "User-Agent": { + "name": "User-Agent", + "description": "User-Agent 请求头", + "hint": "用于覆盖默认客户端标识。", + "type": "string", + "default": "", + }, + "Accept": { + "name": "Accept", + "description": "Accept 请求头", + "hint": "声明可接受的响应类型。默认留空以使用 SDK/服务端默认值,仅在需要时显式覆盖(例如指定 application/json)。", + "type": "string", + "default": "", + }, + "Accept-Language": { + "name": "Accept-Language", + "description": "Accept-Language 请求头", + "hint": "声明偏好语言,例如 zh-CN,zh;q=0.9,en;q=0.8。", + "type": "string", + "default": "", + }, + }, }, "custom_extra_body": { "description": "自定义请求体参数", diff --git a/astrbot/dashboard/routes/config.py b/astrbot/dashboard/routes/config.py index 6d60fb6de..7576ca013 100644 --- a/astrbot/dashboard/routes/config.py +++ b/astrbot/dashboard/routes/config.py @@ -289,6 +289,24 @@ def __init__( } self.register_routes() + @staticmethod + def _with_default_custom_headers(config: dict) -> dict: + normalized = dict(config) if isinstance(config, dict) else {} + headers = normalized.get("custom_headers") + if not isinstance(headers, dict): + normalized["custom_headers"] = {} + return normalized + + # 在返回 provider_sources 和 provider 配置时,如果 custom_headers 字段不是 dict,则强制转换为 dict,避免前端使用时出错 + def _normalize_custom_headers_configs(self, configs: list) -> list: + normalized = [] + for config in configs: + if isinstance(config, dict): + normalized.append(self._with_default_custom_headers(config)) + else: + normalized.append(config) + return normalized + async def delete_provider_source(self): """删除 provider_source,并更新关联的 providers""" post_data = await request.json @@ -345,6 +363,8 @@ async def update_provider_source(self): if not isinstance(new_source_config, dict): return Response().error("缺少或错误的配置数据").__dict__ + new_source_config = self._with_default_custom_headers(new_source_config) + # 确保配置中有 id 字段 if not new_source_config.get("id"): new_source_config["id"] = original_id @@ -426,8 +446,12 @@ async def get_provider_template(self): } data = { "config_schema": config_schema, - "providers": astrbot_config["provider"], - "provider_sources": astrbot_config["provider_sources"], + "providers": self._normalize_custom_headers_configs( + astrbot_config["provider"] + ), + "provider_sources": self._normalize_custom_headers_configs( + astrbot_config["provider_sources"] + ), } return Response().ok(data=data).__dict__ @@ -1136,6 +1160,7 @@ async def post_new_platform(self): async def post_new_provider(self): new_provider_config = await request.json + new_provider_config = self._with_default_custom_headers(new_provider_config) try: await self.core_lifecycle.provider_manager.create_provider( @@ -1179,6 +1204,8 @@ async def post_update_provider(self): if not origin_provider_id or not new_config: return Response().error("参数错误").__dict__ + new_config = self._with_default_custom_headers(new_config) + try: await self.core_lifecycle.provider_manager.update_provider( origin_provider_id, new_config diff --git a/dashboard/src/i18n/locales/en-US/features/config-metadata.json b/dashboard/src/i18n/locales/en-US/features/config-metadata.json index 9665e893a..db6d06bd0 100644 --- a/dashboard/src/i18n/locales/en-US/features/config-metadata.json +++ b/dashboard/src/i18n/locales/en-US/features/config-metadata.json @@ -961,7 +961,24 @@ }, "custom_headers": { "description": "Custom request headers", - "hint": "Key/value pairs added here are merged into the OpenAI SDK default_headers for custom HTTP headers. Values must be strings." + "hint": "Key/value pairs added here are merged into the OpenAI SDK default_headers for custom HTTP headers. Values must be strings.", + "template_schema": { + "User-Agent": { + "description": "User-Agent header", + "hint": "Override the default client identity.", + "name": "User-Agent" + }, + "Accept": { + "description": "Accept header", + "hint": "Declares accepted response formats. Leave empty by default to use SDK/provider defaults and only override when needed (e.g. application/json).", + "name": "Accept" + }, + "Accept-Language": { + "description": "Accept-Language header", + "hint": "Declares preferred languages, e.g. zh-CN,zh;q=0.9,en;q=0.8.", + "name": "Accept-Language" + } + } }, "custom_extra_body": { "description": "Custom request body parameters", diff --git a/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json b/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json index de7e81bcd..2fc3d071f 100644 --- a/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json +++ b/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json @@ -964,7 +964,24 @@ }, "custom_headers": { "description": "自定义添加请求头", - "hint": "此处添加的键值对将被合并到 OpenAI SDK 的 default_headers 中,用于自定义 HTTP 请求头。值必须为字符串。" + "hint": "此处添加的键值对将被合并到 OpenAI SDK 的 default_headers 中,用于自定义 HTTP 请求头。值必须为字符串。", + "template_schema": { + "User-Agent": { + "description": "User-Agent 请求头", + "hint": "用于覆盖默认客户端标识。", + "name": "User-Agent" + }, + "Accept": { + "description": "Accept 请求头", + "hint": "声明可接受的响应类型。默认留空以使用 SDK/服务端默认值,仅在需要时显式覆盖(例如指定 application/json)。", + "name": "Accept" + }, + "Accept-Language": { + "description": "Accept-Language 请求头", + "hint": "声明偏好语言,例如 zh-CN,zh;q=0.9,en;q=0.8。", + "name": "Accept-Language" + } + } }, "custom_extra_body": { "description": "自定义请求体参数",