From e0855b641550189a4efb557fe95bafe871641003 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Mon, 20 Jul 2026 10:48:22 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=B0=86=20kimi-k3=20=E5=A4=9A?= =?UTF-8?q?=E6=A8=A1=E6=80=81=E5=86=85=E5=AE=B9=E6=9E=84=E5=BB=BA=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E7=BA=AF=20Jinja2=20=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 build_kimi_content() Python 函数 - 在 SQL data 模板中用纯 Jinja2 语法构建 content 数组 - 新增 file_to_b64() 和 file_mime() 辅助函数注册到 ServerEnv - 保持向后兼容,功能不变 --- llmage/utils.py | 47 +++++++++++++++------------------------- scripts/kimi_k3_uapi.sql | 28 ++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/llmage/utils.py b/llmage/utils.py index 5222c5f..75a9605 100644 --- a/llmage/utils.py +++ b/llmage/utils.py @@ -502,34 +502,23 @@ async def llm_query_price(llmid, config_data): prices = await env.buffered_charging(llm.ppid, config_data) return prices -def build_kimi_content(prompt='', image_files=None, video_files=None): - """构造 kimi 多模态 content 数组 JSON,注册到 ServerEnv 供 uapi data 模板调用""" - import base64, json, mimetypes, os - fs = FileStorage() - parts = [] - if prompt: - parts.append({"type": "text", "text": prompt}) - for f in (image_files or []): - fp = fs.realPath(f) - if not os.path.isfile(fp): - continue - mime, _ = mimetypes.guess_type(fp) - if not mime: - mime = 'application/octet-stream' - with open(fp, 'rb') as fh: - b64 = base64.b64encode(fh.read()).decode('utf-8') - parts.append({"type": "image_url", "image_url": f'data:{mime};base64,{b64}'}) - for f in (video_files or []): - fp = fs.realPath(f) - if not os.path.isfile(fp): - continue - mime, _ = mimetypes.guess_type(fp) - if not mime: - mime = 'application/octet-stream' - with open(fp, 'rb') as fh: - b64 = base64.b64encode(fh.read()).decode('utf-8') - parts.append({"type": "video_url", "video_url": f'data:{mime};base64,{b64}'}) - return json.dumps(parts, ensure_ascii=False) +import base64 as _base64 +import mimetypes as _mimetypes +import os as _os -ServerEnv().build_kimi_content = build_kimi_content +def _file_to_b64(filepath): + """读取文件并返回 base64 字符串""" + with open(filepath, 'rb') as fh: + return _base64.b64encode(fh.read()).decode('utf-8') + +def _file_mime(filepath): + """猜测文件 MIME 类型""" + mime, _ = _mimetypes.guess_type(filepath) + return mime or 'application/octet-stream' + +ServerEnv().base64 = _base64 +ServerEnv().mimetypes = _mimetypes +ServerEnv().os = _os +ServerEnv().file_to_b64 = _file_to_b64 +ServerEnv().file_mime = _file_mime diff --git a/scripts/kimi_k3_uapi.sql b/scripts/kimi_k3_uapi.sql index 5f360b8..283cbc1 100644 --- a/scripts/kimi_k3_uapi.sql +++ b/scripts/kimi_k3_uapi.sql @@ -2,7 +2,7 @@ -- Kimi K3 API 接入 (月之暗面 / Moonshot) -- Base URL: https://api.moonshot.cn/v1 -- 兼容 OpenAI 格式, 文档: https://platform.kimi.com/docs/api/chat --- 多模态: image_files/video_files 通过 build_kimi_content() 转 data URI 数组 +-- 多模态: image_files/video_files 在 data 模板中用纯 Jinja2 构造 content 数组 -- ============================================================ -- ============================================================ @@ -41,7 +41,31 @@ VALUES ( {% if sys_prompt %} {"role": "system", "content": {{json.dumps(sys_prompt, ensure_ascii=False)}}}, {% endif %} - {"role": "user", "content": {{build_kimi_content(prompt, image_files, video_files)}}} +{% set _parts = [] %} +{% if prompt %} +{% set _ = _parts.append({"type": "text", "text": prompt}) %} +{% endif %} +{% if image_files %} +{% for _f in image_files %} +{% set _fp = FileStorage().realPath(_f) %} +{% if os.path.isfile(_fp) %} +{% set _mime = file_mime(_fp) %} +{% set _b64 = file_to_b64(_fp) %} +{% set _ = _parts.append({"type": "image_url", "image_url": "data:" + _mime + ";base64," + _b64}) %} +{% endif %} +{% endfor %} +{% endif %} +{% if video_files %} +{% for _f in video_files %} +{% set _fp = FileStorage().realPath(_f) %} +{% if os.path.isfile(_fp) %} +{% set _mime = file_mime(_fp) %} +{% set _b64 = file_to_b64(_fp) %} +{% set _ = _parts.append({"type": "video_url", "video_url": "data:" + _mime + ";base64," + _b64}) %} +{% endif %} +{% endfor %} +{% endif %} + {"role": "user", "content": {{json.dumps(_parts, ensure_ascii=False)}}} ], {% endif %} {% if stream %}