mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-01 01:10:21 +08:00
* fix: unify media reference handling * fix: accept bare base64 record media refs * chore: update agents.md * fix: unify file URI handling across media components and utilities * fix: unify media reference type handling with MediaRefStr alias * Potential fix for pull request finding 'CodeQL / Incomplete URL substring sanitization' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update astrbot/core/platform/sources/discord/discord_platform_adapter.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * fix: unify media handling and improve base64 decoding across components * fix: simplify client_kwargs type definition and enhance media message handling in platform adapter * fix: unify media utility documentation and enhance function descriptions * perf: drop "pilk" requirement, improve audio outbound for tencent-related IM apps which using silk * fix: unify Tencent Silk audio handling and enhance media resolver functionality --- - Centralize media reference materialization and base64 resolution for local paths, http(s), base64://, data URIs, and legacy bare base64 payloads. - Normalize incoming Record audio to wav and Image media to temporary jpg during preprocess, with event-scoped cleanup. - Reuse the shared media resolver across OpenAI, Gemini, Anthropic, MiMo, DeerFlow, STT, and platform media paths while sanitizing logs and cleaning temporary conversion outputs. - Ensure generated TTS audio is tracked for cleanup after the event finishes. fix #8676 fix #8543 fix #7588 fix #7580 fix #8030 fix #8034 fix #7461 fix #7565 fix #6509 fix #7144 fix #7795 --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
109 lines
3.2 KiB
Python
109 lines
3.2 KiB
Python
import base64
|
|
from io import BytesIO
|
|
|
|
import pytest
|
|
from PIL import Image as PILImage
|
|
|
|
from astrbot.core.agent.runners.deerflow.deerflow_agent_runner import (
|
|
DeerFlowAgentRunner,
|
|
)
|
|
from astrbot.core.agent.runners.deerflow.deerflow_content_mapper import (
|
|
build_user_content_resolved,
|
|
)
|
|
|
|
|
|
def _png_base64() -> str:
|
|
image_buffer = BytesIO()
|
|
PILImage.new("RGBA", (1, 1), (255, 0, 0, 255)).save(image_buffer, format="PNG")
|
|
return base64.b64encode(image_buffer.getvalue()).decode("ascii")
|
|
|
|
|
|
def test_build_payload_includes_configurable_runtime_overrides_and_legacy_context():
|
|
runner = DeerFlowAgentRunner()
|
|
runner.assistant_id = "lead_agent"
|
|
runner.thinking_enabled = True
|
|
runner.plan_mode = True
|
|
runner.subagent_enabled = True
|
|
runner.max_concurrent_subagents = 5
|
|
runner.model_name = "gpt-4.1"
|
|
runner.recursion_limit = 321
|
|
|
|
payload = runner._build_payload(
|
|
thread_id="thread-123",
|
|
prompt="hello deerflow",
|
|
image_urls=[],
|
|
system_prompt=None,
|
|
)
|
|
|
|
expected_runtime = {
|
|
"thread_id": "thread-123",
|
|
"thinking_enabled": True,
|
|
"is_plan_mode": True,
|
|
"subagent_enabled": True,
|
|
"max_concurrent_subagents": 5,
|
|
"model_name": "gpt-4.1",
|
|
}
|
|
|
|
assert payload["assistant_id"] == "lead_agent"
|
|
assert payload["stream_mode"] == ["values", "messages-tuple", "custom"]
|
|
assert payload["config"]["recursion_limit"] == 321
|
|
assert payload["config"]["configurable"] == expected_runtime
|
|
assert payload["context"] == expected_runtime
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_build_user_content_resolved_supports_base64_scheme(
|
|
tmp_path, monkeypatch
|
|
):
|
|
monkeypatch.setattr(
|
|
"astrbot.core.utils.media_utils.get_astrbot_temp_path",
|
|
lambda: str(tmp_path),
|
|
)
|
|
image_base64 = _png_base64()
|
|
|
|
content = await build_user_content_resolved(
|
|
"look",
|
|
[f"base64://{image_base64}"],
|
|
)
|
|
|
|
assert content == [
|
|
{"type": "text", "text": "look"},
|
|
{
|
|
"type": "image_url",
|
|
"image_url": {"url": f"data:image/png;base64,{image_base64}"},
|
|
},
|
|
]
|
|
assert not list(tmp_path.iterdir())
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_build_payload_resolved_supports_local_image_path(tmp_path, monkeypatch):
|
|
monkeypatch.setattr(
|
|
"astrbot.core.utils.media_utils.get_astrbot_temp_path",
|
|
lambda: str(tmp_path),
|
|
)
|
|
image_path = tmp_path / "image.png"
|
|
image_base64 = _png_base64()
|
|
image_path.write_bytes(base64.b64decode(image_base64))
|
|
|
|
runner = DeerFlowAgentRunner()
|
|
runner.assistant_id = "lead_agent"
|
|
runner.thinking_enabled = False
|
|
runner.plan_mode = False
|
|
runner.subagent_enabled = False
|
|
runner.max_concurrent_subagents = 3
|
|
runner.model_name = ""
|
|
runner.recursion_limit = 1000
|
|
|
|
payload = await runner._build_payload_resolved(
|
|
thread_id="thread-123",
|
|
prompt="look",
|
|
image_urls=[str(image_path)],
|
|
system_prompt=None,
|
|
)
|
|
|
|
content = payload["input"]["messages"][-1]["content"]
|
|
assert content[0] == {"type": "text", "text": "look"}
|
|
assert content[1]["type"] == "image_url"
|
|
assert content[1]["image_url"]["url"] == f"data:image/png;base64,{image_base64}"
|