mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-08 03:01:47 +00:00
The v2 'single POST /v2/{users|groups}/{id}/files' upload path is capped
at ~10 MB inline (base64 'file_data' or 'url'). For larger files the QQ
platform provides a three-step flow:
1. POST /upload_prepare → upload_id + pre-signed COS part URLs
2. PUT each part to its COS URL → POST /upload_part_finish
3. POST /files with {upload_id} → file_info token
This commit adds a new gateway/platforms/qqbot/chunked_upload.py module
that implements the flow, wires it into QQAdapter._send_media for local
files (URL uploads keep the existing inline path), and introduces
structured exceptions so the caller can surface actionable error text:
- UploadDailyLimitExceededError (biz_code 40093002, non-retryable)
- UploadFileTooLargeError (file exceeds the platform limit)
Both carry file_name / file_size_human / limit_human so the model can
compose user-friendly replies instead of seeing opaque HTTP codes.
The part_finish 40093001 retryable-error loop respects the server-
provided retry_timeout (capped at 10 minutes locally) with a 1 s
polling interval. COS PUTs retry transient failures up to 2 times
with exponential backoff. complete_upload retries up to 2 times.
Covers files up to the platform's ~100 MB per-file limit; before this
the adapter silently rejected anything over ~10 MB.
19 new unit tests under TestChunkedUpload* cover the happy path,
prepare-response parsing, helper functions, part retries, COS PUT
retries, group vs c2c routing, and the structured-error mapping.
Co-authored-by: WideLee <limkuan24@gmail.com>
66 lines
1.8 KiB
Python
66 lines
1.8 KiB
Python
"""
|
|
QQBot platform package.
|
|
|
|
Re-exports the main adapter symbols from ``adapter.py`` (the original
|
|
``qqbot.py``) so that **all existing import paths remain unchanged**::
|
|
|
|
from gateway.platforms.qqbot import QQAdapter # works
|
|
from gateway.platforms.qqbot import check_qq_requirements # works
|
|
|
|
New modules:
|
|
- ``constants`` — shared constants (API URLs, timeouts, message types)
|
|
- ``utils`` — User-Agent builder, config helpers
|
|
- ``crypto`` — AES-256-GCM key generation and decryption
|
|
- ``onboard`` — QR-code scan-to-configure flow
|
|
"""
|
|
|
|
# -- Adapter (original qqbot.py) ------------------------------------------
|
|
from .adapter import ( # noqa: F401
|
|
QQAdapter,
|
|
QQCloseError,
|
|
check_qq_requirements,
|
|
_coerce_list,
|
|
_ssrf_redirect_guard,
|
|
)
|
|
|
|
# -- Onboard (QR-code scan-to-configure) -----------------------------------
|
|
from .onboard import ( # noqa: F401
|
|
BindStatus,
|
|
build_connect_url,
|
|
qr_register,
|
|
)
|
|
from .crypto import decrypt_secret, generate_bind_key # noqa: F401
|
|
|
|
# -- Utils -----------------------------------------------------------------
|
|
from .utils import build_user_agent, get_api_headers, coerce_list # noqa: F401
|
|
|
|
# -- Chunked upload --------------------------------------------------------
|
|
from .chunked_upload import ( # noqa: F401
|
|
ChunkedUploader,
|
|
UploadDailyLimitExceededError,
|
|
UploadFileTooLargeError,
|
|
)
|
|
|
|
__all__ = [
|
|
# adapter
|
|
"QQAdapter",
|
|
"QQCloseError",
|
|
"check_qq_requirements",
|
|
"_coerce_list",
|
|
"_ssrf_redirect_guard",
|
|
# onboard
|
|
"BindStatus",
|
|
"build_connect_url",
|
|
"qr_register",
|
|
# crypto
|
|
"decrypt_secret",
|
|
"generate_bind_key",
|
|
# utils
|
|
"build_user_agent",
|
|
"get_api_headers",
|
|
"coerce_list",
|
|
# chunked upload
|
|
"ChunkedUploader",
|
|
"UploadDailyLimitExceededError",
|
|
"UploadFileTooLargeError",
|
|
]
|