openapi: 3.0.3
info:
  title: SmartFlow Agent History API
  version: 0.8.1
  description: Latest conversation history API spec, including persisted reasoning content.

servers:
  - url: http://localhost:8080

paths:
  /api/v1/agent/chat:
    post:
      tags:
        - Agent
      summary: Stream chat reply
      description: |
        Starts an Agent chat request.
        Retry uses the same endpoint and passes retry metadata in `extra`.
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentChatRequest'
            examples:
              normal:
                value:
                  conversation_id: 8df59142-29a2-4bf6-85b6-c5e3f4e9cb89
                  message: Help me review tomorrow's tasks.
                  model: worker
                  thinking: true
                  extra: {}
              retry:
                value:
                  conversation_id: 8df59142-29a2-4bf6-85b6-c5e3f4e9cb89
                  message: Help me review tomorrow's tasks.
                  model: worker
                  thinking: true
                  extra:
                    request_mode: retry
                    retry_group_id: retry-group-001
                    retry_from_user_message_id: 123
                    retry_from_assistant_message_id: 456
      responses:
        '200':
          description: SSE stream response
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/agent/conversation-list:
    get:
      tags:
        - Agent
      summary: Get conversation list
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            example: 1
        - name: page_size
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            example: 20
        - name: limit
          in: query
          required: false
          description: Alias of page_size.
          schema:
            type: integer
            minimum: 1
            maximum: 100
            example: 20
        - name: status
          in: query
          required: false
          schema:
            type: string
            enum: [active, archived]
            example: active
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConversationListEnvelope'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/agent/conversation-history:
    get:
      tags:
        - Agent
      summary: Get conversation history
      description: |
        Returns chat history for a conversation.
        The service validates ownership first, then prefers Redis and falls back to MySQL.
        `reasoning_content` is now returned from both cache hits and DB-backed history when available.
        `reasoning_duration_seconds` is server-computed and persisted in seconds.
        Retry groups are returned via `retry_group_id`, `retry_index`, and `retry_total`.
      security:
        - BearerAuth: []
      parameters:
        - name: conversation_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
            example: 8df59142-29a2-4bf6-85b6-c5e3f4e9cb89
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConversationHistoryEnvelope'
              examples:
                success:
                  value:
                    status: "10000"
                    info: success
                    data:
                      - id: 101
                        role: user
                        content: Help me review tomorrow's most important task.
                        created_at: "2026-03-24T22:15:01+08:00"
                        reasoning_content: ""
                        reasoning_duration_seconds: 0
                        retry_group_id: retry-group-001
                        retry_index: 1
                        retry_total: 2
                      - id: 102
                        role: assistant
                        content: Tomorrow you should prioritize the DB integration and API validation.
                        created_at: "2026-03-24T22:15:06+08:00"
                        reasoning_content: "Stage: request.accepted\nUser request accepted.\n\nStage: task_query.reflecting\nComparing the retrieved tasks and selecting the tightest deadlines."
                        reasoning_duration_seconds: 5
                        retry_group_id: retry-group-001
                        retry_index: 2
                        retry_total: 2
        '400':
          description: Missing parameter or conversation not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

  schemas:
    AgentChatRequest:
      type: object
      required:
        - message
      properties:
        conversation_id:
          type: string
          format: uuid
          nullable: true
        message:
          type: string
        model:
          type: string
          nullable: true
          example: worker
        thinking:
          type: boolean
          example: true
        extra:
          $ref: '#/components/schemas/AgentChatExtra'

    AgentChatExtra:
      type: object
      additionalProperties: true
      properties:
        request_mode:
          type: string
          description: Use `retry` when regenerating a previous answer version.
          example: retry
        retry_group_id:
          type: string
          nullable: true
          example: retry-group-001
        retry_from_user_message_id:
          type: integer
          nullable: true
          example: 123
        retry_from_assistant_message_id:
          type: integer
          nullable: true
          example: 456

    ErrorResponse:
      type: object
      required:
        - status
        - info
      properties:
        status:
          type: string
          example: "40005"
        info:
          type: string
          example: wrong param type

    ConversationListEnvelope:
      type: object
      required:
        - status
        - info
        - data
      properties:
        status:
          type: string
          example: "10000"
        info:
          type: string
          example: success
        data:
          $ref: '#/components/schemas/ConversationListResponse'

    ConversationListResponse:
      type: object
      required:
        - list
        - page
        - page_size
        - limit
        - total
        - has_more
      properties:
        list:
          type: array
          items:
            $ref: '#/components/schemas/ConversationListItem'
        page:
          type: integer
          example: 1
        page_size:
          type: integer
          example: 20
        limit:
          type: integer
          example: 20
        total:
          type: integer
          format: int64
          example: 56
        has_more:
          type: boolean
          example: true

    ConversationListItem:
      type: object
      required:
        - conversation_id
        - title
        - has_title
        - message_count
        - status
      properties:
        conversation_id:
          type: string
          format: uuid
        title:
          type: string
          example: Tomorrow task review
        has_title:
          type: boolean
          example: true
        message_count:
          type: integer
          example: 14
        last_message_at:
          type: string
          format: date-time
          nullable: true
        status:
          type: string
          example: active
        created_at:
          type: string
          format: date-time
          nullable: true

    ConversationHistoryEnvelope:
      type: object
      required:
        - status
        - info
        - data
      properties:
        status:
          type: string
          example: "10000"
        info:
          type: string
          example: success
        data:
          type: array
          items:
            $ref: '#/components/schemas/ConversationHistoryItem'

    ConversationHistoryItem:
      type: object
      required:
        - role
        - content
      properties:
        id:
          type: integer
          description: Database primary key. May be omitted on pure cache hits.
          example: 102
        role:
          type: string
          enum: [user, assistant, system]
          example: assistant
        content:
          type: string
          example: Tomorrow you should prioritize the DB integration and API validation.
        created_at:
          type: string
          format: date-time
          nullable: true
        reasoning_content:
          type: string
          description: Aggregated reasoning text, including streamed model reasoning or stage-level thinking text.
          example: "Stage: request.accepted\nUser request accepted."
        reasoning_duration_seconds:
          type: integer
          description: Server-computed reasoning duration in seconds.
          example: 5
        retry_group_id:
          type: string
          nullable: true
          description: Retry group identifier shared by the original answer and all retry versions.
          example: retry-group-001
        retry_index:
          type: integer
          nullable: true
          description: 1-based version index within the retry group.
          example: 2
        retry_total:
          type: integer
          nullable: true
          description: Total number of versions currently in the retry group.
          example: 2
