Skip to content

Commit 0feaadb

Browse files
Merge branch 'AstrBotDevs:master' into fix/openai-max-retries
2 parents 5735f0c + b9e565f commit 0feaadb

25 files changed

Lines changed: 390 additions & 83 deletions

File tree

astrbot/core/config/astrbot_config.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,6 @@ def __init__(
9898
):
9999
self._reset_generated_dashboard_password(conf)
100100
has_new = True
101-
elif (
102-
"dashboard" in conf
103-
and isinstance(conf["dashboard"], dict)
104-
and stored_dashboard_password_change_required
105-
and conf["dashboard"].get("pbkdf2_password")
106-
):
107-
self._reset_generated_dashboard_password(conf)
108-
has_new = True
109101
self.update(conf)
110102
if has_new:
111103
self.save_config()

astrbot/core/config/default.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2170,6 +2170,13 @@
21702170
"type": "int",
21712171
"default": 8192,
21722172
},
2173+
"reasoning_effort": {
2174+
"name": "Reasoning Effort",
2175+
"description": "推理强度",
2176+
"hint": "控制推理模型的推理强度,支持的值取决于具体模型。",
2177+
"type": "string",
2178+
"default": "high",
2179+
},
21732180
},
21742181
},
21752182
"provider": {

astrbot/core/platform/sources/misskey/misskey_event.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
extract_user_id_from_session_id,
1414
is_valid_room_session_id,
1515
is_valid_user_session_id,
16-
resolve_visibility_from_raw_message,
16+
resolve_message_visibility,
1717
serialize_message_chain,
1818
)
1919

