openapi: 3.1.0 info: title: Model Platform Demo Core Extension version: 1.0.0 description: | 第 15 小步冻结的快速 Demo 目标契约。此文件只描述尚待实现的公共接口; 已实现的脚本、稳定版本详情、文件锁和数据资源接口继续以 platform-api-v1.yaml 为准。两个文件共同组成 Demo 公共 API v1。 当前身份仍使用 X-User-ID 与 X-Workspace-ID,后续替换 JWT 时不改变业务 DTO。 x-implementation-status: frozen-target paths: /api/v1/session-context: get: tags: [context] operationId: getSessionContext summary: 获取当前用户、Workspace 和可切换上下文 parameters: - $ref: '#/components/parameters/XUserId' - $ref: '#/components/parameters/XWorkspaceId' - $ref: '#/components/parameters/XRequestId' responses: '200': description: 当前演示上下文 content: application/json: schema: $ref: '#/components/schemas/SessionContextResponse' '403': $ref: '#/components/responses/ErrorResponse' /api/v1/jupyter/access-tickets: post: tags: [jupyter] operationId: createJupyterAccessTicket x-implementation-status: implemented-step16 summary: 为已加锁编辑会话签发短期 Jupyter 访问票据 description: | 必须校验 edit_session_id、用户、Workspace 和 lock_token。 成功响应同时设置 HttpOnly、SameSite=Lax、Path=/jupyter/ 的 jupyter_access Cookie,浏览器不得获得 Jupyter 内部服务 token。 parameters: - $ref: '#/components/parameters/XUserId' - $ref: '#/components/parameters/XWorkspaceId' - $ref: '#/components/parameters/XRequestId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateJupyterAccessTicketRequest' responses: '201': description: 访问票据已签发 headers: Set-Cookie: schema: type: string description: jupyter_access HttpOnly Cookie content: application/json: schema: $ref: '#/components/schemas/JupyterAccessTicketResponse' '403': $ref: '#/components/responses/ErrorResponse' '409': $ref: '#/components/responses/ErrorResponse' /api/v1/versions: get: tags: [versions] operationId: listStableVersions summary: 获取调度画布可用的稳定版本 parameters: - $ref: '#/components/parameters/XUserId' - $ref: '#/components/parameters/XWorkspaceId' - $ref: '#/components/parameters/XRequestId' - name: keyword in: query required: false schema: type: string maxLength: 100 - name: script_type in: query required: false schema: type: string enum: [python, notebook] - name: limit in: query required: false schema: type: integer minimum: 1 maximum: 200 default: 100 responses: '200': description: 当前 Workspace 可见稳定版本 content: application/json: schema: $ref: '#/components/schemas/StableVersionListResponse' '403': $ref: '#/components/responses/ErrorResponse' /api/v1/schedules: get: tags: [schedules] operationId: listSchedules summary: 查询调度方案 parameters: - $ref: '#/components/parameters/XUserId' - $ref: '#/components/parameters/XWorkspaceId' - $ref: '#/components/parameters/XRequestId' - name: keyword in: query required: false schema: type: string maxLength: 100 - name: enabled in: query required: false schema: type: boolean responses: '200': description: 调度方案列表 content: application/json: schema: $ref: '#/components/schemas/ScheduleListResponse' '403': $ref: '#/components/responses/ErrorResponse' post: tags: [schedules] operationId: createSchedule summary: 创建调度方案及完整 DAG parameters: - $ref: '#/components/parameters/XUserId' - $ref: '#/components/parameters/XWorkspaceId' - $ref: '#/components/parameters/XRequestId' - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateScheduleRequest' responses: '201': description: 调度方案已创建 content: application/json: schema: $ref: '#/components/schemas/ScheduleResponse' '409': $ref: '#/components/responses/ErrorResponse' '422': $ref: '#/components/responses/ErrorResponse' /api/v1/schedules/preview: post: tags: [schedules] operationId: previewScheduleCron summary: 校验 Cron 并预览未来执行时间 parameters: - $ref: '#/components/parameters/XUserId' - $ref: '#/components/parameters/XWorkspaceId' - $ref: '#/components/parameters/XRequestId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CronPreviewRequest' responses: '200': description: Cron 有效 content: application/json: schema: $ref: '#/components/schemas/CronPreviewResponse' '422': $ref: '#/components/responses/ErrorResponse' /api/v1/schedules/{schedule_id}: parameters: - $ref: '#/components/parameters/ScheduleId' get: tags: [schedules] operationId: getSchedule summary: 获取调度方案及完整 DAG parameters: - $ref: '#/components/parameters/XUserId' - $ref: '#/components/parameters/XWorkspaceId' - $ref: '#/components/parameters/XRequestId' responses: '200': description: 调度方案详情 content: application/json: schema: $ref: '#/components/schemas/ScheduleResponse' '404': $ref: '#/components/responses/ErrorResponse' put: tags: [schedules] operationId: updateSchedule summary: 使用 workflow_version 乐观锁替换调度方案和 DAG parameters: - $ref: '#/components/parameters/XUserId' - $ref: '#/components/parameters/XWorkspaceId' - $ref: '#/components/parameters/XRequestId' - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateScheduleRequest' responses: '200': description: 调度方案已更新,workflow_version 已递增 content: application/json: schema: $ref: '#/components/schemas/ScheduleResponse' '404': $ref: '#/components/responses/ErrorResponse' '409': $ref: '#/components/responses/ErrorResponse' '412': $ref: '#/components/responses/ErrorResponse' '422': $ref: '#/components/responses/ErrorResponse' delete: tags: [schedules] operationId: deleteSchedule summary: 软删除调度方案并保留历史运行 parameters: - $ref: '#/components/parameters/XUserId' - $ref: '#/components/parameters/XWorkspaceId' - $ref: '#/components/parameters/XRequestId' responses: '200': description: 调度方案已删除 content: application/json: schema: $ref: '#/components/schemas/ScheduleResponse' '404': $ref: '#/components/responses/ErrorResponse' /api/v1/schedules/{schedule_id}/run: post: tags: [schedule-runs] operationId: runScheduleNow summary: 为当前 workflow_version 创建一次立即运行 parameters: - $ref: '#/components/parameters/ScheduleId' - $ref: '#/components/parameters/XUserId' - $ref: '#/components/parameters/XWorkspaceId' - $ref: '#/components/parameters/XRequestId' - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/RunScheduleRequest' responses: '202': description: 运行事件已进入 Transactional Outbox content: application/json: schema: $ref: '#/components/schemas/ScheduleRunResponse' '404': $ref: '#/components/responses/ErrorResponse' '409': $ref: '#/components/responses/ErrorResponse' /api/v1/schedules/{schedule_id}/enable: post: tags: [schedules] operationId: enableSchedule summary: 启用 Cron 调度 parameters: - $ref: '#/components/parameters/ScheduleId' - $ref: '#/components/parameters/XUserId' - $ref: '#/components/parameters/XWorkspaceId' - $ref: '#/components/parameters/XRequestId' - $ref: '#/components/parameters/IdempotencyKey' responses: '200': description: 调度已启用 content: application/json: schema: $ref: '#/components/schemas/ScheduleResponse' '404': $ref: '#/components/responses/ErrorResponse' '409': $ref: '#/components/responses/ErrorResponse' /api/v1/schedules/{schedule_id}/disable: post: tags: [schedules] operationId: disableSchedule summary: 暂停 Cron 调度 parameters: - $ref: '#/components/parameters/ScheduleId' - $ref: '#/components/parameters/XUserId' - $ref: '#/components/parameters/XWorkspaceId' - $ref: '#/components/parameters/XRequestId' - $ref: '#/components/parameters/IdempotencyKey' responses: '200': description: 调度已暂停 content: application/json: schema: $ref: '#/components/schemas/ScheduleResponse' '404': $ref: '#/components/responses/ErrorResponse' /api/v1/schedule-runs: get: tags: [schedule-runs] operationId: listScheduleRuns summary: 查询最近调度运行 parameters: - $ref: '#/components/parameters/XUserId' - $ref: '#/components/parameters/XWorkspaceId' - $ref: '#/components/parameters/XRequestId' - name: schedule_id in: query required: false schema: $ref: '#/components/schemas/Ulid' - name: status in: query required: false schema: $ref: '#/components/schemas/RunStatus' - name: limit in: query required: false schema: type: integer minimum: 1 maximum: 200 default: 50 responses: '200': description: 最近运行列表 content: application/json: schema: $ref: '#/components/schemas/ScheduleRunListResponse' '403': $ref: '#/components/responses/ErrorResponse' /api/v1/schedule-runs/{run_id}: get: tags: [schedule-runs] operationId: getScheduleRun summary: 获取运行及各节点状态 parameters: - $ref: '#/components/parameters/RunId' - $ref: '#/components/parameters/XUserId' - $ref: '#/components/parameters/XWorkspaceId' - $ref: '#/components/parameters/XRequestId' responses: '200': description: 调度运行详情 content: application/json: schema: $ref: '#/components/schemas/ScheduleRunResponse' '404': $ref: '#/components/responses/ErrorResponse' /api/v1/schedule-runs/{run_id}/cancel: post: tags: [schedule-runs] operationId: cancelScheduleRun summary: 请求取消尚未结束的调度运行 parameters: - $ref: '#/components/parameters/RunId' - $ref: '#/components/parameters/XUserId' - $ref: '#/components/parameters/XWorkspaceId' - $ref: '#/components/parameters/XRequestId' - $ref: '#/components/parameters/IdempotencyKey' responses: '202': description: 取消请求已接受 content: application/json: schema: $ref: '#/components/schemas/ScheduleRunResponse' '404': $ref: '#/components/responses/ErrorResponse' '409': $ref: '#/components/responses/ErrorResponse' components: parameters: XUserId: name: X-User-ID in: header required: true schema: $ref: '#/components/schemas/Ulid' XWorkspaceId: name: X-Workspace-ID in: header required: true schema: $ref: '#/components/schemas/Ulid' XRequestId: name: X-Request-ID in: header required: false schema: type: string minLength: 1 maxLength: 64 IdempotencyKey: name: Idempotency-Key in: header required: true schema: type: string minLength: 8 maxLength: 128 ScheduleId: name: schedule_id in: path required: true schema: $ref: '#/components/schemas/Ulid' RunId: name: run_id in: path required: true schema: $ref: '#/components/schemas/Ulid' responses: ErrorResponse: description: 标准错误响应 content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' schemas: Ulid: type: string minLength: 26 maxLength: 26 pattern: '^[0-9A-HJKMNP-TV-Z]{26}$' UtcDateTime: type: string format: date-time RunStatus: type: string enum: [queued, running, succeeded, failed, cancelled, timed_out] NodeRunStatus: type: string enum: [queued, running, succeeded, failed, skipped, cancelled, timed_out] ErrorEnvelope: type: object additionalProperties: false required: [request_id, error] properties: request_id: type: string error: type: object additionalProperties: false required: [code, message, retryable, details] properties: code: type: string pattern: '^[A-Z][A-Z0-9_]+$' message: type: string retryable: type: boolean details: type: object UserSummary: type: object additionalProperties: false required: [user_id, username, display_name, role_code] properties: user_id: $ref: '#/components/schemas/Ulid' username: type: string display_name: type: string role_code: type: string WorkspaceSummary: type: object additionalProperties: false required: [workspace_id, workspace_code, workspace_name] properties: workspace_id: $ref: '#/components/schemas/Ulid' workspace_code: type: string workspace_name: type: string SessionContextResponse: type: object additionalProperties: false required: [request_id, data, meta] properties: request_id: type: string data: type: object additionalProperties: false required: [current_user, current_workspace, workspaces, workspace_members] properties: current_user: $ref: '#/components/schemas/UserSummary' current_workspace: $ref: '#/components/schemas/WorkspaceSummary' workspaces: type: array items: $ref: '#/components/schemas/WorkspaceSummary' workspace_members: type: array items: $ref: '#/components/schemas/UserSummary' meta: type: object CreateJupyterAccessTicketRequest: type: object additionalProperties: false required: [edit_session_id, lock_token] properties: edit_session_id: $ref: '#/components/schemas/Ulid' lock_token: type: string minLength: 32 maxLength: 256 JupyterAccessTicketResponse: type: object additionalProperties: false required: [request_id, data, meta] properties: request_id: type: string data: type: object additionalProperties: false required: [edit_session_id, jupyter_url, expires_at] properties: edit_session_id: $ref: '#/components/schemas/Ulid' jupyter_url: type: string pattern: '^/jupyter/' expires_at: $ref: '#/components/schemas/UtcDateTime' meta: type: object StableVersionSummary: type: object additionalProperties: false required: - versions_id - script_id - script_name - script_type - version_label - artifact_object_id - source_path - visibility - created_by - created_at properties: versions_id: $ref: '#/components/schemas/Ulid' script_id: $ref: '#/components/schemas/Ulid' script_name: type: string script_type: type: string enum: [python, notebook] version_label: type: string artifact_object_id: $ref: '#/components/schemas/Ulid' source_path: type: string visibility: type: string enum: [private, workspace, public] created_by: $ref: '#/components/schemas/Ulid' created_at: $ref: '#/components/schemas/UtcDateTime' StableVersionListResponse: type: object additionalProperties: false required: [request_id, data, meta] properties: request_id: type: string data: type: array items: $ref: '#/components/schemas/StableVersionSummary' meta: type: object required: [count] properties: count: type: integer minimum: 0 ScheduleNodeInput: type: object required: - node_key - node_name - versions_id - timeout_seconds - retry_count - retry_interval_sec - position_x - position_y - arguments properties: node_key: type: string minLength: 1 maxLength: 64 pattern: '^[A-Za-z0-9_-]+$' node_name: type: string minLength: 1 maxLength: 255 versions_id: $ref: '#/components/schemas/Ulid' timeout_seconds: type: integer minimum: 1 maximum: 86400 default: 600 retry_count: type: integer minimum: 0 maximum: 10 default: 0 retry_interval_sec: type: integer minimum: 0 maximum: 3600 default: 5 position_x: type: number position_y: type: number arguments: type: array maxItems: 100 items: type: string maxLength: 1000 ScheduleEdgeInput: type: object required: [source_node_key, target_node_key] properties: source_node_key: type: string minLength: 1 maxLength: 64 target_node_key: type: string minLength: 1 maxLength: 64 ScheduleDefinitionBase: type: object required: - schedule_name - trigger_type - timezone - max_concurrency - failure_policy - nodes - edges properties: schedule_name: type: string minLength: 1 maxLength: 255 description: type: [string, 'null'] maxLength: 1000 trigger_type: type: string enum: [manual, cron, api] default: cron cron_expression: type: [string, 'null'] maxLength: 128 timezone: type: string minLength: 1 maxLength: 64 default: Asia/Shanghai max_concurrency: type: integer minimum: 1 maximum: 100 default: 1 failure_policy: type: string enum: [stop, continue] default: stop nodes: type: array minItems: 1 maxItems: 100 items: $ref: '#/components/schemas/ScheduleNodeInput' edges: type: array maxItems: 500 items: $ref: '#/components/schemas/ScheduleEdgeInput' CreateScheduleRequest: unevaluatedProperties: false allOf: - $ref: '#/components/schemas/ScheduleDefinitionBase' UpdateScheduleRequest: unevaluatedProperties: false allOf: - $ref: '#/components/schemas/ScheduleDefinitionBase' - type: object required: [workflow_version] properties: workflow_version: type: integer minimum: 1 ScheduleNode: unevaluatedProperties: false allOf: - $ref: '#/components/schemas/ScheduleNodeInput' - type: object required: [node_id] properties: node_id: $ref: '#/components/schemas/Ulid' ScheduleEdge: unevaluatedProperties: false allOf: - $ref: '#/components/schemas/ScheduleEdgeInput' - type: object required: [edge_id, source_node_id, target_node_id] properties: edge_id: $ref: '#/components/schemas/Ulid' source_node_id: $ref: '#/components/schemas/Ulid' target_node_id: $ref: '#/components/schemas/Ulid' Schedule: type: object additionalProperties: false required: - schedule_id - workspace_id - schedule_name - trigger_type - timezone - enabled - workflow_version - max_concurrency - failure_policy - nodes - edges - created_by - updated_by - created_at - updated_at properties: schedule_id: $ref: '#/components/schemas/Ulid' workspace_id: $ref: '#/components/schemas/Ulid' schedule_name: type: string description: type: [string, 'null'] trigger_type: type: string enum: [manual, cron, api] cron_expression: type: [string, 'null'] timezone: type: string enabled: type: boolean workflow_version: type: integer minimum: 1 max_concurrency: type: integer failure_policy: type: string enum: [stop, continue] next_run_at: type: [string, 'null'] format: date-time last_run_at: type: [string, 'null'] format: date-time nodes: type: array items: $ref: '#/components/schemas/ScheduleNode' edges: type: array items: $ref: '#/components/schemas/ScheduleEdge' created_by: $ref: '#/components/schemas/Ulid' updated_by: $ref: '#/components/schemas/Ulid' created_at: $ref: '#/components/schemas/UtcDateTime' updated_at: $ref: '#/components/schemas/UtcDateTime' ScheduleResponse: type: object additionalProperties: false required: [request_id, data, meta] properties: request_id: type: string data: $ref: '#/components/schemas/Schedule' meta: type: object ScheduleListResponse: type: object additionalProperties: false required: [request_id, data, meta] properties: request_id: type: string data: type: array items: $ref: '#/components/schemas/Schedule' meta: type: object required: [count] properties: count: type: integer minimum: 0 CronPreviewRequest: type: object additionalProperties: false required: [cron_expression, timezone] properties: cron_expression: type: string minLength: 9 maxLength: 128 timezone: type: string minLength: 1 maxLength: 64 default: Asia/Shanghai count: type: integer minimum: 1 maximum: 20 default: 5 CronPreviewResponse: type: object additionalProperties: false required: [request_id, data, meta] properties: request_id: type: string data: type: object additionalProperties: false required: [cron_expression, timezone, next_runs] properties: cron_expression: type: string timezone: type: string next_runs: type: array items: $ref: '#/components/schemas/UtcDateTime' meta: type: object RunScheduleRequest: type: object additionalProperties: false properties: reason: type: string maxLength: 255 default: manual_run ScheduleRunSummary: type: object required: - run_id - schedule_id - workspace_id - workflow_version - trigger_type - run_status - state_version - queued_at properties: run_id: $ref: '#/components/schemas/Ulid' schedule_id: $ref: '#/components/schemas/Ulid' workspace_id: $ref: '#/components/schemas/Ulid' workflow_version: type: integer trigger_type: type: string enum: [manual, cron, api, retry] run_status: $ref: '#/components/schemas/RunStatus' state_version: type: integer minimum: 0 queued_at: $ref: '#/components/schemas/UtcDateTime' started_at: type: [string, 'null'] format: date-time finished_at: type: [string, 'null'] format: date-time duration_ms: type: [integer, 'null'] minimum: 0 error_code: type: [string, 'null'] error_message: type: [string, 'null'] logs_object_id: oneOf: - $ref: '#/components/schemas/Ulid' - type: 'null' result_object_id: oneOf: - $ref: '#/components/schemas/Ulid' - type: 'null' ScheduleNodeRun: type: object additionalProperties: false required: - node_run_id - run_id - node_id - versions_id - attempt_no - node_status - state_version properties: node_run_id: $ref: '#/components/schemas/Ulid' run_id: $ref: '#/components/schemas/Ulid' node_id: $ref: '#/components/schemas/Ulid' versions_id: $ref: '#/components/schemas/Ulid' attempt_no: type: integer minimum: 1 node_status: $ref: '#/components/schemas/NodeRunStatus' state_version: type: integer minimum: 0 started_at: type: [string, 'null'] format: date-time finished_at: type: [string, 'null'] format: date-time duration_ms: type: [integer, 'null'] minimum: 0 exit_code: type: [integer, 'null'] message: type: [string, 'null'] logs_object_id: oneOf: - $ref: '#/components/schemas/Ulid' - type: 'null' result_object_id: oneOf: - $ref: '#/components/schemas/Ulid' - type: 'null' ScheduleRunDetail: unevaluatedProperties: false allOf: - $ref: '#/components/schemas/ScheduleRunSummary' - type: object required: [node_runs] properties: node_runs: type: array items: $ref: '#/components/schemas/ScheduleNodeRun' ScheduleRunResponse: type: object additionalProperties: false required: [request_id, data, meta] properties: request_id: type: string data: $ref: '#/components/schemas/ScheduleRunDetail' meta: type: object ScheduleRunListResponse: type: object additionalProperties: false required: [request_id, data, meta] properties: request_id: type: string data: type: array items: $ref: '#/components/schemas/ScheduleRunSummary' meta: type: object required: [count] properties: count: type: integer minimum: 0