Files
Atomizer/atomizer-dashboard/frontend/src/api/intake.ts

412 lines
10 KiB
TypeScript
Raw Normal View History

/**
* Intake API Client
*
* API client methods for the study intake workflow.
*/
import {
CreateInboxRequest,
CreateInboxResponse,
IntrospectRequest,
IntrospectResponse,
ListInboxResponse,
ListTopicsResponse,
InboxStudyDetail,
GenerateReadmeResponse,
FinalizeRequest,
FinalizeResponse,
UploadFilesResponse,
} from '../types/intake';
const API_BASE = '/api';
/**
* Intake API client for study creation workflow.
*/
export const intakeApi = {
/**
* Create a new inbox study folder with initial spec.
*/
async createInbox(request: CreateInboxRequest): Promise<CreateInboxResponse> {
const response = await fetch(`${API_BASE}/intake/create`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to create inbox study');
}
return response.json();
},
/**
* Run NX introspection on an inbox study.
*/
async introspect(request: IntrospectRequest): Promise<IntrospectResponse> {
const response = await fetch(`${API_BASE}/intake/introspect`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Introspection failed');
}
return response.json();
},
/**
* List all studies in the inbox.
*/
async listInbox(): Promise<ListInboxResponse> {
const response = await fetch(`${API_BASE}/intake/list`);
if (!response.ok) {
throw new Error('Failed to fetch inbox studies');
}
return response.json();
},
/**
* List existing topic folders.
*/
async listTopics(): Promise<ListTopicsResponse> {
const response = await fetch(`${API_BASE}/intake/topics`);
if (!response.ok) {
throw new Error('Failed to fetch topics');
}
return response.json();
},
/**
* Get detailed information about an inbox study.
*/
async getInboxStudy(studyName: string): Promise<InboxStudyDetail> {
const response = await fetch(`${API_BASE}/intake/${encodeURIComponent(studyName)}`);
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to fetch inbox study');
}
return response.json();
},
/**
* Delete an inbox study.
*/
async deleteInboxStudy(studyName: string): Promise<{ success: boolean; deleted: string }> {
const response = await fetch(`${API_BASE}/intake/${encodeURIComponent(studyName)}`, {
method: 'DELETE',
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to delete inbox study');
}
return response.json();
},
/**
* Generate README for an inbox study using Claude AI.
*/
async generateReadme(studyName: string): Promise<GenerateReadmeResponse> {
const response = await fetch(
`${API_BASE}/intake/${encodeURIComponent(studyName)}/readme`,
{ method: 'POST' }
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'README generation failed');
}
return response.json();
},
/**
* Finalize an inbox study and move to studies directory.
*/
async finalize(studyName: string, request: FinalizeRequest): Promise<FinalizeResponse> {
const response = await fetch(
`${API_BASE}/intake/${encodeURIComponent(studyName)}/finalize`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Finalization failed');
}
return response.json();
},
/**
* Upload model files to an inbox study.
*/
async uploadFiles(studyName: string, files: File[]): Promise<UploadFilesResponse> {
const formData = new FormData();
files.forEach((file) => {
formData.append('files', file);
});
const response = await fetch(
`${API_BASE}/intake/${encodeURIComponent(studyName)}/upload`,
{
method: 'POST',
body: formData,
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'File upload failed');
}
return response.json();
},
/**
* Upload context files to an inbox study.
* Context files help Claude understand optimization goals.
*/
async uploadContextFiles(studyName: string, files: File[]): Promise<UploadFilesResponse> {
const formData = new FormData();
files.forEach((file) => {
formData.append('files', file);
});
const response = await fetch(
`${API_BASE}/intake/${encodeURIComponent(studyName)}/context`,
{
method: 'POST',
body: formData,
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Context file upload failed');
}
return response.json();
},
/**
* List context files for an inbox study.
*/
async listContextFiles(studyName: string): Promise<{
study_name: string;
context_files: Array<{ name: string; path: string; size: number; extension: string }>;
total: number;
}> {
const response = await fetch(
`${API_BASE}/intake/${encodeURIComponent(studyName)}/context`
);
if (!response.ok) {
throw new Error('Failed to list context files');
}
return response.json();
},
/**
* Delete a context file from an inbox study.
*/
async deleteContextFile(studyName: string, filename: string): Promise<{ success: boolean; deleted: string }> {
const response = await fetch(
`${API_BASE}/intake/${encodeURIComponent(studyName)}/context/${encodeURIComponent(filename)}`,
{ method: 'DELETE' }
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to delete context file');
}
return response.json();
},
/**
* Create design variables from selected expressions.
*/
async createDesignVariables(
studyName: string,
expressionNames: string[],
options?: { autoBounds?: boolean; boundFactor?: number }
): Promise<{
success: boolean;
study_name: string;
created: Array<{
id: string;
name: string;
expression_name: string;
bounds_min: number;
bounds_max: number;
baseline: number;
units: string | null;
}>;
total_created: number;
}> {
const response = await fetch(
`${API_BASE}/intake/${encodeURIComponent(studyName)}/design-variables`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
expression_names: expressionNames,
auto_bounds: options?.autoBounds ?? true,
bound_factor: options?.boundFactor ?? 0.5,
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to create design variables');
}
return response.json();
},
// ===========================================================================
// Studio Endpoints (Atomizer Studio - Unified Creation Environment)
// ===========================================================================
/**
* Create an anonymous draft study for Studio workflow.
* Returns a temporary draft_id that can be renamed during finalization.
*/
async createDraft(): Promise<{
success: boolean;
draft_id: string;
inbox_path: string;
spec_path: string;
status: string;
}> {
const response = await fetch(`${API_BASE}/intake/draft`, {
method: 'POST',
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to create draft');
}
return response.json();
},
/**
* Get extracted text content from context files.
* Used for AI context injection.
*/
async getContextContent(studyName: string): Promise<{
success: boolean;
study_name: string;
content: string;
files_read: Array<{
name: string;
extension: string;
size: number;
status: string;
characters?: number;
error?: string;
}>;
total_characters: number;
}> {
const response = await fetch(
`${API_BASE}/intake/${encodeURIComponent(studyName)}/context/content`
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to get context content');
}
return response.json();
},
/**
* Finalize a Studio draft with rename support.
* Enhanced version that supports renaming draft_xxx to proper names.
*/
async finalizeStudio(
studyName: string,
request: {
topic: string;
newName?: string;
runBaseline?: boolean;
}
): Promise<{
success: boolean;
original_name: string;
final_name: string;
final_path: string;
status: string;
baseline_success: boolean | null;
readme_generated: boolean;
}> {
const response = await fetch(
`${API_BASE}/intake/${encodeURIComponent(studyName)}/finalize/studio`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
topic: request.topic,
new_name: request.newName,
run_baseline: request.runBaseline ?? false,
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Studio finalization failed');
}
return response.json();
},
/**
* Get complete draft information for Studio UI.
* Convenience endpoint that returns everything the Studio needs.
*/
async getStudioDraft(studyName: string): Promise<{
success: boolean;
draft_id: string;
spec: Record<string, unknown>;
model_files: string[];
context_files: string[];
introspection_available: boolean;
design_variable_count: number;
objective_count: number;
}> {
const response = await fetch(
`${API_BASE}/intake/${encodeURIComponent(studyName)}/studio`
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to get studio draft');
}
return response.json();
},
};
export default intakeApi;