@@ -108,7 +108,7 @@ async def send(self, message: MessageChain) -> None:
108108
room_id = extract_room_id_from_session_id(self.session_id)
109109
await self.client.send_room_message(room_id, content)
110110
elif original_message_id and hasattr(self.client, "create_note"):
111-
visibility, visible_user_ids = resolve_visibility_from_raw_message(
111+
visibility, visible_user_ids = resolve_message_visibility(
112112
raw_message,
113113
)
114114
await self.client.create_note(

astrbot/dashboard/services/config_service.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,6 +1358,20 @@ def __init__(self, core_lifecycle: AstrBotCoreLifecycle) -> None:
13581358
self.config = core_lifecycle.astrbot_config
13591359
self.provider_manager = core_lifecycle.provider_manager
13601360

1361+
@staticmethod
1362+
def _strip_legacy_reasoning_metadata(provider: dict) -> dict:
1363+
"""Remove reasoning metadata accidentally stored as provider configuration.
1364+
1365+
Args:
1366+
provider: Provider configuration to sanitize in place.
1367+
1368+
Returns:
1369+
The sanitized provider configuration.
1370+
"""
1371+
if provider.get("provider_source_id"):
1372+
provider.pop("reasoning", None)
1373+
return provider
1374+
13611375
def get_provider_schema(self) -> dict:
13621376
provider_metadata = ConfigMetadataI18n.convert_to_i18n_keys(
13631377
{
@@ -1382,6 +1396,7 @@ def get_provider_schema(self) -> dict:
13821396

13831397
model_metadata = {}
13841398
for provider in providers:
1399+
self._strip_legacy_reasoning_metadata(provider)
13851400
model_id = provider.get("model")
13861401
if isinstance(model_id, str) and model_id in LLM_METADATAS:
13871402
model_metadata[model_id] = LLM_METADATAS[model_id]
@@ -1659,6 +1674,7 @@ def list_providers(
16591674
)
16601675
else:
16611676
provider_response = copy.deepcopy(provider)
1677+
self._strip_legacy_reasoning_metadata(provider_response)
16621678
model_id = provider_response.get("model")
16631679
if isinstance(model_id, str) and model_id in LLM_METADATAS:
16641680
model_metadata[model_id] = LLM_METADATAS[model_id]
@@ -1694,6 +1710,7 @@ def get_provider(self, provider_id: str, *, merged: bool = False) -> dict:
16941710
if provider is None:
16951711
raise ValueError(f"Provider {provider_id} not found")
16961712
provider_response = copy.deepcopy(provider)
1713+
self._strip_legacy_reasoning_metadata(provider_response)
16971714
from astrbot.core.utils.llm_metadata import LLM_METADATAS
16981715

16991716
model_id = provider_response.get("model")
@@ -1706,11 +1723,14 @@ async def create_provider(self, config: dict, source_id: str | None = None) -> N
17061723
config = copy.deepcopy(config)
17071724
if source_id:
17081725
config["provider_source_id"] = source_id
1726+
self._strip_legacy_reasoning_metadata(config)
17091727
await self.provider_manager.create_provider(config)
17101728

17111729
async def update_provider(self, provider_id: str, config: dict) -> None:
1730+
config = copy.deepcopy(config)
17121731
if not config.get("id"):
17131732
config["id"] = provider_id
1733+
self._strip_legacy_reasoning_metadata(config)
17141734
await self.provider_manager.update_provider(provider_id, config)
17151735

17161736
async def set_provider_enabled(self, provider_id: str, enabled: bool) -> None:

astrbot/dashboard/services/knowledge_base_service.py

Lines changed: 84 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import asyncio
4+
import shutil
45
import traceback
56
import uuid
67
from pathlib import Path
@@ -11,7 +12,7 @@
1112
from astrbot.core import logger
1213
from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
1314
from astrbot.core.provider.provider import EmbeddingProvider, RerankProvider
14-
from astrbot.core.utils.astrbot_path import get_astrbot_temp_path
15+
from astrbot.core.utils.astrbot_path import get_astrbot_system_tmp_path
1516
from astrbot.dashboard.schemas import KnowledgeBaseRequest
1617
from astrbot.dashboard.utils import generate_tsne_visualization
1718

@@ -116,17 +117,45 @@ def format_failed_doc_error(file_name: str, error: Exception) -> str:
116117
return message
117118
return f"{file_name}: {message}"
118119

120+
@staticmethod
121+
def _cleanup_staging_dir(staging_dir: Path) -> None:
122+
"""Remove a knowledge base upload staging directory.
123+
124+
Args:
125+
staging_dir: Task-specific system temporary directory to remove.
126+
"""
127+
try:
128+
shutil.rmtree(staging_dir)
129+
except FileNotFoundError:
130+
pass
131+
except OSError as exc:
132+
logger.warning(f"Failed to clean upload staging directory: {exc}")
133+
119134
async def background_upload_task(
120135
self,
121136
task_id: str,
122137
kb_helper,
123138
files_to_upload: list[dict[str, Any]],
139+
staging_dir: Path,
124140
chunk_size: int,
125141
chunk_overlap: int,
126142
batch_size: int,
127143
tasks_limit: int,
128144
max_retries: int,
129145
) -> None:
146+
"""Process staged knowledge base files one at a time.
147+
148+
Args:
149+
task_id: Identifier used to report upload progress and results.
150+
kb_helper: Knowledge base helper that parses and stores documents.
151+
files_to_upload: Metadata and temporary paths for staged files.
152+
staging_dir: Task-specific system temporary directory.
153+
chunk_size: Maximum size of each generated document chunk.
154+
chunk_overlap: Number of overlapping characters between chunks.
155+
batch_size: Number of chunks sent in each embedding batch.
156+
tasks_limit: Maximum number of concurrent embedding tasks.
157+
max_retries: Maximum retries for embedding operations.
158+
"""
130159
try:
131160
self.init_task(task_id, status="processing")
132161
self.upload_progress[task_id] = {
@@ -142,7 +171,11 @@ async def background_upload_task(
142171
failed_docs = []
143172

144173
for file_idx, file_info in enumerate(files_to_upload):
174+
file_content = None
145175
try:
176+
temp_file_path = Path(file_info["temp_file_path"])
177+
async with aiofiles.open(temp_file_path, "rb") as file_obj:
178+
file_content = await file_obj.read()
146179
self.update_progress(
147180
task_id,
148181
status="processing",
@@ -157,7 +190,7 @@ async def background_upload_task(
157190
)
158191
doc = await kb_helper.upload_document(
159192
file_name=file_info["file_name"],
160-
file_content=file_info["file_content"],
193+
file_content=file_content,
161194
file_type=file_info["file_type"],
162195
chunk_size=chunk_size,
163196
chunk_overlap=chunk_overlap,
@@ -177,6 +210,10 @@ async def background_upload_task(
177210
),
178211
},
179212
)
213+
finally:
214+
# Release the current file before reading the next one.
215+
file_content = None
216+
Path(file_info["temp_file_path"]).unlink(missing_ok=True)
180217

181218
self.set_task_result(
182219
task_id,
@@ -194,6 +231,8 @@ async def background_upload_task(
194231
logger.error(f"后台上传任务 {task_id} 失败: {exc}")
195232
logger.error(traceback.format_exc())
196233
self.set_task_result(task_id, "failed", error=str(exc))
234+
finally:
235+
self._cleanup_staging_dir(staging_dir)
197236

198237
async def background_import_task(
199238
self,
@@ -520,52 +559,62 @@ async def upload_document(
520559
file_list.extend(files.getlist(key))
521560
if not file_list:
522561
raise KnowledgeBaseServiceError("缺少文件")
523-
if len(file_list) > 10:
524-
raise KnowledgeBaseServiceError("最多只能上传10个文件")
525562

563+
task_id = str(uuid.uuid4())
564+
system_temp_root = Path(get_astrbot_system_tmp_path())
565+
system_temp_root.mkdir(mode=0o700, parents=True, exist_ok=True)
566+
staging_dir = system_temp_root / f"kb_upload_{task_id}"
567+
staging_dir.mkdir(mode=0o700)
526568
files_to_upload = []
527-
for file in file_list:
528-
file_name = Path(str(file.filename or "document").replace("\\", "/")).name
529-
if file_name in {"", ".", ".."}:
530-
file_name = "document"
531-
temp_file_path = (
532-
Path(get_astrbot_temp_path()) / f"kb_upload_{uuid.uuid4()}_{file_name}"
533-
)
534-
await file.save(temp_file_path)
535-
try:
536-
async with aiofiles.open(temp_file_path, "rb") as file_obj:
537-
file_content = await file_obj.read()
569+
try:
570+
for file in file_list:
571+
file_name = Path(
572+
str(file.filename or "document").replace("\\", "/")
573+
).name
574+
if file_name in {"", ".", ".."}:
575+
file_name = "document"
576+
temp_file_path = staging_dir / f"{uuid.uuid4()}_{file_name}"
538577
file_type = (
539578
file_name.rsplit(".", 1)[-1].lower() if "." in file_name else ""
540579
)
541580
files_to_upload.append(
542581
{
543582
"file_name": file_name,
544-
"file_content": file_content,
583+
"temp_file_path": temp_file_path,
545584
"file_type": file_type,
546585
},
547586
)
548-
finally:
549-
temp_file_path.unlink(missing_ok=True)
587+
await file.save(temp_file_path)
588+
except Exception:
589+
self._cleanup_staging_dir(staging_dir)
590+
raise
550591

551-
kb_helper = await self.get_kb_manager().get_kb(kb_id)
552-
if not kb_helper:
553-
raise KnowledgeBaseServiceError("知识库不存在")
592+
try:
593+
kb_helper = await self.get_kb_manager().get_kb(kb_id)
594+
if not kb_helper:
595+
raise KnowledgeBaseServiceError("知识库不存在")
596+
except Exception:
597+
self._cleanup_staging_dir(staging_dir)
598+
raise
554599

555-
task_id = str(uuid.uuid4())
556-
self.init_task(task_id, status="pending")
557-
asyncio.create_task(
558-
self.background_upload_task(
559-
task_id=task_id,
560-
kb_helper=kb_helper,
561-
files_to_upload=files_to_upload,
562-
chunk_size=chunk_size,
563-
chunk_overlap=chunk_overlap,
564-
batch_size=batch_size,
565-
tasks_limit=tasks_limit,
566-
max_retries=max_retries,
567-
),
568-
)
600+
try:
601+
self.init_task(task_id, status="pending")
602+
asyncio.create_task(
603+
self.background_upload_task(
604+
task_id=task_id,
605+
kb_helper=kb_helper,
606+
files_to_upload=files_to_upload,
607+
staging_dir=staging_dir,
608+
chunk_size=chunk_size,
609+
chunk_overlap=chunk_overlap,
610+
batch_size=batch_size,
611+
tasks_limit=tasks_limit,
612+
max_retries=max_retries,
613+
),
614+
)
615+
except Exception:
616+
self._cleanup_staging_dir(staging_dir)
617+
raise
569618
return {
570619
"task_id": task_id,
571620
"file_count": len(files_to_upload),

dashboard/src/components/provider/ProviderModelsPanel.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ const capabilityBadges = (entry) => {
378378
key: 'reasoning',
379379
icon: 'mdi-brain',
380380
supported: props.supportsReasoning(metadata),
381-
enabled: !isConfigured || Boolean(provider?.reasoning),
381+
enabled: props.supportsReasoning(metadata),
382382
label: props.tm('models.metadata.reasoning')
383383
}
384384
]

0 commit comments

Comments
 (0)