ui-nyla/src/api/workflowApi.ts

423 lines
11 KiB
TypeScript

import { ApiRequestOptions } from '../hooks/useApi';
// ============================================================================
// TYPES & INTERFACES
// ============================================================================
// Workflow interfaces
export interface Workflow {
id: string;
mandateId: string;
status: string;
name?: string;
workflowMode?: string;
[key: string]: any; // Allow additional properties
}
export interface WorkflowMessage {
[key: string]: any;
}
export interface WorkflowStats {
[key: string]: any;
}
export interface WorkflowDocument {
[key: string]: any;
}
export interface WorkflowLog {
[key: string]: any;
}
// Request/Response interfaces based on API documentation
export interface FileAttachment {
id: string;
name: string;
}
export interface UserInputRequest {
input: string;
files?: FileAttachment[]; // optional file attachments - array of {id, name} objects
metadata?: Record<string, any>; // optional metadata
}
export interface StartWorkflowRequest {
prompt: string;
listFileId?: string[]; // Array of file ID strings (files must be uploaded first via /api/files/upload)
userLanguage?: string; // Optional, defaults to "en"
metadata?: Record<string, any>;
}
export interface StartWorkflowResponse extends Workflow {
// Workflow object returned from start endpoint
}
export interface ChatDataResponse {
messages: WorkflowMessage[];
logs: WorkflowLog[];
stats: WorkflowStats[];
documents: WorkflowDocument[];
}
// Type for the request function passed to API functions
export type ApiRequestFunction = (options: ApiRequestOptions<any>) => Promise<any>;
// ============================================================================
// API REQUEST FUNCTIONS
// ============================================================================
/**
* Fetch all workflows for the current user
* Endpoint: GET /api/workflows/
*/
export async function fetchWorkflows(request: ApiRequestFunction): Promise<Workflow[]> {
const data = await request<Workflow[]>({
url: '/api/workflows/',
method: 'get'
});
return Array.isArray(data) ? data : [];
}
/**
* Fetch a single workflow by ID
* Endpoint: GET /api/workflows/{workflowId}
*/
export async function fetchWorkflow(
request: ApiRequestFunction,
workflowId: string
): Promise<Workflow & { messages?: WorkflowMessage[]; logs?: WorkflowLog[] }> {
return await request<any>({
url: `/api/workflows/${workflowId}`,
method: 'get'
});
}
/**
* Fetch workflow status (lightweight status check)
* Endpoint: GET /api/workflows/{workflowId}/status
*/
export async function fetchWorkflowStatus(
request: ApiRequestFunction,
workflowId: string
): Promise<Workflow | { status: string } | null> {
const data = await request<any>({
url: `/api/workflows/${workflowId}/status`,
method: 'get'
});
if (data && typeof data === 'object') {
if (data.status) {
return { status: data.status };
}
return data;
}
return null;
}
/**
* Fetch workflow messages
* Endpoint: GET /api/workflows/{workflowId}/messages
* Query params: messageId (optional) - fetch only newer messages
*/
export async function fetchWorkflowMessages(
request: ApiRequestFunction,
workflowId: string,
messageId?: string
): Promise<WorkflowMessage[]> {
const params = messageId ? { messageId } : undefined;
const data = await request<any>({
url: `/api/workflows/${workflowId}/messages`,
method: 'get',
params
});
if (Array.isArray(data)) {
return data;
}
if (data && typeof data === 'object') {
if (Array.isArray(data.messages)) {
return data.messages;
}
if (Array.isArray(data.data)) {
return data.data;
}
}
return [];
}
/**
* Fetch workflow logs
* Endpoint: GET /api/workflows/{workflowId}/logs
* Query params: logId (optional) - fetch only newer logs
*/
export async function fetchWorkflowLogs(
request: ApiRequestFunction,
workflowId: string,
logId?: string
): Promise<WorkflowLog[]> {
const params = logId ? { logId } : undefined;
const data = await request<any>({
url: `/api/workflows/${workflowId}/logs`,
method: 'get',
params
});
if (Array.isArray(data)) {
return data;
}
if (data && typeof data === 'object') {
if (Array.isArray(data.logs)) {
return data.logs;
}
if (Array.isArray(data.data)) {
return data.data;
}
}
return [];
}
/**
* Fetch unified chat data (messages, logs, stats, documents)
* Endpoint: GET /api/chat/playground/{workflowId}/chatData
* Query params: afterTimestamp (optional) - fetch only data created after this time
*/
export async function fetchChatData(
request: ApiRequestFunction,
workflowId: string,
afterTimestamp?: number
): Promise<ChatDataResponse> {
const params = afterTimestamp ? { afterTimestamp: afterTimestamp.toString() } : undefined;
const requestConfig = {
url: `/api/chat/playground/${workflowId}/chatData`,
method: 'get' as const,
params
};
console.log('📤 fetchChatData request:', requestConfig);
const data = await request<ChatDataResponse>(requestConfig);
console.log('📥 fetchChatData response:', data);
// Ensure all arrays exist
return {
messages: Array.isArray(data.messages) ? data.messages : [],
logs: Array.isArray(data.logs) ? data.logs : [],
stats: Array.isArray(data.stats) ? data.stats : [],
documents: Array.isArray(data.documents) ? data.documents : []
};
}
/**
* Start a new workflow or continue an existing one
* Endpoint: POST /api/chat/playground/start
* Query params: workflowId (optional), workflowMode (default: "Actionplan")
*/
export async function startWorkflowApi(
request: ApiRequestFunction,
workflowData: StartWorkflowRequest,
options?: { workflowId?: string; workflowMode?: 'Dynamic' | 'Automation' }
): Promise<StartWorkflowResponse> {
const params: Record<string, string> = {};
// workflowMode is REQUIRED according to API spec
if (options?.workflowMode) {
params.workflowMode = options.workflowMode;
} else {
// Default to 'Dynamic' if not provided (though it should always be provided)
params.workflowMode = 'Dynamic';
}
if (options?.workflowId) {
params.workflowId = options.workflowId;
}
// Request body uses 'prompt' field (not 'input') according to API spec
const requestBody: any = {
prompt: workflowData.prompt,
...(workflowData.listFileId && workflowData.listFileId.length > 0 && { listFileId: workflowData.listFileId }),
...(workflowData.userLanguage && { userLanguage: workflowData.userLanguage }),
...(workflowData.metadata && { metadata: workflowData.metadata })
};
const requestConfig = {
url: '/api/chat/playground/start',
method: 'post' as const,
data: requestBody,
params: params // Always include workflowMode
};
// Log full request details
console.log('📤 Full startWorkflow request details:');
console.log(' URL:', requestConfig.url);
console.log(' Method:', requestConfig.method);
console.log(' Query Parameters:', params);
console.log(' Request Body:', JSON.stringify(requestBody, null, 2));
console.log(' Full Request Config:', JSON.stringify(requestConfig, null, 2));
const response = await request<StartWorkflowResponse>(requestConfig);
console.log('📥 startWorkflow response:', response);
return response;
}
/**
* Stop a running workflow
* Endpoint: POST /api/chat/playground/{workflowId}/stop
*/
export async function stopWorkflowApi(
request: ApiRequestFunction,
workflowId: string
): Promise<void> {
await request({
url: `/api/chat/playground/${workflowId}/stop`,
method: 'post'
});
}
/**
* Update workflow properties
* Endpoint: PUT /api/workflows/{workflowId}
*/
export async function updateWorkflowApi(
request: ApiRequestFunction,
workflowId: string,
updateData: Partial<{ name: string; description?: string; tags?: string[] }>
): Promise<Workflow> {
return await request<Workflow>({
url: `/api/workflows/${workflowId}`,
method: 'put',
data: updateData
});
}
/**
* Delete a workflow and all associated data
* Endpoint: DELETE /api/workflows/{workflowId}
*/
export async function deleteWorkflowApi(
request: ApiRequestFunction,
workflowId: string
): Promise<void> {
await request({
url: `/api/workflows/${workflowId}`,
method: 'delete'
});
}
/**
* Delete multiple workflows
*/
export async function deleteWorkflowsApi(
request: ApiRequestFunction,
workflowIds: string[]
): Promise<void> {
// Delete workflows one by one since there's no bulk delete endpoint
const deletePromises = workflowIds.map(workflowId =>
request({
url: `/api/workflows/${workflowId}`,
method: 'delete'
}).catch(error => {
console.error(`Failed to delete workflow ${workflowId}:`, error);
throw error;
})
);
await Promise.all(deletePromises);
}
/**
* Delete a message from a workflow
* Endpoint: DELETE /api/workflows/{workflowId}/messages/{messageId}
*/
export async function deleteMessageApi(
request: ApiRequestFunction,
workflowId: string,
messageId: string
): Promise<void> {
await request({
url: `/api/workflows/${workflowId}/messages/${messageId}`,
method: 'delete'
});
}
/**
* Delete a file reference from a message
* Endpoint: DELETE /api/workflows/{workflowId}/messages/{messageId}/files/{fileId}
*/
export async function deleteFileFromMessageApi(
request: ApiRequestFunction,
workflowId: string,
messageId: string,
fileId: string
): Promise<void> {
await request({
url: `/api/workflows/${workflowId}/messages/${messageId}/files/${fileId}`,
method: 'delete'
});
}
/**
* Fetch attributes for a workflow type
* Endpoint: GET /api/attributes/{entityType}
*/
export interface AttributeDefinition {
name: string;
type: 'text' | 'email' | 'date' | 'checkbox' | 'select' | 'multiselect' | 'number' | 'textarea';
label: string;
description?: string;
required?: boolean;
default?: any;
options?: Array<{ value: string | number; label: string | { [key: string]: string } }> | string;
validation?: any;
ui?: any;
readonly?: boolean;
editable?: boolean;
visible?: boolean;
order?: number;
placeholder?: string;
sortable?: boolean;
filterable?: boolean;
searchable?: boolean;
width?: number;
minWidth?: number;
maxWidth?: number;
filterOptions?: string[];
}
export async function fetchAttributes(
request: ApiRequestFunction,
entityType: string = 'ChatWorkflow'
): Promise<AttributeDefinition[]> {
const data = await request<any>({
url: `/api/attributes/${entityType}`,
method: 'get'
});
// Extract attributes from response - check if response.data.attributes exists, otherwise check if response.data is an array
let attrs: AttributeDefinition[] = [];
if (data?.attributes && Array.isArray(data.attributes)) {
attrs = data.attributes;
} else if (Array.isArray(data)) {
attrs = data;
} else if (data && typeof data === 'object') {
// Try to find any array property in the response
const keys = Object.keys(data);
for (const key of keys) {
if (Array.isArray(data[key])) {
attrs = data[key];
break;
}
}
}
return attrs;
}