smart tdee
This commit is contained in:
@@ -130,6 +130,32 @@ class HaClient:
|
||||
):
|
||||
yield chunk
|
||||
|
||||
async def send_message_with_image_stream(
|
||||
self,
|
||||
session_id: int,
|
||||
content: str,
|
||||
image_bytes: bytes,
|
||||
*,
|
||||
filename: str = "photo.jpg",
|
||||
content_type: str = "image/jpeg",
|
||||
) -> AsyncIterator[SseChunk]:
|
||||
url = f"{self.base_url}/chat/sessions/{session_id}/messages"
|
||||
files = {"image": (filename, image_bytes, content_type)}
|
||||
data = {"content": content}
|
||||
async with httpx.AsyncClient(timeout=None) as client:
|
||||
async with client.stream(
|
||||
"POST",
|
||||
url,
|
||||
headers=self._headers({"Accept": "text/event-stream"}),
|
||||
files=files,
|
||||
data=data,
|
||||
) as response:
|
||||
if response.status_code >= 400:
|
||||
body = await response.aread()
|
||||
raise HaApiError(body.decode("utf-8", errors="replace") or f"HTTP {response.status_code}", response.status_code)
|
||||
async for chunk in iter_sse(response):
|
||||
yield chunk
|
||||
|
||||
async def stream_generation(self, session_id: int) -> AsyncIterator[SseChunk]:
|
||||
async for chunk in self._stream("GET", f"/chat/sessions/{session_id}/generation/stream"):
|
||||
yield chunk
|
||||
|
||||
@@ -60,6 +60,32 @@ async def _run_chat_stream(
|
||||
HaClient(settings.ha_api_base_url, linked.api_token),
|
||||
ha_api_base=settings.ha_api_base_url,
|
||||
)
|
||||
elif chunk.event == "vision":
|
||||
data = chunk.data if isinstance(chunk.data, dict) else {}
|
||||
images = data.get("images")
|
||||
if isinstance(images, list) and images:
|
||||
for index, item in enumerate(images, start=1):
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
parsed = item.get("parsed")
|
||||
model = item.get("model")
|
||||
preview = ""
|
||||
if isinstance(parsed, dict):
|
||||
preview = str(parsed.get("description") or "")[:400]
|
||||
lines = [f"Vision {index}/{len(images)} ({model or '?'}):", preview or "(нет описания)"]
|
||||
if isinstance(parsed, dict) and parsed.get("fitness_hints"):
|
||||
lines.append(f"fitness_hints: {parsed.get('fitness_hints')}")
|
||||
await message.answer("\n".join(lines)[:4000])
|
||||
else:
|
||||
parsed = data.get("parsed")
|
||||
model = data.get("model")
|
||||
preview = ""
|
||||
if isinstance(parsed, dict):
|
||||
preview = str(parsed.get("description") or "")[:400]
|
||||
lines = [f"Vision ({model or '?'}):", preview or "(нет описания)"]
|
||||
if isinstance(parsed, dict) and parsed.get("fitness_hints"):
|
||||
lines.append(f"fitness_hints: {parsed.get('fitness_hints')}")
|
||||
await message.answer("\n".join(lines)[:4000])
|
||||
elif chunk.event == "error":
|
||||
err = str(chunk.data.get("message") or "Ошибка генерации")
|
||||
await message.answer(err)
|
||||
@@ -134,3 +160,54 @@ async def handle_chat_message(message: Message, settings: Settings, storage: Sto
|
||||
except Exception as exc:
|
||||
logger.exception("Chat stream failed for telegram_id=%s", message.from_user.id)
|
||||
await message.answer(f"Ошибка при получении ответа: {exc}")
|
||||
|
||||
|
||||
@router.message(F.photo, IsLinked())
|
||||
async def handle_chat_photo(message: Message, settings: Settings, storage: Storage) -> None:
|
||||
if not is_allowed(message, settings):
|
||||
return
|
||||
if not message.from_user or not message.photo:
|
||||
return
|
||||
|
||||
linked = await storage.get_user(message.from_user.id)
|
||||
if not linked:
|
||||
return
|
||||
|
||||
lock = _user_lock(message.from_user.id)
|
||||
if lock.locked():
|
||||
await message.answer("Подожди, предыдущий ответ ещё генерируется.")
|
||||
return
|
||||
|
||||
async with lock:
|
||||
client = HaClient(settings.ha_api_base_url, linked.api_token)
|
||||
caption = (message.caption or "").strip()
|
||||
photo = message.photo[-1]
|
||||
|
||||
try:
|
||||
file = await message.bot.get_file(photo.file_id)
|
||||
if not file.file_path:
|
||||
await message.answer("Не удалось получить файл фото.")
|
||||
return
|
||||
downloaded = await message.bot.download_file(file.file_path)
|
||||
image_bytes = downloaded.read() if hasattr(downloaded, "read") else bytes(downloaded)
|
||||
|
||||
status = await client.generation_status(linked.session_id)
|
||||
if status.get("active"):
|
||||
stream = client.stream_generation(linked.session_id)
|
||||
else:
|
||||
stream = client.send_message_with_image_stream(
|
||||
linked.session_id,
|
||||
caption,
|
||||
image_bytes,
|
||||
filename="telegram.jpg",
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.exception("Failed to start chat photo for telegram_id=%s", message.from_user.id)
|
||||
await message.answer(f"Ошибка связи с Home Assistant: {exc}")
|
||||
return
|
||||
|
||||
try:
|
||||
await _run_chat_stream(message, settings, storage, linked, _iter_stream(stream))
|
||||
except Exception as exc:
|
||||
logger.exception("Chat photo stream failed for telegram_id=%s", message.from_user.id)
|
||||
await message.answer(f"Ошибка при получении ответа: {exc}")
|
||||
|
||||
Reference in New Issue
Block a user