Files
Atomizer/atomizer-dashboard/frontend/src/api/client.ts
Anto01 0eb5028d8f feat(canvas): Add NX model introspection API endpoints
Phase 4 of Canvas Professional Upgrade:
- Add /studies/{id}/nx/introspect endpoint for full model introspection
- Add /studies/{id}/nx/expressions endpoint for expression list
- Add caching to avoid re-running NX journal on each request
- Add frontend API client methods: introspectNxModel, getNxExpressions
- Use existing introspect_part.py extractor

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 22:39:38 -05:00

403 lines
12 KiB
TypeScript

import { StudyListResponse, HistoryResponse, PruningResponse, StudyStatus } from '../types';
const API_BASE = '/api';
export interface OptimizationControlResponse {
success: boolean;
message: string;
pid?: number;
}
export interface ReadmeResponse {
content: string;
path: string;
}
export interface ReportResponse {
content: string;
generated_at?: string;
}
export interface ConfigResponse {
config: Record<string, any>;
objectives: Array<{
name: string;
direction: string;
weight?: number;
target?: number;
units?: string;
}>;
design_variables: Array<{
name: string;
min: number;
max: number;
baseline?: number;
units?: string;
}>;
constraints?: Array<{
name: string;
type: string;
max_value?: number;
min_value?: number;
units?: string;
}>;
}
export interface ProcessStatus {
is_running: boolean;
is_paused?: boolean;
pid?: number;
start_time?: string;
iteration?: number;
fea_count?: number;
nn_count?: number;
total_trials?: number;
completed_trials?: number;
time_per_trial_seconds?: number;
eta_seconds?: number;
eta_formatted?: string;
rate_per_hour?: number;
}
export interface ModelFile {
name: string;
path: string;
extension: string;
size_bytes: number;
size_display: string;
modified: string;
}
export interface ModelFilesResponse {
study_id: string;
model_dir: string;
files: ModelFile[];
count: number;
}
class ApiClient {
async getStudies(): Promise<StudyListResponse> {
const response = await fetch(`${API_BASE}/optimization/studies`);
if (!response.ok) throw new Error('Failed to fetch studies');
return response.json();
}
async getStudyStatus(studyId: string): Promise<StudyStatus> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/status`);
if (!response.ok) throw new Error('Failed to fetch study status');
return response.json();
}
async getStudyHistory(studyId: string): Promise<HistoryResponse> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/history`);
if (!response.ok) throw new Error('Failed to fetch study history');
return response.json();
}
async getStudyPruning(studyId: string): Promise<PruningResponse> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/pruning`);
if (!response.ok) throw new Error('Failed to fetch pruning data');
return response.json();
}
async createStudy(config: any): Promise<{ study_id: string }> {
const response = await fetch(`${API_BASE}/optimization/studies`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config),
});
if (!response.ok) throw new Error('Failed to create study');
return response.json();
}
async getStudyReport(studyId: string): Promise<ReportResponse> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/report`);
if (!response.ok) throw new Error('Failed to fetch report');
return response.json();
}
async getStudyReadme(studyId: string): Promise<ReadmeResponse> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/readme`);
if (!response.ok) throw new Error('Failed to fetch README');
return response.json();
}
async getStudyConfig(studyId: string): Promise<ConfigResponse> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/config`);
if (!response.ok) throw new Error('Failed to fetch config');
return response.json();
}
async getConsoleOutput(studyId: string, lines: number = 200): Promise<{
lines: string[];
total_lines: number;
displayed_lines: number;
log_file: string | null;
timestamp: string;
message?: string;
}> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/console?lines=${lines}`);
if (!response.ok) throw new Error('Failed to fetch console output');
return response.json();
}
async getProcessStatus(studyId: string): Promise<ProcessStatus> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/process`);
if (!response.ok) throw new Error('Failed to fetch process status');
return response.json();
}
// Control operations
async startOptimization(studyId: string, options?: {
freshStart?: boolean;
maxIterations?: number;
trials?: number; // For SAT scripts
feaBatchSize?: number;
tuneTrials?: number;
ensembleSize?: number;
patience?: number;
}): Promise<OptimizationControlResponse> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/start`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(options || {}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to start optimization');
}
return response.json();
}
async stopOptimization(studyId: string): Promise<OptimizationControlResponse> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/stop`, {
method: 'POST',
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to stop optimization');
}
return response.json();
}
async pauseOptimization(studyId: string): Promise<OptimizationControlResponse> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/pause`, {
method: 'POST',
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to pause optimization');
}
return response.json();
}
async resumeOptimization(studyId: string): Promise<OptimizationControlResponse> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/resume`, {
method: 'POST',
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to resume optimization');
}
return response.json();
}
async validateOptimization(studyId: string, options?: {
topN?: number;
}): Promise<OptimizationControlResponse> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/validate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(options || {}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to start validation');
}
return response.json();
}
async generateReport(studyId: string): Promise<ReportResponse> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/report/generate`, {
method: 'POST',
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to generate report');
}
return response.json();
}
// Optuna dashboard
async launchOptunaDashboard(studyId: string): Promise<{ url: string; pid: number }> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/optuna-dashboard`, {
method: 'POST',
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to launch Optuna dashboard');
}
return response.json();
}
async checkOptunaAvailable(): Promise<{
available: boolean;
path: string | null;
message: string;
install_instructions?: string;
}> {
const response = await fetch(`${API_BASE}/optimization/optuna-dashboard/check`);
if (!response.ok) {
return {
available: false,
path: null,
message: 'Failed to check optuna-dashboard availability',
install_instructions: 'pip install optuna-dashboard'
};
}
return response.json();
}
// Optimizer state
async getOptimizerState(studyId: string): Promise<{
available: boolean;
source?: string;
phase?: string;
phase_description?: string;
phase_progress?: number;
current_strategy?: string;
sampler?: {
name: string;
description: string;
};
objectives?: Array<{
name: string;
direction: string;
current_best?: number;
unit?: string;
}>;
plan?: {
total_phases: number;
current_phase: number;
phases: string[];
};
completed_trials?: number;
total_trials?: number;
}> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/optimizer-state`);
if (!response.ok) {
return { available: false };
}
return response.json();
}
// Model files
async getModelFiles(studyId: string): Promise<ModelFilesResponse> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/model-files`);
if (!response.ok) throw new Error('Failed to fetch model files');
return response.json();
}
async openFolder(studyId: string, folderType: 'model' | 'results' | 'setup' = 'model'): Promise<{ success: boolean; message: string; path: string }> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/open-folder?folder_type=${folderType}`, {
method: 'POST',
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to open folder');
}
return response.json();
}
async getBestSolution(studyId: string): Promise<{
study_id: string;
best_trial: {
trial_number: number;
objective: number;
design_variables: Record<string, number>;
user_attrs?: Record<string, any>;
timestamp?: string;
} | null;
first_trial: {
trial_number: number;
objective: number;
design_variables: Record<string, number>;
} | null;
improvements: Record<string, {
initial: number;
final: number;
improvement_pct: number;
absolute_change: number;
}>;
total_trials: number;
}> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/best-solution`);
if (!response.ok) throw new Error('Failed to fetch best solution');
return response.json();
}
async exportData(studyId: string, format: 'csv' | 'json' | 'config'): Promise<{
filename?: string;
content: string;
content_type?: string;
study_id?: string;
total_trials?: number;
trials?: any[];
}> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/export/${format}`);
if (!response.ok) throw new Error(`Failed to export ${format}`);
return response.json();
}
// NX Model introspection
async introspectNxModel(studyId: string, force: boolean = false): Promise<{
study_id: string;
cached: boolean;
introspection: {
success: boolean;
part_file: string;
expressions: {
user: Array<{
name: string;
value: number;
units?: string;
formula?: string;
}>;
internal: any[];
user_count: number;
total_count: number;
};
mass_properties: {
mass_kg: number;
mass_g: number;
volume_mm3: number;
surface_area_mm2: number;
center_of_gravity_mm: [number, number, number];
};
};
}> {
const url = force
? `${API_BASE}/optimization/studies/${studyId}/nx/introspect?force=true`
: `${API_BASE}/optimization/studies/${studyId}/nx/introspect`;
const response = await fetch(url);
if (!response.ok) throw new Error('Failed to introspect NX model');
return response.json();
}
async getNxExpressions(studyId: string): Promise<{
study_id: string;
expressions: Array<{
name: string;
value: number;
units?: string;
formula?: string;
}>;
count: number;
}> {
const response = await fetch(`${API_BASE}/optimization/studies/${studyId}/nx/expressions`);
if (!response.ok) throw new Error('Failed to get NX expressions');
return response.json();
}
}
export const apiClient = new ApiClient();