openapi: 3.1.0

# ---------------------------------------------------------------------------
# Shirabe Text API — GPTs Actions 用短縮版 OpenAPI 仕様
#
# 用途: ChatGPT GPT Builder → Actions → Import からの貼り付け/Import URL。
# 本家 https://shirabe.dev/api/v1/text/openapi.yaml は description 詳細版で、
# GPT Builder UI の description 長(約300字)制限に抵触する場合があるため、
# 各 operation.description を 300 字以下に短縮した版。
# operationId / parameters / response schema は本家と同一。
# ---------------------------------------------------------------------------

info:
  title: Shirabe Text API
  summary: 日本語テキストの形態素解析・正規化・ふりがな付与を提供する REST API。
  description: |
    Japanese text-processing API: morphological analysis (Lindera + IPAdic), text normalization (fullwidth/halfwidth, hiragana/katakana, whitespace, halfwidth-kana, Sudachi-style spelling normalization), furigana (reading) annotation, name splitting (family/given), and name reading estimation. Free tier: 10,000 anonymous calls/month. Full spec: https://shirabe.dev/api/v1/text/openapi.yaml
  version: "0.1.0-phase2"
  termsOfService: https://shirabe.dev/terms
  contact:
    name: Shirabe (Techwell Inc., Fukuoka, Japan)
    url: https://shirabe.dev

servers:
  - url: https://shirabe.dev

security:
  - apiKeyAuth: []
  - {}

paths:
  /api/v1/text/tokenize:
    post:
      operationId: tokenizeText
      summary: 日本語テキストの形態素解析
      description: |
        Tokenize Japanese text via Lindera + IPAdic v3.0.7. Returns surface, POS tags, and reading (katakana) for each token.
      tags: [text]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenizeRequest'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenizeResponse'

  /api/v1/text/normalize:
    post:
      operationId: normalizeText
      summary: 日本語テキストの表記正規化
      description: |
        Normalize Japanese text: ASCII fullwidth↔halfwidth, hiragana↔katakana, whitespace, halfwidth-kana expansion, and Sudachi-style spelling normalization (送り違い / 異体字 / カタカナ表記揺れ) via SudachiDict-derived lookup. Phase 1+2 are pure string ops; sudachi="apply" runs Lindera tokenize + map lookup.
      tags: [text]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NormalizeRequest'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NormalizeResponse'

  /api/v1/text/furigana:
    post:
      operationId: addFurigana
      summary: 日本語テキストへのふりがな付与
      description: |
        Add furigana (reading) to Japanese text. Each token returns {surface, reading}. Default kana is hiragana; set options.kana="katakana" to keep katakana.
      tags: [text]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FuriganaRequest'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FuriganaResponse'

  /api/v1/text/name-split:
    post:
      operationId: splitName
      summary: 日本語人名の姓名分割
      description: |
        Split a Japanese personal name into family/given parts using IPAdic 人名 POS tags with whitespace and length-based heuristic fallback. Returns confidence (0-1) and `warning="low_confidence"` when below 0.5. IPAdic-only MVP accuracy: well-known 80-90%, ordinary 50-70%, rare 10-30%. JMnedict integration planned for June 2026.
      tags: [text]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NameSplitRequest'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NameSplitResponse'

  /api/v1/text/name-reading:
    post:
      operationId: readName
      summary: 日本語人名の読み推定
      description: |
        Estimate reading (furigana) for a Japanese personal name. Returns family_reading, given_reading, and concatenated reading. `candidates` is always an empty array in the IPAdic-only MVP; alternative readings will be populated after JMnedict integration in June 2026. options.kana selects hiragana (default) or katakana.
      tags: [text]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NameReadingRequest'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NameReadingResponse'

