openapi: 3.1.0
info:
  title: EvoCow Prescription API
  version: 1.0.0-draft.1
  summary: A minimal observation-and-hardware-to-feedback lifecycle API.
  license:
    name: All rights reserved
    identifier: LicenseRef-Proprietary
  description: |
    RFC-001 的机器可读草案。observation 与输出 content 都是字符串；
    创建和追加观察时可以附带当前 hardware_data；
    最终 report 只包含 human_feedback、conversation、hardware_data 三块。

    注意：当前 Demo 实现尚未迁移到这份契约。
servers:
  - url: /
    description: Relative API base URL; the production host is not assigned yet.
tags:
  - name: Prescriptions
    description: 创建、补充和读取处方生命周期。
  - name: Reports
    description: 提交一次最终反馈报告。
security:
  - bearerAuth: []
paths:
  /v1/prescriptions:
    post:
      tags: [Prescriptions]
      operationId: createPrescription
      summary: 创建处方
      description: 用第一条自然语言观察创建处方生命周期。
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PrescriptionInput"
            examples:
              observationWithHardware:
                value:
                  observation: 这头牛今天不吃草，精神很差。
                  hardware_data:
                    source: ear-sensor-live
                    samples:
                      - time: "2026-07-13T08:00:00+08:00"
                        temperature_c: 39.8
      responses:
        "201":
          description: Created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Prescription"
              examples:
                needsMoreInformation:
                  $ref: "#/components/examples/NeedsMoreInformation"
                ready:
                  $ref: "#/components/examples/Ready"
                urgentEscalation:
                  $ref: "#/components/examples/UrgentEscalation"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "409": { $ref: "#/components/responses/IdempotencyConflict" }
  /v1/prescriptions/{id}:
    parameters:
      - $ref: "#/components/parameters/PrescriptionId"
    put:
      tags: [Prescriptions]
      operationId: appendObservation
      summary: 追加观察
      description: 在已有处方上下文中追加一条自然语言观察。
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PrescriptionInput"
      responses:
        "200":
          description: Current prescription state after processing the observation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Prescription"
              examples:
                needsMoreInformation:
                  $ref: "#/components/examples/NeedsMoreInformation"
                ready:
                  $ref: "#/components/examples/Ready"
                urgentEscalation:
                  $ref: "#/components/examples/UrgentEscalation"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/IdempotencyConflict" }
    get:
      tags: [Prescriptions]
      operationId: getPrescription
      summary: 读取处方
      description: 返回处方最新的状态与字符串内容。
      responses:
        "200":
          description: Current prescription state.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Prescription"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
  /v1/prescriptions/{id}/report:
    parameters:
      - $ref: "#/components/parameters/PrescriptionId"
    put:
      tags: [Reports]
      operationId: submitReport
      summary: 提交最终报告
      description: |
        为处方创建唯一的最终报告。相同 Idempotency-Key 与相同正文可安全重放；
        报告已经存在时，用新的幂等键再次提交返回 409。
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Report"
      responses:
        "201":
          description: Report accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReportReceipt"
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409":
          description: The idempotency key conflicts or a report was already submitted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API Key
  parameters:
    PrescriptionId:
      name: id
      in: path
      required: true
      description: 服务端生成的处方生命周期 ID。
      schema:
        type: string
        minLength: 1
      example: rx_01JEVOCOW001
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: 每次逻辑写入的唯一键；相同键与相同正文可安全重放。
      schema:
        type: string
        minLength: 8
        maxLength: 255
  schemas:
    PrescriptionInput:
      type: object
      additionalProperties: false
      required: [observation]
      properties:
        observation:
          type: string
          minLength: 1
          maxLength: 8192
          description: 一条自然语言现场观察。
        hardware_data:
          description: 当前这一步可获得的原始硬件快照或时序片段；不强制设备 Schema。
          oneOf:
            - type: object
              additionalProperties: true
            - type: array
              items: {}
    Prescription:
      oneOf:
        - $ref: "#/components/schemas/NeedsMoreInformationPrescription"
        - $ref: "#/components/schemas/ReadyPrescription"
        - $ref: "#/components/schemas/UrgentEscalationPrescription"
      discriminator:
        propertyName: status
        mapping:
          needs_more_information: "#/components/schemas/NeedsMoreInformationPrescription"
          ready: "#/components/schemas/ReadyPrescription"
          urgent_escalation: "#/components/schemas/UrgentEscalationPrescription"
    NeedsMoreInformationPrescription:
      description: 信息不足；content 是下一条需要收集的问题。
      allOf:
        - $ref: "#/components/schemas/PrescriptionResultBase"
        - type: object
          properties:
            status:
              const: needs_more_information
      examples:
        - id: rx_01JEVOCOW001
          status: needs_more_information
          content: 请测量体温，并观察呼吸是否费力、能否正常站立。
    ReadyPrescription:
      description: 信息足够；content 是当前处置建议。
      allOf:
        - $ref: "#/components/schemas/PrescriptionResultBase"
        - type: object
          properties:
            status:
              const: ready
      examples:
        - id: rx_01JEVOCOW001
          status: ready
          content: 建议尽快联系驻场兽医检查。等待期间隔离观察并持续记录体温。
    UrgentEscalationPrescription:
      description: 存在紧急风险；content 是立即升级给人工或兽医的提示。
      allOf:
        - $ref: "#/components/schemas/PrescriptionResultBase"
        - type: object
          properties:
            status:
              const: urgent_escalation
      examples:
        - id: rx_01JEVOCOW002
          status: urgent_escalation
          content: 发现无法站立并伴随呼吸费力，请立即联系兽医进行现场急诊处理。
    PrescriptionResultBase:
      type: object
      additionalProperties: false
      required: [id, status, content]
      properties:
        id:
          type: string
          description: 处方生命周期 ID。
        status:
          type: string
          enum: [needs_more_information, ready, urgent_escalation]
        content:
          type: string
          minLength: 1
          description: 下一条追问、最终建议或紧急升级提醒。
    Report:
      type: object
      additionalProperties: false
      required: [human_feedback, conversation, hardware_data]
      properties:
        human_feedback:
          type: string
          minLength: 1
          maxLength: 32768
          description: 现场人员对执行过程和结果的自然语言总结。
        conversation:
          description: 调用方保留的全量原始对话；EvoCow 不强制消息 Schema。
          oneOf:
            - type: object
              additionalProperties: true
            - type: array
              items: {}
        hardware_data:
          description: 原始硬件/时序数据；暂无统一 Schema；没有数据时传 null。
          oneOf:
            - type: object
              additionalProperties: true
            - type: array
              items: {}
            - type: "null"
    ReportReceipt:
      type: object
      additionalProperties: false
      required: [id, prescription_id, received_at]
      properties:
        id:
          type: string
        prescription_id:
          type: string
        received_at:
          type: string
          format: date-time
    Error:
      type: object
      additionalProperties: false
      required: [error]
      properties:
        error:
          type: object
          additionalProperties: false
          required: [code, message]
          properties:
            code: { type: string }
            message: { type: string }
  responses:
    BadRequest:
      description: Request validation failed.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    Unauthorized:
      description: API Key is missing or invalid.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    NotFound:
      description: Prescription not found in the authenticated tenant.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    IdempotencyConflict:
      description: The same idempotency key was already used with a different body.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
  examples:
    NeedsMoreInformation:
      summary: 需要补充信息
      value:
        id: rx_01JEVOCOW001
        status: needs_more_information
        content: 请测量体温，并观察呼吸是否费力、能否正常站立。
    Ready:
      summary: 已生成建议
      value:
        id: rx_01JEVOCOW001
        status: ready
        content: 建议尽快联系驻场兽医检查。等待期间隔离观察并持续记录体温。
    UrgentEscalation:
      summary: 紧急升级
      value:
        id: rx_01JEVOCOW002
        status: urgent_escalation
        content: 发现无法站立并伴随呼吸费力，请立即联系兽医进行现场急诊处理。
