From 6a48edb74236279dd24a2e11c89fde99d09eee24 Mon Sep 17 00:00:00 2001 From: WangLiZhao <1838393649@qq.com> Date: Tue, 7 Jul 2026 09:47:30 +0800 Subject: [PATCH] =?UTF-8?q?docs(readme):=20=E6=9B=B4=E6=96=B0=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E6=96=87=E6=A1=A3=E4=B8=BA=E6=99=BA=E8=83=BD=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E7=BD=91=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(database): 优化模型配置表结构和索引设计 - 重新设计 model_gateway_models 表的字段结构和约束 - 新增 billing_config 和 special_params 配置字段 - 优化索引策略,包括复合索引和条件索引的创建 - 统一表注释格式和字段说明文档 - 移除过时的配置字段和冗余索引 --- README.md | 560 +++++++++++++++++++++++++++++++++++++---------------- update.sql | 399 ++++++++++++++++++-------------------- 2 files changed, 578 insertions(+), 381 deletions(-) diff --git a/README.md b/README.md index 1ec443c..14ce5cf 100644 --- a/README.md +++ b/README.md @@ -1,206 +1,434 @@ -# model-asynch(模型异步中间件)[2026.5.12前,暂时弃置] +# Model Gateway — 智能模型网关 -一个独立的异步中间件服务:按模型配置路由调用不同模型服务,统一生成 `task_id`,后台异步执行,结果上传 OSS,并提供查询/批量领取/自动重试/自动清理能力,便于业务方“拿走结果并转移”。 - -> 分支约定:`dev` 为开发分支;`main`(或 master)为线上主分支。 +统一的 AI 模型网关服务,负责多模态(文本、图像、音频、向量、视频、全模态)模型调用的路由、执行与管理。基于 Go (GoFrame v2) 构建,使用 PostgreSQL、Redis 和 Consul 服务发现。 --- -## 1. 核心功能 +## 架构概览 -### 1.1 模型配置(asynch_models) -- 增删改查模型服务配置(`model_name` 唯一标识) -- 支持配置: - - 请求地址:`base_url + route` - - 请求方式:`http_method`(GET/POST) - - 请求头:`head_msg`(以请求头注入,支持多个 header) - - 超时:`timeout_seconds` - - 并发:`max_concurrency`(按租户+模型的 Redis 分布式信号量限流) - - 重试:`retry_times`(失败后最多再重试 N 次) - - 保留:`auto_clean_seconds`(任务被业务领取到 `state=4` 后的保留秒数,到期清理) +``` +客户端 ──> HTTP API ──> Controller ──> Service ──> DAO ──> PostgreSQL + │ └─> Redis(信号量/队列) + │ + ├──> AI 模型服务商(OpenAI、阿里云、火山引擎等) + ├──> OSS 文件服务 + ├──> Admin-go(租户/余额) + ├──> Prompts-core(会话回调) + └──> Skill 技能服务 +``` -### 1.2 异步任务(asynch_task) -- 创建任务:生成 `task_id`,入库排队 -- 后台 Worker: - - PostgreSQL `FOR UPDATE SKIP LOCKED` 抢占任务,支持多实例不重复消费 - - 调用模型服务(GET/POST) - - 结果上传 OSS(调用你们的 OSS 文件服务 `oss/file/uploadFile`,透传 `Authorization/X-User-Info`) -- 批量领取结果:批量查询 `task_id` 列表,返回 `task_id/state/oss_file`,并把成功的任务从 `state=2` 更新为 `state=4` -- 自动重试:失败 `state=3` 会由清理器按 `retry_times` 重新入队到队尾 -- 自动清理: - - `state=4` 且 `expire_at` 到期 → 硬删除任务 - - 失败重试耗尽仍失败 → 硬删除任务 - - `state=0/1` 超时 → 标记失败(防止卡死) +系统分为六个层次: -### 1.3 统计(asynch_model_stat) -- 按天统计:`day + tenant_id + creator + model_name -> request_count` -- 统计口径:仅在 Worker 真正调用模型服务时计数(OSS 重试不计数) -- 用途:给其他服务提供全局限流/监控依据 +| 层次 | 目录 | 职责 | +|---|---|---| +| **控制器层** | `controller/` | HTTP 路由注册与请求绑定 | +| **业务逻辑层** | `service/` | 模型配置 CRUD、异步任务执行、提示词构建、队列管理、统计 | +| **数据访问层** | `dao/` | PostgreSQL 数据访问(GoFrame ORM) | +| **模型层** | `model/` | DTO 请求/响应结构与数据库实体定义 | +| **通用工具层** | `common/` | 计费、类型转换、文件处理、请求头、映射、提示词、流式解析等工具 | +| **常量层** | `consts/` | 公共常量和表名定义 | --- -## 2. 使用流程(业务方如何接入) +## 核心功能 -### 第一步:创建模型配置 -业务方(或运维)先在中间件里创建/更新模型配置(`model_name` 为唯一键),例如: -- `POST /model/createModel`(或 `/model/updateModel`) +### 1. 模型配置管理 (`model_gateway_models`) + +提供 AI 模型服务配置的完整增删改查: + +| 字段 | 说明 | +|---|---| +| `model_name` | 模型名称(唯一标识) | +| `model_type` | 模型分类(100=推理, 200=图像, 300=音频, 400=向量, 500=全模态, 600=视频) | +| `operator_name` | 运营商标识(OpenAI、阿里云、火山引擎等) | +| `base_url` | 模型服务地址 | +| `http_method` | GET / POST | +| `head_msg` | 每次调用注入的请求头 | +| `form_json` | 动态表单定义(用于前端按模型渲染参数表单) | +| `request_mapping` / `response_mapping` | 标准格式与提供商 API 之间的参数映射 | +| `call_mode` | 0=同步, 1=异步, 2=流式 | +| `max_concurrency` | 单模型最大并发数(按租户维度限流) | +| `timeout_seconds` | 请求超时时间(秒) | +| `retry_times` | 失败重试次数 | +| `billing_config` | 计费规则(推理阶梯计价 / 视频分辨率计价) | +| `stream_config` | SSE 流式解析配置 | +| `query_config` | 异步任务轮询/查询配置 | + +**支持的模型类型:** + +| 类型码 | 类别 | 子类型 | +|---|---|---| +| 100 | 推理模型 | 文本生成、对话 | +| 200-205 | 图像模型 | 文生图、图生图、图片编辑、图片变体、图文生图 | +| 300-303 | 音频模型 | 文生语音、语音转文字、语音转语音 | +| 400-402 | 向量模型 | 文本嵌入、重排序 | +| 500-502 | 全模态模型 | 文图音频、视觉理解 | +| 600-604 | 视频模型 | 文生视频、图生视频、图文生视频、视频生视频 | + +### 2. 异步任务执行 (`model_gateway_task`) + +任务生命周期如下: + +``` +CreateTask ──> state=0(排队中) + │ + Worker 抢占(state=1,执行中) + │ + ├── 同步模式:直接调用模型 + ├── 异步模式:提交任务,通过 QueryConfig 轮询 + └── 流式模式:通过 StreamConfig 解析 SSE 事件 + │ + ├── 成功(state=2)──> 上传结果到 OSS + │ └──> 触发回调(如有配置) + └── 失败(state=3)──> 重试(最多 retry_times 次) + │ + 客户端下载(state=4,已下载) +``` + +**关键特性:** + +- **任务创建**:立即返回 `taskId`,执行在 goroutine 中异步完成 +- **并发控制**:基于 Redis 的分布式信号量,按模型维度限制并发数 +- **队列门控**:基于 Redis Lua 脚本的严格队列插槽机制,防止分布式创建下超限 +- **自动重试**:临时性错误(超时、内部错误)自动重试;硬错误直接失败 +- **OSS 上传**:结果以 JSON 格式上传到 OSS 文件服务,OSS URL 存入任务记录 +- **回调通知**:任务完成时可选触发 HTTP 回调(`TriggerCallback`、`TriggerPromptsCallback`、`CallbackBuildResult`) +- **阶梯计费**:支持推理 token 阶梯计价和视频分辨率计价两种模型 + +### 3. 提示词构建 (`/buildMessages`) + +异步构建推理模型的结构化消息: + +- 从配置合并系统提示词(`modelPrompts.types`) +- 拉取技能 Markdown 内容(`SkillMdContent`) +- 通过 `GetSessionHistory` 注入会话历史 +- 根据附件数量自动拆分多轮(`SplitByAttachment`) +- 支持视频模型通过对话模型编排的多轮生成 + +### 4. 动态调参 (`/autoTune`) + +周期性自动调参(建议每小时触发),基于近期 P90 执行耗时和到达率动态调整 `max_concurrency` 和队列上限: + +- 读取模型配置(数据库中的上限值 cap) +- 根据时间窗口内已完成任务统计 P90 执行耗时 +- 使用 Little 定律(到达率 × P90 / 利用率)计算新的并发数 +- 单次调整幅度限制在 ±50% +- 运行时参数写入 Redis(2 小时 TTL),不修改数据库中的上限值 + +### 5. 日统计 (`model_gateway_log_stat`) + +按天、租户、创建人、模型维度的请求计数,使用原子 upsert(`ON DUPLICATE KEY UPDATE`)。 + +按天、租户、创建人、模型维度的请求计数,使用原子 upsert(`ON DUPLICATE KEY UPDATE`)。 + +### 6. 核心接口详细说明 + +#### 6.1 构建消息结构 `/buildMessages` + +构建推理模型的提示词消息体,将系统提示词、技能知识、会话历史、用户自定义提示词合并为最终的消息结构。 + +**请求示例(JSON):** -请求示例(JSON): ```json { - "modelName": "model-service", - "modelsType": "1,2,3", - "baseUrl": "http://127.0.0.1:8000", - "route": "/api/v1/chat", - "httpMethod": "POST", - "headMsg": "API_KEY:model-key,API_STATE:true,API_NUM:123", - "enabled": 1, - "maxConcurrency": 5, - "queueLimit": 20, - "timeoutSeconds": 1800, - "expectedSeconds": 600, - "retryTimes": 3, - "retryQueueMaxSeconds": 600, - "autoCleanSeconds": 3600, - "remark": "Model-Service 模型服务" + "modelName": "gpt-4o", + "buildType": 1, + "skillName": "code-review", + "callbackUrl": "http://callback.example.com/result", + "nodeId": "node_001", + "sessionId": "session_abc", + "customPrompt": "请用中文回答", + "messages": { + "model": "gpt-4o", + "max_tokens": 4096, + "messages": [ + {"role": "system", "content": [{"type": "text", "text": "你是一名助手"}]}, + {"role": "user", "content": [{"type": "text", "text": "写一封邮件"}]} + ] + } } ``` -参数说明: -- `modelName`:模型名称(唯一标识/路由键) -- `modelsType`:模型类型ID列表(逗号分隔),示例:`1,2,3`(关联 `asynch_models_type.type_id`) +| 请求字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `modelName` | string | 是 | 网关模型名称 | +| `buildType` | int | 是 | 构建类型:1=单轮构建, 2=多轮构建 | +| `skillName` | string | 否 | 技能名称,用于拉取技能 MD 知识 | +| `callbackUrl` | string | 否 | 构建完成后的回调地址 | +| `nodeId` | string | 否 | 节点 ID(用于查询会话历史) | +| `sessionId` | string | 否 | 会话 ID | +| `messages` | object | 是 | 前端构建的消息结构 | +| `customPrompt` | string | 否 | 用户自定义提示词 | -### 模型类型同步 -- `POST /model/type/createModelType` 创建成功后,会同步 `POST` 到 `prompts-core` 的 `/prompt/createPrompt` -- 同步字段映射: - - `typeId` -> `modelTypeId` - - `type` -> `modelType` - - `promptInfo` -> `promptInfo` - - `responseJsonSchema` -> `responseJsonSchema` - - `version` -> `version` -- 若 `prompts-core` 同步失败,`model-gateway` 会回滚本地新建的模型类型,避免两边数据不一致 -- `form`:动态表单配置(JSON数组),用于前端按模型渲染参数表单(字段示例:field/label/type/required) -- `baseUrl`:模型服务地址(Base URL) -- `route`:模型服务路由(拼接到 baseUrl 后) -- `httpMethod`:请求方式(GET/POST) -- `headMsg`:请求头绑定(支持多个 header,逗号分隔,格式 `Key:Value`;布尔/数字也会以字符串形式注入 header) -- `enabled`:是否启用(0禁用/1启用) -- `maxConcurrency`:单模型最大并发(按租户+模型维度限流) -- `queueLimit`:排队上限(严格控制)。创建任务时通过 Redis Lua 原子闸门校验并占位,保证分布式并发创建不会超限;任务进入成功/失败态后释放占位,失败重试重新入队时会再次占位。 -- `timeoutSeconds`:调用模型服务超时(秒) -- `expectedSeconds`:模型预计执行时间(秒,用于超时判定/排队策略等) -- `retryTimes`:失败后最多再重试 N 次(不含首次) -- `retryQueueMaxSeconds`:失败重试最大排队时间(秒);0 表示重试插队到队首;>0 表示排队超过该时间后插队,否则仍到队尾 -- `autoCleanSeconds`:任务被领取到 `state=4` 后的保留时间(秒),到期清理 -- `remark`:备注说明 +**响应:** -### 第二步:创建任务拿到 task_id -业务方发起推理请求时调用: -- `POST /task/createTask`(传 `modelName + requestPayload + bizName + callbackUrl(可选) + modelKey(可选)`) -- 中间件返回 `task_id` -- 业务方将 `task_id` 落到自己的业务表,并把业务状态置为「生成中」 - -> `modelKey` 用于“动态覆盖/补充”模型配置中的 `head_msg`(例如每次请求携带不同的 `X-API-Key:xxx`)。 -> -> `callbackUrl` 用于任务成功后的回调通知:当任务 `state=2` 成功时,中间件会发起一次 GET 请求: -> - 实际回调地址:`callbackUrl/{bizName}` -> - query 参数:`task_id/state/oss_file/file_type/text(可选,最多2000字符)` - -### 第三步:同步任务进度(推荐批量) -业务方通过轮询/定时任务同步进度: -- 推荐:`POST /task/getTaskBatch`(批量传 `taskIds`,返回每个任务的 `state + oss_file`) -- 或单条:`GET /task/getTaskResult?taskId=...` - -业务侧拿到 `oss_file` 后自行做资源处理(直接保存或转存),并把业务状态更新为「成功/失败」。 - -> 说明:批量接口对 `state=2(成功)` 的任务会自动标记为 `state=4(已下载)` 并写入 `expire_at`,用于后续清理。 - -### 后台执行(由上层定时任务控制) -本项目不再在服务进程内常驻轮询 worker/cleaner,而是提供两个接口供上层定时任务触发: -- `POST /task/runWork`:执行一次 Worker(抢占并处理一批排队任务;适合处理 createTask 立即执行时未处理到的任务和积压队列) -- `POST /task/cleanWork`:执行一次 Cleaner(清理过期任务、失败重试、超时任务失败等) - -创建任务执行策略: -- `POST /task/createTask` 成功入库后,会立即异步尝试执行当前任务。 -- 若当前模型并发已满,或当前任务未成功抢占,则会按 `asynch.worker.intervalSeconds` 对当前任务做轻量级定向轮询;只要任务仍为 `state=0` 就继续尝试,一旦进入 `state=1/2/3/4` 就立即停止,不会一直轮询。 -- 若任务执行成功且配置了 `callbackUrl + bizName`,会在成功落库后异步触发回调钩子。 - -本地调试(可选): -可在 `config.yml` 中开启自动执行,避免手工频繁调用接口: -```yml -asynch: - worker: - enabled: true - intervalSeconds: 5 - batchSize: 10 - goroutines: 1 - cleaner: - enabled: true - intervalSeconds: 30 -``` - -### 动态并发/队列调参(接口请求控制) -为支持根据最近一段时间的耗时与吞吐对 `max_concurrency/queue_limit` 做动态调整,本项目提供接口供上层定时任务触发(建议每小时一次): -- `POST /model/autoTune` - -请求参数(JSON,可选): ```json { - "windowSeconds": 3600 + "taskId": "550e8400-e29b-41d4-a716-446655440000" } ``` -> `windowSeconds` 不传/<=0 默认 3600(1小时)。 -动态调参口径(默认近 1 小时窗口,按 `model_name` 维度): -- 执行耗时:`finished_at - started_at`(取 P90) -- 吞吐:近 1 小时完成数 / 3600 +**处理流程:** -调参结果不会覆盖 `asynch_models` 中配置的最大上限(cap),而是写入 Redis 运行时参数(带 TTL,默认 2 小时): -- `asynch:runtime:max_concurrency:{model_name}` -- `asynch:runtime:queue_limit:{model_name}` +1. **查模型配置** — 根据 `modelName` 查询 `model_gateway_models` 获取模型定义 +2. **创建构建记录** — 写入 `model_gateway_build_record`,状态为处理中 +3. **异步执行构建**(goroutine): + - **推理模型(type=100)**: + - 从配置读取系统提示词(`modelPrompts.types.100`) + - 若有 `skillName`,通过 `GetSkillUser` 拉取技能 ZIP,提取 MD 内容 + - 合并系统提示词 + 技能知识 + 用户自定义提示词到 `messages` + - 通过 `GetSessionHistory` 注入会话历史 + - 按附件数量自动拆分多轮(`SplitByAttachment`,按 video/image/audio 的 `maxCount` 约束) + - 返回单轮或多轮消息结构 + - **视频模型(type=600-699)**: + - 查找当前用户的对话模型(`is_chat_model=1`) + - 获取该对话模型的协议模板(`prompts_provider_protocol`) + - 构建对话模型请求体,调用对话模型生成视频分镜的 JSON rounds + - 将 rounds 上传 OSS,存入构建记录 +4. **回写结果** — 更新构建记录状态(成功/失败 + 耗时) +5. **触发回调** — 若有 `callbackUrl`,POST 回调通知任务完成 -生效位置: -- CreateTask 入队时,严格 queue_limit 闸门会优先使用运行时 `queue_limit`(若无运行时值则回退 cap)。 -- Worker 获取并发令牌时,优先使用运行时 `max_concurrency`(若无运行时值则回退 cap)。 +**对应源码:** + +- Controller:[controller/model_gateway_task_controller.go](`ModelGatewayTask.BuildMessages`) +- Service:[service/task/task_service.go](`taskService.BuildMessages` → `executeBuild` → `buildResult`) +- Util(提示词合并):[common/util/prompt.go](`MergePrompt`、`InjectHistory`、`SplitByAttachment`) +- Util(技能知识):[service/prompt/prompt_files_handle_service.go](`SkillMdContent`) + +#### 6.2 创建异步任务 `/createTask` + +创建异步模型调用任务,立即返回 `taskId`,后端 goroutine 异步执行模型调用、结果上传与计费。 + +**请求示例(JSON):** + +```json +{ + "modelName": "dall-e-3", + "bizName": "image-generator", + "callbackUrl": "http://callback.example.com/result", + "epicycleId": 12345, + "buildModelName": "gpt-4o", + "requestPayload": { + "model": "dall-e-3", + "prompt": "一只站在树枝上的猫头鹰,水墨风格", + "n": 1, + "size": "1024x1024" + } +} +``` + +| 请求字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `modelName` | string | 是 | 模型名称 | +| `bizName` | string | 否 | 业务名称,用于统计区分 | +| `callbackUrl` | string | 否 | 任务完成后的回调地址 | +| `requestPayload` | object | 是 | 透传给模型服务的请求参数 | +| `epicycleId` | int64 | 否 | 轮次 ID(prompts-core 场景) | +| `buildModelName` | string | 否 | 构建阶段使用的模型名(prompts-core 场景) | + +**响应:** + +```json +{ + "taskId": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +**处理流程:** + +1. **鉴权与参数校验** — 获取用户信息,校验模型配置是否存在且已启用 +2. **队列门控(可选)** — 通过 Redis Lua 脚本做严格的分布式队列插槽校验,避免并发超限 +3. **写入任务记录** — 状态为 `1(执行中)`,记录 `requestPayload`、`callbackUrl`、`bizName` 等 +4. **操作日志** — 记录创建任务的审计日志到 `model_gateway_logs_op` +5. **计费预处理** — 若模型配置了 `billing_config`,从请求参数中提取计费维度数据 +6. **异步执行**(goroutine `AsyncWorker.handleOne`): + +``` +检查租户余额 ──> 调用模型服务 ──> 解析响应映射 ──> 计费计算并扣费 + │ + ┌── 同步模式(call_mode=0):直接调用,等待 HTTP 响应 + ├── 异步模式(call_mode=1):提交任务后通过 QueryConfig 轮询结果 + └── 流式模式(call_mode=2):通过 StreamConfig 解析 SSE 事件流 + │ + ┌── prompts-core 场景:对模型输出做 ParseAndValidate + 重试机制 + └── 普通场景:直接映射响应 + │ + 上传 OSS ──> 更新任务为成功 ──> 触发回调 +``` + +7. **重试机制**:超时、内部错误等可重试错误会按 `retry_times` 重试;硬错误(参数错误等)直接失败 +8. **回调通知**:任务成功或失败后,若有 `callbackUrl` 则 POST 回调;prompts-core 场景额外触发 `TriggerPromptsCallback` + +**调用模式对比:** + +| 模式 | 值 | 行为 | +|---|---|---| +| 同步 | 0 | 直接 HTTP 调用模型,等待完整响应后返回 | +| 异步 | 1 | 提交任务后通过 `QueryConfig` 配置的轮询地址定时查询结果 | +| 流式 | 2 | 对 SSE 事件流按 `StreamConfig` 规则解析(支持 concat/base64_concat/collect/final) | + +**对应源码:** + +- Controller:[controller/model_gateway_task_controller.go](`ModelGatewayTask.CreateTask`) +- Service:[service/task/task_service.go](`taskService.Create`) +- Worker:[service/task/worker.go](`asyncWorker.handleOne`、`InvokeModel`) +- 队列门控:[service/queue/queue_gate.go](`AcquireQueueSlot`、`ReleaseQueueSlot`) +- 运行时调参:[service/queue/runtime_tune.go](`GetRuntimeMaxConcurrency`、`GetRuntimeQueueLimit`) +- 异步轮询:[common/util/pull_task.go](`PullTaskResult`) +- 流式解析:[common/util/streaming.go](`ParseStreamResponse`) +- 计费:[common/util/billing.go](`CalculateBilling`、`ExtractRequestBilling`) + +### 7. 全量 API 接口一览 + +| 分类 | 接口路径 | 方法 | 说明 | +|---|---|---|---| +| **模型配置** | `/createModel` | POST | 创建模型配置 | +| | `/updateModel` | PUT | 更新模型配置 | +| | `/deleteModel` | DELETE | 删除模型配置 | +| | `/getModel` | GET | 获取模型详情 | +| | `/listModel` | GET | 模型列表(分页+筛选) | +| | `/listType` | GET | 模型类型列表 | +| | `/listOperator` | GET | 运营商列表 | +| | `/updateChatModel` | POST | 设置当前用户的对话模型 | +| | `/getIsChatModel` | GET | 获取当前对话模型 | +| | `/autoTune` | POST | 触发动态调参 | +| **任务管理** | `/createTask` | POST | 创建异步任务 | +| | `/jobTask` | POST | 定时批量任务处理器 | +| | `/getTaskResult` | GET | 获取单条任务结果 | +| | `/getTaskBatch` | POST | 批量查询任务(成功任务自动标记为已下载) | +| | `/listTask` | GET | 任务列表分页查询 | +| | `/modelCallback` | POST | 接收异步模型回调通知 | +| | `/queryPending` | GET | 轮询进行中的异步任务 | +| **提示词** | `/buildMessages` | POST | 构建结构化提示词消息(异步) | +| **统计** | `/listModelStat` | GET | 日使用量统计 | +-- + +## 技术栈 + +| 组件 | 技术选型 | +|---|---| +| 语言 | Go 1.26 | +| 框架 | GoFrame v2(`github.com/gogf/gf/v2`) | +| 数据库 | PostgreSQL(GoFrame pgsql 驱动) | +| 缓存/队列 | Redis(分布式信号量、队列门控、运行时调参存储) | +| 服务发现 | Consul | +| 链路追踪 | Jaeger(OTLP HTTP) | +| 模型调用 | 原生 HTTP(可定制请求头、鉴权、超时) | +| 文件存储 | OSS 文件上传服务(multipart 表单) | + +## 数据库表 + +| 表名 | 说明 | +|---|---| +| `model_gateway_models` | 模型服务配置(动态表单、请求/响应映射、计费规则) | +| `model_gateway_task` | 异步任务记录(状态机、重试、OSS 结果文件) | +| `model_gateway_build_record` | 提示词构建记录 | +| `model_gateway_logs_op` | 操作审计日志 | +| `model_gateway_logs_stat` | 日维度请求量统计 | +| `prompts_provider_protocol` | 视频模型编排的提供商协议模板 | + +## 配置说明 + +详见 `config.yml`。主要配置块: + +- `database` — PostgreSQL 连接(双数据源:`default` + `model_gateway`) +- `redis` — Redis 连接 +- `consul` — Consul 地址 +- `jaeger` — Jaeger OTLP HTTP 端点 +- `queryPending` — 异步任务自动轮询配置(调试开关) +- `jobTask` — 批量处理器间隔、批大小、协程池大小 +- `modelPrompts.types` — 各模型类型的系统提示词(100/200/300/400/500) +- `nodePrompts` — 节点路由提示词模板 + +## 快速开始 + +### 环境要求 + +- Go 1.26+ +- PostgreSQL +- Redis +- Consul(可选,用于服务发现) + +### 本地开发 + +1. 克隆仓库 +2. 在 PostgreSQL 中执行 `update.sql` 创建所有表 +3. 修改 `config.yml` 中的数据库、Redis 和 Consul 配置 +4. 启动服务: + ```bash + go run main.go + ``` +5. 服务默认监听 `3004` 端口(可在 `config.yml` 中修改) + +### Docker 构建 + +```bash +docker build -t model-gateway . +docker run -p 3004:3004 model-gateway +``` + +Dockerfile 使用多阶段构建,基于 `golang:alpine` 镜像,配置了国内 Go 代理,输出剥离调试信息的精简二进制。 --- -## 3. 状态机说明(asynch_task.state) +## 项目目录结构 -| state | 含义 | 产生方 | -|---:|---|---| -| 0 | 排队中 | 创建任务/重试入队 | -| 1 | 执行中 | Worker 抢占后 | -| 2 | 成功(已上传 OSS) | Worker | -| 3 | 失败 | Worker / 超时处理 | -| 4 | 已下载(已领取) | 批量领取接口(2→4) | - -字段补充: -- `retry_count`:已重试次数(不含首次) -- `enqueue_at`:入队时间(用于排队顺序,重试会更新为 NOW() 放到队尾) -- `expire_at`:仅对 `state=4` 生效,表示保留到期时间 +``` +. +├── main.go # 入口:路由注册、自动执行器启动 +├── config.yml # 应用配置 +├── Dockerfile # 多阶段 Docker 构建 +├── go.mod / go.sum # Go 模块依赖 +├── update.sql # 数据库 DDL +│ +├── controller/ # HTTP 接口控制器 +│ ├── model_gateway_models_controller.go +│ ├── model_gateway_task_controller.go +│ └── model_gateway_logs_stat_controller.go +│ +├── service/ # 业务逻辑层 +│ ├── gateway/ # OSS 上传、回调、余额、技能/会话查询 +│ ├── model/ # 模型 CRUD、对话模型管理 +│ ├── prompt/ # 文件拉取、技能 Markdown、提示词构建 +│ ├── queue/ # 动态调参、信号量、队列门控、运行时调参 +│ ├── stat/ # 使用量统计 +│ └── task/ # 任务创建、Worker 执行、异步结果处理 +│ +├── dao/ # 数据访问层 +│ ├── model_gateway_models_dao.go +│ ├── model_gateway_task_dao.go +│ ├── model_gateway_build_record_dao.go +│ ├── model_gateway_logs_stat_dao.go +│ ├── model_gateway_logs_op_dao.go +│ └── provider_protocol_dao.go +│ +├── model/ # 数据模型 +│ ├── dto/ # 请求/响应结构体 +│ └── entity/ # 数据库实体 +│ +├── common/ # 通用工具 +│ └── util/ # 计费、类型转换、文件处理、请求头、映射、提示词、流式、异步轮询 +│ +└── consts/ # 常量定义 + └── public/ # 模型类型、任务状态、运营商列表、表名 +``` --- -## 4. 配置说明(config.yml) +## 任务状态机 -关键配置: -- `database.default`: PostgreSQL 连接 -- `redis.default`: Redis 连接(并发令牌、可扩展用途) +| 状态码 | 含义 | 转换来源 | +|---|---:|---| +| 0 | 排队中 | 创建任务或重试入队 | +| 1 | 执行中 | Worker 抢占成功 | +| 2 | 成功(已上传 OSS) | Worker 执行完成 | +| 3 | 失败 | Worker 执行出错或超时 | +| 4 | 已下载 | 批量查询接口标记 | --- -## 5. 数据库初始化 +## License -项目根目录提供 `update.sql`:首次部署执行建表 SQL。 +内部项目 — 红未来科技 ---- -## 6. 开发与发布建议(Git) - -- `dev`:日常开发与联调 -- `main`:线上稳定分支 -- 推荐流程: - 1) 从 `main` 拉出 `dev` - 2) 功能完成后提 MR/PR 合并回 `main` - 3) 打 tag / 发布镜像 diff --git a/update.sql b/update.sql index 248f060..f4c376d 100644 --- a/update.sql +++ b/update.sql @@ -1,233 +1,202 @@ --- ========================= --- model_gateway_models --- ========================= -CREATE TABLE IF NOT EXISTS model_gateway_models ( - id int8 PRIMARY KEY, - tenant_id int8 NOT NULL DEFAULT 0, - creator varchar(64) NOT NULL, - created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater varchar(64) NOT NULL, - updated_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted_at timestamp(6), - model_name varchar(128) NOT NULL, - model_type int2 NOT NULL DEFAULT 0, - operator_name varchar(64) NOT NULL DEFAULT '', - base_url varchar(256) NOT NULL, - http_method varchar(8) NOT NULL DEFAULT 'POST', - head_msg jsonb NOT NULL DEFAULT '{}', - api_key varchar(256) NOT NULL DEFAULT '', - is_private int2 NOT NULL DEFAULT 0, - enabled int2 NOT NULL DEFAULT 1, - is_chat_model int2 NOT NULL DEFAULT 0, - is_owner int2 NOT NULL DEFAULT 99, - form_json jsonb NOT NULL DEFAULT '{}', - request_mapping jsonb NOT NULL DEFAULT '{}', - response_mapping jsonb NOT NULL DEFAULT '{}', - response_body varchar(128) NOT NULL DEFAULT '', - token_config jsonb NOT NULL DEFAULT '{}', - extend_mapping jsonb NOT NULL DEFAULT '{}', - query_config jsonb NOT NULL DEFAULT '{}', - stream_config jsonb NOT NULL DEFAULT '{}', - first_frame varchar(128) NOT NULL DEFAULT '', - last_frame varchar(128) NOT NULL DEFAULT '', - max_concurrency int4 NOT NULL DEFAULT 10, - timeout_seconds int4 NOT NULL DEFAULT 600, - retry_times int2 NOT NULL DEFAULT 3, - auto_clean_seconds int4 NOT NULL DEFAULT 86400, - response_token_field varchar(128) NOT NULL DEFAULT '', - call_mode int2 NOT NULL DEFAULT 0, - required_fields jsonb NOT NULL DEFAULT '[]', - max_tokens int4 DEFAULT 0 - ); +-- ============================================ +-- 模型网关 (model-gateway) 建表语句 +-- ============================================ -CREATE UNIQUE INDEX IF NOT EXISTS uk_model_gateway_models_tenant_creator_model ON model_gateway_models (tenant_id, creator, model_name); -CREATE INDEX IF NOT EXISTS idx_model_gateway_models_model_name ON model_gateway_models (model_name); -CREATE INDEX IF NOT EXISTS idx_model_gateway_models_model_type ON model_gateway_models (model_type); -CREATE INDEX IF NOT EXISTS idx_model_gateway_models_tenant_id ON model_gateway_models (tenant_id); -CREATE INDEX IF NOT EXISTS idx_model_gateway_models_deleted_at ON model_gateway_models (deleted_at); -CREATE INDEX IF NOT EXISTS idx_model_gateway_models_enabled ON model_gateway_models (enabled); +-- 1. 模型配置表 +CREATE TABLE "public"."model_gateway_models" ( + "id" int8 NOT NULL, + "tenant_id" int8 NOT NULL DEFAULT 0, + "creator" varchar(64) NOT NULL, + "created_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) NOT NULL, + "updated_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted_at" timestamp(6), + "model_name" varchar(128) NOT NULL, + "model_type" int2 NOT NULL DEFAULT 0, + "operator_name" varchar(64) NOT NULL DEFAULT '', + "base_url" varchar(256) NOT NULL, + "http_method" varchar(8) NOT NULL DEFAULT 'POST', + "head_msg" jsonb NOT NULL DEFAULT '{}', + "api_key" varchar(256) NOT NULL DEFAULT '', + "is_private" int2 NOT NULL DEFAULT 0, + "enabled" int2 NOT NULL DEFAULT 1, + "is_chat_model" int2 NOT NULL DEFAULT 0, + "form_json" jsonb NOT NULL DEFAULT '{}', + "request_mapping" jsonb NOT NULL DEFAULT '{}', + "response_mapping" jsonb NOT NULL DEFAULT '{}', + "extend_mapping" jsonb NOT NULL DEFAULT '{}', + "query_config" jsonb NOT NULL DEFAULT '{}', + "stream_config" jsonb NOT NULL DEFAULT '{}', + "max_concurrency" int4 NOT NULL DEFAULT 10, + "timeout_seconds" int4 NOT NULL DEFAULT 600, + "retry_times" int2 NOT NULL DEFAULT 3, + "call_mode" int2 NOT NULL DEFAULT 0, + "required_fields" jsonb NOT NULL DEFAULT '[]', + "billing_config" jsonb NOT NULL DEFAULT '{}', + "special_params" jsonb NOT NULL DEFAULT '{}', + CONSTRAINT "model_gateway_models_pkey" PRIMARY KEY ("id") +); -COMMENT ON TABLE model_gateway_models IS '模型配置表'; -COMMENT ON COLUMN model_gateway_models.id IS '主键ID(非自增)'; -COMMENT ON COLUMN model_gateway_models.tenant_id IS '租户ID'; -COMMENT ON COLUMN model_gateway_models.creator IS '创建人'; -COMMENT ON COLUMN model_gateway_models.created_at IS '创建时间'; -COMMENT ON COLUMN model_gateway_models.updater IS '更新人'; -COMMENT ON COLUMN model_gateway_models.updated_at IS '更新时间'; -COMMENT ON COLUMN model_gateway_models.deleted_at IS '删除时间(软删)'; -COMMENT ON COLUMN model_gateway_models.model_name IS '模型名称'; -COMMENT ON COLUMN model_gateway_models.model_type IS '模型类型'; -COMMENT ON COLUMN model_gateway_models.operator_name IS '运营商名称'; -COMMENT ON COLUMN model_gateway_models.base_url IS '模型地址'; -COMMENT ON COLUMN model_gateway_models.http_method IS '请求方式 GET/POST'; -COMMENT ON COLUMN model_gateway_models.head_msg IS '请求头信息'; -COMMENT ON COLUMN model_gateway_models.api_key IS '调用凭证/密钥'; +ALTER TABLE "public"."model_gateway_models" OWNER TO "postgres"; -COMMENT ON COLUMN model_gateway_models.is_private IS '是否私有化:0-私有 1-公共'; -COMMENT ON COLUMN model_gateway_models.enabled IS '是否启用:0-停用 1-启用'; -COMMENT ON COLUMN model_gateway_models.is_chat_model IS '是否为对话模型:0-否 1-是'; -COMMENT ON COLUMN model_gateway_models.is_owner IS '1=当前用户创建 0=超级管理员'; +CREATE INDEX "idx_models_deleted_at" ON "public"."model_gateway_models" ("deleted_at"); +CREATE INDEX "idx_models_enabled" ON "public"."model_gateway_models" ("enabled"); +CREATE INDEX "idx_models_model_name" ON "public"."model_gateway_models" ("model_name"); +CREATE INDEX "idx_models_model_type" ON "public"."model_gateway_models" ("model_type"); +CREATE INDEX "idx_models_tenant_id" ON "public"."model_gateway_models" ("tenant_id"); +CREATE INDEX "idx_models_tenant_creator" ON "public"."model_gateway_models" ("tenant_id", "creator"); +CREATE INDEX "idx_models_tenant_creator_model_deleted" ON "public"."model_gateway_models" ("tenant_id", "creator", "model_name") WHERE deleted_at IS NULL; +CREATE UNIQUE INDEX "uk_models_tenant_creator_model" ON "public"."model_gateway_models" ("tenant_id", "creator", "model_name"); -COMMENT ON COLUMN model_gateway_models.form_json IS '动态表单结构'; -COMMENT ON COLUMN model_gateway_models.request_mapping IS '请求映射'; -COMMENT ON COLUMN model_gateway_models.response_mapping IS '返回映射'; -COMMENT ON COLUMN model_gateway_models.response_body IS '返回主体'; -COMMENT ON COLUMN model_gateway_models.token_config IS 'Token计算配置'; -COMMENT ON COLUMN model_gateway_models.extend_mapping IS '附加映射'; -COMMENT ON COLUMN model_gateway_models.query_config IS '查询/回调配置'; -COMMENT ON COLUMN model_gateway_models.stream_config IS '流式输出配置'; -COMMENT ON COLUMN model_gateway_models.first_frame IS '首帧图片参数'; -COMMENT ON COLUMN model_gateway_models.last_frame IS '尾帧图片参数'; -COMMENT ON COLUMN model_gateway_models.max_concurrency IS '最大并发数'; -COMMENT ON COLUMN model_gateway_models.timeout_seconds IS '调用模型超时(秒)'; -COMMENT ON COLUMN model_gateway_models.retry_times IS '失败重试次数'; -COMMENT ON COLUMN model_gateway_models.auto_clean_seconds IS '任务完成后自动清理时间(秒)'; -COMMENT ON COLUMN model_gateway_models.response_token_field IS '响应中消耗token的字段映射'; -COMMENT ON COLUMN model_gateway_models.call_mode IS '调用模式:0-同步 1-异步 2-流式'; -COMMENT ON COLUMN model_gateway_models.required_fields IS '必选字段列表'; -COMMENT ON COLUMN model_gateway_models.max_tokens IS '最大 token 数,0 表示不传'; +COMMENT ON TABLE "public"."model_gateway_models" IS '模型配置表'; +COMMENT ON COLUMN "public"."model_gateway_models"."model_name" IS '模型名称'; +COMMENT ON COLUMN "public"."model_gateway_models"."model_type" IS '模型类型'; +COMMENT ON COLUMN "public"."model_gateway_models"."operator_name" IS '运营商名称'; +COMMENT ON COLUMN "public"."model_gateway_models"."base_url" IS '模型地址'; +COMMENT ON COLUMN "public"."model_gateway_models"."http_method" IS '请求方式 GET/POST'; +COMMENT ON COLUMN "public"."model_gateway_models"."head_msg" IS '请求头信息'; +COMMENT ON COLUMN "public"."model_gateway_models"."api_key" IS '调用凭证/密钥'; +COMMENT ON COLUMN "public"."model_gateway_models"."is_private" IS '是否私有化:0-私有 1-公共'; +COMMENT ON COLUMN "public"."model_gateway_models"."enabled" IS '是否启用:0-停用 1-启用'; +COMMENT ON COLUMN "public"."model_gateway_models"."is_chat_model" IS '是否为对话模型:0-否 1-是'; +COMMENT ON COLUMN "public"."model_gateway_models"."form_json" IS '动态表单结构'; +COMMENT ON COLUMN "public"."model_gateway_models"."request_mapping" IS '请求映射'; +COMMENT ON COLUMN "public"."model_gateway_models"."response_mapping" IS '返回映射'; +COMMENT ON COLUMN "public"."model_gateway_models"."extend_mapping" IS '附加映射'; +COMMENT ON COLUMN "public"."model_gateway_models"."query_config" IS '查询/回调配置'; +COMMENT ON COLUMN "public"."model_gateway_models"."stream_config" IS '流式输出配置'; +COMMENT ON COLUMN "public"."model_gateway_models"."max_concurrency" IS '最大并发数'; +COMMENT ON COLUMN "public"."model_gateway_models"."timeout_seconds" IS '调用模型超时(秒)'; +COMMENT ON COLUMN "public"."model_gateway_models"."retry_times" IS '失败重试次数'; +COMMENT ON COLUMN "public"."model_gateway_models"."call_mode" IS '调用模式:0-同步 1-异步 2-流式'; +COMMENT ON COLUMN "public"."model_gateway_models"."required_fields" IS '必选字段列表'; +COMMENT ON COLUMN "public"."model_gateway_models"."billing_config" IS '计费配置'; +COMMENT ON COLUMN "public"."model_gateway_models"."special_params" IS '请求特殊参数'; +-- 2. 模型网关任务表 +CREATE TABLE "public"."model_gateway_task" ( + "id" int8 NOT NULL, + "tenant_id" int8 NOT NULL DEFAULT 0, + "creator" varchar(64) NOT NULL, + "created_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) NOT NULL, + "updated_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted_at" timestamp(6), + "model_name" varchar(128) NOT NULL, + "task_id" varchar(64) NOT NULL, + "biz_name" varchar(128) NOT NULL DEFAULT '', + "callback_url" varchar(512) DEFAULT '', + "state" int2 NOT NULL DEFAULT 0, + "retry_count" int4 NOT NULL DEFAULT 0, + "error_msg" text DEFAULT '', + "result_file" jsonb NOT NULL DEFAULT '{}', + "request_payload" jsonb NOT NULL DEFAULT '{}', + "duration_seconds" int8 NOT NULL DEFAULT 0, + "epicycle_id" varchar(64) NOT NULL DEFAULT '', + "billing_data" jsonb NOT NULL DEFAULT '[]', + "build_model_name" varchar(128) NOT NULL DEFAULT '', + CONSTRAINT "model_gateway_task_pkey" PRIMARY KEY ("id") +); --- ========================= --- model_gateway_task --- ========================= -CREATE TABLE IF NOT EXISTS model_gateway_task ( - id int8 PRIMARY KEY, - tenant_id int8 NOT NULL DEFAULT 0, - creator varchar(64) NOT NULL, - created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater varchar(64) NOT NULL, - updated_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted_at timestamp(6), - model_name varchar(128) NOT NULL, - task_id varchar(64) NOT NULL, - biz_name varchar(128) NOT NULL DEFAULT '', - callback_url varchar(512) DEFAULT '', - state int2 NOT NULL DEFAULT 0, - retry_count int4 NOT NULL DEFAULT 0, - phase int2 NOT NULL DEFAULT 0, - tmp_file text DEFAULT '', - error_msg text DEFAULT '', - result_file jsonb NOT NULL DEFAULT '{}', - request_payload jsonb NOT NULL DEFAULT '{}', - text_result jsonb NOT NULL DEFAULT '{}', - expend_tokens int8 NOT NULL DEFAULT 0, - duration_seconds int8 NOT NULL DEFAULT 0, - epicycle_id varchar(64) NOT NULL DEFAULT '' - ); +ALTER TABLE "public"."model_gateway_task" OWNER TO "postgres"; -CREATE UNIQUE INDEX IF NOT EXISTS uk_model_gateway_task_tenant_creator_task_id ON model_gateway_task (tenant_id, creator, task_id); -CREATE INDEX IF NOT EXISTS idx_model_gateway_task_task_id ON model_gateway_task (task_id); -CREATE INDEX IF NOT EXISTS idx_model_gateway_task_state ON model_gateway_task (state); -CREATE INDEX IF NOT EXISTS idx_model_gateway_task_deleted_at ON model_gateway_task (deleted_at); +CREATE INDEX "idx_task_creator_created" ON "public"."model_gateway_task" ("creator", "created_at" DESC); +CREATE INDEX "idx_task_model_state" ON "public"."model_gateway_task" ("model_name", "state"); +CREATE INDEX "idx_task_state" ON "public"."model_gateway_task" ("state"); +CREATE INDEX "idx_task_state_created" ON "public"."model_gateway_task" ("state", "created_at"); +CREATE INDEX "idx_task_task_id" ON "public"."model_gateway_task" ("task_id"); +CREATE INDEX "idx_task_tenant_model" ON "public"."model_gateway_task" ("tenant_id", "model_name"); +CREATE UNIQUE INDEX "uk_task_task_id" ON "public"."model_gateway_task" ("task_id"); -COMMENT ON TABLE model_gateway_task IS '模型网关任务表'; -COMMENT ON COLUMN model_gateway_task.id IS '主键ID'; -COMMENT ON COLUMN model_gateway_task.tenant_id IS '租户ID'; -COMMENT ON COLUMN model_gateway_task.creator IS '创建人'; -COMMENT ON COLUMN model_gateway_task.created_at IS '创建时间'; -COMMENT ON COLUMN model_gateway_task.updater IS '更新人'; -COMMENT ON COLUMN model_gateway_task.updated_at IS '更新时间'; -COMMENT ON COLUMN model_gateway_task.deleted_at IS '删除时间(软删)'; -COMMENT ON COLUMN model_gateway_task.model_name IS '模型名称'; -COMMENT ON COLUMN model_gateway_task.task_id IS '任务ID(对外返回)'; -COMMENT ON COLUMN model_gateway_task.biz_name IS '业务名称(调用方模块/系统)'; -COMMENT ON COLUMN model_gateway_task.callback_url IS '回调地址'; -COMMENT ON COLUMN model_gateway_task.state IS '0排队中/1执行中/2成功/3失败/4已下载'; -COMMENT ON COLUMN model_gateway_task.retry_count IS '已重试次数'; -COMMENT ON COLUMN model_gateway_task.phase IS '执行阶段:0模型阶段/1OSS阶段'; -COMMENT ON COLUMN model_gateway_task.tmp_file IS '临时结果文件路径'; -COMMENT ON COLUMN model_gateway_task.error_msg IS '错误信息'; -COMMENT ON COLUMN model_gateway_task.result_file IS '结果文件:{oss_file, file_type, file_size}'; -COMMENT ON COLUMN model_gateway_task.request_payload IS '请求参数(JSON)'; -COMMENT ON COLUMN model_gateway_task.text_result IS '文本类结果'; -COMMENT ON COLUMN model_gateway_task.expend_tokens IS '消耗token数'; -COMMENT ON COLUMN model_gateway_task.duration_seconds IS '耗时(秒)'; -COMMENT ON COLUMN model_gateway_task.epicycle_id IS '轮次ID'; +COMMENT ON TABLE "public"."model_gateway_task" IS '模型网关任务表'; +COMMENT ON COLUMN "public"."model_gateway_task"."model_name" IS '模型名称'; +COMMENT ON COLUMN "public"."model_gateway_task"."task_id" IS '任务ID(对外返回)'; +COMMENT ON COLUMN "public"."model_gateway_task"."biz_name" IS '业务名称'; +COMMENT ON COLUMN "public"."model_gateway_task"."callback_url" IS '回调地址'; +COMMENT ON COLUMN "public"."model_gateway_task"."state" IS '0排队中/1执行中/2成功/3失败/4已下载'; +COMMENT ON COLUMN "public"."model_gateway_task"."retry_count" IS '已重试次数'; +COMMENT ON COLUMN "public"."model_gateway_task"."error_msg" IS '错误信息'; +COMMENT ON COLUMN "public"."model_gateway_task"."result_file" IS '结果文件:{oss_file, file_type, file_size}'; +COMMENT ON COLUMN "public"."model_gateway_task"."request_payload" IS '请求参数(JSON)'; +COMMENT ON COLUMN "public"."model_gateway_task"."duration_seconds" IS '耗时(秒)'; +COMMENT ON COLUMN "public"."model_gateway_task"."epicycle_id" IS '轮次ID'; +COMMENT ON COLUMN "public"."model_gateway_task"."billing_data" IS '计费数据'; +COMMENT ON COLUMN "public"."model_gateway_task"."build_model_name" IS '构建模型名称'; +-- 3. 操作日志表 +CREATE TABLE "public"."model_gateway_logs_op" ( + "id" int8 NOT NULL, + "tenant_id" int8 NOT NULL DEFAULT 0, + "creator" varchar(64) NOT NULL, + "created_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) NOT NULL, + "updated_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted_at" timestamp(6), + "ip" varchar(64) DEFAULT '', + "user_agent" varchar(256) DEFAULT '', + "api_path" varchar(256) DEFAULT '', + "http_method" varchar(16) DEFAULT '', + "biz_name" varchar(128) NOT NULL DEFAULT '', + "model_name" varchar(128) NOT NULL DEFAULT '', + "task_id" varchar(64) NOT NULL DEFAULT '', + "op_type" varchar(64) NOT NULL DEFAULT 'createTask', + "success" int2 NOT NULL DEFAULT 1, + "error_msg" text DEFAULT '', + "cost_ms" int8 NOT NULL DEFAULT 0, + "request_payload" jsonb, + "response_payload" jsonb, + CONSTRAINT "model_gateway_logs_op_pkey" PRIMARY KEY ("id") +); --- ========================= --- model_gateway_log_stat --- ========================= -CREATE TABLE IF NOT EXISTS model_gateway_log_stat ( - day date NOT NULL, - tenant_id int8 NOT NULL DEFAULT 0, - creator varchar(64) NOT NULL DEFAULT '', - model_name varchar(128) NOT NULL DEFAULT '', - request_count int8 NOT NULL DEFAULT 0, - created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (day, tenant_id, creator, model_name) - ); +ALTER TABLE "public"."model_gateway_logs_op" OWNER TO "postgres"; -CREATE INDEX IF NOT EXISTS idx_model_gateway_log_stat_day ON model_gateway_log_stat (day); -CREATE INDEX IF NOT EXISTS idx_model_gateway_log_stat_creator ON model_gateway_log_stat (creator); -CREATE INDEX IF NOT EXISTS idx_model_gateway_log_stat_model_name ON model_gateway_log_stat (model_name); -CREATE INDEX IF NOT EXISTS idx_model_gateway_log_stat_tenant_day ON model_gateway_log_stat (tenant_id, day); +CREATE INDEX "idx_op_log_biz_name" ON "public"."model_gateway_logs_op" ("biz_name"); +CREATE INDEX "idx_op_log_deleted_at" ON "public"."model_gateway_logs_op" ("deleted_at"); +CREATE INDEX "idx_op_log_model_name" ON "public"."model_gateway_logs_op" ("model_name"); +CREATE INDEX "idx_op_log_op_type" ON "public"."model_gateway_logs_op" ("op_type"); +CREATE INDEX "idx_op_log_task_id" ON "public"."model_gateway_logs_op" ("task_id"); +CREATE INDEX "idx_op_log_tenant_time" ON "public"."model_gateway_logs_op" ("tenant_id", "created_at"); +CREATE INDEX "idx_op_log_model_time" ON "public"."model_gateway_logs_op" ("model_name", "created_at"); -COMMENT ON TABLE model_gateway_log_stat IS '按天统计表'; -COMMENT ON COLUMN model_gateway_log_stat.day IS '天(YYYY-MM-DD)'; -COMMENT ON COLUMN model_gateway_log_stat.tenant_id IS '租户ID'; -COMMENT ON COLUMN model_gateway_log_stat.creator IS '创建人'; -COMMENT ON COLUMN model_gateway_log_stat.model_name IS '模型名称'; -COMMENT ON COLUMN model_gateway_log_stat.request_count IS '请求次数'; -COMMENT ON COLUMN model_gateway_log_stat.created_at IS '创建时间'; -COMMENT ON COLUMN model_gateway_log_stat.updated_at IS '更新时间'; +COMMENT ON TABLE "public"."model_gateway_logs_op" IS '操作日志表'; +COMMENT ON COLUMN "public"."model_gateway_logs_op"."ip" IS '客户端IP'; +COMMENT ON COLUMN "public"."model_gateway_logs_op"."user_agent" IS 'User-Agent'; +COMMENT ON COLUMN "public"."model_gateway_logs_op"."api_path" IS '接口路径'; +COMMENT ON COLUMN "public"."model_gateway_logs_op"."http_method" IS 'HTTP方法'; +COMMENT ON COLUMN "public"."model_gateway_logs_op"."biz_name" IS '业务名称'; +COMMENT ON COLUMN "public"."model_gateway_logs_op"."model_name" IS '模型名称'; +COMMENT ON COLUMN "public"."model_gateway_logs_op"."task_id" IS '任务ID'; +COMMENT ON COLUMN "public"."model_gateway_logs_op"."op_type" IS '操作类型'; +COMMENT ON COLUMN "public"."model_gateway_logs_op"."success" IS '是否成功:1成功/0失败'; +COMMENT ON COLUMN "public"."model_gateway_logs_op"."error_msg" IS '错误信息'; +COMMENT ON COLUMN "public"."model_gateway_logs_op"."cost_ms" IS '耗时(毫秒)'; +COMMENT ON COLUMN "public"."model_gateway_logs_op"."request_payload" IS '请求 JSON'; +COMMENT ON COLUMN "public"."model_gateway_logs_op"."response_payload" IS '响应 JSON'; --- ========================= --- model_gateway_logs_op --- ========================= -CREATE TABLE IF NOT EXISTS model_gateway_logs_op ( - id int8 PRIMARY KEY, - tenant_id int8 NOT NULL DEFAULT 0, - creator varchar(64) NOT NULL, - created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater varchar(64) NOT NULL, - updated_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted_at timestamp(6), - ip varchar(64) DEFAULT '', - user_agent varchar(256) DEFAULT '', - api_path varchar(256) DEFAULT '', - http_method varchar(16) DEFAULT '', - biz_name varchar(128) NOT NULL DEFAULT '', - model_name varchar(128) NOT NULL DEFAULT '', - task_id varchar(64) NOT NULL DEFAULT '', - op_type varchar(64) NOT NULL DEFAULT 'createTask', - success int2 NOT NULL DEFAULT 1, - error_msg text DEFAULT '', - cost_ms int8 NOT NULL DEFAULT 0, - request_payload jsonb, - response_payload jsonb - ); +-- 4. 按天统计表 +CREATE TABLE "public"."model_gateway_logs_stat" ( + "day" date NOT NULL, + "tenant_id" int8 NOT NULL DEFAULT 0, + "creator" varchar(64) NOT NULL DEFAULT '', + "model_name" varchar(128) NOT NULL DEFAULT '', + "request_count" int8 NOT NULL DEFAULT 0, + "created_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP +); -CREATE INDEX IF NOT EXISTS idx_model_gateway_logs_op_task_id ON model_gateway_logs_op (task_id); -CREATE INDEX IF NOT EXISTS idx_model_gateway_logs_op_biz_name ON model_gateway_logs_op (biz_name); -CREATE INDEX IF NOT EXISTS idx_model_gateway_logs_op_model_name ON model_gateway_logs_op (model_name); -CREATE INDEX IF NOT EXISTS idx_model_gateway_logs_op_op_type ON model_gateway_logs_op (op_type); -CREATE INDEX IF NOT EXISTS idx_model_gateway_logs_op_deleted_at ON model_gateway_logs_op (deleted_at); -CREATE INDEX IF NOT EXISTS idx_model_gateway_logs_op_tenant_time ON model_gateway_logs_op (tenant_id, created_at); +ALTER TABLE "public"."model_gateway_logs_stat" OWNER TO "postgres"; -COMMENT ON TABLE model_gateway_logs_op IS '操作日志表'; -COMMENT ON COLUMN model_gateway_logs_op.id IS '主键ID(非自增)'; -COMMENT ON COLUMN model_gateway_logs_op.tenant_id IS '租户ID'; -COMMENT ON COLUMN model_gateway_logs_op.creator IS '创建人'; -COMMENT ON COLUMN model_gateway_logs_op.created_at IS '创建时间'; -COMMENT ON COLUMN model_gateway_logs_op.updater IS '更新人'; -COMMENT ON COLUMN model_gateway_logs_op.updated_at IS '更新时间'; -COMMENT ON COLUMN model_gateway_logs_op.deleted_at IS '删除时间(软删)'; -COMMENT ON COLUMN model_gateway_logs_op.ip IS '客户端IP'; -COMMENT ON COLUMN model_gateway_logs_op.user_agent IS 'User-Agent'; -COMMENT ON COLUMN model_gateway_logs_op.api_path IS '接口路径'; -COMMENT ON COLUMN model_gateway_logs_op.http_method IS 'HTTP方法'; -COMMENT ON COLUMN model_gateway_logs_op.biz_name IS '业务名称(调用方模块/系统)'; -COMMENT ON COLUMN model_gateway_logs_op.model_name IS '模型名称'; -COMMENT ON COLUMN model_gateway_logs_op.task_id IS '任务ID'; -COMMENT ON COLUMN model_gateway_logs_op.op_type IS '操作类型'; -COMMENT ON COLUMN model_gateway_logs_op.success IS '是否成功:1成功/0失败'; -COMMENT ON COLUMN model_gateway_logs_op.error_msg IS '错误信息(失败时)'; -COMMENT ON COLUMN model_gateway_logs_op.cost_ms IS '耗时(毫秒)'; -COMMENT ON COLUMN model_gateway_logs_op.request_payload IS '请求 JSON'; -COMMENT ON COLUMN model_gateway_logs_op.response_payload IS '响应 JSON'; \ No newline at end of file +CREATE INDEX "idx_stat_creator" ON "public"."model_gateway_logs_stat" ("creator"); +CREATE INDEX "idx_stat_day" ON "public"."model_gateway_logs_stat" ("day"); +CREATE INDEX "idx_stat_model_name" ON "public"."model_gateway_logs_stat" ("model_name"); +CREATE INDEX "idx_stat_tenant_day" ON "public"."model_gateway_logs_stat" ("tenant_id", "day"); + +COMMENT ON TABLE "public"."model_gateway_logs_stat" IS '按天统计表'; +COMMENT ON COLUMN "public"."model_gateway_logs_stat"."day" IS '天(YYYY-MM-DD)'; +COMMENT ON COLUMN "public"."model_gateway_logs_stat"."tenant_id" IS '租户ID'; +COMMENT ON COLUMN "public"."model_gateway_logs_stat"."creator" IS '创建人'; +COMMENT ON COLUMN "public"."model_gateway_logs_stat"."model_name" IS '模型名称'; +COMMENT ON COLUMN "public"."model_gateway_logs_stat"."request_count" IS '请求次数'; \ No newline at end of file