@@ -1247,6 +1247,77 @@ async def test_build_main_agent_skips_caption_when_main_provider_supports_images
12471247 )
12481248 mock_provider .text_chat .assert_not_called ()
12491249
1250+ @pytest .mark .asyncio
1251+ async def test_build_main_agent_does_not_caption_quoted_image_twice (
1252+ self , mock_event , mock_context
1253+ ):
1254+ """Quoted images should not be captioned again after request image captioning."""
1255+ module = ama
1256+ text_provider = MagicMock (spec = Provider )
1257+ text_provider .provider_config = {
1258+ "id" : "text-provider" ,
1259+ "modalities" : ["text" , "tool_use" ],
1260+ }
1261+ text_provider .get_model .return_value = "text-model"
1262+
1263+ caption_provider = MagicMock (spec = Provider )
1264+ caption_provider .text_chat = AsyncMock (
1265+ return_value = MagicMock (completion_text = "quoted image caption" )
1266+ )
1267+
1268+ mock_reply = Reply (
1269+ id = "reply-1" ,
1270+ chain = [Plain (text = "quoted text" ), Image (file = "file:///tmp/quoted.jpg" )],
1271+ sender_nickname = "Alice" ,
1272+ message_str = "quoted text" ,
1273+ )
1274+ mock_event .message_obj .message = [Plain (text = "Hello" ), mock_reply ]
1275+
1276+ mock_context .get_provider_by_id .return_value = caption_provider
1277+ mock_context .get_using_provider .return_value = text_provider
1278+ mock_context .get_config .return_value = {}
1279+
1280+ conv_mgr = mock_context .conversation_manager
1281+ _setup_conversation_for_build (conv_mgr )
1282+
1283+ with (
1284+ patch ("astrbot.core.astr_main_agent.AgentRunner" ) as mock_runner_cls ,
1285+ patch ("astrbot.core.astr_main_agent.AstrAgentContext" ),
1286+ patch .object (
1287+ Image ,
1288+ "convert_to_file_path" ,
1289+ AsyncMock (return_value = "/tmp/quoted.jpg" ),
1290+ ),
1291+ patch (
1292+ "astrbot.core.astr_main_agent._compress_image_for_provider" ,
1293+ AsyncMock (side_effect = lambda path , _settings : path ),
1294+ ),
1295+ ):
1296+ mock_runner = MagicMock ()
1297+ mock_runner .reset = AsyncMock ()
1298+ mock_runner_cls .return_value = mock_runner
1299+
1300+ result = await module .build_main_agent (
1301+ event = mock_event ,
1302+ plugin_context = mock_context ,
1303+ config = module .MainAgentBuildConfig (
1304+ tool_call_timeout = 60 ,
1305+ provider_settings = {
1306+ "default_image_caption_provider_id" : "caption-provider" ,
1307+ },
1308+ ),
1309+ provider = text_provider ,
1310+ )
1311+
1312+ assert result is not None
1313+ assert caption_provider .text_chat .await_count == 1
1314+
1315+ extra_text = "\n " .join (
1316+ part .text for part in result .provider_request .extra_user_content_parts
1317+ )
1318+ assert "<image_caption>quoted image caption</image_caption>" in extra_text
1319+ assert "[Image Caption in quoted message]" not in extra_text
1320+
12501321 @pytest .mark .asyncio
12511322 async def test_build_main_agent_uses_image_fallback_provider (
12521323 self , mock_event , mock_context
0 commit comments