690619:1128 240 #02
CI / CD Pipeline / build (push) Successful in 7m30s
CI / CD Pipeline / deploy (push) Successful in 14m25s

This commit is contained in:
2026-06-19 11:28:59 +07:00
parent 5f29fdbe8c
commit 12e230332e
3 changed files with 120 additions and 80 deletions
@@ -138,10 +138,31 @@ export class VramMonitorService {
/**
* ตรวจสอบว่า VRAM เพียงพอสำหรับความต้องการโหลดโมเดลหรือไม่
* ถ้าไม่มีโมเดลโหลดอยู่เลย จะอนุญาตให้โหลดโมเดลใหม่ได้เสมอ (ป้องกัน false positive)
*/
async hasVramCapacity(requiredMb: number): Promise<boolean> {
const headroom = await this.getVramHeadroom();
return headroom.availableMb >= requiredMb;
// ถ้าไม่มีโมเดลโหลดอยู่เลย อนุญาตให้โหลดโมเดลใหม่ได้เสมอ
if (headroom.usedMb === 0 && headroom.querySuccess) {
this.logger.log(
`No models loaded in VRAM, allowing model load (required=${requiredMb}MB)`
);
return true;
}
// ถ้า query ล้มเหลว ใช้ optimistic fallback (assume no VRAM used)
if (!headroom.querySuccess) {
this.logger.log(
`VRAM query failed, using optimistic fallback (required=${requiredMb}MB)`
);
return true;
}
const hasCapacity = headroom.availableMb >= requiredMb;
if (!hasCapacity) {
this.logger.warn(
`VRAM insufficient: available=${headroom.availableMb}MB, required=${requiredMb}MB`
);
}
return hasCapacity;
}
/**
@@ -100,6 +100,22 @@ describe('VramMonitorService', () => {
const result = await service.hasVramCapacity(3000); // query available is 2048MB, required 3000MB
expect(result).toBe(false);
});
it('ควรคืน true เมื่อไม่มีโมเดลโหลดอยู่เลย (ป้องกัน false positive)', async () => {
mockedAxios.get.mockResolvedValue({
data: {
models: [], // ไม่มีโมเดลโหลด
},
});
const result = await service.hasVramCapacity(5000); // ต้องการ 5GB แม้ availableMb = 8192MB
expect(result).toBe(true);
});
it('ควรคืน true เมื่อ query ล้มเหลว (optimistic fallback)', async () => {
mockedAxios.get.mockRejectedValue(new Error('Connection timeout'));
const result = await service.hasVramCapacity(5000);
expect(result).toBe(true);
});
});
describe('getVramStatus', () => {
it('ควรคืน status ที่ถูกต้องเมื่อ Ollama คืน models', async () => {
+9 -6
View File
@@ -615,6 +615,14 @@ export default function AiAdminConsolePage() {
</div>
</div>
<Tabs defaultValue="overview" className="w-full space-y-6">
<TabsList className="grid w-full grid-cols-3 max-w-[500px]">
<TabsTrigger value="overview">System Toggle</TabsTrigger>
<TabsTrigger value="playground">RAG Playground</TabsTrigger>
<TabsTrigger value="sandbox">3-Step Pipeline Sandbox</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
@@ -694,12 +702,7 @@ export default function AiAdminConsolePage() {
</CardContent>
</Card>
</div>
<Tabs defaultValue="overview" className="w-full space-y-6">
<TabsList className="grid w-full grid-cols-3 max-w-[500px]">
<TabsTrigger value="overview">System Toggle</TabsTrigger>
<TabsTrigger value="playground">RAG Playground</TabsTrigger>
<TabsTrigger value="sandbox">3-Step Pipeline Sandbox</TabsTrigger>
</TabsList>
</TabsContent>
<TabsContent value="playground" className="space-y-6">
<Card className="border border-border/50 bg-background/50 backdrop-blur-md">