openapi: 3.1.0
info:
  title: LoreKeeper Service API
  version: 1.0.0
  description: >
    Produktionsreifer Agentic RAG Backend-Dienst für Buch- und
    Universums-Konsistenz.

    Orchestriert Fastify, Directus Headless CMS und LiteLLM (Google Gemini).
servers:
  - url: http://localhost:3005
    description: Lokaler Fastify AI Server (Development)
  - url: http://lorekeeper-api:3005
    description: Docker Compose internes Netzwerk
  - url: https://laefnabeiafnaleiafafe.2.28.78.110.sslip.io
    description: Produktions Directus Instanz
paths:
  /api/books/ingest:
    post:
      tags:
        - Books
      summary: Buch einlesen & Ingestion starten
      description: >-
        Nimmt ein E-Book (EPUB 2 oder EPUB 3) als Base64-Buffer oder
        Multipart-Upload entgegen und startet die vollautomatisierte 2-Phasen
        Ingestion:

        1. **Phase 1 Text-Analyse:** Kapitel-Parsing, linguistische Bereinigung,
        semantische Figuren- und Welten-Extraktion.

        2. **Phase 2 Bild-Verarbeitung:** Unüberwachte Bildsegmentierung mit
        Gemini 3 Flash und Bounding-Box Refinement.


        #### cURL Beispiel:

        ```bash

        curl -X POST http://localhost:3005/api/books/ingest \
          -H "Content-Type: application/json" \
          -d '{
            "bufferBase64": "UEsDBBQAAAAIA...",
            "filename": "mein-buch.epub",
            "options": {
              "persistToDirectus": true,
              "language": "de",
              "targetWordsPerScene": 350,
              "filterPromos": true,
              "includeImages": true
            }
          }'
        ```


        #### Python Beispiel:

        ```python

        import requests, base64


        with open("buch.epub", "rb") as f:
            b64 = base64.b64encode(f.read()).decode("utf-8")

        res = requests.post("http://localhost:3005/api/books/ingest", json={
            "bufferBase64": b64,
            "filename": "buch.epub",
            "options": {"persistToDirectus": True, "language": "de"}
        })

        print(res.json())

        ```
      operationId: ingestBook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - bufferBase64
              properties:
                bufferBase64:
                  type: string
                  description: Base64-kodierter Inhalt der EPUB-Datei
                filename:
                  type: string
                  example: abenteuer-im-zauberwald.epub
                options:
                  type: object
                  properties:
                    persistToDirectus:
                      type: boolean
                      default: true
                      description: Datensätze direkt in Directus anlegen
                    authorId:
                      type: string
                      format: uuid
                      description: UUID des Autors in Directus
                    seriesId:
                      type: string
                      format: uuid
                      description: UUID der Buchserie
                    language:
                      type: string
                      example: de
                      description: ISO 639-1 Sprachcode
                    targetWordsPerScene:
                      type: integer
                      default: 350
                      description: Zielgröße einer Szene in Wörtern
                    filterPromos:
                      type: boolean
                      default: true
                      description: Verlags-Werbung & Impressum ausfiltern
                    includeImages:
                      type: boolean
                      default: true
                      description: Illustrationen extrahieren und verarbeiten
            example:
              bufferBase64: UEsDBBQAAAAIAKBmV1YAAAAAA...
              filename: die-chroniken-von-eldoria.epub
              options:
                persistToDirectus: true
                language: de
                targetWordsPerScene: 350
                filterPromos: true
                includeImages: true
          multipart/form-data:
            schema:
              type: object
              required:
                - file
              properties:
                file:
                  type: string
                  format: binary
                  description: EPUB-Datei (bis 100 MB)
      responses:
        "200":
          description: Ingestion erfolgreich abgeschlossen
          content:
            application/json:
              example:
                success: true
                bookId: b1a2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d
                title: Die Chroniken von Eldoria
                language: de
                chaptersCount: 12
                scenesCount: 38
                entitiesCount: 14
                imagesCount: 26
                durationMs: 4210
        "400":
          description: Ungültige Parameter oder fehlerhaftes EPUB-Archiv
          content:
            application/json:
              example:
                error: Validation failed
                details:
                  bufferBase64:
                    _errors:
                      - bufferBase64 is required
  /api/books/ingest/upload:
    post:
      tags:
        - Books
      summary: Direkter Multipart-Upload für große E-Books
      description: >-
        Nimmt eine EPUB-Datei als binären `multipart/form-data` Stream bis zu
        **100 MB** entgegen.


        #### cURL Beispiel:

        ```bash

        curl -X POST http://localhost:3005/api/books/ingest/upload \
          -F "file=@/pfad/zu/buch.epub" \
          -F "persistToDirectus=true" \
          -F "language=de"
        ```
      operationId: uploadBookFile
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
              properties:
                file:
                  type: string
                  format: binary
                  description: EPUB-Datei
                persistToDirectus:
                  type: boolean
                  default: true
                language:
                  type: string
                  default: de
      responses:
        "200":
          description: Upload und Verarbeitung erfolgreich
          content:
            application/json:
              example:
                success: true
                bookId: c2b3a4d5-e6f7-8a9b-0c1d-2e3f4a5b6c7d
                title: Das Geheimnis der alten Burg
                scenesCount: 24
                imagesCount: 18
  /api/books/{id}/status:
    get:
      tags:
        - Books
      summary: Ingestion-Status eines Buches abfragen
      description: |-
        Gibt den aktuellen Fortschritt, den Status der Text- und Bildphasen sowie Fehlerprotokolle zurück.

        #### cURL Beispiel:
        ```bash
        curl -X GET http://localhost:3005/api/books/b1a2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d/status
        ```
      operationId: getBookStatus
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          example: b1a2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d
          description: UUID des Buchs in Directus
      responses:
        "200":
          description: Statusdetails des Ingestion-Vorgangs
          content:
            application/json:
              example:
                bookId: b1a2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d
                status: completed
                progress: 100
                phases:
                  textExtraction: done
                  visualClustering: done
                  bipartiteMatching: done
                stats:
                  totalChapters: 12
                  totalScenes: 38
                  totalEntities: 14
                  totalCrops: 45
        "404":
          description: Buch mit dieser ID existiert nicht
          content:
            application/json:
              example:
                error: Book 'b1a2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d' not found
  /api/books/{id}/export:
    get:
      tags:
        - Books
      summary: Buch als EPUB 3 Archiv exportieren
      description: |-
        Generiert ein standardkonformes EPUB-Archiv inklusive aller generierten Szenen, Metadaten, CSS-Formatierungen und Illustrationen.

        #### cURL Beispiel:
        ```bash
        curl -X GET http://localhost:3005/api/books/b1a2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d/export \
          --output exportiertes-buch.epub
        ```
      operationId: exportBookEpub
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          example: b1a2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d
      responses:
        "200":
          description: Binärer EPUB 3 Dateidownload
          content:
            application/epub+zip:
              schema:
                type: string
                format: binary
  /api/entities/extract-visual:
    post:
      tags:
        - Entities
      summary: Unüberwachte Bildsegmentierung durchführen
      description: >-
        Scannt eine Buch-Illustration mit **Gemini 3 Flash**, erkennt Figuren
        morphologisch korrekt, erzeugt präzise Bounding-Box-Koordinaten ($0
        \dots 1000$ Promille) und schneidet hochauflösende WebP-Avatare zu.


        #### cURL Beispiel:

        ```bash

        curl -X POST http://localhost:3005/api/entities/extract-visual \
          -H "Content-Type: application/json" \
          -d '{
            "imageFileId": "6761713f-29da-4962-8369-bea14af16d7c",
            "candidates": [
              {"name": "Matilda", "type": "character"},
              {"name": "Kokosnuss", "type": "character"}
            ],
            "options": {
              "persistToDirectus": true
            }
          }'
        ```
      operationId: extractVisualEntities
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                imageFileId:
                  type: string
                  format: uuid
                  description: Directus File UUID des Bildes
                image:
                  type: string
                  description: "Alternativ: Base64-kodiertes Bild"
                candidates:
                  type: array
                  items:
                    type: object
                    properties:
                      name:
                        type: string
                      type:
                        type: string
                        enum:
                          - character
                          - object
                          - location
                bookId:
                  type: string
                  format: uuid
                sceneId:
                  type: string
                  format: uuid
                options:
                  type: object
                  properties:
                    persistToDirectus:
                      type: boolean
                      default: true
            example:
              imageFileId: 6761713f-29da-4962-8369-bea14af16d7c
              candidates:
                - name: Matilda
                  type: character
                - name: Kokosnuss
                  type: character
              options:
                persistToDirectus: true
      responses:
        "200":
          description: Erfolgreich extrahierte visuelle Entitäten mit Koordinaten und
            Crop-File-IDs
          content:
            application/json:
              example:
                success: true
                detectionsCount: 2
                detections:
                  - name: Kokosnuss
                    type: character
                    box:
                      - 280
                      - 120
                      - 750
                      - 480
                    confidence: 0.96
                    cropFileId: d3e4f5a6-b7c8-9d0e-1f2a-3b4c5d6e7f8a
                    visualTraits:
                      colors:
                        - "#10b981"
                        - "#ef4444"
                      posture: standing
                      expression: cheerful
                  - name: Matilda
                    type: character
                    box:
                      - 410
                      - 490
                      - 890
                      - 780
                    confidence: 0.94
                    cropFileId: e4f5a6b7-c8d9-0e1f-2a3b-4c5d6e7f8a9b
                    visualTraits:
                      colors:
                        - "#78350f"
                      posture: reading
                      expression: attentive
  /api/entities:
    get:
      tags:
        - Entities
      summary: Alle Lore-Entitäten abfragen
      description: >-
        Liefert alle Figuren, Gegenstände und Schauplätze aus Directus mit
        flexiblen Filtern nach Typ, Rolle/Wichtigkeit und Buch.


        #### cURL Beispiel:

        ```bash

        curl -X GET
        "http://localhost:3005/api/entities?type=character&tier=main"

        ```
      operationId: getLoreEntities
      parameters:
        - name: type
          in: query
          schema:
            type: string
            enum:
              - character
              - object
              - location
          example: character
        - name: tier
          in: query
          schema:
            type: string
            enum:
              - main
              - supporting
              - extra
          example: main
        - name: bookId
          in: query
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Array aller passenden Lore-Entitäten
          content:
            application/json:
              example:
                - id: 33333333-4444-5555-6666-777777777777
                  name: Kokosnuss
                  type: character
                  avatar_file_id: a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d
                  static_traits:
                    importance_tier: main
                    frequency_tier: frequent
                    species: Kleiner Drache
                    exact_colors:
                      - "#10b981"
                      - "#ef4444"
                    negative_rules:
                      - Kein Erwachsener
                      - Keine Zähne im Mund
                    relative_scale:
                      anchor_entity: Matilda
                      height_ratio: 1.25
  /api/entities/{id}:
    get:
      tags:
        - Entities
      summary: Vollständigen Steckbrief einer Entität abrufen
      description: >-
        Ruft alle statischen Kanon-Eigenschaften, Farbpaletten, Regeln,
        Beziehungsanker und Vorkommnisse einer Figur ab.


        #### cURL Beispiel:

        ```bash

        curl -X GET
        http://localhost:3005/api/entities/33333333-4444-5555-6666-777777777777

        ```
      operationId: getEntityById
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          example: 33333333-4444-5555-6666-777777777777
      responses:
        "200":
          description: Vollständiges Entitäts-Profil
          content:
            application/json:
              example:
                id: 33333333-4444-5555-6666-777777777777
                name: Kokosnuss
                type: character
                description: Der kleine feuerrote und grüne Drache von der Dracheninsel.
                avatar_file_id: a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d
                static_traits:
                  importance_tier: main
                  species: Drache
                  exact_colors:
                    - "#10b981"
                    - "#ef4444"
                  physical_features: Grüner Schuppenkörper, rote Kappe, kleine Flügel
                  negative_rules:
                    - Kein Raubdrache
                    - Kann anfangs nur schwer fliegen
                  scene_occurrences:
                    - 1
                    - 2
                    - 4
                    - 5
                    - 8
                    - 9
                    - 12
        "404":
          description: Entität nicht gefunden
  /api/entities/{id}/crops:
    get:
      tags:
        - Entities
      summary: Alle visuellen Bildausschnitte einer Figur abrufen
      description: |-
        Gibt alle in Buch-Illustrationen erkannten Segmente und Bounding-Boxes für diese Entität zurück.

        #### cURL Beispiel:
        ```bash
        curl -X GET http://localhost:3005/api/entities/33333333-4444-5555-6666-777777777777/crops
        ```
      operationId: getEntityCrops
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          example: 33333333-4444-5555-6666-777777777777
      responses:
        "200":
          description: Array aller Segmente
          content:
            application/json:
              example:
                entityId: 33333333-4444-5555-6666-777777777777
                name: Kokosnuss
                cropsCount: 5
                crops:
                  - cropFileId: d3e4f5a6-b7c8-9d0e-1f2a-3b4c5d6e7f8a
                    sourceImageId: b1d489f6-3d37-4048-82cc-907abd82bbe1
                    box:
                      - 280
                      - 120
                      - 750
                      - 480
                    sceneNumber: 2
                    chapterNumber: 1
  /api/entities/{id}/tier:
    post:
      tags:
        - Entities
      summary: Rolle/Bedeutung einer Figur dynamisch ändern
      description: |-
        Befördert oder stuft eine Entität um (`main` ➔ Hauptfigur, `supporting` ➔ Nebenrolle, `extra` ➔ Sonstiges). Aktualisiert die Lore-Profile und Filter.

        #### cURL Beispiel:
        ```bash
        curl -X POST http://localhost:3005/api/entities/33333333-4444-5555-6666-777777777777/tier \
          -H "Content-Type: application/json" \
          -d '{"tier": "main"}'
        ```
      operationId: updateEntityTier
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          example: 33333333-4444-5555-6666-777777777777
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - tier
              properties:
                tier:
                  type: string
                  enum:
                    - main
                    - supporting
                    - extra
                    - frequent
                    - moderate
                    - rare
                  example: main
            example:
              tier: main
      responses:
        "200":
          description: Rolle erfolgreich aktualisiert
          content:
            application/json:
              example:
                success: true
                id: 33333333-4444-5555-6666-777777777777
                newTier: main
  /api/entities/{id}/refine-bbox:
    post:
      tags:
        - Entities
      summary: Bounding-Box via Computer Vision verfeinern
      description: |-
        Führt eine Konturanalyse und Hintergrund-Farbprofil-Abtastung durch, um abgeschnittene Gliedmaßen einzuschließen und Boxen an Objektgrenzen einzurasten.

        #### cURL Beispiel:
        ```bash
        curl -X POST http://localhost:3005/api/entities/33333333-4444-5555-6666-777777777777/refine-bbox \
          -H "Content-Type: application/json" \
          -d '{"paddingPercent": 0.05, "snapToEdges": true}'
        ```
      operationId: refineEntityBoundingBox
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          example: 33333333-4444-5555-6666-777777777777
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                paddingPercent:
                  type: number
                  default: 0.05
                  example: 0.05
                snapToEdges:
                  type: boolean
                  default: true
                  example: true
            example:
              paddingPercent: 0.05
              snapToEdges: true
      responses:
        "200":
          description: Bounding-Box erfolgreich verfeinert
          content:
            application/json:
              example:
                success: true
                originalBox:
                  - 310
                  - 150
                  - 710
                  - 460
                refinedBox:
                  - 280
                  - 120
                  - 750
                  - 480
                deltaPromille: 40
  /api/scenes/generate:
    post:
      tags:
        - Scenes
      summary: Narrative Szene generieren (Agentic RAG)
      description: >-
        Generiert eine kanontreue Szene im Schreibstil des Autors unter
        Berücksichtigung aller Figuren-Steckbriefe und bitemporaler
        State-Patches.


        #### cURL Beispiel:

        ```bash

        curl -X POST http://localhost:3005/api/scenes/generate \
          -H "Content-Type: application/json" \
          -d '{
            "bookId": "b1a2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
            "chapterNumber": 2,
            "sceneNumber": 1,
            "title": "Das Floß am Klippenstrand",
            "prompt": "Kokosnuss und Matilda bauen gemeinsam ein Floß aus Treibholz.",
            "saveToDirectus": true
          }'
        ```
      operationId: generateScene
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - bookId
                - prompt
              properties:
                bookId:
                  type: string
                  format: uuid
                chapterId:
                  type: string
                  format: uuid
                chapterNumber:
                  type: integer
                  default: 1
                sceneNumber:
                  type: integer
                  default: 1
                title:
                  type: string
                  example: Das Floß am Klippenstrand
                prompt:
                  type: string
                  example: Kokosnuss und Matilda bauen gemeinsam ein Floß aus Treibholz.
                entityIds:
                  type: array
                  items:
                    type: string
                    format: uuid
                saveToDirectus:
                  type: boolean
                  default: true
            example:
              bookId: b1a2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d
              chapterNumber: 2
              sceneNumber: 1
              title: Das Floß am Klippenstrand
              prompt: Kokosnuss und Matilda bauen gemeinsam ein Floß aus Treibholz.
              saveToDirectus: true
      responses:
        "201":
          description: Szene erfolgreich generiert
          content:
            application/json:
              example:
                success: true
                persistedSceneId: f5a6b7c8-d9e0-1f2a-3b4c-5d6e7f8a9b0c
                sceneText: Die Wellen schlugen sanft gegen das Ufer der Dracheninsel. Kokosnuss
                  zog einen dicken Ast aus dem Sand...
                tokensUsed: 642
                continuityAudit:
                  verdict: compliant
                  score: 100
                  issuesCount: 0
  /api/scenes/audit:
    post:
      tags:
        - Scenes
      summary: Kontinuitäts-Audit für Szenen durchführen
      description: >-
        Prüft einen Textabsatz zeilenweise gegen alle Kanon-Regeln, Magiegesetze
        und Figuren-Traits der Welt.


        #### cURL Beispiel:

        ```bash

        curl -X POST http://localhost:3005/api/scenes/audit \
          -H "Content-Type: application/json" \
          -d '{
            "bookId": "b1a2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
            "sceneText": "Kokosnuss schwamm wie ein Fisch durch die tiefe Strömung und rettete Matilda."
          }'
        ```
      operationId: auditScene
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - bookId
                - sceneText
              properties:
                bookId:
                  type: string
                  format: uuid
                sceneText:
                  type: string
                  minLength: 10
            example:
              bookId: b1a2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d
              sceneText: Kokosnuss schwamm wie ein Fisch durch die tiefe Strömung und rettete
                Matilda.
      responses:
        "200":
          description: Detaillierter Audit-Report
          content:
            application/json:
              example:
                verdict: canon_violation
                score: 45
                summary: "Kanon-Widerspruch entdeckt: Drachen dieser Spezies können laut
                  Regelwerk nicht tauchen."
                issues:
                  - severity: critical
                    category: lore_rule
                    description: Kokosnuss schwimmt unter Wasser, obwohl seine negative Regel 'Kann
                      nicht tauchen' lautet.
                    suggestedCorrection: Lass Kokosnuss stattdessen ein Paddel benutzen oder auf
                      einem Baumstamm balancieren.
  /api/scenes/{id}:
    get:
      tags:
        - Scenes
      summary: Szene per ID abrufen
      description: Gibt den Fließtext, die Kapitelzuordnung und Metadaten einer Szene
        aus Directus zurück.
      operationId: getSceneById
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          example: f5a6b7c8-d9e0-1f2a-3b4c-5d6e7f8a9b0c
      responses:
        "200":
          description: Szenenobjekt
          content:
            application/json:
              example:
                id: f5a6b7c8-d9e0-1f2a-3b4c-5d6e7f8a9b0c
                title: Das Floß am Klippenstrand
                chapter_number: 2
                scene_number: 1
                raw_text: Die Wellen schlugen sanft gegen das Ufer...
  /api/agent/chat:
    post:
      tags:
        - Agent
      summary: Interaktiver Dialog mit dem LoreKeeper ReAct-Agenten
      description: >-
        Führt eine mehrstufige ReAct-Konversation mit dem autonomen Agenten. Der
        Agent kann eigenständig Directus-Datenbankabfragen durchführen und
        kanontreue Antworten formulieren.


        #### cURL Beispiel:

        ```bash

        curl -X POST http://localhost:3005/api/agent/chat \
          -H "Content-Type: application/json" \
          -d '{
            "message": "Welche Farbe hat die Kappe von Kokosnuss und wer sind seine besten Freunde?",
            "bookId": "b1a2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"
          }'
        ```
      operationId: chatWithAgent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - message
              properties:
                message:
                  type: string
                bookId:
                  type: string
                  format: uuid
                conversationId:
                  type: string
            example:
              message: Welche Farbe hat die Kappe von Kokosnuss und wer sind seine besten
                Freunde?
              bookId: b1a2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d
      responses:
        "200":
          description: Antwort des Agenten inklusive genutzter Tools
          content:
            application/json:
              example:
                response: Kokosnuss trägt eine markante feuerrote Mütze (#ef4444). Seine engsten
                  Freunde sind das Stachelschwein Matilda und der Fressdrache
                  Oskar.
                toolsExecuted:
                  - get_entity_lore
                  - get_character_relationships
  /api/agent/tools:
    get:
      tags:
        - Agent
      summary: Verfügbare Agenten-Tools abfragen
      description: Listet alle registrierten Function-Calling Tools auf, die der Agent
        autonom nutzen kann.
      operationId: getAgentTools
      responses:
        "200":
          description: Array aller registrierten Tools mit Parametern
          content:
            application/json:
              example:
                - name: get_entity_lore
                  description: Liest Steckbrief und Traits einer Figur
                - name: find_scene_mentions
                  description: Sucht Szenen, in denen eine Figur auftaucht
  /api/agent/tools/execute:
    post:
      tags:
        - Agent
      summary: Agenten-Tool programmatisch direkt ausführen
      description: Führt ein registriertes Tool direkt aus (z. B. für automatisierte
        Skripte oder Frontend-Komponenten).
      operationId: executeAgentTool
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - toolName
                - arguments
              properties:
                toolName:
                  type: string
                  example: get_entity_lore
                arguments:
                  type: object
                  example:
                    name: Kokosnuss
            example:
              toolName: get_entity_lore
              arguments:
                name: Kokosnuss
      responses:
        "200":
          description: Ergebnis der Tool-Ausführung
          content:
            application/json:
              example:
                success: true
                data:
                  name: Kokosnuss
                  species: Drache
  /api/images/crop:
    post:
      tags:
        - Images
      summary: Verlustfreies Master-Cropping durchführen
      description: >-
        Schneidet anhand relativer Promille-Koordinaten (0..1000) einen
        verlustfreien WebP-Ausschnitt aus dem Original-Masterbild zu.


        #### cURL Beispiel:

        ```bash

        curl -X POST http://localhost:3005/api/images/crop \
          -H "Content-Type: application/json" \
          -d '{
            "imageFileId": "b1d489f6-3d37-4048-82cc-907abd82bbe1",
            "box": [280, 120, 750, 480],
            "format": "webp",
            "quality": 90
          }'
        ```
      operationId: cropImage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - imageFileId
                - box
              properties:
                imageFileId:
                  type: string
                  format: uuid
                box:
                  type: array
                  items:
                    type: integer
                  minItems: 4
                  maxItems: 4
                  example:
                    - 280
                    - 120
                    - 750
                    - 480
                format:
                  type: string
                  enum:
                    - webp
                    - png
                    - jpeg
                  default: webp
                quality:
                  type: integer
                  default: 90
            example:
              imageFileId: b1d489f6-3d37-4048-82cc-907abd82bbe1
              box:
                - 280
                - 120
                - 750
                - 480
              format: webp
              quality: 90
      responses:
        "200":
          description: Crop erfolgreich generiert
          content:
            application/json:
              example:
                success: true
                cropFileId: d3e4f5a6-b7c8-9d0e-1f2a-3b4c5d6e7f8a
                dimensions:
                  width: 360
                  height: 470
                format: webp
  /api/images/generate:
    post:
      tags:
        - Images
      summary: Buch-Illustration generieren
      description: Erzeugt eine neue Illustration im kanonischen Zeichenstil des
        Buches via Imagen 4 oder Gemini Image.
      operationId: generateBookImage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - prompt
              properties:
                prompt:
                  type: string
                  example: Kokosnuss und Matilda am Sandstrand, Aquarell-Stil, warme Töne
                aspectRatio:
                  type: string
                  enum:
                    - 1:1
                    - 4:3
                    - 3:4
                    - 16:9
                  default: 4:3
            example:
              prompt: Kokosnuss und Matilda am Sandstrand, Aquarell-Stil, warme Töne
              aspectRatio: 4:3
      responses:
        "200":
          description: Bild generiert
          content:
            application/json:
              example:
                success: true
                fileId: 9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
  /api/images/re-prompt:
    post:
      tags:
        - Images
      summary: Konsistenten Bild-Prompt synthetisieren
      description: Kombiniert Szenen-Handlung, Figuren-Farben und Buch-Zeichenstil zu
        einem optimalen Diffusion-Prompt.
      operationId: synthesizeImagePrompt
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - sceneDescription
              properties:
                sceneDescription:
                  type: string
                  example: Die Freunde betreten die düstere Höhle
            example:
              sceneDescription: Die Freunde betreten die düstere Höhle
      responses:
        "200":
          description: Synthetisierter Prompt
          content:
            application/json:
              example:
                prompt: watercolor illustration of a small green dragon holding a torch...
  /api/styles/generate-prompt:
    post:
      tags:
        - Styles
      summary: Stil-Prompt generieren
      description: Wandelt eine Stil-Beschreibung in kanonische Prompt-Snippets für
        Text- und Bildgeneratoren um.
      operationId: generateStylePrompt
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - styleName
              properties:
                styleName:
                  type: string
                  example: Klassisches Aquarell-Kinderbuch
            example:
              styleName: Klassisches Aquarell-Kinderbuch
      responses:
        "200":
          description: Generierter Stil-Snippet
          content:
            application/json:
              example:
                styleSnippet: delicate pencil linework with soft watercolor washes, gentle paper
                  grain
  /api/styles/apply:
    post:
      tags:
        - Styles
      summary: Stilvorlage auf Text anwenden
      description: Überarbeitet einen bestehenden Textabsatz im Tonfall der gewählten
        Stilvorlage.
      operationId: applyStyleToText
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - text
                - styleId
              properties:
                text:
                  type: string
                  example: Er ging schnell durch den Wald.
                styleId:
                  type: string
                  format: uuid
            example:
              text: Er ging schnell durch den Wald.
              styleId: e1f2a3b4-c5d6-7e8f-9a0b-1c2d3e4f5a6b
      responses:
        "200":
          description: Umgewandelter Text
          content:
            application/json:
              example:
                transformedText: Huschenden Schrittes durchquerte er das schattige Dickicht...
  /health:
    get:
      tags:
        - System
      summary: System-Healthcheck abfragen
      description: >-
        Prüft die Erreichbarkeit und Latenz von Fastify, Directus CMS und dem
        LiteLLM Proxy.


        #### cURL Beispiel:

        ```bash

        curl -X GET http://localhost:3005/health

        ```
      operationId: getHealthStatus
      responses:
        "200":
          description: Alle Dienste gesund
          content:
            application/json:
              example:
                status: ok
                timestamp: 2026-09-21T22:35:00.000Z
                services:
                  directus: healthy
                  litellm: healthy
                  keydb: healthy
components:
  schemas:
    GenerateSceneInput:
      type: object
      required:
        - bookId
        - prompt
      properties:
        bookId:
          type: string
          format: uuid
          description: UUID des Zielbuchs in Directus
        chapterId:
          type: string
          format: uuid
          description: Optional UUID des Kapitels, dem die Szene zugeordnet wird
        chapterNumber:
          type: integer
          minimum: 1
          description: Kapitelnummer (wird genutzt, falls chapterId nicht angegeben ist)
        sceneNumber:
          type: integer
          minimum: 1
          description: Szenennummer innerhalb des Kapitels
        title:
          type: string
          description: Vorgeschlagener Arbeitstitel der Szene
        prompt:
          type: string
          minLength: 3
          description: Narrative Zielsetzung, Handlung und Ereignisse der Szene
        entityIds:
          type: array
          items:
            type: string
            format: uuid
          description: Optionale UUIDs explizit zu ladender Lore-Entitäten
        saveToDirectus:
          type: boolean
          default: true
          description: Ob die generierte Szene direkt in der `scenes`-Tabelle persistiert
            werden soll
    SceneGenerationResult:
      type: object
      required:
        - sceneTitle
        - summary
        - content
        - consistencyNotes
        - retrievedContext
      properties:
        sceneTitle:
          type: string
          example: Der Schwur im Schattenwald
        summary:
          type: string
          example: Vaelin empfängt brisante Informationen über den feindlichen
            Truppenaufmarsch.
        content:
          type: string
          description: Vollständiger generierter Prosatext der Szene
        consistencyNotes:
          type: string
          description: Erklärung des Writer-Agenten, wie Weltregeln und Charaktertraits
            gewahrt wurden
        retrievedContext:
          type: object
          properties:
            charactersCount:
              type: integer
            locationsCount:
              type: integer
            factionsCount:
              type: integer
            rulesAndObjectsCount:
              type: integer
            loreStatesCount:
              type: integer
        persistedSceneId:
          type: string
          format: uuid
          description: UUID des erstellten Datensatzes in Directus
    AuditSceneInput:
      type: object
      required:
        - bookId
        - sceneText
      properties:
        bookId:
          type: string
          format: uuid
          description: UUID des Buchs, dessen Kanon als Prüfgrundlage dient
        chapterId:
          type: string
          format: uuid
          description: Optional UUID des Kapitels für kontextspezifische Validierung
        sceneTitle:
          type: string
          description: Titel der zu prüfenden Szene
        sceneText:
          type: string
          minLength: 10
          description: Der zu auditierende Fließtext
    AuditIssue:
      type: object
      required:
        - severity
        - category
        - description
        - suggestedCorrection
      properties:
        severity:
          type: string
          enum:
            - info
            - warning
            - critical
        category:
          type: string
          enum:
            - character_voice
            - lore_rule
            - timeline
            - geography
            - magic_or_tech
        description:
          type: string
          description: Genaue Beschreibung des Kontinuitätsbruchs
        conflictingLoreReference:
          type: string
          description: Zitat oder Verweis auf die widersprüchliche Kanon-Regel
        suggestedCorrection:
          type: string
          description: Konkrete Handlungsanweisung zur Behebung
    AuditReport:
      type: object
      required:
        - verdict
        - score
        - summary
        - issues
      properties:
        verdict:
          type: string
          enum:
            - compliant
            - minor_discrepancies
            - canon_violation
        score:
          type: number
          minimum: 0
          maximum: 100
          description: Kanon-Konformitäts-Score (100 = absolut fehlerfrei)
        summary:
          type: string
          description: Gesamteinschätzung des Auditors
        issues:
          type: array
          items:
            $ref: "#/components/schemas/AuditIssue"
    Scene:
      type: object
      required:
        - id
        - chapter_id
        - scene_number
      properties:
        id:
          type: string
          format: uuid
        chapter_id:
          type: string
          format: uuid
        scene_number:
          type: integer
        title:
          type: string
          nullable: true
        raw_text:
          type: string
          nullable: true
        generated_image_id:
          type: string
          format: uuid
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    HealthStatus:
      type: object
      required:
        - status
        - service
        - environment
        - timestamp
        - models
        - directus
      properties:
        status:
          type: string
          example: ok
        service:
          type: string
          example: lorekeeper-service
        environment:
          type: string
          example: development
        timestamp:
          type: string
          format: date-time
        models:
          type: object
          properties:
            router:
              type: string
              example: router-model
            writer:
              type: string
              example: writer-model
        directus:
          type: object
          properties:
            url:
              type: string
              example: https://laefnabeiafnaleiafafe.2.28.78.110.sslip.io
            status:
              type: string
              example: connected
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: string
        message:
          type: string
        details:
          type: object
    AgentChatInput:
      type: object
      required:
        - bookId
        - messages
      properties:
        bookId:
          type: string
          format: uuid
          example: 11111111-2222-3333-4444-555555555555
        messages:
          type: array
          items:
            $ref: "#/components/schemas/ChatMessage"
        contextChapterId:
          type: string
          format: uuid
        maxIterations:
          type: integer
          minimum: 1
          maximum: 15
          default: 6
    ChatMessage:
      type: object
      required:
        - role
        - content
      properties:
        role:
          type: string
          enum:
            - system
            - user
            - assistant
        content:
          type: string
    AgentChatResponse:
      type: object
      required:
        - success
        - data
      properties:
        success:
          type: boolean
          example: true
        data:
          $ref: "#/components/schemas/AgentChatResult"
    AgentChatResult:
      type: object
      required:
        - reply
        - toolCallsExecuted
        - thoughtProcess
      properties:
        reply:
          type: string
        toolCallsExecuted:
          type: array
          items:
            $ref: "#/components/schemas/ToolExecutionRecord"
        thoughtProcess:
          type: array
          items:
            type: string
    ToolExecutionRecord:
      type: object
      required:
        - name
        - arguments
        - result
      properties:
        name:
          type: string
          example: search_lore
        arguments:
          type: object
        result:
          description: Das von Directus oder der internen Logik zurückgegebene Ergebnis
    AgentToolsResponse:
      type: object
      required:
        - success
        - count
        - tools
      properties:
        success:
          type: boolean
          example: true
        count:
          type: integer
          example: 6
        tools:
          type: array
          items:
            type: object
tags:
  - name: Books
    description: Buch-Ingestion, Parsing von EPUB/PDF, Status, Kapitelzerlegung und Export
  - name: Entities
    description: Visuelle Extraktion, Bounding-Box Refinement, Lore-Steckbriefe und
      Rollen-Promotion
  - name: Scenes
    description: Agentic RAG Szenen-Generierung und Kontinuitäts-Audit gegen Weltenregeln
  - name: Agent
    description: Autonomer ReAct-Agent, Konversation und dynamische Directus Tool-Ausführung
  - name: Images
    description: Verlustfreies Master-Cropping, Bild-Generierung und Stil-Synthese
  - name: Styles
    description: Modulare Stilvorlagen für Text, Bild und Audio
  - name: System
    description: Healthchecks, Statusüberwachung und Konfigurationsprüfungen
