feat(ai-runtime): complete ai runtime policy refactor (ADR-035)
This commit is contained in:
@@ -56,9 +56,16 @@ function normalizeLoadedModels(value: unknown): VramLoadedModelView[] {
|
||||
}
|
||||
return value.map((item, index) => {
|
||||
if (typeof item === 'string') {
|
||||
const name = item.toLowerCase();
|
||||
let normName = item;
|
||||
if (name.includes('ocr') || name.includes('typhoon-np-dms-ocr')) {
|
||||
normName = 'np-dms-ocr';
|
||||
} else if (name.includes('typhoon') || name.includes('np-dms-ai')) {
|
||||
normName = 'np-dms-ai';
|
||||
}
|
||||
return {
|
||||
modelId: `${item}-${index}`,
|
||||
modelName: item,
|
||||
modelName: normName,
|
||||
};
|
||||
}
|
||||
if (item && typeof item === 'object') {
|
||||
@@ -68,10 +75,17 @@ function normalizeLoadedModels(value: unknown): VramLoadedModelView[] {
|
||||
name?: string;
|
||||
vramUsageMB?: number;
|
||||
};
|
||||
const modelName = model.modelName ?? model.name ?? `model-${index + 1}`;
|
||||
const rawName = model.modelName ?? model.name ?? `model-${index + 1}`;
|
||||
const name = rawName.toLowerCase();
|
||||
let normName = rawName;
|
||||
if (name.includes('ocr') || name.includes('typhoon-np-dms-ocr')) {
|
||||
normName = 'np-dms-ocr';
|
||||
} else if (name.includes('typhoon') || name.includes('np-dms-ai')) {
|
||||
normName = 'np-dms-ai';
|
||||
}
|
||||
return {
|
||||
modelId: model.modelId ?? modelName,
|
||||
modelName,
|
||||
modelId: model.modelId ?? rawName,
|
||||
modelName: normName,
|
||||
vramUsageMB: model.vramUsageMB,
|
||||
};
|
||||
}
|
||||
@@ -122,7 +136,13 @@ export default function AiAdminConsolePage() {
|
||||
return res as SandboxProject[];
|
||||
},
|
||||
});
|
||||
const healthOllamaModels = ensureArray<string>(health?.ollama?.models);
|
||||
const rawHealthOllamaModels = ensureArray<string>(health?.ollama?.models);
|
||||
const healthOllamaModels = Array.from(new Set(rawHealthOllamaModels.map((m) => {
|
||||
const name = m.toLowerCase();
|
||||
if (name.includes('ocr') || name.includes('typhoon-np-dms-ocr')) return 'np-dms-ocr';
|
||||
if (name.includes('typhoon') || name.includes('np-dms-ai')) return 'np-dms-ai';
|
||||
return m;
|
||||
})));
|
||||
const healthQdrantCollections = ensureArray<string>(health?.qdrant?.collections);
|
||||
const vramLoadedModels = normalizeLoadedModels(vramStatus?.loadedModels);
|
||||
const sandboxProjects = ensureArray<SandboxProject>(projects);
|
||||
|
||||
@@ -592,7 +592,7 @@ export default function OcrSandboxPromptManager() {
|
||||
</CardTitle>
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{ocrResult.engineUsed === 'typhoon-np-dms-ocr'
|
||||
? 'Typhoon OCR'
|
||||
? 'np-dms-ocr'
|
||||
: ocrResult.ocrUsed
|
||||
? 'Tesseract'
|
||||
: 'Fast Path (Text Layer)'}
|
||||
@@ -601,7 +601,7 @@ export default function OcrSandboxPromptManager() {
|
||||
<CardContent className="pt-4">
|
||||
{ocrResult.fallbackUsed && (
|
||||
<div className="mb-3 rounded-md border border-amber-500/20 bg-amber-500/5 px-3 py-2 text-xs text-amber-600 dark:text-amber-400">
|
||||
Typhoon OCR unavailable. Fallback to Tesseract was used for this run.
|
||||
np-dms-ocr unavailable. Fallback to Tesseract was used for this run.
|
||||
</div>
|
||||
)}
|
||||
<div className="relative rounded-md bg-muted p-4 font-mono text-xs overflow-auto max-h-[200px] border border-border/10">
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// - 2026-06-02: normalize VRAM response ให้รองรับ field names จาก backend ปัจจุบันและรูปแบบ loadedModels แบบเดิม
|
||||
|
||||
import api from '../api/client';
|
||||
import { AiJobResponse } from '../../types/ai';
|
||||
|
||||
export interface AiAdminSettings {
|
||||
aiFeaturesEnabled: boolean;
|
||||
@@ -315,6 +316,23 @@ export const adminAiService = {
|
||||
const { data } = await api.post(`/ai/ocr-engines/${encodeURIComponent(engineId)}/select`, {});
|
||||
return extractData<{ activeEngineName: string }>(data);
|
||||
},
|
||||
|
||||
submitAiJob: async (
|
||||
type: string,
|
||||
documentPublicId?: string,
|
||||
attachmentPublicId?: string,
|
||||
payload?: Record<string, unknown>,
|
||||
projectPublicId?: string
|
||||
): Promise<AiJobResponse> => {
|
||||
const { data } = await api.post('/ai/jobs', {
|
||||
type,
|
||||
documentPublicId,
|
||||
attachmentPublicId,
|
||||
payload,
|
||||
projectPublicId,
|
||||
});
|
||||
return extractData<AiJobResponse>(data);
|
||||
},
|
||||
};
|
||||
|
||||
export interface OcrEngineResponse {
|
||||
|
||||
@@ -44,5 +44,14 @@
|
||||
"delete_confirm": "Delete this pattern?",
|
||||
"loading": "Loading...",
|
||||
"not_found": "Intent not found"
|
||||
},
|
||||
"ai_runtime_policy": {
|
||||
"error_model_key_forbidden": "model.key is not allowed. The system selects the model automatically.",
|
||||
"error_execution_profile_forbidden": "executionProfile is not allowed in the request payload.",
|
||||
"error_temperature_forbidden": "temperature override is not allowed. Runtime parameters are managed by policy.",
|
||||
"error_top_p_forbidden": "top_p override is not allowed. Runtime parameters are managed by policy.",
|
||||
"error_max_tokens_forbidden": "maxTokens override is not allowed. Runtime parameters are managed by policy.",
|
||||
"error_cpu_timeout": "Retrieval operation timed out on CPU fallback. Please retry later.",
|
||||
"error_large_context_unauthorized": "The large-context profile requires administrator privileges."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,14 @@
|
||||
"processing": "กำลังประมวลผลด้วย Typhoon LLM...",
|
||||
"error_vram": "VRAM ไม่เพียงพอสำหรับโหลดโมเดล Typhoon LLM",
|
||||
"error_timeout": "หมดเวลาการประมวลผล LLM (120 วินาที)"
|
||||
},
|
||||
"ai_runtime_policy": {
|
||||
"error_model_key_forbidden": "ไม่อนุญาตให้ระบุ model.key ระบบจะเลือกโมเดลให้อัตโนมัติ",
|
||||
"error_execution_profile_forbidden": "ไม่อนุญาตให้ระบุ executionProfile ใน payload",
|
||||
"error_temperature_forbidden": "ไม่อนุญาตให้ override ค่า temperature พารามิเตอร์ถูกควบคุมโดย Runtime Policy",
|
||||
"error_top_p_forbidden": "ไม่อนุญาตให้ override ค่า top_p พารามิเตอร์ถูกควบคุมโดย Runtime Policy",
|
||||
"error_max_tokens_forbidden": "ไม่อนุญาตให้ override ค่า maxTokens พารามิเตอร์ถูกควบคุมโดย Runtime Policy",
|
||||
"error_cpu_timeout": "การดึงข้อมูลหมดเวลาขณะใช้ CPU fallback กรุณาลองใหม่อีกครั้ง",
|
||||
"error_large_context_unauthorized": "Profile large-context ต้องการสิทธิ์ผู้ดูแลระบบ"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,3 +74,13 @@ export interface AiPaginatedResult<T> {
|
||||
limit: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
export type ExecutionProfile = 'interactive' | 'standard' | 'quality' | 'deep-analysis';
|
||||
|
||||
export interface AiJobResponse {
|
||||
jobId: string;
|
||||
status: 'queued' | 'completed' | 'failed';
|
||||
modelUsed: 'np-dms-ai' | 'np-dms-ocr';
|
||||
effectiveProfile: ExecutionProfile;
|
||||
queueName: 'ai-realtime' | 'ai-batch';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user