ForcePilot/backend/package/yuxi/channels/adapters/imessage/media_ai.py
Kris 068cf70fe9 feat(imessage): 实现完整的 iMessage 适配器基础模块
新增 iMessage 通道适配器完整实现,包含:
1. 核心适配器与工具工厂导出
2. 运行时存储、反射防护、会话路由等基础组件
3. 消息信封、线程管理、回复上下文格式化
4. Tapback 表情反应处理、自定义异常体系
5. 审批按钮、联系人解析、速率限制功能
6. 文本净化、目标解析、缓存管理模块
7. 配置 schema、多账户支持、安装向导等配置模块
8. 审计日志、媒体AI处理等扩展功能
2026-05-12 00:44:44 +08:00

85 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
from typing import Any
from yuxi.utils.logging_config import logger
async def describe_image(image_data: bytes, vision_api_url: str = "", vision_api_key: str = "") -> str:
"""通过外部 Vision API 描述图片内容。
如果未配置 API URL返回基本元数据描述。
"""
if not vision_api_url:
return f"Image attachment ({len(image_data)} bytes)"
try:
import base64
import aiohttp
encoded = base64.b64encode(image_data).decode("utf-8")
payload = {
"model": "gpt-4o-mini",
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "Describe this image briefly in one sentence."},
{
"type": "image_url",
"image_url": {"url": f"data:image/jpeg;base64,{encoded}"},
},
],
}
],
"max_tokens": 100,
}
headers = {"Authorization": f"Bearer {vision_api_key}"}
async with aiohttp.ClientSession() as session:
async with session.post(
vision_api_url, json=payload, headers=headers, timeout=aiohttp.ClientTimeout(total=15)
) as resp:
if resp.status == 200:
data = await resp.json()
return data["choices"][0]["message"]["content"]
return f"Image attachment ({len(image_data)} bytes)"
except Exception as e:
logger.warning(f"[iMessage/AI] Vision API failed: {e}")
return f"Image attachment ({len(image_data)} bytes)"
async def transcribe_audio(audio_data: bytes, transcription_api_url: str = "", transcription_api_key: str = "") -> str:
"""通过外部 API 转录音频内容。"""
if not transcription_api_url:
return f"Audio attachment ({len(audio_data)} bytes)"
try:
import aiohttp
from aiohttp import FormData
form = FormData()
form.add_field("file", audio_data, filename="audio.caf", content_type="audio/x-caf")
form.add_field("model", "whisper-1")
headers = {"Authorization": f"Bearer {transcription_api_key}"}
async with aiohttp.ClientSession() as session:
async with session.post(
transcription_api_url, data=form, headers=headers, timeout=aiohttp.ClientTimeout(total=30)
) as resp:
if resp.status == 200:
data = await resp.json()
return data.get("text", "")
return f"Audio attachment ({len(audio_data)} bytes)"
except Exception as e:
logger.warning(f"[iMessage/AI] Transcription API failed: {e}")
return f"Audio attachment ({len(audio_data)} bytes)"
def is_vision_configured(config: dict[str, Any]) -> bool:
return bool(config.get("vision_api_url") and config.get("vision_api_key"))
def is_transcription_configured(config: dict[str, Any]) -> bool:
return bool(config.get("transcription_api_url") and config.get("transcription_api_key"))