diff --git a/json/llm.json b/json/llm.json deleted file mode 100644 index 538c227..0000000 --- a/json/llm.json +++ /dev/null @@ -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"] - } -} diff --git a/models/llm.json b/models/llm.json deleted file mode 100644 index 3f50515..0000000 --- a/models/llm.json +++ /dev/null @@ -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"]} - ] -} diff --git a/pipeline_core/agent_config.py b/pipeline_core/agent_config.py index c8fbf9b..6fe376d 100644 --- a/pipeline_core/agent_config.py +++ b/pipeline_core/agent_config.py @@ -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,具备通用推理与判断能力,并根据当前产线配备了对应的工具。像一名有经验的负责人那样思考:先理解意图,再拆解问题,判断自己能否解决,必要时澄清或诚实说明。 diff --git a/pipeline_core/init.py b/pipeline_core/init.py index 580eb04..2a05082 100644 --- a/pipeline_core/init.py +++ b/pipeline_core/init.py @@ -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 diff --git a/wwwroot/api/agent_model_options.dspy b/wwwroot/api/agent_model_options.dspy index 38137e7..f37d925 100644 --- a/wwwroot/api/agent_model_options.dspy +++ b/wwwroot/api/agent_model_options.dspy @@ -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 diff --git a/wwwroot/api/llm_v1/chat/completions.dspy b/wwwroot/api/llm_v1/chat/completions.dspy index 30da72b..c0bff86 100644 --- a/wwwroot/api/llm_v1/chat/completions.dspy +++ b/wwwroot/api/llm_v1/chat/completions.dspy @@ -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:///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)