feat(ai): add ADR-036 unified OCR architecture and frontend test coverage
- Add ADR-036 unified OCR architecture (typhoon-ocr via Ollama) - Extend AI execution profiles for OCR sandbox configuration - Add comprehensive frontend test coverage (components, hooks, services) - Add backend test coverage for document-numbering services - Update OCR sidecar with typhoon-ocr integration - Add AI policy service and execution profile management - Update AGENTS.md and architecture documentation
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
// File: backend/src/modules/ai/entities/ai-execution-profile.entity.ts
|
||||
// Change Log:
|
||||
// - 2026-06-11: Initial creation of AiExecutionProfile entity for AI execution profiles
|
||||
// - 2026-06-13: ADR-036 — เพิ่ม canonicalModel และรองรับ nullable OCR params
|
||||
|
||||
import {
|
||||
Column,
|
||||
@@ -19,17 +20,20 @@ export class AiExecutionProfile {
|
||||
@Column({ name: 'profile_name', unique: true, length: 50 })
|
||||
profileName!: string;
|
||||
|
||||
@Column({ name: 'canonical_model', length: 20, default: 'np-dms-ai' })
|
||||
canonicalModel!: 'np-dms-ai' | 'np-dms-ocr';
|
||||
|
||||
@Column({ type: 'decimal', precision: 4, scale: 3 })
|
||||
temperature!: number;
|
||||
|
||||
@Column({ name: 'top_p', type: 'decimal', precision: 4, scale: 3 })
|
||||
topP!: number;
|
||||
|
||||
@Column({ name: 'max_tokens', type: 'int' })
|
||||
maxTokens!: number;
|
||||
@Column({ name: 'max_tokens', type: 'int', nullable: true })
|
||||
maxTokens!: number | null;
|
||||
|
||||
@Column({ name: 'num_ctx', type: 'int' })
|
||||
numCtx!: number;
|
||||
@Column({ name: 'num_ctx', type: 'int', nullable: true })
|
||||
numCtx!: number | null;
|
||||
|
||||
@Column({ name: 'repeat_penalty', type: 'decimal', precision: 5, scale: 3 })
|
||||
repeatPenalty!: number;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// File: backend/src/modules/ai/entities/ai-sandbox-profile.entity.ts
|
||||
// Change Log:
|
||||
// - 2026-06-13: ADR-036 — เพิ่ม sandbox draft profile entity สำหรับ AI parameter tuning
|
||||
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
/** Entity สำหรับเก็บ draft parameters ที่ admin ทดลองก่อน Apply to Production */
|
||||
@Entity('ai_sandbox_profiles')
|
||||
export class AiSandboxProfile {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
|
||||
@Column({ name: 'profile_name', unique: true, length: 50 })
|
||||
profileName!: string;
|
||||
|
||||
@Column({ name: 'canonical_model', length: 20, default: 'np-dms-ai' })
|
||||
canonicalModel!: 'np-dms-ai' | 'np-dms-ocr';
|
||||
|
||||
@Column({ type: 'decimal', precision: 4, scale: 3 })
|
||||
temperature!: number;
|
||||
|
||||
@Column({ name: 'top_p', type: 'decimal', precision: 4, scale: 3 })
|
||||
topP!: number;
|
||||
|
||||
@Column({ name: 'max_tokens', type: 'int', nullable: true })
|
||||
maxTokens!: number | null;
|
||||
|
||||
@Column({ name: 'num_ctx', type: 'int', nullable: true })
|
||||
numCtx!: number | null;
|
||||
|
||||
@Column({ name: 'repeat_penalty', type: 'decimal', precision: 5, scale: 3 })
|
||||
repeatPenalty!: number;
|
||||
|
||||
@Column({ name: 'keep_alive_seconds', type: 'int' })
|
||||
keepAliveSeconds!: number;
|
||||
|
||||
@Column({ name: 'updated_by', type: 'int', nullable: true })
|
||||
updatedBy?: number | null;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt!: Date;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at' })
|
||||
updatedAt!: Date;
|
||||
}
|
||||
Reference in New Issue
Block a user