{
  "openapi": "3.1.0",
  "info": {
    "title": "维多乐布家庭与幼教活动课程 API",
    "version": "1.2.0",
    "description": "面向成年家长、监护人和幼教工作者的文本生成与结构化活动课程服务，基于实际配置的火山方舟基础模型和服务指令，不声称完成微调、训练、安全评估或监管审核。Chat Completions 请求和回复正文不持久化；课程接口有意按账号保存课程、大纲、生成教案及用户修改，删除课程或注销账号可删除这些课程记录。有效密钥校验后的 /v1 请求记录方法、路径、状态、时间及所属密钥，统计实际请求而非计费额度。网站聊天、API 聊天、课程大纲和教案生成按账号共享每60秒12次、每86400秒120次的进程内滑动窗口；进程重启清零。生成失败也可能消耗生成尝试次数。全服务模型请求最多8个并发，同一账号一次生成或保存操作。调用方应提示内容由AI生成，避免提交孩子身份信息，并审阅生成草案；不提供经核验的课程效果或儿童诊断。 集成课程辅助编辑读取本人已保存课程；模型可回答问题或生成有限字段修改提案，不自动改写课程。提案及差异按课程/账号持久化，用户确认apply后才原子写入；discard只修改提案状态。删除课程或注销账号会级联删除提案。客户端历史只作低优先级交流背景，不作为已保存课程事实。 GET课程assist读取该课程最新待确认提案（最多30条），供页面刷新恢复。新提案记录会保存对应真实模型reply；旧记录无reply时省略，不编造恢复文本。"
  },
  "servers": [
    {
      "url": "/",
      "description": "部署本服务的同一来源；本地地址或真实 HTTPS 地址以实际部署为准"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Models"
    },
    {
      "name": "Chat"
    },
    {
      "name": "Account keys"
    },
    {
      "name": "Courses"
    },
    {
      "name": "Course assistant"
    }
  ],
  "paths": {
    "/v1/models": {
      "get": {
        "tags": [
          "Models"
        ],
        "operationId": "listModels",
        "summary": "列出服务模型标识",
        "description": "需要有效 Bearer API Key，不接受 Cookie 作为替代。模型列表不代表上游当前可用。created 是本 API 合同引入日期，不代表上游训练或发布日期。",
        "responses": {
          "200": {
            "description": "模型列表",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Model"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "403": {
            "$ref": "#/components/responses/ApiError"
          },
          "429": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      }
    },
    "/v1/chat/completions": {
      "post": {
        "tags": [
          "Chat"
        ],
        "operationId": "createChatCompletion",
        "summary": "生成家长信息辅助、互动活动或故事内容",
        "description": "仅支持列出的参数。调用方 system 消息作为不可信任务上下文转成 user 消息，不能覆盖服务安全指令。输入和输出均受当前已实施的基础规则拦截；并不代表完整或经评估的内容审核覆盖。最多 12 条文本消息，每条最多 4000 字符，合计最多 16000 字符；JSON 请求体最多 131072 字节。当前不支持 stream=true、工具调用、temperature 等额外参数。同一账号同时只处理一个模型请求，网站与 API 共用限制。响应 finish_reason 来自上游；usage 仅在上游返回有效真实计数时出现。 audience 可省略，默认为 parent；educator 面向成年幼教工作者的班级活动。",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "实际模型响应",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatCompletion"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ApiError"
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "403": {
            "$ref": "#/components/responses/ApiError"
          },
          "409": {
            "$ref": "#/components/responses/ApiError"
          },
          "413": {
            "$ref": "#/components/responses/ApiError"
          },
          "415": {
            "$ref": "#/components/responses/ApiError"
          },
          "422": {
            "$ref": "#/components/responses/ApiError"
          },
          "429": {
            "$ref": "#/components/responses/ApiError"
          },
          "500": {
            "$ref": "#/components/responses/ApiError"
          },
          "502": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "$ref": "#/components/responses/ApiError"
          },
          "504": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      }
    },
    "/api/api-keys": {
      "get": {
        "tags": [
          "Account keys"
        ],
        "operationId": "listAccountApiKeys",
        "summary": "查看本人 API Key 掩码与真实请求统计",
        "security": [
          {
            "cookieSession": []
          },
          {
            "secureCookieSession": []
          }
        ],
        "description": "仅限网站 Cookie 登录，Bearer 不替代账号会话。usage 汇总该账号已通过有效 Key 校验的 /v1 请求，含模型列表及已认证失败请求，不含网站聊天；密钥撤销保留历史统计，账号注销删除密钥和调用元数据。未认证请求不归属任何账号。",
        "responses": {
          "200": {
            "description": "仅返回密钥掩码，没有历史明文密钥",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "keys",
                    "usage"
                  ],
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AccountKey"
                      }
                    },
                    "usage": {
                      "$ref": "#/components/schemas/RequestUsage"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/PrivateError"
          },
          "403": {
            "$ref": "#/components/responses/PrivateError"
          },
          "429": {
            "$ref": "#/components/responses/PrivateError"
          }
        }
      },
      "post": {
        "tags": [
          "Account keys"
        ],
        "operationId": "createAccountApiKey",
        "summary": "创建本人 API Key；完整密钥只返回一次",
        "security": [
          {
            "cookieSession": []
          },
          {
            "secureCookieSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SameOrigin"
          }
        ],
        "description": "最多 5 个有效密钥。32 字节安全随机密钥，数据库仅保存哈希和展示掩码；服务不能再次取回明文。每小时最多 10 次创建尝试的进程内限制。未保存时应撤销后重建。",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 60
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "只在本次响应显示完整密钥",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "key",
                    "secret"
                  ],
                  "properties": {
                    "key": {
                      "$ref": "#/components/schemas/AccountKey"
                    },
                    "secret": {
                      "type": "string",
                      "pattern": "^vd_[A-Za-z0-9_-]{43}$",
                      "writeOnly": false,
                      "description": "请安全保存，不写入前端代码、URL、日志或版本库。"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PrivateError"
          },
          "401": {
            "$ref": "#/components/responses/PrivateError"
          },
          "403": {
            "$ref": "#/components/responses/PrivateError"
          },
          "409": {
            "$ref": "#/components/responses/PrivateError"
          },
          "429": {
            "$ref": "#/components/responses/PrivateError"
          }
        }
      }
    },
    "/api/api-keys/{id}": {
      "delete": {
        "tags": [
          "Account keys"
        ],
        "operationId": "revokeAccountApiKey",
        "summary": "撤销本人密钥",
        "security": [
          {
            "cookieSession": []
          },
          {
            "secureCookieSession": []
          }
        ],
        "description": "已撤销密钥再次撤销返回成功；不存在或属于他人的 ID 返回 404。已开始处理的调用无法追溯取消；后续鉴权拒绝已撤销密钥。",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "$ref": "#/components/parameters/SameOrigin"
          }
        ],
        "responses": {
          "200": {
            "description": "撤销已持久化",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/PrivateError"
          },
          "403": {
            "$ref": "#/components/responses/PrivateError"
          },
          "404": {
            "$ref": "#/components/responses/PrivateError"
          },
          "429": {
            "$ref": "#/components/responses/PrivateError"
          }
        }
      }
    },
    "/api/courses": {
      "get": {
        "tags": [
          "Courses"
        ],
        "operationId": "listCoursesWebsite",
        "summary": "列出本人课程摘要",
        "description": "只返回本人课程；最多50门。读取不调用模型。API课程记录与统计元数据分开，课程内容会按账号持久化。",
        "security": [
          {
            "cookieSession": []
          },
          {
            "secureCookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "实际持久化结果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "courses"
                  ],
                  "properties": {
                    "courses": {
                      "type": "array",
                      "maxItems": 50,
                      "items": {
                        "$ref": "#/components/schemas/CourseSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PrivateError"
          },
          "401": {
            "$ref": "#/components/responses/PrivateError"
          },
          "403": {
            "$ref": "#/components/responses/PrivateError"
          },
          "404": {
            "$ref": "#/components/responses/PrivateError"
          },
          "405": {
            "$ref": "#/components/responses/PrivateError"
          },
          "409": {
            "$ref": "#/components/responses/PrivateError"
          },
          "413": {
            "$ref": "#/components/responses/PrivateError"
          },
          "415": {
            "$ref": "#/components/responses/PrivateError"
          },
          "422": {
            "$ref": "#/components/responses/PrivateError"
          },
          "429": {
            "$ref": "#/components/responses/PrivateError"
          },
          "500": {
            "$ref": "#/components/responses/PrivateError"
          },
          "502": {
            "$ref": "#/components/responses/PrivateError"
          },
          "503": {
            "$ref": "#/components/responses/PrivateError"
          },
          "504": {
            "$ref": "#/components/responses/PrivateError"
          }
        }
      },
      "post": {
        "tags": [
          "Courses"
        ],
        "operationId": "createCourseWebsite",
        "summary": "生成并保存课程大纲",
        "description": "8个字段全部必填。真实调用模型生成与目标相对应的lesson_count节大纲，验证JSON与非空title/objective/focus后才原子保存。初始状态draft，尚未生成单节教案。不返回虚构评估、通过率或课程效果。输入仅纯文本；不支持HTML或孩子身份信息。JSON请求体最多131072字节。结构或安全要求不满足时COURSE_INVALID_RESPONSE，不保存模拟内容。",
        "security": [
          {
            "cookieSession": []
          },
          {
            "secureCookieSession": []
          }
        ],
        "responses": {
          "201": {
            "description": "实际持久化结果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "course"
                  ],
                  "properties": {
                    "course": {
                      "$ref": "#/components/schemas/Course"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PrivateError"
          },
          "401": {
            "$ref": "#/components/responses/PrivateError"
          },
          "403": {
            "$ref": "#/components/responses/PrivateError"
          },
          "404": {
            "$ref": "#/components/responses/PrivateError"
          },
          "405": {
            "$ref": "#/components/responses/PrivateError"
          },
          "409": {
            "$ref": "#/components/responses/PrivateError"
          },
          "413": {
            "$ref": "#/components/responses/PrivateError"
          },
          "415": {
            "$ref": "#/components/responses/PrivateError"
          },
          "422": {
            "$ref": "#/components/responses/PrivateError"
          },
          "429": {
            "$ref": "#/components/responses/PrivateError"
          },
          "500": {
            "$ref": "#/components/responses/PrivateError"
          },
          "502": {
            "$ref": "#/components/responses/PrivateError"
          },
          "503": {
            "$ref": "#/components/responses/PrivateError"
          },
          "504": {
            "$ref": "#/components/responses/PrivateError"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/SameOrigin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCourseRequest"
              }
            }
          }
        }
      }
    },
    "/api/courses/{id}": {
      "get": {
        "tags": [
          "Courses"
        ],
        "operationId": "getCourseWebsite",
        "summary": "读取本人课程与课时",
        "description": "课程与教案仍为需成年使用者审阅的草案；不调用模型。非本人课程统一404。",
        "security": [
          {
            "cookieSession": []
          },
          {
            "secureCookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "实际持久化结果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "course"
                  ],
                  "properties": {
                    "course": {
                      "$ref": "#/components/schemas/Course"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PrivateError"
          },
          "401": {
            "$ref": "#/components/responses/PrivateError"
          },
          "403": {
            "$ref": "#/components/responses/PrivateError"
          },
          "404": {
            "$ref": "#/components/responses/PrivateError"
          },
          "405": {
            "$ref": "#/components/responses/PrivateError"
          },
          "409": {
            "$ref": "#/components/responses/PrivateError"
          },
          "413": {
            "$ref": "#/components/responses/PrivateError"
          },
          "415": {
            "$ref": "#/components/responses/PrivateError"
          },
          "422": {
            "$ref": "#/components/responses/PrivateError"
          },
          "429": {
            "$ref": "#/components/responses/PrivateError"
          },
          "500": {
            "$ref": "#/components/responses/PrivateError"
          },
          "502": {
            "$ref": "#/components/responses/PrivateError"
          },
          "503": {
            "$ref": "#/components/responses/PrivateError"
          },
          "504": {
            "$ref": "#/components/responses/PrivateError"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          }
        ]
      },
      "patch": {
        "tags": [
          "Courses"
        ],
        "operationId": "patchCourseWebsite",
        "summary": "保存课程标题、目标或材料修改",
        "description": "至少一项，其他字段不可修改。不会自动重生成大纲或已生成课时；可人工调整或主动生成。",
        "security": [
          {
            "cookieSession": []
          },
          {
            "secureCookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "实际持久化结果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "course"
                  ],
                  "properties": {
                    "course": {
                      "$ref": "#/components/schemas/Course"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PrivateError"
          },
          "401": {
            "$ref": "#/components/responses/PrivateError"
          },
          "403": {
            "$ref": "#/components/responses/PrivateError"
          },
          "404": {
            "$ref": "#/components/responses/PrivateError"
          },
          "405": {
            "$ref": "#/components/responses/PrivateError"
          },
          "409": {
            "$ref": "#/components/responses/PrivateError"
          },
          "413": {
            "$ref": "#/components/responses/PrivateError"
          },
          "415": {
            "$ref": "#/components/responses/PrivateError"
          },
          "422": {
            "$ref": "#/components/responses/PrivateError"
          },
          "429": {
            "$ref": "#/components/responses/PrivateError"
          },
          "500": {
            "$ref": "#/components/responses/PrivateError"
          },
          "502": {
            "$ref": "#/components/responses/PrivateError"
          },
          "503": {
            "$ref": "#/components/responses/PrivateError"
          },
          "504": {
            "$ref": "#/components/responses/PrivateError"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "$ref": "#/components/parameters/SameOrigin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchCourseRequest"
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Courses"
        ],
        "operationId": "deleteCourseWebsite",
        "summary": "删除本人课程及其课时",
        "description": "实际删除本人课程正文及所有课时。API调用元数据仍计入本人请求统计；注销账号会删除该元数据。DELETE可无body或空JSON对象。",
        "security": [
          {
            "cookieSession": []
          },
          {
            "secureCookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "实际持久化结果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PrivateError"
          },
          "401": {
            "$ref": "#/components/responses/PrivateError"
          },
          "403": {
            "$ref": "#/components/responses/PrivateError"
          },
          "404": {
            "$ref": "#/components/responses/PrivateError"
          },
          "405": {
            "$ref": "#/components/responses/PrivateError"
          },
          "409": {
            "$ref": "#/components/responses/PrivateError"
          },
          "413": {
            "$ref": "#/components/responses/PrivateError"
          },
          "415": {
            "$ref": "#/components/responses/PrivateError"
          },
          "422": {
            "$ref": "#/components/responses/PrivateError"
          },
          "429": {
            "$ref": "#/components/responses/PrivateError"
          },
          "500": {
            "$ref": "#/components/responses/PrivateError"
          },
          "502": {
            "$ref": "#/components/responses/PrivateError"
          },
          "503": {
            "$ref": "#/components/responses/PrivateError"
          },
          "504": {
            "$ref": "#/components/responses/PrivateError"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "$ref": "#/components/parameters/SameOrigin"
          }
        ]
      }
    },
    "/api/courses/{id}/lessons/{lessonId}": {
      "patch": {
        "tags": [
          "Courses"
        ],
        "operationId": "patchCourseLessonWebsite",
        "summary": "保存单节教案人工修改",
        "description": "至少一项受支持字段。activities若提供必须1至8项，每项正整数分钟，合计严格等于sessionMinutes；反思/观察数组可为空，最多6条、每条最多500字符。保存不调用模型，不改变真实generatedAt。",
        "security": [
          {
            "cookieSession": []
          },
          {
            "secureCookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "实际持久化结果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "course",
                    "lesson"
                  ],
                  "properties": {
                    "course": {
                      "$ref": "#/components/schemas/Course"
                    },
                    "lesson": {
                      "$ref": "#/components/schemas/CourseLesson"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PrivateError"
          },
          "401": {
            "$ref": "#/components/responses/PrivateError"
          },
          "403": {
            "$ref": "#/components/responses/PrivateError"
          },
          "404": {
            "$ref": "#/components/responses/PrivateError"
          },
          "405": {
            "$ref": "#/components/responses/PrivateError"
          },
          "409": {
            "$ref": "#/components/responses/PrivateError"
          },
          "413": {
            "$ref": "#/components/responses/PrivateError"
          },
          "415": {
            "$ref": "#/components/responses/PrivateError"
          },
          "422": {
            "$ref": "#/components/responses/PrivateError"
          },
          "429": {
            "$ref": "#/components/responses/PrivateError"
          },
          "500": {
            "$ref": "#/components/responses/PrivateError"
          },
          "502": {
            "$ref": "#/components/responses/PrivateError"
          },
          "503": {
            "$ref": "#/components/responses/PrivateError"
          },
          "504": {
            "$ref": "#/components/responses/PrivateError"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "lessonId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "$ref": "#/components/parameters/SameOrigin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchLessonRequest"
              }
            }
          }
        }
      }
    },
    "/api/courses/{id}/lessons/{lessonId}/generate": {
      "post": {
        "tags": [
          "Courses"
        ],
        "operationId": "generateCourseLessonWebsite",
        "summary": "真实生成指定单节结构化教案",
        "description": "由服务器读取课程条件与完整大纲生成这一节。可无body或{}，不支持其他参数。模型结果必须含全部8个教案内容字段，文本非空，activities1至8项、分钟合计=sessionMinutes，反思/观察各1至6条。模型JSON不合格返回422 COURSE_INVALID_RESPONSE。成功后覆盖该课时内容并写真实generatedAt；失败保留原内容。使用既有1800 max_tokens及共享模型限流，不伪造或自动填补缺失结果。",
        "security": [
          {
            "cookieSession": []
          },
          {
            "secureCookieSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "实际持久化结果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "course",
                    "lesson"
                  ],
                  "properties": {
                    "course": {
                      "$ref": "#/components/schemas/Course"
                    },
                    "lesson": {
                      "$ref": "#/components/schemas/CourseLesson"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PrivateError"
          },
          "401": {
            "$ref": "#/components/responses/PrivateError"
          },
          "403": {
            "$ref": "#/components/responses/PrivateError"
          },
          "404": {
            "$ref": "#/components/responses/PrivateError"
          },
          "405": {
            "$ref": "#/components/responses/PrivateError"
          },
          "409": {
            "$ref": "#/components/responses/PrivateError"
          },
          "413": {
            "$ref": "#/components/responses/PrivateError"
          },
          "415": {
            "$ref": "#/components/responses/PrivateError"
          },
          "422": {
            "$ref": "#/components/responses/PrivateError"
          },
          "429": {
            "$ref": "#/components/responses/PrivateError"
          },
          "500": {
            "$ref": "#/components/responses/PrivateError"
          },
          "502": {
            "$ref": "#/components/responses/PrivateError"
          },
          "503": {
            "$ref": "#/components/responses/PrivateError"
          },
          "504": {
            "$ref": "#/components/responses/PrivateError"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "lessonId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "$ref": "#/components/parameters/SameOrigin"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "maxProperties": 0
              }
            }
          }
        }
      }
    },
    "/v1/courses": {
      "get": {
        "tags": [
          "Courses"
        ],
        "operationId": "listCourses",
        "summary": "列出本人课程摘要",
        "description": "只返回本人课程；最多50门。读取不调用模型。API课程记录与统计元数据分开，课程内容会按账号持久化。",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "实际持久化结果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "courses"
                  ],
                  "properties": {
                    "courses": {
                      "type": "array",
                      "maxItems": 50,
                      "items": {
                        "$ref": "#/components/schemas/CourseSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ApiError"
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "403": {
            "$ref": "#/components/responses/ApiError"
          },
          "404": {
            "$ref": "#/components/responses/ApiError"
          },
          "405": {
            "$ref": "#/components/responses/ApiError"
          },
          "409": {
            "$ref": "#/components/responses/ApiError"
          },
          "413": {
            "$ref": "#/components/responses/ApiError"
          },
          "415": {
            "$ref": "#/components/responses/ApiError"
          },
          "422": {
            "$ref": "#/components/responses/ApiError"
          },
          "429": {
            "$ref": "#/components/responses/ApiError"
          },
          "500": {
            "$ref": "#/components/responses/ApiError"
          },
          "502": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "$ref": "#/components/responses/ApiError"
          },
          "504": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      },
      "post": {
        "tags": [
          "Courses"
        ],
        "operationId": "createCourse",
        "summary": "生成并保存课程大纲",
        "description": "8个字段全部必填。真实调用模型生成与目标相对应的lesson_count节大纲，验证JSON与非空title/objective/focus后才原子保存。初始状态draft，尚未生成单节教案。不返回虚构评估、通过率或课程效果。输入仅纯文本；不支持HTML或孩子身份信息。JSON请求体最多131072字节。结构或安全要求不满足时COURSE_INVALID_RESPONSE，不保存模拟内容。",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "201": {
            "description": "实际持久化结果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "course"
                  ],
                  "properties": {
                    "course": {
                      "$ref": "#/components/schemas/Course"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ApiError"
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "403": {
            "$ref": "#/components/responses/ApiError"
          },
          "404": {
            "$ref": "#/components/responses/ApiError"
          },
          "405": {
            "$ref": "#/components/responses/ApiError"
          },
          "409": {
            "$ref": "#/components/responses/ApiError"
          },
          "413": {
            "$ref": "#/components/responses/ApiError"
          },
          "415": {
            "$ref": "#/components/responses/ApiError"
          },
          "422": {
            "$ref": "#/components/responses/ApiError"
          },
          "429": {
            "$ref": "#/components/responses/ApiError"
          },
          "500": {
            "$ref": "#/components/responses/ApiError"
          },
          "502": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "$ref": "#/components/responses/ApiError"
          },
          "504": {
            "$ref": "#/components/responses/ApiError"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCourseRequest"
              }
            }
          }
        }
      }
    },
    "/v1/courses/{id}": {
      "get": {
        "tags": [
          "Courses"
        ],
        "operationId": "getCourse",
        "summary": "读取本人课程与课时",
        "description": "课程与教案仍为需成年使用者审阅的草案；不调用模型。非本人课程统一404。",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "实际持久化结果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "course"
                  ],
                  "properties": {
                    "course": {
                      "$ref": "#/components/schemas/Course"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ApiError"
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "403": {
            "$ref": "#/components/responses/ApiError"
          },
          "404": {
            "$ref": "#/components/responses/ApiError"
          },
          "405": {
            "$ref": "#/components/responses/ApiError"
          },
          "409": {
            "$ref": "#/components/responses/ApiError"
          },
          "413": {
            "$ref": "#/components/responses/ApiError"
          },
          "415": {
            "$ref": "#/components/responses/ApiError"
          },
          "422": {
            "$ref": "#/components/responses/ApiError"
          },
          "429": {
            "$ref": "#/components/responses/ApiError"
          },
          "500": {
            "$ref": "#/components/responses/ApiError"
          },
          "502": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "$ref": "#/components/responses/ApiError"
          },
          "504": {
            "$ref": "#/components/responses/ApiError"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          }
        ]
      },
      "patch": {
        "tags": [
          "Courses"
        ],
        "operationId": "patchCourse",
        "summary": "保存课程标题、目标或材料修改",
        "description": "至少一项，其他字段不可修改。不会自动重生成大纲或已生成课时；可人工调整或主动生成。",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "实际持久化结果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "course"
                  ],
                  "properties": {
                    "course": {
                      "$ref": "#/components/schemas/Course"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ApiError"
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "403": {
            "$ref": "#/components/responses/ApiError"
          },
          "404": {
            "$ref": "#/components/responses/ApiError"
          },
          "405": {
            "$ref": "#/components/responses/ApiError"
          },
          "409": {
            "$ref": "#/components/responses/ApiError"
          },
          "413": {
            "$ref": "#/components/responses/ApiError"
          },
          "415": {
            "$ref": "#/components/responses/ApiError"
          },
          "422": {
            "$ref": "#/components/responses/ApiError"
          },
          "429": {
            "$ref": "#/components/responses/ApiError"
          },
          "500": {
            "$ref": "#/components/responses/ApiError"
          },
          "502": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "$ref": "#/components/responses/ApiError"
          },
          "504": {
            "$ref": "#/components/responses/ApiError"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchCourseRequest"
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Courses"
        ],
        "operationId": "deleteCourse",
        "summary": "删除本人课程及其课时",
        "description": "实际删除本人课程正文及所有课时。API调用元数据仍计入本人请求统计；注销账号会删除该元数据。DELETE可无body或空JSON对象。",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "实际持久化结果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ApiError"
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "403": {
            "$ref": "#/components/responses/ApiError"
          },
          "404": {
            "$ref": "#/components/responses/ApiError"
          },
          "405": {
            "$ref": "#/components/responses/ApiError"
          },
          "409": {
            "$ref": "#/components/responses/ApiError"
          },
          "413": {
            "$ref": "#/components/responses/ApiError"
          },
          "415": {
            "$ref": "#/components/responses/ApiError"
          },
          "422": {
            "$ref": "#/components/responses/ApiError"
          },
          "429": {
            "$ref": "#/components/responses/ApiError"
          },
          "500": {
            "$ref": "#/components/responses/ApiError"
          },
          "502": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "$ref": "#/components/responses/ApiError"
          },
          "504": {
            "$ref": "#/components/responses/ApiError"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          }
        ]
      }
    },
    "/v1/courses/{id}/lessons/{lessonId}": {
      "patch": {
        "tags": [
          "Courses"
        ],
        "operationId": "patchCourseLesson",
        "summary": "保存单节教案人工修改",
        "description": "至少一项受支持字段。activities若提供必须1至8项，每项正整数分钟，合计严格等于sessionMinutes；反思/观察数组可为空，最多6条、每条最多500字符。保存不调用模型，不改变真实generatedAt。",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "实际持久化结果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "course",
                    "lesson"
                  ],
                  "properties": {
                    "course": {
                      "$ref": "#/components/schemas/Course"
                    },
                    "lesson": {
                      "$ref": "#/components/schemas/CourseLesson"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ApiError"
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "403": {
            "$ref": "#/components/responses/ApiError"
          },
          "404": {
            "$ref": "#/components/responses/ApiError"
          },
          "405": {
            "$ref": "#/components/responses/ApiError"
          },
          "409": {
            "$ref": "#/components/responses/ApiError"
          },
          "413": {
            "$ref": "#/components/responses/ApiError"
          },
          "415": {
            "$ref": "#/components/responses/ApiError"
          },
          "422": {
            "$ref": "#/components/responses/ApiError"
          },
          "429": {
            "$ref": "#/components/responses/ApiError"
          },
          "500": {
            "$ref": "#/components/responses/ApiError"
          },
          "502": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "$ref": "#/components/responses/ApiError"
          },
          "504": {
            "$ref": "#/components/responses/ApiError"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "lessonId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchLessonRequest"
              }
            }
          }
        }
      }
    },
    "/v1/courses/{id}/lessons/{lessonId}/generate": {
      "post": {
        "tags": [
          "Courses"
        ],
        "operationId": "generateCourseLesson",
        "summary": "真实生成指定单节结构化教案",
        "description": "由服务器读取课程条件与完整大纲生成这一节。可无body或{}，不支持其他参数。模型结果必须含全部8个教案内容字段，文本非空，activities1至8项、分钟合计=sessionMinutes，反思/观察各1至6条。模型JSON不合格返回422 COURSE_INVALID_RESPONSE。成功后覆盖该课时内容并写真实generatedAt；失败保留原内容。使用既有1800 max_tokens及共享模型限流，不伪造或自动填补缺失结果。",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "实际持久化结果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "course",
                    "lesson"
                  ],
                  "properties": {
                    "course": {
                      "$ref": "#/components/schemas/Course"
                    },
                    "lesson": {
                      "$ref": "#/components/schemas/CourseLesson"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ApiError"
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "403": {
            "$ref": "#/components/responses/ApiError"
          },
          "404": {
            "$ref": "#/components/responses/ApiError"
          },
          "405": {
            "$ref": "#/components/responses/ApiError"
          },
          "409": {
            "$ref": "#/components/responses/ApiError"
          },
          "413": {
            "$ref": "#/components/responses/ApiError"
          },
          "415": {
            "$ref": "#/components/responses/ApiError"
          },
          "422": {
            "$ref": "#/components/responses/ApiError"
          },
          "429": {
            "$ref": "#/components/responses/ApiError"
          },
          "500": {
            "$ref": "#/components/responses/ApiError"
          },
          "502": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "$ref": "#/components/responses/ApiError"
          },
          "504": {
            "$ref": "#/components/responses/ApiError"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "lessonId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "maxProperties": 0
              }
            }
          }
        }
      }
    },
    "/api/courses/{id}/assist": {
      "post": {
        "tags": [
          "Course assistant"
        ],
        "operationId": "assistCourseWebsite",
        "summary": "基于已保存课程回答问题或提出待确认修改",
        "description": "调用真实模型，max_tokens沿用1800，使用已有账号12/60秒及120/86400秒共享生成窗口和8模型并发限制。scope=lesson仅能改指定课时已有可编辑字段；scope=course仅能改课程title/goal/materials和任意既有大纲title/objective/focus，不批量改写活动教案。全课程活动修改应先澄清并选择单节。活动变更分钟合计必须=sessionMinutes，反思/观察数组最多6条。history不接受system角色。只有真实差异产生proposal；模型无效返回422 ASSISTANT_INVALID_RESPONSE，课程保持原样。提案独立持久化，assist不自动保存修改；过期会话/已撤销Key不会提交提案。",
        "security": [
          {
            "cookieSession": []
          },
          {
            "secureCookieSession": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "$ref": "#/components/parameters/SameOrigin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CourseAssistRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "真实辅助结果或提案状态",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseAssistResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PrivateError"
          },
          "401": {
            "$ref": "#/components/responses/PrivateError"
          },
          "403": {
            "$ref": "#/components/responses/PrivateError"
          },
          "404": {
            "$ref": "#/components/responses/PrivateError"
          },
          "405": {
            "$ref": "#/components/responses/PrivateError"
          },
          "409": {
            "$ref": "#/components/responses/PrivateError"
          },
          "413": {
            "$ref": "#/components/responses/PrivateError"
          },
          "415": {
            "$ref": "#/components/responses/PrivateError"
          },
          "422": {
            "$ref": "#/components/responses/PrivateError"
          },
          "429": {
            "$ref": "#/components/responses/PrivateError"
          },
          "500": {
            "$ref": "#/components/responses/PrivateError"
          },
          "502": {
            "$ref": "#/components/responses/PrivateError"
          },
          "503": {
            "$ref": "#/components/responses/PrivateError"
          },
          "504": {
            "$ref": "#/components/responses/PrivateError"
          }
        }
      },
      "get": {
        "tags": [
          "Course assistant"
        ],
        "operationId": "listPendingCourseProposalsWebsite",
        "summary": "恢复本人当前课程的待确认提案",
        "description": "只读取已保存提案，不调用模型或重新计算差异。按createdAt及数据库记录顺序倒序返回最新最多30条pending；仅限本人该课程，已应用/已丢弃提案不在列表。课程不存在或不属于本人返回404。页面打开或刷新后应恢复最新pending，确认应用或丢弃后再发新指令；处理后可以再次读取剩余pending。",
        "security": [
          {
            "cookieSession": []
          },
          {
            "secureCookieSession": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "由新到旧的本人该课程pending提案；无待确认提案时为空数组",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PendingCourseEditProposals"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/PrivateError"
          },
          "403": {
            "$ref": "#/components/responses/PrivateError"
          },
          "404": {
            "$ref": "#/components/responses/PrivateError"
          },
          "405": {
            "$ref": "#/components/responses/PrivateError"
          },
          "429": {
            "$ref": "#/components/responses/PrivateError"
          },
          "500": {
            "$ref": "#/components/responses/PrivateError"
          },
          "503": {
            "$ref": "#/components/responses/PrivateError"
          }
        }
      }
    },
    "/api/courses/{id}/assist/{proposalId}/apply": {
      "post": {
        "tags": [
          "Course assistant"
        ],
        "operationId": "applyCourseProposalWebsite",
        "summary": "确认应用已验证的课程修改提案",
        "description": "需要本人课程及本人提案。事务中校验课程完整canonical SHA256与生成提案时基线一致，不只比较秒级updatedAt。内容变化返回409 COURSE_CHANGED，保留pending但不写课程；可丢弃后基于最新内容重提。成功仅应用白名单差异，更新时间，不改id、年龄、身份、时长、课时数或generatedAt。再次apply同一已applied提案返回当前课程及applied，不覆盖后来的人工编辑。已discarded提案apply返回409 PROPOSAL_NOT_PENDING。可传{}或无body，不收其它参数；提交前重新核实会话/Key。",
        "security": [
          {
            "cookieSession": []
          },
          {
            "secureCookieSession": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "proposalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "$ref": "#/components/parameters/SameOrigin"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "maxProperties": 0
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "真实辅助结果或提案状态",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "course",
                    "proposal"
                  ],
                  "properties": {
                    "course": {
                      "$ref": "#/components/schemas/Course"
                    },
                    "proposal": {
                      "type": "object",
                      "required": [
                        "id",
                        "status"
                      ],
                      "properties": {
                        "id": {
                          "type": "string",
                          "pattern": "^[a-f0-9]{32}$"
                        },
                        "status": {
                          "const": "applied"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PrivateError"
          },
          "401": {
            "$ref": "#/components/responses/PrivateError"
          },
          "403": {
            "$ref": "#/components/responses/PrivateError"
          },
          "404": {
            "$ref": "#/components/responses/PrivateError"
          },
          "405": {
            "$ref": "#/components/responses/PrivateError"
          },
          "409": {
            "$ref": "#/components/responses/PrivateError"
          },
          "413": {
            "$ref": "#/components/responses/PrivateError"
          },
          "415": {
            "$ref": "#/components/responses/PrivateError"
          },
          "422": {
            "$ref": "#/components/responses/PrivateError"
          },
          "429": {
            "$ref": "#/components/responses/PrivateError"
          },
          "500": {
            "$ref": "#/components/responses/PrivateError"
          },
          "502": {
            "$ref": "#/components/responses/PrivateError"
          },
          "503": {
            "$ref": "#/components/responses/PrivateError"
          },
          "504": {
            "$ref": "#/components/responses/PrivateError"
          }
        }
      }
    },
    "/api/courses/{id}/assist/{proposalId}/discard": {
      "post": {
        "tags": [
          "Course assistant"
        ],
        "operationId": "discardCourseProposalWebsite",
        "summary": "丢弃待确认提案，课程保持原样",
        "description": "只更新提案状态；重复discard仍成功。已applied提案不能discard，返回409 PROPOSAL_NOT_PENDING，不回退课程。可传{}或无body，不收其它参数。删除课程或注销账号将级联删除全部所属提案。",
        "security": [
          {
            "cookieSession": []
          },
          {
            "secureCookieSession": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "proposalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "$ref": "#/components/parameters/SameOrigin"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "maxProperties": 0
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "真实辅助结果或提案状态",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "proposal"
                  ],
                  "properties": {
                    "proposal": {
                      "type": "object",
                      "required": [
                        "id",
                        "status"
                      ],
                      "properties": {
                        "id": {
                          "type": "string",
                          "pattern": "^[a-f0-9]{32}$"
                        },
                        "status": {
                          "const": "discarded"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PrivateError"
          },
          "401": {
            "$ref": "#/components/responses/PrivateError"
          },
          "403": {
            "$ref": "#/components/responses/PrivateError"
          },
          "404": {
            "$ref": "#/components/responses/PrivateError"
          },
          "405": {
            "$ref": "#/components/responses/PrivateError"
          },
          "409": {
            "$ref": "#/components/responses/PrivateError"
          },
          "413": {
            "$ref": "#/components/responses/PrivateError"
          },
          "415": {
            "$ref": "#/components/responses/PrivateError"
          },
          "422": {
            "$ref": "#/components/responses/PrivateError"
          },
          "429": {
            "$ref": "#/components/responses/PrivateError"
          },
          "500": {
            "$ref": "#/components/responses/PrivateError"
          },
          "502": {
            "$ref": "#/components/responses/PrivateError"
          },
          "503": {
            "$ref": "#/components/responses/PrivateError"
          },
          "504": {
            "$ref": "#/components/responses/PrivateError"
          }
        }
      }
    },
    "/v1/courses/{id}/assist": {
      "post": {
        "tags": [
          "Course assistant"
        ],
        "operationId": "assistCourse",
        "summary": "基于已保存课程回答问题或提出待确认修改",
        "description": "调用真实模型，max_tokens沿用1800，使用已有账号12/60秒及120/86400秒共享生成窗口和8模型并发限制。scope=lesson仅能改指定课时已有可编辑字段；scope=course仅能改课程title/goal/materials和任意既有大纲title/objective/focus，不批量改写活动教案。全课程活动修改应先澄清并选择单节。活动变更分钟合计必须=sessionMinutes，反思/观察数组最多6条。history不接受system角色。只有真实差异产生proposal；模型无效返回422 ASSISTANT_INVALID_RESPONSE，课程保持原样。提案独立持久化，assist不自动保存修改；过期会话/已撤销Key不会提交提案。",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CourseAssistRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "真实辅助结果或提案状态",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseAssistResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ApiError"
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "403": {
            "$ref": "#/components/responses/ApiError"
          },
          "404": {
            "$ref": "#/components/responses/ApiError"
          },
          "405": {
            "$ref": "#/components/responses/ApiError"
          },
          "409": {
            "$ref": "#/components/responses/ApiError"
          },
          "413": {
            "$ref": "#/components/responses/ApiError"
          },
          "415": {
            "$ref": "#/components/responses/ApiError"
          },
          "422": {
            "$ref": "#/components/responses/ApiError"
          },
          "429": {
            "$ref": "#/components/responses/ApiError"
          },
          "500": {
            "$ref": "#/components/responses/ApiError"
          },
          "502": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "$ref": "#/components/responses/ApiError"
          },
          "504": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      },
      "get": {
        "tags": [
          "Course assistant"
        ],
        "operationId": "listPendingCourseProposals",
        "summary": "恢复本人当前课程的待确认提案",
        "description": "只读取已保存提案，不调用模型或重新计算差异。按createdAt及数据库记录顺序倒序返回最新最多30条pending；仅限本人该课程，已应用/已丢弃提案不在列表。课程不存在或不属于本人返回404。页面打开或刷新后应恢复最新pending，确认应用或丢弃后再发新指令；处理后可以再次读取剩余pending。",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "由新到旧的本人该课程pending提案；无待确认提案时为空数组",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PendingCourseEditProposals"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "403": {
            "$ref": "#/components/responses/ApiError"
          },
          "404": {
            "$ref": "#/components/responses/ApiError"
          },
          "405": {
            "$ref": "#/components/responses/ApiError"
          },
          "429": {
            "$ref": "#/components/responses/ApiError"
          },
          "500": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      }
    },
    "/v1/courses/{id}/assist/{proposalId}/apply": {
      "post": {
        "tags": [
          "Course assistant"
        ],
        "operationId": "applyCourseProposal",
        "summary": "确认应用已验证的课程修改提案",
        "description": "需要本人课程及本人提案。事务中校验课程完整canonical SHA256与生成提案时基线一致，不只比较秒级updatedAt。内容变化返回409 COURSE_CHANGED，保留pending但不写课程；可丢弃后基于最新内容重提。成功仅应用白名单差异，更新时间，不改id、年龄、身份、时长、课时数或generatedAt。再次apply同一已applied提案返回当前课程及applied，不覆盖后来的人工编辑。已discarded提案apply返回409 PROPOSAL_NOT_PENDING。可传{}或无body，不收其它参数；提交前重新核实会话/Key。",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "proposalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "maxProperties": 0
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "真实辅助结果或提案状态",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "course",
                    "proposal"
                  ],
                  "properties": {
                    "course": {
                      "$ref": "#/components/schemas/Course"
                    },
                    "proposal": {
                      "type": "object",
                      "required": [
                        "id",
                        "status"
                      ],
                      "properties": {
                        "id": {
                          "type": "string",
                          "pattern": "^[a-f0-9]{32}$"
                        },
                        "status": {
                          "const": "applied"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ApiError"
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "403": {
            "$ref": "#/components/responses/ApiError"
          },
          "404": {
            "$ref": "#/components/responses/ApiError"
          },
          "405": {
            "$ref": "#/components/responses/ApiError"
          },
          "409": {
            "$ref": "#/components/responses/ApiError"
          },
          "413": {
            "$ref": "#/components/responses/ApiError"
          },
          "415": {
            "$ref": "#/components/responses/ApiError"
          },
          "422": {
            "$ref": "#/components/responses/ApiError"
          },
          "429": {
            "$ref": "#/components/responses/ApiError"
          },
          "500": {
            "$ref": "#/components/responses/ApiError"
          },
          "502": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "$ref": "#/components/responses/ApiError"
          },
          "504": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      }
    },
    "/v1/courses/{id}/assist/{proposalId}/discard": {
      "post": {
        "tags": [
          "Course assistant"
        ],
        "operationId": "discardCourseProposal",
        "summary": "丢弃待确认提案，课程保持原样",
        "description": "只更新提案状态；重复discard仍成功。已applied提案不能discard，返回409 PROPOSAL_NOT_PENDING，不回退课程。可传{}或无body，不收其它参数。删除课程或注销账号将级联删除全部所属提案。",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "proposalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "maxProperties": 0
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "真实辅助结果或提案状态",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "proposal"
                  ],
                  "properties": {
                    "proposal": {
                      "type": "object",
                      "required": [
                        "id",
                        "status"
                      ],
                      "properties": {
                        "id": {
                          "type": "string",
                          "pattern": "^[a-f0-9]{32}$"
                        },
                        "status": {
                          "const": "discarded"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ApiError"
          },
          "401": {
            "$ref": "#/components/responses/ApiError"
          },
          "403": {
            "$ref": "#/components/responses/ApiError"
          },
          "404": {
            "$ref": "#/components/responses/ApiError"
          },
          "405": {
            "$ref": "#/components/responses/ApiError"
          },
          "409": {
            "$ref": "#/components/responses/ApiError"
          },
          "413": {
            "$ref": "#/components/responses/ApiError"
          },
          "415": {
            "$ref": "#/components/responses/ApiError"
          },
          "422": {
            "$ref": "#/components/responses/ApiError"
          },
          "429": {
            "$ref": "#/components/responses/ApiError"
          },
          "500": {
            "$ref": "#/components/responses/ApiError"
          },
          "502": {
            "$ref": "#/components/responses/ApiError"
          },
          "503": {
            "$ref": "#/components/responses/ApiError"
          },
          "504": {
            "$ref": "#/components/responses/ApiError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "vd_ API Key",
        "description": "完整用户 API Key；不是上游火山密钥。无需 Origin，不支持 Cookie 回退。"
      },
      "cookieSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "poptale_session",
        "description": "本地开发网站登录会话；不用于 /v1。"
      },
      "secureCookieSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Host-poptale_session",
        "description": "生产 HTTPS 网站登录会话；HttpOnly、Secure、SameSite=Strict。"
      }
    },
    "parameters": {
      "SameOrigin": {
        "name": "Origin",
        "in": "header",
        "required": false,
        "description": "浏览器自动提供与本站一致的 Origin；若缺失则要求同源 Referer。仅供网站控制台，不能以 Bearer Key 代替 Cookie/同源校验。",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "ApiError": {
        "description": "结构化 API 错误。常用 code：INVALID_API_KEY、MODEL_NOT_SUPPORTED、STREAM_NOT_SUPPORTED、UNSUPPORTED_PARAMETER、INVALID_MESSAGES、MESSAGES_TOO_LONG、CONTENT_BLOCKED、PERSONAL_DATA_BLOCKED、CHAT_IN_PROGRESS、RATE_LIMITED、MODEL_NOT_CONFIGURED、MODEL_BUSY、MODEL_UPSTREAM_ERROR、MODEL_OUTPUT_BLOCKED、MODEL_INVALID_RESPONSE、MODEL_TIMEOUT。不会返回上游凭证或原始错误响应。 课程错误包括 INVALID_COURSE_FIELD、COURSE_NOT_FOUND、LESSON_NOT_FOUND、COURSE_LIMIT、COURSE_INVALID_RESPONSE；audience 无效为 INVALID_AUDIENCE。 辅助编辑错误：INVALID_ASSISTANT_FIELD、ASSISTANT_INVALID_RESPONSE、PROPOSAL_NOT_FOUND、PROPOSAL_NOT_PENDING、PROPOSAL_LIMIT、COURSE_CHANGED。",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            }
          }
        }
      },
      "PrivateError": {
        "description": "网站控制台错误，包括 AUTH_REQUIRED、INVALID_ORIGIN、API_KEY_LIMIT、API_KEY_NOT_FOUND、RATE_LIMITED。 辅助编辑也可能返回INVALID_ASSISTANT_FIELD、ASSISTANT_INVALID_RESPONSE、PROPOSAL_NOT_FOUND、PROPOSAL_NOT_PENDING、PROPOSAL_LIMIT、COURSE_CHANGED。",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PrivateError"
            }
          }
        }
      }
    },
    "schemas": {
      "Model": {
        "type": "object",
        "required": [
          "id",
          "object",
          "owned_by",
          "created"
        ],
        "properties": {
          "id": {
            "const": "vidolebu-parenting-1"
          },
          "object": {
            "const": "model"
          },
          "owned_by": {
            "const": "vidolebu"
          },
          "created": {
            "const": 1791676800,
            "description": "2026-10-11 UTC，API 合同引入日期。"
          }
        }
      },
      "ChatRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "model",
          "messages"
        ],
        "properties": {
          "model": {
            "const": "vidolebu-parenting-1"
          },
          "messages": {
            "type": "array",
            "minItems": 1,
            "maxItems": 12,
            "description": "content 合计最多 16000 字符；不支持图像或数组型 content。",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "role",
                "content"
              ],
              "properties": {
                "role": {
                  "enum": [
                    "system",
                    "user",
                    "assistant"
                  ]
                },
                "content": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 4000,
                  "pattern": "\\S"
                }
              }
            }
          },
          "stream": {
            "type": "boolean",
            "const": false,
            "default": false
          },
          "max_tokens": {
            "type": "integer",
            "minimum": 128,
            "maximum": 1800,
            "default": 1800
          },
          "task": {
            "enum": [
              "insight",
              "interaction",
              "story"
            ],
            "default": "insight"
          },
          "age_group": {
            "enum": [
              "3-4",
              "5-6",
              "7-9",
              "10-12"
            ],
            "default": "5-6"
          },
          "audience": {
            "enum": [
              "parent",
              "educator"
            ],
            "default": "parent",
            "description": "成年家长/监护人的家庭情境，或成年幼教工作者的班级情境。"
          }
        }
      },
      "ChatCompletion": {
        "type": "object",
        "required": [
          "id",
          "object",
          "created",
          "model",
          "choices",
          "ai_generated"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^chatcmpl-[a-f0-9]{32}$"
          },
          "object": {
            "const": "chat.completion"
          },
          "created": {
            "type": "integer",
            "description": "本次服务响应的实际 Unix 秒时间。"
          },
          "model": {
            "const": "vidolebu-parenting-1"
          },
          "ai_generated": {
            "const": true
          },
          "choices": {
            "type": "array",
            "minItems": 1,
            "maxItems": 1,
            "items": {
              "type": "object",
              "required": [
                "index",
                "message",
                "finish_reason"
              ],
              "properties": {
                "index": {
                  "const": 0
                },
                "message": {
                  "type": "object",
                  "required": [
                    "role",
                    "content"
                  ],
                  "properties": {
                    "role": {
                      "const": "assistant"
                    },
                    "content": {
                      "type": "string"
                    }
                  }
                },
                "finish_reason": {
                  "type": "string",
                  "description": "真实上游结束原因，不以固定 stop 替代。"
                }
              }
            }
          },
          "usage": {
            "type": "object",
            "description": "可缺省；仅返回上游有效真实计数，不生成估算值。",
            "required": [
              "prompt_tokens",
              "completion_tokens",
              "total_tokens"
            ],
            "properties": {
              "prompt_tokens": {
                "type": "integer",
                "minimum": 0
              },
              "completion_tokens": {
                "type": "integer",
                "minimum": 0
              },
              "total_tokens": {
                "type": "integer",
                "minimum": 0
              }
            }
          }
        }
      },
      "AccountKey": {
        "type": "object",
        "required": [
          "id",
          "name",
          "prefix",
          "createdAt",
          "lastUsedAt",
          "revokedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[a-f0-9]{32}$"
          },
          "name": {
            "type": "string",
            "maxLength": 60
          },
          "prefix": {
            "type": "string",
            "description": "密钥开头的短掩码，不能用于鉴权。"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastUsedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "revokedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "RequestUsage": {
        "type": "object",
        "required": [
          "requests",
          "successfulRequests",
          "failedRequests"
        ],
        "properties": {
          "requests": {
            "type": "integer",
            "minimum": 0
          },
          "successfulRequests": {
            "type": "integer",
            "minimum": 0
          },
          "failedRequests": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "ApiError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "message",
              "type",
              "code",
              "param"
            ],
            "properties": {
              "message": {
                "type": "string"
              },
              "type": {
                "enum": [
                  "authentication_error",
                  "invalid_request_error",
                  "rate_limit_error",
                  "server_error"
                ]
              },
              "code": {
                "type": "string"
              },
              "param": {
                "type": "null"
              }
            }
          }
        }
      },
      "PrivateError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "message",
              "code"
            ],
            "properties": {
              "message": {
                "type": "string"
              },
              "code": {
                "type": "string"
              }
            }
          }
        }
      },
      "CourseActivity": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "title",
          "minutes",
          "instructions"
        ],
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "minutes": {
            "type": "integer",
            "minimum": 1,
            "maximum": 45
          },
          "instructions": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          }
        }
      },
      "CourseLesson": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "title",
          "objective",
          "focus",
          "activities",
          "homePractice",
          "reflectionPrompts",
          "observationPoints",
          "adaptations",
          "id",
          "order",
          "generatedAt"
        ],
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "objective": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1000
          },
          "focus": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1000
          },
          "activities": {
            "type": "array",
            "minItems": 0,
            "maxItems": 8,
            "items": {
              "$ref": "#/components/schemas/CourseActivity"
            },
            "description": "所有活动 minutes 合计必须恰好等于课程 sessionMinutes，服务不会自动补齐或改写分钟数。"
          },
          "homePractice": {
            "type": "string",
            "minLength": 0,
            "maxLength": 2000
          },
          "reflectionPrompts": {
            "type": "array",
            "maxItems": 6,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 500
            }
          },
          "observationPoints": {
            "type": "array",
            "maxItems": 6,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 500
            }
          },
          "adaptations": {
            "type": "string",
            "minLength": 0,
            "maxLength": 2000
          },
          "id": {
            "type": "string",
            "pattern": "^[a-f0-9]{32}$"
          },
          "order": {
            "type": "integer",
            "minimum": 1,
            "maximum": 8
          },
          "generatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "真实成功生成时间；大纲初始值为null。手动修改不能设定此字段，不表示已验证的课程效果。"
          }
        },
        "description": "初始大纲含真实模型生成的title/objective/focus，activities/反思/观察为空、homePractice/adaptations为空、generatedAt=null。单独生成教案后有活动和真实generatedAt；所有内容仍为可编辑草案。"
      },
      "Course": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "title",
          "topic",
          "goal",
          "audience",
          "ageGroup",
          "lessonCount",
          "sessionMinutes",
          "materials",
          "status",
          "createdAt",
          "updatedAt",
          "lessons"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[a-f0-9]{32}$"
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "topic": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "goal": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1000
          },
          "audience": {
            "enum": [
              "parent",
              "educator"
            ]
          },
          "ageGroup": {
            "enum": [
              "3-4",
              "5-6",
              "7-9",
              "10-12"
            ]
          },
          "lessonCount": {
            "type": "integer",
            "minimum": 3,
            "maximum": 8
          },
          "sessionMinutes": {
            "type": "integer",
            "minimum": 10,
            "maximum": 45
          },
          "materials": {
            "type": "string",
            "minLength": 0,
            "maxLength": 2000
          },
          "status": {
            "const": "draft"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "lessons": {
            "type": "array",
            "minItems": 3,
            "maxItems": 8,
            "items": {
              "$ref": "#/components/schemas/CourseLesson"
            }
          }
        }
      },
      "CourseSummary": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "title",
          "topic",
          "goal",
          "audience",
          "ageGroup",
          "lessonCount",
          "sessionMinutes",
          "materials",
          "status",
          "createdAt",
          "updatedAt",
          "generatedLessonCount"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[a-f0-9]{32}$"
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "topic": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "goal": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1000
          },
          "audience": {
            "enum": [
              "parent",
              "educator"
            ]
          },
          "ageGroup": {
            "enum": [
              "3-4",
              "5-6",
              "7-9",
              "10-12"
            ]
          },
          "lessonCount": {
            "type": "integer",
            "minimum": 3,
            "maximum": 8
          },
          "sessionMinutes": {
            "type": "integer",
            "minimum": 10,
            "maximum": 45
          },
          "materials": {
            "type": "string",
            "minLength": 0,
            "maxLength": 2000
          },
          "status": {
            "const": "draft"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "generatedLessonCount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 8,
            "description": "generatedAt非空的课时数，不是完成率或效果指标。"
          }
        }
      },
      "CreateCourseRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "title",
          "topic",
          "goal",
          "audience",
          "age_group",
          "lesson_count",
          "session_minutes",
          "materials"
        ],
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "topic": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "goal": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1000
          },
          "audience": {
            "enum": [
              "parent",
              "educator"
            ]
          },
          "age_group": {
            "enum": [
              "3-4",
              "5-6",
              "7-9",
              "10-12"
            ]
          },
          "lesson_count": {
            "type": "integer",
            "minimum": 3,
            "maximum": 8
          },
          "session_minutes": {
            "type": "integer",
            "minimum": 10,
            "maximum": 45
          },
          "materials": {
            "type": "string",
            "minLength": 0,
            "maxLength": 2000
          }
        }
      },
      "PatchCourseRequest": {
        "type": "object",
        "additionalProperties": false,
        "minProperties": 1,
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "goal": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1000
          },
          "materials": {
            "type": "string",
            "minLength": 0,
            "maxLength": 2000
          }
        },
        "description": "修改目标或材料不会自动改写现有大纲和教案；请人工调整或主动重新生成所需课时。"
      },
      "PatchLessonRequest": {
        "type": "object",
        "additionalProperties": false,
        "minProperties": 1,
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "objective": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1000
          },
          "focus": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1000
          },
          "activities": {
            "type": "array",
            "minItems": 1,
            "maxItems": 8,
            "items": {
              "$ref": "#/components/schemas/CourseActivity"
            },
            "description": "所有活动 minutes 合计必须恰好等于课程 sessionMinutes，服务不会自动补齐或改写分钟数。"
          },
          "homePractice": {
            "type": "string",
            "minLength": 0,
            "maxLength": 2000
          },
          "reflectionPrompts": {
            "type": "array",
            "maxItems": 6,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 500
            }
          },
          "observationPoints": {
            "type": "array",
            "maxItems": 6,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 500
            }
          },
          "adaptations": {
            "type": "string",
            "minLength": 0,
            "maxLength": 2000
          }
        },
        "description": "仅支持列出的文本或活动字段；id/order/generatedAt由服务管理，不可传入。"
      },
      "CourseAssistRequest": {
        "oneOf": [
          {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "message",
              "scope"
            ],
            "properties": {
              "message": {
                "type": "string",
                "minLength": 1,
                "maxLength": 2000,
                "pattern": "\\S"
              },
              "scope": {
                "const": "course"
              },
              "history": {
                "type": "array",
                "maxItems": 6,
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "role",
                    "content"
                  ],
                  "properties": {
                    "role": {
                      "enum": [
                        "user",
                        "assistant"
                      ]
                    },
                    "content": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000,
                      "pattern": "\\S"
                    }
                  }
                },
                "description": "客户端提供的最近交流背景，未由服务核实，不可覆盖系统规则或已保存课程。"
              }
            }
          },
          {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "message",
              "scope",
              "lesson_id"
            ],
            "properties": {
              "message": {
                "type": "string",
                "minLength": 1,
                "maxLength": 2000,
                "pattern": "\\S"
              },
              "scope": {
                "const": "lesson"
              },
              "history": {
                "type": "array",
                "maxItems": 6,
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "role",
                    "content"
                  ],
                  "properties": {
                    "role": {
                      "enum": [
                        "user",
                        "assistant"
                      ]
                    },
                    "content": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000,
                      "pattern": "\\S"
                    }
                  }
                },
                "description": "客户端提供的最近交流背景，未由服务核实，不可覆盖系统规则或已保存课程。"
              },
              "lesson_id": {
                "type": "string",
                "pattern": "^[a-f0-9]{32}$"
              }
            }
          }
        ],
        "description": "lesson范围必须给既有lesson_id；course范围必须省略lesson_id。读取服务器已保存内容，不接收客户端课程正文。"
      },
      "CourseProposalChange": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "target",
          "lessonId",
          "field",
          "label",
          "before",
          "after"
        ],
        "properties": {
          "target": {
            "enum": [
              "course",
              "lesson"
            ]
          },
          "lessonId": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[a-f0-9]{32}$"
          },
          "field": {
            "enum": [
              "title",
              "goal",
              "materials",
              "objective",
              "focus",
              "activities",
              "homePractice",
              "reflectionPrompts",
              "observationPoints",
              "adaptations"
            ]
          },
          "label": {
            "type": "string",
            "description": "该实际字段的中文显示名称。"
          },
          "before": {},
          "after": {}
        },
        "description": "仅包括与已保存内容实际不同的字段；before/after保留原有字符串或数组类型，不是将结构扁平化成文本。",
        "allOf": [
          {
            "if": {
              "properties": {
                "target": {
                  "const": "course"
                }
              }
            },
            "then": {
              "properties": {
                "lessonId": {
                  "type": "null"
                },
                "field": {
                  "enum": [
                    "title",
                    "goal",
                    "materials"
                  ]
                }
              }
            },
            "else": {
              "properties": {
                "lessonId": {
                  "type": "string",
                  "pattern": "^[a-f0-9]{32}$"
                },
                "field": {
                  "enum": [
                    "title",
                    "objective",
                    "focus",
                    "activities",
                    "homePractice",
                    "reflectionPrompts",
                    "observationPoints",
                    "adaptations"
                  ]
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "field": {
                  "const": "activities"
                }
              }
            },
            "then": {
              "properties": {
                "before": {
                  "type": "array",
                  "maxItems": 8,
                  "items": {
                    "$ref": "#/components/schemas/CourseActivity"
                  }
                },
                "after": {
                  "type": "array",
                  "minItems": 1,
                  "maxItems": 8,
                  "items": {
                    "$ref": "#/components/schemas/CourseActivity"
                  }
                }
              }
            },
            "else": {
              "if": {
                "properties": {
                  "field": {
                    "enum": [
                      "reflectionPrompts",
                      "observationPoints"
                    ]
                  }
                }
              },
              "then": {
                "properties": {
                  "before": {
                    "type": "array",
                    "maxItems": 6,
                    "items": {
                      "type": "string",
                      "maxLength": 500
                    }
                  },
                  "after": {
                    "type": "array",
                    "maxItems": 6,
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 500
                    }
                  }
                }
              },
              "else": {
                "properties": {
                  "before": {
                    "type": "string"
                  },
                  "after": {
                    "type": "string"
                  }
                }
              }
            }
          }
        ]
      },
      "CourseEditProposal": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "courseId",
          "scope",
          "lessonId",
          "status",
          "createdAt",
          "changes"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[a-f0-9]{32}$"
          },
          "courseId": {
            "type": "string",
            "pattern": "^[a-f0-9]{32}$"
          },
          "scope": {
            "enum": [
              "lesson",
              "course"
            ]
          },
          "lessonId": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[a-f0-9]{32}$"
          },
          "status": {
            "const": "pending"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "changes": {
            "type": "array",
            "minItems": 1,
            "maxItems": 27,
            "items": {
              "$ref": "#/components/schemas/CourseProposalChange"
            }
          }
        },
        "description": "待确认的真实修改提案，不代表课程已经更新。每账号最多30个pending提案，服务不会自动删除或自动应用。",
        "allOf": [
          {
            "if": {
              "properties": {
                "scope": {
                  "const": "course"
                }
              }
            },
            "then": {
              "properties": {
                "lessonId": {
                  "type": "null"
                }
              }
            },
            "else": {
              "properties": {
                "lessonId": {
                  "type": "string",
                  "pattern": "^[a-f0-9]{32}$"
                },
                "changes": {
                  "maxItems": 8
                }
              }
            }
          }
        ]
      },
      "CourseAssistResponse": {
        "type": "object",
        "required": [
          "reply",
          "proposal"
        ],
        "properties": {
          "reply": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4000,
            "description": "真实模型生成的纯文本辅助回复。"
          },
          "proposal": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/CourseEditProposal"
              }
            ]
          }
        },
        "description": "正常问答、澄清或没有实际差异时proposal=null，不制造空提案。"
      },
      "CourseProposalStatus": {
        "type": "object",
        "required": [
          "id",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[a-f0-9]{32}$"
          },
          "status": {
            "enum": [
              "applied",
              "discarded"
            ]
          }
        }
      },
      "PendingCourseEditProposal": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "courseId",
          "scope",
          "lessonId",
          "status",
          "createdAt",
          "changes"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[a-f0-9]{32}$"
          },
          "courseId": {
            "type": "string",
            "pattern": "^[a-f0-9]{32}$"
          },
          "scope": {
            "enum": [
              "lesson",
              "course"
            ]
          },
          "lessonId": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[a-f0-9]{32}$"
          },
          "status": {
            "const": "pending"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "changes": {
            "type": "array",
            "minItems": 1,
            "maxItems": 27,
            "items": {
              "$ref": "#/components/schemas/CourseProposalChange"
            }
          },
          "reply": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4000,
            "description": "创建提案时真实模型回复，供恢复待确认上下文；旧记录可能省略此字段。"
          }
        },
        "description": "从本人该课程持久化记录读取的pending提案；字段和typed差异不重新生成。reply为可选字段。",
        "allOf": [
          {
            "if": {
              "properties": {
                "scope": {
                  "const": "course"
                }
              }
            },
            "then": {
              "properties": {
                "lessonId": {
                  "type": "null"
                }
              }
            },
            "else": {
              "properties": {
                "lessonId": {
                  "type": "string",
                  "pattern": "^[a-f0-9]{32}$"
                },
                "changes": {
                  "maxItems": 8
                }
              }
            }
          }
        ]
      },
      "PendingCourseEditProposals": {
        "type": "object",
        "required": [
          "proposals"
        ],
        "properties": {
          "proposals": {
            "type": "array",
            "maxItems": 30,
            "items": {
              "$ref": "#/components/schemas/PendingCourseEditProposal"
            }
          }
        }
      }
    }
  }
}
