refactor(llm): 模型选择收敛到模型治理模块——agent_model_options改薄壳委托;旧llm CRUD(json/models/wwwroot/llm+init.py函数)删除;agent_config缺省模型名清空(交机构策略);llm_v1端点委托统一推理引擎
This commit is contained in:
parent
148dbe9cb1
commit
0d5c5ea00f
@ -1,19 +0,0 @@
|
||||
{
|
||||
"tblname": "llm",
|
||||
"title": "大语言模型管理",
|
||||
"params": {
|
||||
"sortby": "created_at",
|
||||
"logined_userorgid": "org_id",
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "api_key"],
|
||||
"cwidth": {}
|
||||
},
|
||||
"editexclouded": ["id", "created_at", "updated_at"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('./add_llm.dspy')}}",
|
||||
"update_data_url": "{{entire_url('./update_llm.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('./delete_llm.dspy')}}"
|
||||
},
|
||||
"confidential_fields": ["api_key"]
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "llm",
|
||||
"title": "大语言模型表",
|
||||
"primary": ["id"]
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "主键", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "name", "title": "模型名称", "type": "str", "length": 100, "nullable": "no"},
|
||||
{"name": "provider", "title": "供应商", "type": "str", "length": 50, "nullable": "no"},
|
||||
{"name": "model_id", "title": "模型标识", "type": "str", "length": 100},
|
||||
{"name": "api_base", "title": "API地址", "type": "str", "length": 500},
|
||||
{"name": "api_key", "title": "API密钥", "type": "str", "length": 500},
|
||||
{"name": "max_tokens", "title": "最大Token", "type": "int", "default": "8192"},
|
||||
{"name": "status", "title": "状态", "type": "str", "length": 20, "default": "active"},
|
||||
{"name": "capabilities", "title": "能力分类", "type": "str", "length": 20, "default": "text"},
|
||||
{"name": "org_id", "title": "所属机构ID", "type": "str", "length": 32, "default": "0"},
|
||||
{"name": "description", "title": "描述", "type": "text"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp"},
|
||||
{"name": "updated_at", "title": "更新时间", "type": "timestamp"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_llm_status", "idxtype": "index", "idxfields": ["status"]}
|
||||
]
|
||||
}
|
||||
@ -324,7 +324,7 @@ GENERAL_TOOLS = [
|
||||
|
||||
|
||||
DEFAULT_AGENT_CONFIG = AgentConfig(
|
||||
model_name="deepseek-v4-pro",
|
||||
model_name="", # 2026-09-04 起缺省模型由机构策略决定(模型治理),不再写死模型名
|
||||
temperature=0.4,
|
||||
max_turns=30,
|
||||
system_prompt="""你是一个通用 agent,具备通用推理与判断能力,并根据当前产线配备了对应的工具。像一名有经验的负责人那样思考:先理解意图,再拆解问题,判断自己能否解决,必要时澄清或诚实说明。
|
||||
|
||||
@ -3,11 +3,10 @@
|
||||
产线定义已收敛为代码能力包(PipelineAbility,见 ability.py)+ pipelines 表元数据
|
||||
(id/name/default_model,agent 运行时读取)。原「产线管理」在线定义/步骤/发布三功能
|
||||
已移除(2026-08-28,能力无法通过页面定义,属死库存)。
|
||||
|
||||
2026-09-04:旧 llm 表 CRUD(create_llm/update_llm/delete_llm)已移除——
|
||||
模型注册/管理统一收敛到模型治理模块(pipeline-llm),旧 `llm` 表停用。
|
||||
"""
|
||||
import json
|
||||
from appPublic.uniqueID import getID
|
||||
from sqlor.dbpools import DBPools
|
||||
from ahserver.serverenv import ServerEnv
|
||||
from appPublic.log import debug
|
||||
|
||||
|
||||
@ -15,65 +14,9 @@ MODULE_NAME = 'pipeline_core'
|
||||
DBNAME = 'pipeline'
|
||||
|
||||
|
||||
def _get_sor():
|
||||
"""Get database pool and dbname for pipeline_core module."""
|
||||
return DBPools(), DBNAME
|
||||
|
||||
|
||||
async def create_llm(params_kw):
|
||||
result = {'success': False, 'message': ''}
|
||||
try:
|
||||
db, dbname = _get_sor()
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
data = params_kw.copy()
|
||||
data.pop('page', None)
|
||||
data.pop('rows', None)
|
||||
data.pop('data_filter', None)
|
||||
data['id'] = getID()
|
||||
await sor.C('llm', data)
|
||||
result['success'] = True
|
||||
result['message'] = '创建成功'
|
||||
except Exception as e:
|
||||
result['message'] = str(e)
|
||||
return json.dumps(result, ensure_ascii=False, default=str)
|
||||
|
||||
|
||||
async def update_llm(params_kw):
|
||||
result = {'success': False, 'message': ''}
|
||||
try:
|
||||
db, dbname = _get_sor()
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
data = params_kw.copy()
|
||||
data.pop('page', None)
|
||||
data.pop('rows', None)
|
||||
data.pop('data_filter', None)
|
||||
await sor.U('llm', data)
|
||||
result['success'] = True
|
||||
result['message'] = '更新成功'
|
||||
except Exception as e:
|
||||
result['message'] = str(e)
|
||||
return json.dumps(result, ensure_ascii=False, default=str)
|
||||
|
||||
|
||||
async def delete_llm(params_kw):
|
||||
result = {'success': False, 'message': ''}
|
||||
try:
|
||||
db, dbname = _get_sor()
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
await sor.D('llm', {'id': params_kw.get('id')})
|
||||
result['success'] = True
|
||||
result['message'] = '删除成功'
|
||||
except Exception as e:
|
||||
result['message'] = str(e)
|
||||
return json.dumps(result, ensure_ascii=False, default=str)
|
||||
|
||||
|
||||
def load_pipeline_core():
|
||||
"""注册函数到 ServerEnv"""
|
||||
env = ServerEnv()
|
||||
# LLM
|
||||
env.create_llm = create_llm
|
||||
env.update_llm = update_llm
|
||||
env.delete_llm = delete_llm
|
||||
# 2026-09-04:旧 llm 表 CRUD 已移除——
|
||||
# 模型注册/管理统一收敛到模型治理模块(pipeline-llm),旧 `llm` 表停用。
|
||||
debug(f'[{MODULE_NAME}] module loaded')
|
||||
return True
|
||||
|
||||
@ -1,64 +1,13 @@
|
||||
# agent_model_options.dspy - 返回 active 模型列表(供 AgentIO 模型选择下拉)
|
||||
# llm 表是 pipeline 库的通用配置,产线无关
|
||||
# org_id 多租户隔离:非系统级机构只看到本机构的模型(不含系统级兜底)
|
||||
# 下拉值 = llm.id(唯一主键);显示 = llm.name。不同供应商可有相同 model_id
|
||||
# (API 模型名),但 name 必须唯一,用 id 做值才能区分供应商、避免解析漂移。
|
||||
# 前端 agent_input.js 读 valueField:'model_id' / textField:'model_id_text',
|
||||
# 故这里 model_id 字段放 llm.id、model_id_text 放 llm.name(字段名是历史遗留)。
|
||||
# selected 标记 = 当前项目的已设模型(sd_projects.default_model,产线通用、跨会话持久);
|
||||
# 项目未设模型时回退个人全局选择(default_llm_id)。前端 UiCode 按 selected 恢复选中项,
|
||||
# 不再每次重建都回退第一项。
|
||||
# agent_model_options.dspy — AgentIO 模型下拉
|
||||
# 2026-09-04 收敛:实现统一在模型治理模块 pipeline_llm.selection.model_options,
|
||||
# 本文件只作 URL 入口(前端 AgentIO 的 model_dataurl 指向此处)。
|
||||
# 返回格式与历史一致:value/model_id=模型id,model_id_text=模型名,含 selected 标记。
|
||||
|
||||
dbname = get_module_dbname('pipeline_core')
|
||||
uid = await get_user()
|
||||
org_id = (await get_userorgid()) or ''
|
||||
session_id = (params_kw or {}).get('session_id', '') or ''
|
||||
pipeline_id = (params_kw or {}).get('pipeline_id', '') or ''
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
sql = "SELECT id, name, provider, model_id, capabilities FROM llm WHERE status='active'"
|
||||
params = {}
|
||||
if org_id and org_id != '0':
|
||||
sql += " AND org_id=${org}$"
|
||||
params['org'] = org_id
|
||||
sql += " ORDER BY name"
|
||||
recs = await sor.sqlExe(sql, params)
|
||||
|
||||
# 项目级模型(优先级最高):按会话解析当前项目 → sd_projects.default_model。
|
||||
# session_id 为空/无会话记录时自动回退全局设置,与消息链路的项目解析同一函数,不漂移。
|
||||
project_model = ''
|
||||
try:
|
||||
from pipeline_service.workspace import get_session_project_id
|
||||
# 产线隔离(2026-09-02):传入口产线,跨产线项目不算本会话项目——
|
||||
# 否则平台/其他产线页面的模型选中态会被投标项目的 default_model 劫持
|
||||
_pid = await get_session_project_id(sor, uid or '', session_id, pipeline_id)
|
||||
if _pid:
|
||||
_p = await sor.sqlExe(
|
||||
"SELECT default_model FROM sd_projects WHERE id=${p}$", {"p": _pid})
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
if _p:
|
||||
project_model = getattr(_p[0], 'default_model', '') or ''
|
||||
except Exception as e:
|
||||
debug(f'agent_model_options project_model error: {e}')
|
||||
|
||||
# 个人之前选择的默认模型(项目未设模型时的回退选中项)
|
||||
current_llm_id = ''
|
||||
if uid:
|
||||
_s = await sor.sqlExe(
|
||||
"SELECT default_llm_id FROM pipeline_agent_settings WHERE user_id=${u}$", {"u": uid})
|
||||
if _s:
|
||||
current_llm_id = getattr(_s[0], 'default_llm_id', '') or ''
|
||||
|
||||
rows = []
|
||||
for r in recs:
|
||||
rows.append({
|
||||
'value': r.id,
|
||||
'text': f"{r.name} ({r.provider})",
|
||||
'provider': r.provider,
|
||||
'model_id': r.id, # 下拉值:llm.id(唯一,区分同名不同供应商)
|
||||
'model_id_text': r.name, # 下拉显示:llm.name
|
||||
'capabilities': r.capabilities or 'text',
|
||||
'selected': (r.name == project_model) if project_model else (r.id == current_llm_id),
|
||||
})
|
||||
|
||||
rows = await llm_model_options(org_id, uid=uid or '', session_id=session_id,
|
||||
pipeline_id=pipeline_id, value_field='id')
|
||||
return rows
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
# completions.dspy — OpenAI 兼容 LLM 代理端点(内部运行环境专用)
|
||||
# completions.dspy — OpenAI 兼容 LLM 代理端点(内部运行环境专用,兼容保留)
|
||||
#
|
||||
# URL: /pipeline_core/api/llm_v1/chat/completions
|
||||
# OpenAI 兼容客户端(browser-use / LangChain / openai SDK)配置:
|
||||
# base_url = http://<host>/pipeline_core/api/llm_v1
|
||||
# api_key = <短期 token>(pipeline_llm_tokens 签发,非真实模型 key)
|
||||
# 2026-09-04 起统一收敛到模型治理模块推理引擎(/pipeline-llm/api/v1 同一实现),
|
||||
# 本端点保留仅为兼容已配置此 base_url 的运行环境——调用链:
|
||||
# token 校验 → chat_inference(门禁链①-⑥+预授权)→ 上游调用 → 结算(双维度记账)
|
||||
#
|
||||
# 鉴权:Authorization: Bearer <短期 token>,不依赖登录会话(运行环境无 session)。
|
||||
# 机构隔离:按 token 绑定的 org_id 解析真实模型 key,运行环境拿不到真 key。
|
||||
# 鉴权:Authorization: Bearer *** token>,不依赖登录会话(运行环境无 session)。
|
||||
# 机构隔离:按 token 绑定的 org_id 解析,运行环境拿不到真 key。
|
||||
|
||||
auth = ''
|
||||
try:
|
||||
@ -47,8 +47,26 @@ if not isinstance(payload, dict) or not payload.get('messages'):
|
||||
"type": "invalid_request_error", "code": "missing_messages"}},
|
||||
ensure_ascii=False)
|
||||
|
||||
ok, result = await proxy_chat_completion(auth, payload)
|
||||
if ok:
|
||||
return json.dumps(result, ensure_ascii=False, default=str)
|
||||
return json.dumps({"error": {"message": result, "type": "invalid_request_error",
|
||||
"code": "proxy_error"}}, ensure_ascii=False)
|
||||
ok, info = await verify_llm_token(auth)
|
||||
if not ok:
|
||||
return json.dumps({"error": {"message": str(info),
|
||||
"type": "invalid_request_error", "code": "invalid_token"}},
|
||||
ensure_ascii=False)
|
||||
|
||||
task_ref = 'v1legacy:%s' % (info.get('project_id') or '')
|
||||
try:
|
||||
data = await llm_chat_inference(
|
||||
info.get('org_id', '') or '',
|
||||
info.get('created_by', '') or '',
|
||||
payload,
|
||||
model_name=(info.get('model_name') or payload.get('model') or ''),
|
||||
task_ref=task_ref)
|
||||
try:
|
||||
await record_llm_token_usage(info.get('id', ''), data.get('usage') or {})
|
||||
except Exception:
|
||||
pass
|
||||
return json.dumps(data, ensure_ascii=False, default=str)
|
||||
except Exception as e:
|
||||
return json.dumps({"error": {"message": str(e),
|
||||
"type": "invalid_request_error", "code": "govern_error"}},
|
||||
ensure_ascii=False)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user