feat(ai): unify AI architecture, implement RAG and legacy migration
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
openapi: "3.1.0"
|
||||
info:
|
||||
title: AI Jobs API
|
||||
version: "1.0.0"
|
||||
description: BullMQ-based AI job submission endpoints (ADR-023A)
|
||||
|
||||
paths:
|
||||
/api/ai/suggest:
|
||||
post:
|
||||
summary: Queue AI Suggestion job (ai-realtime)
|
||||
description: |
|
||||
Triggered internally after document commit.
|
||||
Queues ai-suggest job to extract metadata from PDF (max 3 pages).
|
||||
Returns jobId for polling.
|
||||
security:
|
||||
- BearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AiSuggestRequest'
|
||||
responses:
|
||||
"202":
|
||||
description: Job queued
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/JobQueuedResponse'
|
||||
"400":
|
||||
$ref: '#/components/responses/ValidationError'
|
||||
"503":
|
||||
description: AI Service unavailable (Desk-5439 offline) — document saved, AI skipped
|
||||
|
||||
/api/ai/jobs/{jobId}/status:
|
||||
get:
|
||||
summary: Poll job status
|
||||
parameters:
|
||||
- name: jobId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: Job status
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/JobStatusResponse'
|
||||
|
||||
/api/ai/rag/query:
|
||||
post:
|
||||
summary: RAG Q&A query (ai-realtime)
|
||||
description: |
|
||||
Queues rag-query job. Results returned via polling or WebSocket event.
|
||||
projectPublicId REQUIRED — enforces multi-tenant isolation.
|
||||
security:
|
||||
- BearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RagQueryRequest'
|
||||
responses:
|
||||
"202":
|
||||
description: Job queued
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/JobQueuedResponse'
|
||||
|
||||
/api/ai/embed:
|
||||
post:
|
||||
summary: Queue embed-document job (ai-batch)
|
||||
description: |
|
||||
Triggered internally after document commit (parallel with ai-suggest).
|
||||
Full-document chunked embedding — NOT limited to 3 pages.
|
||||
security:
|
||||
- BearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EmbedDocumentRequest'
|
||||
responses:
|
||||
"202":
|
||||
$ref: '#/components/schemas/JobQueuedResponse'
|
||||
|
||||
components:
|
||||
schemas:
|
||||
AiSuggestRequest:
|
||||
type: object
|
||||
required: [documentPublicId, projectPublicId, idempotencyKey]
|
||||
properties:
|
||||
documentPublicId:
|
||||
type: string
|
||||
format: uuid
|
||||
description: UUIDv7 of the committed document
|
||||
projectPublicId:
|
||||
type: string
|
||||
format: uuid
|
||||
description: UUIDv7 of the project — REQUIRED for multi-tenancy
|
||||
idempotencyKey:
|
||||
type: string
|
||||
description: Prevents duplicate AI job on retry
|
||||
|
||||
RagQueryRequest:
|
||||
type: object
|
||||
required: [projectPublicId, question]
|
||||
properties:
|
||||
projectPublicId:
|
||||
type: string
|
||||
format: uuid
|
||||
description: UUIDv7 — limits search to this project only
|
||||
question:
|
||||
type: string
|
||||
maxLength: 500
|
||||
description: Natural language question (Thai or English)
|
||||
topK:
|
||||
type: integer
|
||||
default: 5
|
||||
minimum: 1
|
||||
maximum: 20
|
||||
|
||||
EmbedDocumentRequest:
|
||||
type: object
|
||||
required: [documentPublicId, projectPublicId, idempotencyKey]
|
||||
properties:
|
||||
documentPublicId:
|
||||
type: string
|
||||
format: uuid
|
||||
projectPublicId:
|
||||
type: string
|
||||
format: uuid
|
||||
idempotencyKey:
|
||||
type: string
|
||||
|
||||
JobQueuedResponse:
|
||||
type: object
|
||||
properties:
|
||||
jobId:
|
||||
type: string
|
||||
queue:
|
||||
type: string
|
||||
enum: [ai-realtime, ai-batch]
|
||||
estimatedWaitSecs:
|
||||
type: integer
|
||||
|
||||
JobStatusResponse:
|
||||
type: object
|
||||
properties:
|
||||
jobId:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
enum: [waiting, active, completed, failed]
|
||||
result:
|
||||
type: object
|
||||
nullable: true
|
||||
description: AI suggestion payload when completed
|
||||
|
||||
responses:
|
||||
ValidationError:
|
||||
description: Validation failed (class-validator)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
statusCode:
|
||||
type: integer
|
||||
message:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
securitySchemes:
|
||||
BearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
@@ -0,0 +1,206 @@
|
||||
openapi: "3.1.0"
|
||||
info:
|
||||
title: Migration Queue API
|
||||
version: "1.0.0"
|
||||
description: Legacy Document Migration pipeline endpoints (ADR-023A)
|
||||
|
||||
paths:
|
||||
/api/ai/migration/queue:
|
||||
post:
|
||||
summary: Submit legacy document for AI processing (n8n → DMS API)
|
||||
description: |
|
||||
Called by n8n during Legacy Migration Phase.
|
||||
Queues OCR + extract-metadata jobs (ai-batch).
|
||||
Result stored in migration_review_queue (status=PENDING).
|
||||
Requires Idempotency-Key header to prevent duplicates.
|
||||
security:
|
||||
- BearerAuth: []
|
||||
parameters:
|
||||
- name: Idempotency-Key
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
example: "SD-001-2026:batch-001"
|
||||
description: Format — <doc_number>:<batch_id>
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MigrationQueueRequest'
|
||||
responses:
|
||||
"202":
|
||||
description: Job queued for AI processing
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MigrationQueuedResponse'
|
||||
"409":
|
||||
description: Duplicate — idempotency_key already exists
|
||||
|
||||
get:
|
||||
summary: List migration_review_queue (Admin only)
|
||||
security:
|
||||
- BearerAuth: []
|
||||
parameters:
|
||||
- name: status
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
enum: [PENDING, IMPORTED, REJECTED]
|
||||
- name: batchId
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
- name: page
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 1
|
||||
- name: limit
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 20
|
||||
responses:
|
||||
"200":
|
||||
description: Paginated queue list
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MigrationQueueListResponse'
|
||||
|
||||
/api/ai/migration/queue/{publicId}/approve:
|
||||
post:
|
||||
summary: Admin approves migration item → imports document
|
||||
description: |
|
||||
Imports document from temp path to DMS.
|
||||
Automatically queues embed-document job after import.
|
||||
security:
|
||||
- BearerAuth: []
|
||||
parameters:
|
||||
- name: publicId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
"200":
|
||||
description: Document imported, embed job queued
|
||||
"400":
|
||||
description: Item not in PENDING status
|
||||
"404":
|
||||
description: Queue item not found
|
||||
|
||||
/api/ai/migration/queue/{publicId}/reject:
|
||||
post:
|
||||
summary: Admin rejects migration item
|
||||
security:
|
||||
- BearerAuth: []
|
||||
parameters:
|
||||
- name: publicId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [reason]
|
||||
properties:
|
||||
reason:
|
||||
type: string
|
||||
maxLength: 500
|
||||
responses:
|
||||
"200":
|
||||
description: Item rejected
|
||||
"400":
|
||||
description: Item not in PENDING status
|
||||
|
||||
components:
|
||||
schemas:
|
||||
MigrationQueueRequest:
|
||||
type: object
|
||||
required: [batchId, filename, tempPath]
|
||||
properties:
|
||||
batchId:
|
||||
type: string
|
||||
description: n8n batch identifier
|
||||
filename:
|
||||
type: string
|
||||
tempPath:
|
||||
type: string
|
||||
description: Absolute path in temp storage on server
|
||||
|
||||
MigrationQueuedResponse:
|
||||
type: object
|
||||
properties:
|
||||
publicId:
|
||||
type: string
|
||||
format: uuid
|
||||
status:
|
||||
type: string
|
||||
enum: [PENDING]
|
||||
jobId:
|
||||
type: string
|
||||
description: BullMQ job ID for tracking
|
||||
|
||||
MigrationQueueListResponse:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/MigrationQueueItem'
|
||||
total:
|
||||
type: integer
|
||||
page:
|
||||
type: integer
|
||||
limit:
|
||||
type: integer
|
||||
|
||||
MigrationQueueItem:
|
||||
type: object
|
||||
properties:
|
||||
publicId:
|
||||
type: string
|
||||
format: uuid
|
||||
batchId:
|
||||
type: string
|
||||
originalFilename:
|
||||
type: string
|
||||
aiMetadata:
|
||||
type: object
|
||||
description: AI-extracted metadata suggestion
|
||||
confidenceScore:
|
||||
type: number
|
||||
format: float
|
||||
minimum: 0
|
||||
maximum: 1
|
||||
ocrUsed:
|
||||
type: boolean
|
||||
status:
|
||||
type: string
|
||||
enum: [PENDING, IMPORTED, REJECTED]
|
||||
reviewedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
nullable: true
|
||||
rejectionReason:
|
||||
type: string
|
||||
nullable: true
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
securitySchemes:
|
||||
BearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
Reference in New Issue
Block a user