test_workflow
Some checks failed
Some checks failed
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
# Stage 1: Build
|
||||
FROM node:26-bookworm-slim AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files and install scripts first (better cache)
|
||||
COPY package.json package-lock.json* ./
|
||||
COPY scripts/ ./scripts/
|
||||
|
||||
RUN npm ci
|
||||
|
||||
# Copy source and build
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Runtime (nginx)
|
||||
FROM nginx:alpine
|
||||
|
||||
# Remove default nginx static files
|
||||
RUN rm -rf /usr/share/nginx/html/*
|
||||
|
||||
# Copy built assets from builder
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
# Custom nginx config for SPA routing + caching
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -1,13 +1,25 @@
|
||||
# Frontend application configuration
|
||||
# This is the single source of truth for frontend feature flags and URLs.
|
||||
# Edit this file directly instead of using .env / .env.development.
|
||||
#
|
||||
# Deployment note: the backend servers (main.py, cv_inference_server.py) enforce
|
||||
# CORS via the CORS_ORIGINS environment variable. Make sure the origins listed here
|
||||
# (especially the production frontend origin) are included in that env var.
|
||||
|
||||
VITE_USE_BACKEND_SEGMENTATION: "true"
|
||||
VITE_SEGMENT_API_BASE: ""
|
||||
# Leave empty in dev — Vite proxy handles /api/* → backend.
|
||||
# Set to your production API origin (e.g. "https://api.your-domain.com") for builds.
|
||||
VITE_API_BASE_URL: "https://api.lumina-msk.io.vn"
|
||||
# Leave empty in dev — Vite proxy handles /api/test/* → CV inference server.
|
||||
# Set to your production CV inference origin (e.g. "https://cv.your-domain.com") for builds.
|
||||
VITE_SEGMENT_API_BASE: "https://cv.lumina-msk.io.vn"
|
||||
# Comma-separated list of origins the frontend may be served from.
|
||||
# Used by deployment scripts to populate backend CORS_ORIGINS env var.
|
||||
VITE_ALLOWED_ORIGINS: "https://lumina-msk.io.vn,https://www.lumina-msk.io.vn"
|
||||
VITE_USE_CV_CELERY: "false"
|
||||
VITE_API_BASE_URL: ""
|
||||
VITE_CLINICAL_CHAT_USE_LLM: "true"
|
||||
VITE_CLINICAL_CHAT_MOCK_TOOLS: "true"
|
||||
VITE_OLLAMA_CHAT_URL: "/api/ollama-chat/api/chat"
|
||||
VITE_OLLAMA_MODEL: "gemma4:e4b"
|
||||
VITE_MODAL_OLLAMA_TARGET: "https://dtj-tran--ollama-gemma4-e4b-ollamaserver-web.modal.run"
|
||||
VITE_DISTRIBUTE_DIRRECTORY: "workspace/sprint_1_2/CODEBASE/frontend/distribute_asset"
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
import React, { Suspense } from 'react';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
import { PatientStoreProvider } from './lib/patientStore';
|
||||
import PatientWorklistPage from './pages/PatientWorklistPage';
|
||||
import ClinicalWorkspacePage from './pages/ClinicalWorkspacePage';
|
||||
|
||||
const PatientWorklistPage = React.lazy(() => import('./pages/PatientWorklistPage'));
|
||||
const ClinicalWorkspacePage = React.lazy(() => import('./pages/ClinicalWorkspacePage'));
|
||||
|
||||
function LoadingFallback() {
|
||||
return <div style={{ padding: '2rem', textAlign: 'center' }}>Loading…</div>;
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<PatientStoreProvider>
|
||||
<Routes>
|
||||
<Route path="/" element={<PatientWorklistPage />} />
|
||||
<Route path="/workspace/:patientId" element={<ClinicalWorkspacePage />} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
<Suspense fallback={<LoadingFallback />}>
|
||||
<Routes>
|
||||
<Route path="/" element={<PatientWorklistPage />} />
|
||||
<Route path="/workspace/:patientId" element={<ClinicalWorkspacePage />} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
</PatientStoreProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,8 +19,12 @@ export async function probeCapabilities(): Promise<CapabilityReport> {
|
||||
if (!secureContext) {
|
||||
notes.push('Not a secure context. Use https:// or http://localhost.');
|
||||
}
|
||||
if (location.hostname === '127.0.0.1') {
|
||||
notes.push('OPFS is origin-scoped: 127.0.0.1 and localhost use separate storage.');
|
||||
if (opfs && location.protocol !== 'https:' && location.hostname !== 'localhost') {
|
||||
notes.push(
|
||||
'OPFS is origin-scoped: caches are isolated per scheme+host+port. ' +
|
||||
'Models downloaded on one origin (e.g. localhost, 127.0.0.1, preview domains) ' +
|
||||
'are not available on another. Expect a full re-download when the origin changes.',
|
||||
);
|
||||
}
|
||||
|
||||
const ready = webgpu && opfs && worker && secureContext;
|
||||
|
||||
@@ -14,13 +14,13 @@ export const BOOTSTRAP_DECODE: DecodeParams = {
|
||||
maxTokens: MIN_COT_MAX_TOKENS,
|
||||
};
|
||||
|
||||
function envFlag(name: string, defaultValue: boolean): boolean {
|
||||
const raw = import.meta.env[name];
|
||||
if (raw === undefined || raw === '') {
|
||||
return defaultValue;
|
||||
}
|
||||
return raw === '1' || raw.toLowerCase() === 'true';
|
||||
}
|
||||
// function envFlag(name: string, defaultValue: boolean): boolean {
|
||||
// const raw = import.meta.env[name];
|
||||
// if (raw === undefined || raw === '') {
|
||||
// return defaultValue;
|
||||
// }
|
||||
// return raw === '1' || raw.toLowerCase() === 'true';
|
||||
// }
|
||||
|
||||
/** Try local Gemma worker when OPFS model is available. Falls back to mock replies otherwise. */
|
||||
export function useLocalLlmWhenAvailable(): boolean {
|
||||
@@ -33,7 +33,19 @@ export function useMockAgentTools(): boolean {
|
||||
}
|
||||
|
||||
export function clinicalChatBffBaseUrl(): string {
|
||||
return import.meta.env.VITE_API_BASE_URL ?? '';
|
||||
const raw = import.meta.env.VITE_API_BASE_URL;
|
||||
const trimmed = raw != null ? String(raw).trim() : '';
|
||||
if (trimmed) {
|
||||
return trimmed;
|
||||
}
|
||||
if (import.meta.env.DEV) {
|
||||
return '';
|
||||
}
|
||||
console.warn(
|
||||
'[clinicalChatConfig] VITE_API_BASE_URL is not set. ' +
|
||||
'Clinical chat BFF calls will fail. Set it in config/frontend.config.yaml for production builds.',
|
||||
);
|
||||
return '';
|
||||
}
|
||||
|
||||
const DEFAULT_OLLAMA_CHAT_URL = '/api/ollama-chat/api/chat';
|
||||
|
||||
@@ -216,7 +216,7 @@ export async function runDirectChatTurn(
|
||||
promptOptions,
|
||||
decode,
|
||||
input.onToken,
|
||||
input.onSegmentStart,
|
||||
// input.onSegmentStart,
|
||||
);
|
||||
|
||||
if (signal?.aborted) {
|
||||
|
||||
@@ -71,7 +71,7 @@ export interface SegmentationApiResult {
|
||||
}
|
||||
|
||||
|
||||
/** Resolve API base: dev uses Vite proxy (empty); preview/build targets local test server. */
|
||||
/** Resolve API base: dev uses Vite proxy (empty); production uses configured URL. */
|
||||
export function getSegmentApiBase(): string {
|
||||
const configured = import.meta.env.VITE_SEGMENT_API_BASE;
|
||||
if (configured != null && String(configured).trim() !== '') {
|
||||
@@ -80,7 +80,11 @@ export function getSegmentApiBase(): string {
|
||||
if (import.meta.env.DEV) {
|
||||
return '';
|
||||
}
|
||||
return 'http://127.0.0.1:8001';
|
||||
console.warn(
|
||||
'[segmentationApi] VITE_SEGMENT_API_BASE is not set. ' +
|
||||
'Segmentation API calls will fail. Set it in config/frontend.config.yaml for production builds.',
|
||||
);
|
||||
return '';
|
||||
}
|
||||
|
||||
/** ML inference always uses the test proxy / production API — mock path detached. */
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -24,6 +24,30 @@ const MODAL_OLLAMA_TARGET =
|
||||
frontendConfig.VITE_MODAL_OLLAMA_TARGET ??
|
||||
'https://dtj-tran--ollama-gemma4-e4b-ollamaserver-web.modal.run';
|
||||
|
||||
// API base URLs from YAML — empty in dev so Vite proxy handles them.
|
||||
const apiBaseUrl = (frontendConfig.VITE_API_BASE_URL ?? '').trim();
|
||||
const segmentApiBase = (frontendConfig.VITE_SEGMENT_API_BASE ?? '').trim();
|
||||
|
||||
// Resolve build output directory from YAML config, falling back to 'dist'
|
||||
function findProjectRoot(startDir: string): string {
|
||||
let current = startDir;
|
||||
while (current !== path.parse(current).root) {
|
||||
if (fs.existsSync(path.join(current, '.git'))) {
|
||||
return current;
|
||||
}
|
||||
current = path.dirname(current);
|
||||
}
|
||||
return startDir;
|
||||
}
|
||||
|
||||
const projectRoot = findProjectRoot(__dirname);
|
||||
const configuredOutDir = frontendConfig.VITE_DISTRIBUTE_DIRRECTORY;
|
||||
const buildOutDir = configuredOutDir
|
||||
? path.isAbsolute(configuredOutDir)
|
||||
? configuredOutDir
|
||||
: path.resolve(projectRoot, configuredOutDir)
|
||||
: path.resolve(__dirname, 'dist');
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
worker: {
|
||||
@@ -33,7 +57,7 @@ export default defineConfig({
|
||||
alias: {
|
||||
'@vkist/agent-runtime': path.resolve(
|
||||
__dirname,
|
||||
'../../ml/implementation/nlp/agent_runtime/src/index.ts',
|
||||
'../../ml/implementation/nlp/agent_runtime/src'
|
||||
),
|
||||
},
|
||||
},
|
||||
@@ -41,7 +65,7 @@ export default defineConfig({
|
||||
host: '127.0.0.1',
|
||||
port: 5173,
|
||||
strictPort: true,
|
||||
// Keep HMR WebSocket on the same host/port as the page (avoids localhost vs 127.0.0.1 mismatch).
|
||||
// Keep HMR WebSocket consistent (avoid localhost vs 127.0.0.1 mismatch).
|
||||
hmr: {
|
||||
host: '127.0.0.1',
|
||||
port: 5173,
|
||||
@@ -58,18 +82,31 @@ export default defineConfig({
|
||||
proxyTimeout: 300_000,
|
||||
},
|
||||
'/api/test': {
|
||||
target: 'http://127.0.0.1:8001',
|
||||
target: segmentApiBase || 'http://127.0.0.1:8001',
|
||||
changeOrigin: true,
|
||||
timeout: 300_000,
|
||||
proxyTimeout: 300_000,
|
||||
},
|
||||
'/api': {
|
||||
target: 'http://127.0.0.1:8001',
|
||||
target: apiBaseUrl || 'http://127.0.0.1:8001',
|
||||
changeOrigin: true,
|
||||
timeout: 300_000,
|
||||
proxyTimeout: 300_000,
|
||||
},
|
||||
},
|
||||
},
|
||||
build: {
|
||||
outDir: buildOutDir,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
'vendor-react': ['react', 'react-dom', 'react-router-dom'],
|
||||
'vendor-ml': ['@litert-lm/core', '@mediapipe/tasks-genai'],
|
||||
'agent-runtime': ['@vkist/agent-runtime'],
|
||||
},
|
||||
},
|
||||
},
|
||||
chunkSizeWarningLimit: 250,
|
||||
},
|
||||
define: defineVars,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user