refactor: introduce llm_api_map table and remove uapiset intermediate layer
- New llm_api_map table: extract ability-specific fields (apiname, query_apiname, query_period, ppid) from llm table to support one-model-multi-ability without redundancy - Remove uapiset from llmage JOIN chain: upapp.apisetid now directly joins uapi.apisetid - Updated BufferedLLMs.get_llm() to JOIN llm_api_map for query_apiname/query_period/ppid fields - Updated llmcheck.dspy and list_paging_catelog_llms.dspy to remove uapiset references - Added migration script to generate llm_api_map INSERTs from existing llm data
This commit is contained in:
parent
f9a7293c46
commit
d2ffd9c6d0
@ -350,7 +350,7 @@ llmage
|
||||
## 开发注意事项
|
||||
|
||||
1. **llm.stream 字段**:控制推理模式 — `'async'` 为异步任务、`False` 为同步、`True` 为流式
|
||||
2. **llm 表关联链**:llm → upapp → uapiset → uapi + uapiio,新增模型需在 uapi 模块中先配置好 API 定义
|
||||
2. **llm 表关联链**:llm → upapp → uapi + uapiio,新增模型需在 uapi 模块中先配置好 API 定义。模型能力字段(apiname, query_apiname, query_period, ppid)已拆分到 llm_api_map 表。
|
||||
3. **input_fields**:模型的输入字段定义存储在 uapiio 表中,BufferedLLMs 加载时自动关联
|
||||
4. **计费开关**:目前联机不调账(代码中已注释),所有 amount/cost 为 0,由后台任务统一处理
|
||||
5. **异步任务 query_apiname**:支持多个 API 名称逗号分隔,逐个轮询直到状态变为 SUCCEEDED/FAILED
|
||||
|
||||
17
json/llm_api_map.json
Normal file
17
json/llm_api_map.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"summary": [{"name": "llm_api_map", "title": "模型能力映射表", "primary": ["id"], "catelog": "relation"}],
|
||||
"fields": [
|
||||
{"name": "id", "title": "主键", "type": "str", "length": 21, "nullable": "no"},
|
||||
{"name": "llmid", "title": "模型ID", "type": "str", "length": 21, "nullable": "no"},
|
||||
{"name": "llmcatelogid", "title": "类目ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "apiname", "title": "API接口名", "type": "str", "length": 100, "nullable": "no"},
|
||||
{"name": "query_apiname", "title": "结果查询API名", "type": "str", "length": 100},
|
||||
{"name": "query_period", "title": "查询间隔(秒)", "type": "int"},
|
||||
{"name": "ppid", "title": "定价项目ID", "type": "str", "length": 21}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_llm_api_llm", "idxtype": "index", "idxfields": ["llmid"]},
|
||||
{"name": "idx_llm_api_catelog", "idxtype": "unique", "idxfields": ["llmid", "llmcatelogid"]}
|
||||
],
|
||||
"codes": []
|
||||
}
|
||||
@ -235,19 +235,19 @@ class BufferedLLMs:
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
sql = """select x.*,
|
||||
z.input_fields
|
||||
from (
|
||||
select a.*, e.ioid, e.stream, e.callbackurl, f.input_fields as inputfields
|
||||
from llm a, upapp c, uapiset d, uapi e, uapiio f
|
||||
where a.upappid = c.id
|
||||
and c.apisetid = d.id
|
||||
and e.apisetid = d.id
|
||||
and e.ioid = f.id
|
||||
and a.apiname = e.name
|
||||
and a.expired_date > ${today}$
|
||||
and a.enabled_date <= ${today}$
|
||||
) x left join uapiio z on x.ioid = z.id
|
||||
where x.id = ${llmid}$
|
||||
"""
|
||||
from (
|
||||
select a.*, e.ioid, e.stream, e.callbackurl, f.input_fields as inputfields,
|
||||
m.query_apiname, m.query_period, m.ppid
|
||||
from llm a
|
||||
join llm_api_map m on a.id = m.llmid
|
||||
join upapp c on a.upappid = c.id
|
||||
join uapi e on c.apisetid = e.apisetid and m.apiname = e.name
|
||||
join uapiio f on e.ioid = f.id
|
||||
where a.expired_date > ${today}$
|
||||
and a.enabled_date <= ${today}$
|
||||
) x left join uapiio z on x.ioid = z.id
|
||||
where x.id = ${llmid}$
|
||||
"""
|
||||
ns = {'llmid': llmid, 'today': today}
|
||||
recs = await sor.sqlExe(sql, ns.copy())
|
||||
if len(recs) > 0:
|
||||
|
||||
165
scripts/migrate_llm_api_map.py
Normal file
165
scripts/migrate_llm_api_map.py
Normal file
@ -0,0 +1,165 @@
|
||||
"""
|
||||
Migration script: Generate llm_api_map records from existing llm table data.
|
||||
|
||||
This script reads existing llm records and produces SQL INSERT statements
|
||||
for the new llm_api_map table. It does NOT directly modify the database.
|
||||
|
||||
Usage:
|
||||
python migrate_llm_api_map.py [--db-config CONFIG_PATH] [--output OUTPUT_FILE]
|
||||
|
||||
The script outputs INSERT SQL statements that can be reviewed and executed manually.
|
||||
"""
|
||||
import sys
|
||||
import json
|
||||
import argparse
|
||||
from appPublic.uniqueID import getID
|
||||
|
||||
|
||||
def generate_migration_sql(llm_records, catalog_rel_records=None):
|
||||
"""
|
||||
Generate INSERT statements for llm_api_map from existing llm data.
|
||||
|
||||
For each llm record:
|
||||
- If llm_catalog_rel exists: create one llm_api_map per (llmid, llmcatelogid)
|
||||
- If no catalog_rel: create one llm_api_map with the llm's default catalog
|
||||
"""
|
||||
inserts = []
|
||||
|
||||
# Build catalog_rel lookup: llmid -> [llmcatelogid, ...]
|
||||
catelog_map = {}
|
||||
if catalog_rel_records:
|
||||
for rel in catalog_rel_records:
|
||||
llmid = rel.get('llmid')
|
||||
catelogid = rel.get('llmcatelogid')
|
||||
if llmid and catelogid:
|
||||
catelog_map.setdefault(llmid, []).append(catelogid)
|
||||
|
||||
for llm in llm_records:
|
||||
llmid = llm.get('id')
|
||||
if not llmid:
|
||||
continue
|
||||
|
||||
apiname = llm.get('apiname', '')
|
||||
query_apiname = llm.get('query_apiname', '')
|
||||
query_period = llm.get('query_period', '')
|
||||
ppid = llm.get('ppid', '')
|
||||
upappid = llm.get('upappid', '')
|
||||
|
||||
# Get catalog IDs for this llm
|
||||
catelog_ids = catelog_map.get(llmid)
|
||||
if not catelog_ids:
|
||||
# Fallback: use a default or skip
|
||||
# In practice, every llm should have at least one catalog_rel entry
|
||||
# If not, we can try to infer from the model type
|
||||
catelog_ids = [llm.get('llmcatelogid', '')]
|
||||
if not catelog_ids[0]:
|
||||
print(f"WARNING: llm {llmid} has no catalog_rel entry, skipping",
|
||||
file=sys.stderr)
|
||||
continue
|
||||
|
||||
for catelogid in catelog_ids:
|
||||
map_id = getID()
|
||||
|
||||
# Build VALUES
|
||||
values = {
|
||||
'id': f"'{map_id}'",
|
||||
'llmid': f"'{llmid}'",
|
||||
'llmcatelogid': f"'{catelogid}'",
|
||||
'apiname': f"'{apiname}'",
|
||||
}
|
||||
|
||||
if query_apiname:
|
||||
values['query_apiname'] = f"'{query_apiname}'"
|
||||
else:
|
||||
values['query_apiname'] = 'NULL'
|
||||
|
||||
if query_period is not None and query_period != '':
|
||||
values['query_period'] = str(int(query_period))
|
||||
else:
|
||||
values['query_period'] = 'NULL'
|
||||
|
||||
if ppid:
|
||||
values['ppid'] = f"'{ppid}'"
|
||||
else:
|
||||
values['ppid'] = 'NULL'
|
||||
|
||||
cols = ', '.join(values.keys())
|
||||
vals = ', '.join(values.values())
|
||||
sql = f"INSERT INTO llm_api_map ({cols}) VALUES ({vals});"
|
||||
inserts.append(sql)
|
||||
|
||||
return inserts
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate llm_api_map migration SQL from existing llm data')
|
||||
parser.add_argument('--input', '-i',
|
||||
help='Input JSON file with llm records (for offline mode)')
|
||||
parser.add_argument('--catalog-rel', '-c',
|
||||
help='Input JSON file with llm_catalog_rel records')
|
||||
parser.add_argument('--output', '-o', default='-',
|
||||
help='Output file for SQL statements (default: stdout)')
|
||||
parser.add_argument('--dry-run', action='store_true',
|
||||
help='Only show count of generated statements')
|
||||
args = parser.parse_args()
|
||||
|
||||
# Load llm records from JSON input (offline mode)
|
||||
# In production, this would connect to the database
|
||||
if args.input:
|
||||
with open(args.input, 'r', encoding='utf-8') as f:
|
||||
llm_records = json.load(f)
|
||||
else:
|
||||
print("No --input provided. Use --input to provide llm records JSON.",
|
||||
file=sys.stderr)
|
||||
print("Example: python migrate_llm_api_map.py -i llm_dump.json",
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
catalog_rel_records = None
|
||||
if args.catalog_rel:
|
||||
with open(args.catalog_rel, 'r', encoding='utf-8') as f:
|
||||
catalog_rel_records = json.load(f)
|
||||
|
||||
inserts = generate_migration_sql(llm_records, catalog_rel_records)
|
||||
|
||||
if args.dry_run:
|
||||
print(f"Would generate {len(inserts)} INSERT statements for llm_api_map")
|
||||
return
|
||||
|
||||
# Output
|
||||
header_lines = [
|
||||
"-- Migration: Create llm_api_map records from existing llm data",
|
||||
"-- Generated by migrate_llm_api_map.py",
|
||||
"-- Review these statements before executing!",
|
||||
"",
|
||||
"-- Step 1: Create the llm_api_map table (if not exists)",
|
||||
"""CREATE TABLE llm_api_map (
|
||||
id VARCHAR(21) NOT NULL PRIMARY KEY,
|
||||
llmid VARCHAR(21) NOT NULL,
|
||||
llmcatelogid VARCHAR(32) NOT NULL,
|
||||
apiname VARCHAR(100) NOT NULL,
|
||||
query_apiname VARCHAR(100),
|
||||
query_period INT,
|
||||
ppid VARCHAR(21)
|
||||
);""",
|
||||
"",
|
||||
"CREATE INDEX idx_llm_api_llm ON llm_api_map (llmid);",
|
||||
"CREATE UNIQUE INDEX idx_llm_api_catelog ON llm_api_map (llmid, llmcatelogid);",
|
||||
"",
|
||||
"-- Step 2: Insert data",
|
||||
""
|
||||
]
|
||||
|
||||
output_text = '\n'.join(header_lines) + '\n'.join(inserts) + '\n'
|
||||
|
||||
if args.output == '-':
|
||||
print(output_text)
|
||||
else:
|
||||
with open(args.output, 'w', encoding='utf-8') as f:
|
||||
f.write(output_text)
|
||||
print(f"Generated {len(inserts)} INSERT statements -> {args.output}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -17,8 +17,7 @@ from llm a
|
||||
join llm_catalog_rel rel on a.id = rel.llmid
|
||||
join llmcatelog b on rel.llmcatelogid = b.id
|
||||
join upapp c on a.upappid = c.id
|
||||
join uapiset d on c.apisetid = d.id
|
||||
join uapi e on e.apisetid = d.id and a.apiname = e.name
|
||||
join uapi e on c.apisetid = e.apisetid and a.apiname = e.name
|
||||
) x left join historyformat y on x.hfid = y.id
|
||||
left join uapiio z on x.ioid = z.id
|
||||
where rel.llmcatelogid = ${llmcatelogid}$
|
||||
|
||||
@ -10,22 +10,10 @@ async with get_sor_context(request._run_ns, 'llmage') as sor:
|
||||
else:
|
||||
msgs.append(f'llm not found: {llmid=}, {today=}')
|
||||
return msgs
|
||||
sql = """select a.* from llm a, upapp b
|
||||
where a.id=${llmid}$
|
||||
sql = """select a.* from llm a, upapp b
|
||||
where a.id=${llmid}$
|
||||
and a.upappid = b.id
|
||||
and a.enabled_date <= ${today}$
|
||||
and a.expired_date > ${today}$"""
|
||||
recs = await sor.sqlExe(sql, ns.copy())
|
||||
if recs:
|
||||
msgs.append('llm join upapp read ok')
|
||||
else:
|
||||
msgs.append('llm join upapp not found: {llmid=}, {today=}')
|
||||
return msgs
|
||||
sql = """select a.* from llm a, upapp b, uapiset c
|
||||
where a.id=${llmid}$
|
||||
and a.upappid = b.id
|
||||
and b.apisetid = c.id
|
||||
and a.enabled_date <= ${today}$
|
||||
and a.enabled_date <= ${today}$
|
||||
and a.expired_date > ${today}$"""
|
||||
recs = await sor.sqlExe(sql, ns.copy())
|
||||
if recs:
|
||||
@ -35,12 +23,10 @@ where a.id=${llmid}$
|
||||
return msgs
|
||||
|
||||
sql = """select a.*, e.ioid, e.stream
|
||||
from llm a, upapp c, uapiset d, uapi e
|
||||
where a.upappid = c.id
|
||||
and c.apisetid = d.id
|
||||
and e.apisetid = d.id
|
||||
and a.apiname = e.name
|
||||
and a.id=${llmid}$
|
||||
from llm a
|
||||
join upapp c on a.upappid = c.id
|
||||
join uapi e on c.apisetid = e.apisetid and a.apiname = e.name
|
||||
where a.id=${llmid}$
|
||||
and a.expired_date > ${today}$
|
||||
and a.enabled_date <= ${today}$"""
|
||||
recs = await sor.sqlExe(sql, ns.copy())
|
||||
@ -51,13 +37,11 @@ where a.upappid = c.id
|
||||
return msgs
|
||||
|
||||
sql = """select a.*, e.ioid, e.stream
|
||||
from llm a, upapp c, uapiset d, uapi e, uapiio b
|
||||
where a.upappid = c.id
|
||||
and c.apisetid = d.id
|
||||
and e.apisetid = d.id
|
||||
and a.apiname = e.name
|
||||
and e.ioid = b.id
|
||||
and a.id=${llmid}$
|
||||
from llm a
|
||||
join upapp c on a.upappid = c.id
|
||||
join uapi e on c.apisetid = e.apisetid and a.apiname = e.name
|
||||
join uapiio b on e.ioid = b.id
|
||||
where a.id=${llmid}$
|
||||
and a.expired_date > ${today}$
|
||||
and a.enabled_date <= ${today}$"""
|
||||
recs = await sor.sqlExe(sql, ns.copy())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user