25 KiB
| name | description | author | tags | |||||||
|---|---|---|---|---|---|---|---|---|---|---|
| llm-api-config-from-url | 根据大模型API的URL,快速生成Sage平台所需的完整数据库配置数据(upapp/upappkey/uapiio/uapi/llm/llm_api_map/pricing_program/pricing_program_timing),支持OpenAI兼容接口及异步任务型API。 | Hermes Agent |
|
LLM API配置技能(从URL到完整数据库配置)
触发条件
- 用户提供一个API URL(如
https://dashscope.aliyuncs.com/compatible-mode/v1),要求配置大模型 - 用户需要为新的LLM供应商或模型在Sage平台中注册
- 用户询问如何将某个外部LLM API接入Sage
概述
Sage平台通过 llmage(模型业务层) + uapi(API网关层) 协作来接入外部LLM API。配置一条API需要向8张表写入数据,遵循严格的顺序和ID关联规则。
表依赖关系(配置顺序)
1. llmcatelog ← 模型分类(通常已存在,直接引用)
2. upapp ← 外部应用注册(base URL、认证方式)
3. upappkey ← API密钥(加密存储)
4. uapiio ← 输入输出字段定义(UI表单schema)
5. uapi ← API端点定义(请求路径、方法、headers、data/response模板)
6. llm ← 模型元数据(名称、model名、关联upapp)
7. llm_api_map ← 模型能力映射(关联catalog + apiname + 定价)
8. pricing_program / pricing_program_timing ← 定价规则
核心原则: pricing_program.id = llm_api_map.ppid = pricing_program_timing.ppid 必须是同一个ID。
各表完整结构
1. llmcatelog(模型分类)
CREATE TABLE `llmcatelog` (
`id` varchar(32) NOT NULL,
`name` varchar(100) DEFAULT NULL COMMENT '分类名',
`description` longtext DEFAULT NULL,
`hfid` varchar(32) DEFAULT NULL,
`ioid` varchar(32) DEFAULT NULL COMMENT '关联的uapiio',
PRIMARY KEY (`id`)
);
已有分类ID(直接复用,无需新建):
| 分类 | ID | 对应uapiio |
|---|---|---|
| 文生文 | text2text |
Is8l4TGkcZcqFSjbbeIK2(文本会话) |
| 文生图 | text2image |
p4K0-HTPKG3Ap--BZYqm5(t2i) |
| 文生语音 | text2speech |
UJm-sp08Q31QgOJWk_2E2(tts) |
| 图像理解 | image2text |
PONIk8Br7ADTbzWQijJl-(文本图像转文本) |
| 文生视频 | -i2ET0YkhfVQdHONfk9pX |
QU8F6f6yfRCAGpToq1B9I(万相t2v) |
| 图生视频 | RdsO6pXgXcUTvUj819-7X |
21QAi9ZUgs8iEzJ1czGEX(hailuo-ti2v) |
| 参考生视频 | fHrfsOnAFCz53DAILMO7G |
— |
| 音乐生成 | HaRXiNCaAACurZsmEqpsU |
ZuIZvoKP996JJv2kccjl0(t2m) |
| 语音识别 | audio2text |
0kO4bWFZ_D0gkc9zj237L(index-tts) |
| 3D生成 | s6-nhQtEvDKxG_qDPWwT7 |
— |
| 数字人 | czKvk-clQTRLS2KVddSWo |
5cpgFUC-zxXg79b_rlkh8(万相数字人) |
| 视频工具 | sRmpG8draTM-tsbO5nMJO |
— |
| 语言翻译 | t7sUuj8BCnsD762PwMUKM |
— |
| AI搜索 | 9_P5y-qiQzQASacTVk2Lq |
— |
| 文本分类 | Rqj-QBj1v4560l-FPCrIU |
— |
| 文本媒体转文本 | — | t-ujII59ku45tIPcdXu4O |
2. upapp(外部应用注册)
CREATE TABLE `upapp` (
`id` varchar(32) NOT NULL,
`name` varchar(200) DEFAULT NULL COMMENT '上位应用名',
`description` longtext DEFAULT '0',
`ownerid` varchar(32) DEFAULT NULL,
`apisetid` varchar(32) DEFAULT NULL,
`secretkey` varchar(255) DEFAULT NULL,
`baseurl` varchar(500) DEFAULT NULL COMMENT '基础URL',
`myappid` varchar(100) DEFAULT NULL,
`dynamic_func` longtext DEFAULT NULL,
`auth_apiname` varchar(100) DEFAULT NULL COMMENT '认证API名',
PRIMARY KEY (`id`)
);
已有upapp(优先复用):
| 供应商 | upappid | baseurl |
|---|---|---|
| 阿里百炼 | ali-qwen |
https://dashscope.aliyuncs.com/compatible-mode/v1 |
| OpenAI | 4f4VUCUb4qThwdRroATF7 |
https://api.openai.com/v1 |
| 智谱AI | ESX0csV3pd9P_U2cLODwA |
https://open.bigmodel.cn/api/paas/v4 |
| 火山方舟 | huoshanfangzhou |
https://ark.cn-beijing.volces.com/api/v3 |
| 千帆大模型 | qianfan |
https://qianfan.baidubce.com/v2 |
| 深度求索 | deepseek |
https://api.deepseek.com |
| MiniMax | minimax |
https://api.minimax.chat/v1 |
| 通义万象 | tongyi-wan |
https://dashscope.aliyuncs.com/api/v1 |
| Grok | Fc8ElDTJKGG9I0gCTL8eI |
https://api.x.ai/v1 |
| 海螺 | hailuo |
https://api.minimaxi.com/v1 |
| vidu | vidu |
https://api.vidu.cn |
| stepfun | stepfun |
https://api.stepfun.com/v1 |
| wetokenai | wetokenai |
https://api.wetokenai.com |
| 百度 | a6su6IbB0yHDW4gc2ncfZ |
百度API |
新建upapp SQL模板:
INSERT INTO upapp (id, name, description, ownerid, apisetid, secretkey, baseurl, myappid, dynamic_func, auth_apiname)
VALUES ('<21-char-ID>', '<app_name>', '<description>', '0', NULL, '', '<base_url>', '', NULL, NULL);
3. upappkey(API密钥)
CREATE TABLE `upappkey` (
`id` varchar(32) NOT NULL,
`upappid` varchar(32) DEFAULT NULL,
`ownerid` varchar(32) DEFAULT '0',
`apikey` varchar(4000) DEFAULT '0' COMMENT '加密后的API密钥',
`apiuser` varchar(100) DEFAULT NULL,
`apipasswd` varchar(100) DEFAULT NULL,
`orgid` varchar(32) DEFAULT NULL,
`is_first` varchar(1) DEFAULT NULL,
PRIMARY KEY (`id`)
);
关键: apikey 必须用 ServerEnv.password_encode() 加密后存储,绝不可明文。
SQL模板:
-- NOTE: apikey必须先用 ServerEnv.password_encode('sk-xxx') 加密
INSERT INTO upappkey (id, upappid, ownerid, apikey, apiuser, apipasswd, orgid, is_first)
VALUES ('<21-char-ID>', '<upappid>', '<ownerid>', '<encrypted_apikey>', '', '', '<orgid_or_0>', '1');
4. uapiio(输入输出字段定义)
CREATE TABLE `uapiio` (
`id` varchar(32) NOT NULL,
`name` varchar(100) DEFAULT NULL COMMENT '类型名',
`description` longtext DEFAULT NULL,
`input_fields` longtext DEFAULT NULL COMMENT 'JSON数组,定义UI表单字段',
PRIMARY KEY (`id`)
);
input_fields格式:与bricks Form字段完全一致的字典数组。每个字段必须包含完整的表单描述属性:
[
{
"name": "字段名(与uapi.data模板变量名一致)",
"label": "前端显示标签",
"uitype": "text|textarea|image|audio|video|code|number|date|str",
"required": true,
"defaultvalue": "默认值(可选)",
"data": [{"value": "v1", "text": "显示文本"}],
"placeholder": "输入提示(可选)"
}
]
各uitype必填属性:
| uitype | 必填属性 | 说明 |
|---|---|---|
| text/str/textarea | name, label, uitype | 文本输入,textarea多行 |
| image/audio/video | name, label, uitype | 文件上传控件 |
| code | name, label, uitype, data | 下拉选择,data提供选项数组 |
| number | name, label, uitype | 数字输入 |
完整示例(图生视频):
[
{"name": "prompt", "label": "提示词", "uitype": "textarea", "required": true, "placeholder": "描述你想生成的视频内容"},
{"name": "image_file", "label": "首帧图片", "uitype": "image", "required": true},
{"name": "image_file1", "label": "尾帧图片", "uitype": "image"},
{"name": "duration", "label": "时长(秒)", "uitype": "code", "data": [{"value": "5", "text": "5秒"}, {"value": "10", "text": "10秒"}], "defaultvalue": "5"},
{"name": "resolution", "label": "分辨率", "uitype": "code", "data": [{"value": "720p", "text": "720P"}, {"value": "1080p", "text": "1080P"}], "defaultvalue": "720p"}
]
常用uapiio:
| 名称 | ioid | 适用场景 |
|---|---|---|
| 文本会话 | Is8l4TGkcZcqFSjbbeIK2 |
text2text |
| t2i | p4K0-HTPKG3Ap--BZYqm5 |
text2image |
| tts | UJm-sp08Q31QgOJWk_2E2 |
text2speech |
| 文本图像转文本 | PONIk8Br7ADTbzWQijJl- |
image2text (vision) |
| t2m | ZuIZvoKP996JJv2kccjl0 |
text2music |
| 万相t2v | QU8F6f6yfRCAGpToq1B9I |
text2video |
| hailuo-ti2v | 21QAi9ZUgs8iEzJ1czGEX |
image2video |
| 万相数字人 | 5cpgFUC-zxXg79b_rlkh8 |
avatar |
| index-tts | 0kO4bWFZ_D0gkc9zj237L |
index-tts |
新建uapiio SQL模板:
INSERT INTO uapiio (id, name, description, input_fields)
VALUES ('<21-char-ID>', '<name>', '<description>', '<JSON_escaped_input_fields>');
5. uapi(API端点定义)
CREATE TABLE `uapi` (
`id` varchar(32) NOT NULL,
`name` varchar(100) DEFAULT NULL COMMENT 'API名(代码中引用)',
`title` varchar(200) DEFAULT NULL COMMENT '显示名',
`upappid` varchar(32) DEFAULT NULL COMMENT '关联upapp.id',
`description` longtext DEFAULT NULL,
`need_auth` varchar(1) DEFAULT NULL,
`stream` varchar(10) DEFAULT NULL COMMENT 'stream/sync/async/False',
`path` varchar(500) DEFAULT NULL COMMENT '请求路径',
`httpmethod` varchar(10) DEFAULT NULL COMMENT 'GET/POST/PUT',
`chunk_match` longtext DEFAULT NULL,
`headers` longtext DEFAULT NULL COMMENT 'JSON: 请求头模板',
`params` longtext DEFAULT NULL COMMENT 'JSON: URL参数模板',
`data` longtext DEFAULT NULL COMMENT 'Jinja2: 请求体模板',
`response` longtext DEFAULT NULL COMMENT 'Jinja2: 响应解析模板',
`ioid` varchar(32) DEFAULT NULL COMMENT '关联uapiio.id',
`callbackurl` varchar(500) DEFAULT NULL,
PRIMARY KEY (`id`)
);
stream模式:
| 值 | 含义 | 适用场景 |
|---|---|---|
stream |
SSE流式响应 | 文本对话/生成 |
sync |
同步一次性响应 | 翻译/分类/图片生成(同步) |
async |
异步任务提交+轮询 | 视频生成/3D生成等耗时任务 |
False |
同步非流式 | 某些老式API |
OpenAI兼容 text2text uapi模板(最常见):
INSERT INTO uapi (id, name, title, upappid, description, need_auth, stream, path, httpmethod, chunk_match, headers, params, data, response, ioid, callbackurl)
VALUES (
'<21-char-ID>',
't2t',
'模型对话',
'<upappid>',
'OpenAI兼容对话接口',
'0',
'stream',
'/chat/completions',
'POST',
NULL,
'{"Content-Type": "application/json", "Authorization": "Bearer {{apikey}}"}',
NULL,
'{"model": "{{model}}", "stream_options": {"include_usage": true}, "messages": [{% if sys_prompt %}{"role": "system", "content": {{json.dumps(sys_prompt, ensure_ascii=False)}}},{% endif %}{"role": "user", "content": {{json.dumps(prompt, ensure_ascii=False)}}}]}',
'{"model": "{{model}}", {% if object == "chat.completion" %}"content":{{json.dumps(choices[0].message.content, ensure_ascii=False)}},{% else %}"content":{{json.dumps(choices[0].delta.content, ensure_ascii=False)}},{% endif %} "usage":{"prompt_tokens":{{usage.prompt_tokens}},"completion_tokens":{{usage.completion_tokens}},"total_tokens":{{usage.total_tokens}}}}',
'<ioid>',
NULL
);
异步任务型uapi(提交+查询两个端点):
-- 任务提交
INSERT INTO uapi (id, name, title, upappid, description, need_auth, stream, path, httpmethod, chunk_match, headers, params, data, response, ioid, callbackurl)
VALUES ('<21-char-ID>', '<apiname>', '<title>', '<upappid>', '<desc>', '0', 'async', '<path>', 'POST', NULL, '{"Authorization": "Bearer {{apikey}}", "Content-Type": "application/json"}', NULL, '<data_template>', '{"taskid":"{{task_id}}"}', '<ioid>', NULL);
-- 状态查询(response必须返回usage + 文件URL用downloadfile2url转本地 + status统一映射)
INSERT INTO uapi (id, name, title, upappid, description, need_auth, stream, path, httpmethod, chunk_match, headers, params, data, response, ioid, callbackurl)
VALUES ('<21-char-ID>', '<status_apiname>', '查询任务状态', '<upappid>', '', '0', 'sync', '<status_path>', 'GET', NULL, '{"Authorization": "Bearer {{apikey}}"}', NULL, NULL, '{% if status in ["SUCCEEDED","succeeded","success","COMPLETE","completed"] %}"status": "SUCCEEDED", "result_url": "{{downloadfile2url(request, output_url)}}", "usage":{"prompt_tokens":{{usage.input_tokens|default(0)}},"completion_tokens":{{usage.output_tokens|default(0)}},"total_tokens":{{usage.total_tokens|default(0)}}}{% elif status in ["FAILED","failed","error","ERROR"] %}"status": "FAILED"{% elif status in ["RUNNING","running","processing","IN_PROGRESS","in_progress"] %}"status": "RUNNING"{% elif status in ["CREATED","created","queued","QUEUED"] %}"status": "CREATED"{% else %}"status": "PENDING"{% endif %}', NULL, NULL);
6. llm(模型定义)
CREATE TABLE `llm` (
`id` varchar(32) NOT NULL,
`name` varchar(100) DEFAULT NULL COMMENT '显示名',
`model` varchar(100) DEFAULT NULL COMMENT 'API model名',
`description` longtext DEFAULT NULL,
`llmcatelogid` varchar(32) DEFAULT NULL COMMENT '关联llmcatelog.id',
`iconid` varchar(32) DEFAULT NULL COMMENT '图标ID',
`upappid` varchar(32) DEFAULT NULL COMMENT '关联upapp.id',
`apiname` varchar(100) DEFAULT NULL COMMENT '关联uapi.name',
`providerid` varchar(32) DEFAULT NULL COMMENT '供应商org ID',
`ownerid` varchar(32) DEFAULT NULL,
`enabled_date` date DEFAULT NULL,
`expired_date` date DEFAULT NULL,
`query_apiname` varchar(100) DEFAULT NULL COMMENT '异步结果查询API名',
`query_period` int(11) DEFAULT NULL COMMENT '查询间隔(秒)',
`min_balance` double(18,2) DEFAULT 10.00,
PRIMARY KEY (`id`)
);
注意: llm表没有stream字段,流式/异步控制由uapi.stream和llm_api_map.query_apiname决定。
7. llm_api_map(模型能力映射)
CREATE TABLE `llm_api_map` (
`id` varchar(32) NOT NULL,
`llmid` varchar(32) NOT NULL COMMENT '关联llm.id',
`llmcatelogid` varchar(32) NOT NULL COMMENT '关联llmcatelog.id',
`apiname` varchar(100) NOT NULL COMMENT '关联uapi.name',
`query_apiname` varchar(100) DEFAULT NULL COMMENT '异步轮询API名',
`query_period` int(11) DEFAULT NULL COMMENT '轮询间隔(秒)',
`ppid` varchar(32) DEFAULT NULL COMMENT '关联pricing_program.id',
`isdefaultcatelog` varchar(1) DEFAULT NULL COMMENT '是否默认分类',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_llm_api_catelog` (`llmid`,`llmcatelogid`)
);
注意: 所有ID字段均为varchar(32),不是21。使用 getID() 生成的nanoid为21字符,varchar(32)足够容纳。
8. pricing_program + pricing_program_timing(定价)
CREATE TABLE `pricing_program` (
`id` varchar(32) NOT NULL,
`name` varchar(256) DEFAULT NULL COMMENT '定价项目名',
`ownerid` varchar(32) DEFAULT NULL,
`providerid` varchar(32) DEFAULT NULL,
`pricing_belong` varchar(32) DEFAULT NULL COMMENT 'provider/output',
`discount` double(18,3) DEFAULT NULL,
`description` longtext DEFAULT NULL,
`pricing_spec` longtext DEFAULT NULL COMMENT 'YAML: 规格定义',
PRIMARY KEY (`id`)
);
CREATE TABLE `pricing_program_timing` (
`id` varchar(32) NOT NULL,
`ppid` varchar(32) DEFAULT NULL COMMENT 'pricing_program.id',
`name` varchar(256) DEFAULT NULL,
`pricing_data` longtext DEFAULT NULL COMMENT 'YAML: 定价规则',
`enabled_date` date DEFAULT NULL,
`expired_date` date DEFAULT '9999-12-31',
PRIMARY KEY (`id`)
);
执行步骤(给定API URL后)
Step 1: 分析URL,识别供应商
- 从URL提取baseurl(如
https://dashscope.aliyuncs.com/compatible-mode/v1→ 阿里百炼) - 查已有upapp表,如果供应商已存在 → 复用upappid
- 如果新供应商 → 新建upapp
Step 2: 确定模型分类
- 根据API功能(文本/图片/视频/语音)匹配llmcatelog
- 多数情况直接用已有分类
Step 3: 确定uapiio
- 根据分类选择对应uapiio
- OpenAI兼容text2text →
Is8l4TGkcZcqFSjbbeIK2(文本会话)
Step 4: 确定uapi
- OpenAI兼容API → 复用已有
t2tuapi(同一个upapp下name唯一) - 自定义API → 新建uapi记录
- 异步API → 需要两个uapi(提交+查询)
Step 5: 生成ID
- 使用
appPublic.uniqueID.getID()生成21-char nanoid - 关键:
ppid必须在Python中生成一次,然后在pricing_program、pricing_program_timing、llm_api_map三处复用
Step 6: 生成Python脚本
生成可运行的Python脚本(参考 templates/config-generator.py),输出完整SQL。
ID生成规则
- 所有ID: 21-char nanoid(通过
getID()生成),VARCHAR(32)字段足够容纳 - ppid: 在Python中
ppid = getID()一次,然后三处复用 - llm id: 每个模型独立ID
- llm_api_map id: 每个能力映射独立ID(一个模型多个能力=多行)
- uapi id: 每个API端点独立ID
参考文件
references/uapiio-patterns.md— 从数据库提取的uapiio定义、uitype类型、ioinfo存储格式references/ali-qwen-config.md— 阿里百炼已有配置(upapp/upappkey/uapi/providerid复用值)
输出格式
- 模型摘要表: 名称、model、分类、供应商、stream模式
- 复用vs新建清单: 哪些表记录复用已有、哪些需要新建
- Python配置脚本: 可运行的脚本,输出完整SQL
- 执行说明: 用户需手动执行加密API密钥和SQL
Pitfalls
- upappid复用优先: 同一供应商的多个模型共享一个upapp,不要重复创建
- uapi name唯一性: 同一upapp下uapi.name必须唯一。OpenAI兼容接口通常复用已有
t2t - API密钥必须加密: 使用
ServerEnv.password_encode(),绝不明文 - ppid三处一致:
pricing_program.id=pricing_program_timing.ppid=llm_api_map.ppid - 异步模型需要两个uapi: 提交(stream='async') + 查询(stream='sync'),
llm_api_map.query_apiname指向查询API - llm.stream vs uapi.stream:
llm.stream控制推理模式('async'/True/False),uapi.stream控制HTTP调用方式('stream'/'sync'/'async')。两者不同,不要混淆 - uapi通过upappid直连upapp: 不再使用apisetid中间表
- 多能力模型: 一个llm + 多个llm_api_map(每行对应一个能力+分类组合)
- ownerid默认为'0': 除非明确指定属主
- providerid是org ID: 指向供应商的机构ID,不是upappid
- 必须生成Python脚本: 不可直接输出原始SQL,因为ID一致性无法在纯SQL中保证
- pricing是必选项: 每个模型必须配定价,否则无法计费
文件处理机制
文件上传流程
- 前端上传:用户通过uapiio定义的字段(
uitype: "image"/"audio"/"video"/"file")上传文件,或直接输入URL - 服务器存储:文件自动保存到服务器指定目录
- API请求转换:无论用户输入URL还是上传文件,统一转换为服务器URL格式传给大模型。不使用base64编码
b64media2url函数(已弃用)
⚠️ 旧机制:早期使用 b64media2url(request, filename) 将文件转为base64 data URL。现已改为统一使用URL方式。输入文件和用户输入的URL都转换为服务器URL直接传给大模型,不做base64编码。
uapiio中定义文件上传字段
[
{"name": "prompt", "label": "提示词", "uitype": "text", "required": true},
{"name": "image_file", "label": "首帧图片", "uitype": "image"},
{"name": "image_file1", "label": "尾帧图片", "uitype": "image"},
{"name": "audio_file", "label": "配音文件", "uitype": "audio"},
{"name": "video_file", "label": "输入视频", "uitype": "video"}
]
生成文件处理(模型输出)
本地化机制:大模型API返回的生成文件(图片/视频/音频)通常是远端URL。使用 downloadfile2url(request, output.video_url) 将远端文件下载到本服务器,转换为本服务器URL。用户直接从本地下载文件,不暴露远端URL。
用法:
"result_url": "{{downloadfile2url(request, output.video_url)}}"
存储方式:
- 文本输出:直接存储在
llmusage.ioinfo字段(JSON格式) - 文件输出(图片/视频/音频):存储为JSON文件,
llmusage.ioinfo保存webpath(如/llmio/182/138/79/46/xxx.json)
ioinfo JSON结构示例:
{
"input": {"model": "wan2.1", "prompt": "...", "image_url": "https://server.example.com/llmio/uploads/xxx.png"},
"output": [
{"model": "wan2.1", "content": "", "finish": "0", "llmusageid": "xxx"},
{"model": "wan2.1", "content": "https://cdn.example.com/generated-video.mp4", "finish": "1", "usage": {...}, "llmusageid": "xxx"}
]
}
异步模型response模板提取生成文件URL(必须包含usage + downloadfile2url + status标准化):
{# 从API响应中提取状态(标准化)、结果URL(转本地)和usage #}
{
"taskid": "{{task_id}}",
{% if state in ['SUCCEEDED','succeeded','success','COMPLETE','completed'] %}
"status": "SUCCEEDED",
"result_url": "{{downloadfile2url(request, output_video_url)}}",
"usage":{"prompt_tokens":{{usage.input_tokens|default(0)}},"completion_tokens":{{usage.output_tokens|default(0)}},"total_tokens":{{usage.total_tokens|default(0)}}}
{% elif state in ['FAILED','failed','error','ERROR'] %}
"status": "FAILED"
{% elif state in ['RUNNING','running','processing','IN_PROGRESS','in_progress'] %}
"status": "RUNNING"
{% elif state in ['CREATED','created','queued','QUEUED'] %}
"status": "CREATED"
{% else %}
"status": "PENDING"
{% endif %}
}
同步模型response模板(必须包含usage):
{# 同步API响应:内容 + usage(必须) + 文件URL用downloadfile2url转本地 #}
{
"model": "{{model}}",
"content": {{json.dumps(output_text, ensure_ascii=False)}},
"usage":{"prompt_tokens":{{usage.prompt_tokens}},"completion_tokens":{{usage.completion_tokens}},"total_tokens":{{usage.total_tokens}}}
}
uapi.data模板中的文件处理示例
图生视频(异步):
{
"model": "{{model or 'viduq2'}}",
"payload": "i2v",
"images": ["{{image_file1_url}}", "{{image_file2_url}}"],
"duration": {{duration or 10}},
"prompt": {{json.dumps(prompt, ensure_ascii=False)}},
"resolution": "{{resolution or '1080p'}}"
}
图像理解(vision):
{
"model": "{{model}}",
"messages": [
{"role": "user", "content": [
{"type": "text", "text": {{json.dumps(prompt, ensure_ascii=False)}}},
{"type": "image_url", "image_url": {"url": "{{image_file_url}}"}}
]}
]
}
文件处理Pitfalls
- 文件变量传递request:uapi.data模板中的文件处理函数必须传request作为第一参数
- 文件字段名必须匹配:uapi.data模板中的变量名必须与uapiio.input_fields中的name一致
- 文件未上传时的错误:如果uapiio定义了image_file但用户未上传,会抛出变量未定义错误
- URL文件大小限制:不同API provider对URL方式引用文件有不同大小限制,需查阅文档
- 生成文件本地化(强制):大模型生成的远端文件URL必须用
downloadfile2url(request, output.file_url)下载并转为本服务器URL。response模板中直接调用:"result_url": "{{downloadfile2url(request, output.video_url)}}"。绝不可直接暴露远端URL给客户 - ioinfo存储:大输入输出内容存储为JSON文件,llmusage.ioinfo保存webpath而非完整JSON
- llm.model严格按API文档:llm表的model字段必须严格填写API文档中要求的模型名称/ID,不可自行编造、缩写或改写。不同供应商的模型命名规则不同(如OpenAI的
gpt-4o、阿里的qwen-max),必须以官方API文档为准 - 输入文件URL化:用户输入的文件(上传或URL)统一转换为服务器URL传给大模型,不使用base64编码
- llm表字段顺序:
id, name, model, description, llmcatelogid, iconid, upappid, apiname, providerid, ownerid, enabled_date, expired_date, query_apiname, query_period, min_balance。注意没有stream字段 - ali-qwen providerid约定:阿里百炼的providerid固定使用upappkey的id
6fadgewjraOyvxC_EkHou,不是upappid也不是org id。所有已有ali-qwen模型都使用此值,新建必须一致 - response必须返回usage(强制):无论同步还是异步任务查询,uapi.response模板必须包含usage字段(prompt_tokens/completion_tokens/total_tokens)。同步响应从API的usage对象直接取;异步任务查询从任务结果的usage/metrics对象取。usage是计费的必要数据,缺失会导致无法正确扣费
- uapiio input_fields必须完整描述:input_fields中每个字段必须使用与bricks Form字段一致的完整字典格式(name/label/uitype为必填,code类型还需data选项数组,可选required/defaultvalue/placeholder)。不可只写name和uitype
- status必须标准化为5种值(强制):不同供应商返回的任务状态五花八门(如SUCCEEDED/succeeded/success/COMPLETE、RUNNING/running/processing/IN_PROGRESS、CREATED/created/queued、FAILED/failed/error等),uapi.response模板中必须用Jinja2
in判断将供应商原始状态映射为以下5种标准值之一:标准值 含义 供应商常见原始值 CREATED任务已创建,尚未开始 CREATED, created, queued, QUEUED PENDING排队中/等待中(默认兜底) PENDING, pending, waiting, 以及所有未识别的状态 RUNNING正在处理中 RUNNING, running, processing, IN_PROGRESS, in_progress SUCCEEDED成功完成 SUCCEEDED, succeeded, success, COMPLETE, completed FAILED失败 FAILED, failed, error, ERROR
兜底规则:{% else %}"status": "PENDING"{% endif %},任何未识别的状态都归为PENDING