Skip to content

Commit 597690e

Browse files
committed
fix: preserve redaction context and restore provider hooks
1 parent 086a11e commit 597690e

3 files changed

Lines changed: 58 additions & 30 deletions

File tree

astrbot/builtin_stars/builtin_commands/commands/provider.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,11 @@ async def _find_provider_for_model(
263263
batch_providers = all_providers[start : start + max_concurrency]
264264
batch_results = await asyncio.gather(
265265
*[
266-
self._get_provider_models(provider, umo=umo)
266+
self._get_provider_models(
267+
provider,
268+
use_cache=False,
269+
umo=umo,
270+
)
267271
for provider in batch_providers
268272
],
269273
return_exceptions=True,

astrbot/core/provider/manager.py

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -73,40 +73,44 @@ def __init__(
7373
self.curr_tts_provider_inst: TTSProvider | None = None
7474
"""默认的 Text To Speech Provider 实例。已弃用,请使用 get_using_provider() 方法获取当前使用的 Provider 实例。"""
7575
self.db_helper = db_helper
76-
self._on_provider_changed: (
77-
Callable[[str, ProviderType, str | None], None] | None
78-
) = None
76+
self._provider_change_hooks: list[
77+
Callable[[str, ProviderType, str | None], None]
78+
] = []
7979

8080
def set_provider_change_callback(
8181
self,
8282
cb: Callable[[str, ProviderType, str | None], None] | None,
8383
) -> None:
84-
self._on_provider_changed = cb
84+
# Backward-compatible single-callback setter.
85+
self._provider_change_hooks.clear()
86+
if cb is not None:
87+
self._provider_change_hooks.append(cb)
8588

8689
def register_provider_change_hook(
8790
self,
8891
hook: Callable[[str, ProviderType, str | None], None],
8992
) -> None:
90-
# Backward-compatible wrapper for older call sites.
91-
self.set_provider_change_callback(hook)
93+
if hook not in self._provider_change_hooks:
94+
self._provider_change_hooks.append(hook)
9295

9396
def _notify_provider_changed(
9497
self,
9598
provider_id: str,
9699
provider_type: ProviderType,
97100
umo: str | None,
98101
) -> None:
99-
if not self._on_provider_changed:
102+
if not self._provider_change_hooks:
100103
return
101-
try:
102-
self._on_provider_changed(provider_id, provider_type, umo)
103-
except Exception as e:
104-
logger.warning(
105-
"调用 provider 变更回调失败: provider_id=%s, type=%s, err=%s",
106-
provider_id,
107-
provider_type,
108-
safe_error("", e),
109-
)
104+
for hook in list(self._provider_change_hooks):
105+
try:
106+
hook(provider_id, provider_type, umo)
107+
except Exception as e:
108+
logger.warning(
109+
"调用 provider 变更钩子失败: provider_id=%s, type=%s, err=%s",
110+
provider_id,
111+
provider_type,
112+
safe_error("", e),
113+
)
110114

111115
@property
112116
def persona_configs(self) -> list:

astrbot/core/utils/error_redaction.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,59 @@
11
import re
22

33
_SECRET_KEYS = (
4-
r"(api_?key|access_?token|auth_?token|refresh_?token|session_?id|secret|password)"
4+
r"(?:api_?key|access_?token|auth_?token|refresh_?token|session_?id|secret|password)"
55
)
66

77
_JSON_FIELD_PATTERN = re.compile(
8-
rf"(?i)(['\"])({_SECRET_KEYS})\1\s*:\s*(['\"])[^'\"]+\3"
8+
rf"(?i)(?P<prefix>(?P<kq>['\"]){_SECRET_KEYS}(?P=kq)\s*:\s*)(?P<vq>['\"])(?P<value>[^'\"]+)(?P=vq)"
99
)
1010
_AUTH_JSON_FIELD_PATTERN = re.compile(
11-
r"(?i)(['\"])authorization\1\s*:\s*(['\"])bearer\s+[^'\"]+\2"
11+
r"(?i)(?P<prefix>(?P<kq>['\"])authorization(?P=kq)\s*:\s*)(?P<vq>['\"])bearer\s+[^'\"]+(?P=vq)"
12+
)
13+
_QUERY_FIELD_PATTERN = re.compile(
14+
rf"(?i)(?P<prefix>{_SECRET_KEYS}\s*=\s*)(?P<value>[^&'\" ]+)"
1215
)
13-
_QUERY_FIELD_PATTERN = re.compile(rf"(?i)\b{_SECRET_KEYS}\s*=\s*[^&'\" ]+")
1416
_QUERY_PARAM_PATTERN = re.compile(
15-
r"(?i)([?&](?:api_?key|key|access_?token|auth_?token))=[^&'\" ]+"
17+
r"(?i)(?P<prefix>[?&](?:api_?key|key|access_?token|auth_?token)=)(?P<value>[^&'\" ]+)"
1618
)
1719
_AUTH_HEADER_PATTERN = re.compile(
18-
r"(?i)\bauthorization\s*:\s*bearer\s+[A-Za-z0-9._\-]+"
20+
r"(?i)(?P<prefix>\bauthorization\s*:\s*bearer\s+)(?P<token>[A-Za-z0-9._\-]+)"
1921
)
20-
_BEARER_PATTERN = re.compile(r"(?i)\bbearer\s+[A-Za-z0-9._\-]+")
22+
_BEARER_PATTERN = re.compile(r"(?i)(?P<prefix>\bbearer\s+)(?P<token>[A-Za-z0-9._\-]+)")
2123
_SK_PATTERN = re.compile(r"\bsk-[A-Za-z0-9]{16,}\b")
2224

2325

26+
def _redact_json_field(match: re.Match[str]) -> str:
27+
quote = match.group("vq")
28+
return f"{match.group('prefix')}{quote}[REDACTED]{quote}"
29+
30+
31+
def _redact_auth_json_field(match: re.Match[str]) -> str:
32+
quote = match.group("vq")
33+
return f"{match.group('prefix')}{quote}Bearer [REDACTED]{quote}"
34+
35+
36+
def _redact_prefixed_value(match: re.Match[str]) -> str:
37+
return f"{match.group('prefix')}[REDACTED]"
38+
39+
40+
def _redact_bearer_token(match: re.Match[str]) -> str:
41+
return f"{match.group('prefix')}[REDACTED]"
42+
43+
2444
def _redact_json_like(text: str) -> str:
25-
text = _JSON_FIELD_PATTERN.sub("[REDACTED]", text)
26-
return _AUTH_JSON_FIELD_PATTERN.sub("[REDACTED]", text)
45+
text = _JSON_FIELD_PATTERN.sub(_redact_json_field, text)
46+
return _AUTH_JSON_FIELD_PATTERN.sub(_redact_auth_json_field, text)
2747

2848

2949
def _redact_query_like(text: str) -> str:
30-
text = _QUERY_FIELD_PATTERN.sub("[REDACTED]", text)
31-
return _QUERY_PARAM_PATTERN.sub("[REDACTED]", text)
50+
text = _QUERY_FIELD_PATTERN.sub(_redact_prefixed_value, text)
51+
return _QUERY_PARAM_PATTERN.sub(_redact_prefixed_value, text)
3252

3353

3454
def _redact_tokens(text: str) -> str:
35-
text = _AUTH_HEADER_PATTERN.sub("[REDACTED]", text)
36-
text = _BEARER_PATTERN.sub("[REDACTED]", text)
55+
text = _AUTH_HEADER_PATTERN.sub(_redact_bearer_token, text)
56+
text = _BEARER_PATTERN.sub(_redact_bearer_token, text)
3757
return _SK_PATTERN.sub("[REDACTED]", text)
3858

3959

0 commit comments

Comments
 (0)