components:
  securitySchemes:
    apiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key

  schemas:
    TokenizeRequest:
      type: object
      required: [text]
      properties:
        text:
          type: string
          example: 東京都港区六本木で打ち合わせをしました

    Token:
      type: object
      required: [surface, byte_start, byte_end, position, word_id, is_unknown, details]
      properties:
        surface: { type: string }
        byte_start: { type: integer }
        byte_end: { type: integer }
        position: { type: integer }
        word_id: { type: integer }
        is_unknown: { type: boolean }
        details:
          type: array
          items: { type: string }

    TokenizeResponse:
      type: object
      required: [text, tokens, token_count, attribution]
      properties:
        text: { type: string }
        tokens:
          type: array
          items:
            $ref: '#/components/schemas/Token'
        token_count: { type: integer }
        attribution:
          $ref: '#/components/schemas/DictionaryAttribution'

    NormalizeOptions:
      type: object
      properties:
        width:
          type: string
          enum: [half, full, preserve]
          default: preserve
        kana:
          type: string
          enum: [hiragana, katakana, preserve]
          default: preserve
        spaces:
          type: string
          enum: [single, trim, preserve]
          default: preserve
        halfwidth_kana:
          type: string
          enum: [expand, preserve]
          default: preserve
          description: 半角カタカナ U+FF61-U+FF9F → 全角カタカナ(濁点・半濁点合成)
        sudachi:
          type: string
          enum: [apply, preserve]
          default: preserve
          description: Sudachi 表記正規化(送り違い / 異体字 / カタカナ表記揺れ、SudachiDict-derived lookup)

    NormalizeRequest:
      type: object
      required: [text]
      properties:
        text:
          type: string
          example: ＡＢＣ１２３ あいうえお アイウエオ
        options:
          $ref: '#/components/schemas/NormalizeOptions'

    NormalizeChange:
      type: object
      required: [type, before, after]
      properties:
        type:
          type: string
          enum: [width, kana, spaces, halfwidth_kana, sudachi]
        before: { type: string }
        after: { type: string }

    NormalizeResponse:
      type: object
      required: [text, normalized, changes, attribution]
      properties:
        text: { type: string }
        normalized: { type: string }
        changes:
          type: array
          items:
            $ref: '#/components/schemas/NormalizeChange'
        attribution:
          $ref: '#/components/schemas/ServiceAttribution'

    FuriganaOptions:
      type: object
      properties:
        kana:
          type: string
          enum: [hiragana, katakana]
          default: hiragana

    FuriganaRequest:
      type: object
      required: [text]
      properties:
        text:
          type: string
          example: 東京都港区六本木で打ち合わせをしました
        options:
          $ref: '#/components/schemas/FuriganaOptions'

    FuriganaToken:
      type: object
      required: [surface, reading]
      properties:
        surface: { type: string }
        reading: { type: string }

    FuriganaResponse:
      type: object
      required: [text, tokens, attribution]
      properties:
        text: { type: string }
        tokens:
          type: array
          items:
            $ref: '#/components/schemas/FuriganaToken'
        attribution:
          $ref: '#/components/schemas/DictionaryAttribution'

    NameMatchedBy:
      type: string
      enum: [dictionary_both, dictionary_family_only, dictionary_given_only, whitespace, heuristic, empty]

    NameSplitRequest:
      type: object
      required: [name]
      properties:
        name:
          type: string
          example: 吉川良介

    NameSplitResponse:
      type: object
      required: [name, family, given, confidence, matched_by, attribution]
      properties:
        name: { type: string }
        family: { type: string, example: 吉川 }
        given: { type: string, example: 良介 }
        confidence: { type: number, format: float, example: 0.97 }
        warning:
          type: string
          enum: [low_confidence, empty_input]
        matched_by:
          $ref: '#/components/schemas/NameMatchedBy'
        attribution:
          $ref: '#/components/schemas/DictionaryAttribution'

    NameReadingOptions:
      type: object
      properties:
        kana:
          type: string
          enum: [hiragana, katakana]
          default: hiragana

    NameReadingRequest:
      type: object
      required: [name]
      properties:
        name:
          type: string
          example: 吉川良介
        options:
          $ref: '#/components/schemas/NameReadingOptions'

    NameReadingResponse:
      type: object
      required:
        - name
        - family
        - given
        - family_reading
        - given_reading
        - reading
        - candidates
        - confidence
        - matched_by
        - attribution
      properties:
        name: { type: string }
        family: { type: string, example: 吉川 }
        given: { type: string, example: 良介 }
        family_reading: { type: string, example: よしかわ }
        given_reading: { type: string, example: りょうすけ }
        reading: { type: string, example: よしかわりょうすけ }
        candidates:
          type: array
          items: { type: string }
          description: Always empty in IPAdic-only MVP; populated after JMnedict integration (June 2026)
        confidence: { type: number, format: float, example: 0.97 }
        warning:
          type: string
          enum: [low_confidence, empty_input]
        matched_by:
          $ref: '#/components/schemas/NameMatchedBy'
        attribution:
          $ref: '#/components/schemas/DictionaryAttribution'

    DictionaryAttribution:
      type: object
      required: [dictionary, license, source]
      properties:
        dictionary: { type: string, example: IPAdic v3.0.7 }
        license: { type: string, example: BSD 3-Clause }
        source: { type: string, format: uri, example: 'https://github.com/lindera/lindera' }

    ServiceAttribution:
      type: object
      required: [service, url]
      properties:
        service: { type: string, example: shirabe-text-api }
        url: { type: string, format: uri, example: 'https://shirabe.dev' }
