| 52 | |
| 53 | |
| 54 | class FunctionToolExecutor(BaseFunctionToolExecutor[AstrAgentContext]): |
| 55 | @classmethod |
| 56 | def _collect_image_urls_from_args(cls, image_urls_raw: T.Any) -> list[str]: |
| 57 | if image_urls_raw is None: |
| 58 | return [] |
| 59 | |
| 60 | if isinstance(image_urls_raw, str): |
| 61 | return [image_urls_raw] |
| 62 | |
| 63 | if isinstance(image_urls_raw, (Sequence, AbstractSet)) and not isinstance( |
| 64 | image_urls_raw, (str, bytes, bytearray) |
| 65 | ): |
| 66 | return [item for item in image_urls_raw if isinstance(item, str)] |
| 67 | |
| 68 | logger.debug( |
| 69 | "Unsupported image_urls type in handoff tool args: %s", |
| 70 | type(image_urls_raw).__name__, |
| 71 | ) |
| 72 | return [] |
| 73 | |
| 74 | @classmethod |
| 75 | async def _collect_image_urls_from_message( |
| 76 | cls, run_context: ContextWrapper[AstrAgentContext] |
| 77 | ) -> list[str]: |
| 78 | urls: list[str] = [] |
| 79 | event = getattr(run_context.context, "event", None) |
| 80 | message_obj = getattr(event, "message_obj", None) |
| 81 | message = getattr(message_obj, "message", None) |
| 82 | if message: |
| 83 | for idx, component in enumerate(message): |
| 84 | if not isinstance(component, Image): |
| 85 | continue |
| 86 | try: |
| 87 | path = await component.convert_to_file_path() |
| 88 | if path: |
| 89 | urls.append(path) |
| 90 | except Exception as e: |
| 91 | logger.error( |
| 92 | "Failed to convert handoff image component at index %d: %s", |
| 93 | idx, |
| 94 | e, |
| 95 | exc_info=True, |
| 96 | ) |
| 97 | return urls |
| 98 | |
| 99 | @classmethod |
| 100 | async def _collect_handoff_image_urls( |
| 101 | cls, |
| 102 | run_context: ContextWrapper[AstrAgentContext], |
| 103 | image_urls_raw: T.Any, |
| 104 | ) -> list[str]: |
| 105 | candidates: list[str] = [] |
| 106 | candidates.extend(cls._collect_image_urls_from_args(image_urls_raw)) |
| 107 | candidates.extend(await cls._collect_image_urls_from_message(run_context)) |
| 108 | |
| 109 | normalized = normalize_and_dedupe_strings(candidates) |
| 110 | extensionless_local_roots = (get_astrbot_temp_path(),) |
| 111 | sanitized = [ |
no outgoing calls