fix feature instance passing
This commit is contained in:
parent
5ceb243de9
commit
9cad69fd0c
6 changed files with 66 additions and 52 deletions
|
|
@ -68,17 +68,23 @@ export type ApiRequestFunction = (options: ApiRequestOptions<any>) => Promise<an
|
|||
// API REQUEST FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
function _requireApiBaseUrl(apiBaseUrl: string | undefined, caller: string): string {
|
||||
if (!apiBaseUrl) {
|
||||
throw new Error(`${caller}: apiBaseUrl is required (instanceId/featureCode missing)`);
|
||||
}
|
||||
return apiBaseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch workflows - feature-scoped or global (legacy)
|
||||
* When apiBaseUrl is provided: GET {apiBaseUrl}/workflows
|
||||
* Otherwise (legacy): GET /api/workflows/
|
||||
* Fetch workflows - feature-scoped: GET {apiBaseUrl}/workflows
|
||||
*/
|
||||
export async function fetchWorkflows(
|
||||
request: ApiRequestFunction,
|
||||
params?: { pagination?: string },
|
||||
apiBaseUrl?: string
|
||||
): Promise<Workflow[] | { items: Workflow[]; pagination?: any }> {
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows` : '/api/workflows/';
|
||||
const base = _requireApiBaseUrl(apiBaseUrl, 'fetchWorkflows');
|
||||
const url = `${base}/workflows`;
|
||||
console.log('📤 fetchWorkflows: Making API request to', url);
|
||||
|
||||
try {
|
||||
|
|
@ -135,30 +141,28 @@ export async function fetchWorkflows(
|
|||
}
|
||||
|
||||
/**
|
||||
* Fetch a single workflow by ID
|
||||
* When apiBaseUrl provided: GET {apiBaseUrl}/workflows/{workflowId}
|
||||
* Otherwise: GET /api/workflows/{workflowId}
|
||||
* Fetch a single workflow by ID: GET {apiBaseUrl}/workflows/{workflowId}
|
||||
*/
|
||||
export async function fetchWorkflow(
|
||||
request: ApiRequestFunction,
|
||||
workflowId: string,
|
||||
apiBaseUrl?: string
|
||||
): Promise<Workflow & { messages?: WorkflowMessage[]; logs?: WorkflowLog[] }> {
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}` : `/api/workflows/${workflowId}`;
|
||||
const base = _requireApiBaseUrl(apiBaseUrl, 'fetchWorkflow');
|
||||
const url = `${base}/workflows/${workflowId}`;
|
||||
return await request({ url, method: 'get' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch workflow status (lightweight status check)
|
||||
* When apiBaseUrl provided: GET {apiBaseUrl}/workflows/{workflowId}/status
|
||||
* Otherwise: GET /api/workflows/{workflowId}/status
|
||||
* Fetch workflow status (lightweight status check): GET {apiBaseUrl}/workflows/{workflowId}/status
|
||||
*/
|
||||
export async function fetchWorkflowStatus(
|
||||
request: ApiRequestFunction,
|
||||
workflowId: string,
|
||||
apiBaseUrl?: string
|
||||
): Promise<Workflow | { status: string } | null> {
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}/status` : `/api/workflows/${workflowId}/status`;
|
||||
const base = _requireApiBaseUrl(apiBaseUrl, 'fetchWorkflowStatus');
|
||||
const url = `${base}/workflows/${workflowId}/status`;
|
||||
const data = await request({ url, method: 'get' });
|
||||
|
||||
if (data && typeof data === 'object') {
|
||||
|
|
@ -172,9 +176,7 @@ export async function fetchWorkflowStatus(
|
|||
}
|
||||
|
||||
/**
|
||||
* Fetch workflow messages
|
||||
* When apiBaseUrl provided: GET {apiBaseUrl}/workflows/{workflowId}/messages
|
||||
* Otherwise: GET /api/workflows/{workflowId}/messages
|
||||
* Fetch workflow messages: GET {apiBaseUrl}/workflows/{workflowId}/messages
|
||||
*/
|
||||
export async function fetchWorkflowMessages(
|
||||
request: ApiRequestFunction,
|
||||
|
|
@ -183,7 +185,8 @@ export async function fetchWorkflowMessages(
|
|||
apiBaseUrl?: string
|
||||
): Promise<WorkflowMessage[]> {
|
||||
const params = messageId ? { messageId } : undefined;
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}/messages` : `/api/workflows/${workflowId}/messages`;
|
||||
const base = _requireApiBaseUrl(apiBaseUrl, 'fetchWorkflowMessages');
|
||||
const url = `${base}/workflows/${workflowId}/messages`;
|
||||
const data = await request({ url, method: 'get', params });
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
|
|
@ -203,9 +206,7 @@ export async function fetchWorkflowMessages(
|
|||
}
|
||||
|
||||
/**
|
||||
* Fetch workflow logs
|
||||
* When apiBaseUrl provided: GET {apiBaseUrl}/workflows/{workflowId}/logs
|
||||
* Otherwise: GET /api/workflows/{workflowId}/logs
|
||||
* Fetch workflow logs: GET {apiBaseUrl}/workflows/{workflowId}/logs
|
||||
*/
|
||||
export async function fetchWorkflowLogs(
|
||||
request: ApiRequestFunction,
|
||||
|
|
@ -214,7 +215,8 @@ export async function fetchWorkflowLogs(
|
|||
apiBaseUrl?: string
|
||||
): Promise<WorkflowLog[]> {
|
||||
const params = logId ? { logId } : undefined;
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}/logs` : `/api/workflows/${workflowId}/logs`;
|
||||
const base = _requireApiBaseUrl(apiBaseUrl, 'fetchWorkflowLogs');
|
||||
const url = `${base}/workflows/${workflowId}/logs`;
|
||||
const data = await request({ url, method: 'get', params });
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
|
|
@ -389,9 +391,7 @@ export async function stopWorkflowApi(
|
|||
}
|
||||
|
||||
/**
|
||||
* Update workflow properties
|
||||
* When apiBaseUrl provided: PUT {apiBaseUrl}/workflows/{workflowId}
|
||||
* Otherwise: PUT /api/workflows/{workflowId}
|
||||
* Update workflow properties: PUT {apiBaseUrl}/workflows/{workflowId}
|
||||
*/
|
||||
export async function updateWorkflowApi(
|
||||
request: ApiRequestFunction,
|
||||
|
|
@ -399,34 +399,33 @@ export async function updateWorkflowApi(
|
|||
updateData: Partial<{ name: string; description?: string; tags?: string[] }>,
|
||||
apiBaseUrl?: string
|
||||
): Promise<Workflow> {
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}` : `/api/workflows/${workflowId}`;
|
||||
const base = _requireApiBaseUrl(apiBaseUrl, 'updateWorkflowApi');
|
||||
const url = `${base}/workflows/${workflowId}`;
|
||||
return await request({ url, method: 'put', data: updateData });
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a workflow and all associated data
|
||||
* When apiBaseUrl provided: DELETE {apiBaseUrl}/workflows/{workflowId}
|
||||
* Otherwise: DELETE /api/workflows/{workflowId}
|
||||
* Delete a workflow and all associated data: DELETE {apiBaseUrl}/workflows/{workflowId}
|
||||
*/
|
||||
export async function deleteWorkflowApi(
|
||||
request: ApiRequestFunction,
|
||||
workflowId: string,
|
||||
apiBaseUrl?: string
|
||||
): Promise<void> {
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}` : `/api/workflows/${workflowId}`;
|
||||
const base = _requireApiBaseUrl(apiBaseUrl, 'deleteWorkflowApi');
|
||||
const url = `${base}/workflows/${workflowId}`;
|
||||
await request({ url, method: 'delete' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete multiple workflows
|
||||
* When apiBaseUrl provided, uses feature-scoped delete endpoint
|
||||
* Delete multiple workflows: DELETE {apiBaseUrl}/workflows/{workflowId} (per workflow)
|
||||
*/
|
||||
export async function deleteWorkflowsApi(
|
||||
request: ApiRequestFunction,
|
||||
workflowIds: string[],
|
||||
apiBaseUrl?: string
|
||||
): Promise<void> {
|
||||
const base = apiBaseUrl ? `${apiBaseUrl}/workflows` : '/api/workflows';
|
||||
const base = `${_requireApiBaseUrl(apiBaseUrl, 'deleteWorkflowsApi')}/workflows`;
|
||||
const deletePromises = workflowIds.map(workflowId =>
|
||||
request({
|
||||
url: `${base}/${workflowId}`,
|
||||
|
|
@ -441,9 +440,7 @@ export async function deleteWorkflowsApi(
|
|||
}
|
||||
|
||||
/**
|
||||
* Delete a message from a workflow
|
||||
* When apiBaseUrl provided: DELETE {apiBaseUrl}/workflows/{workflowId}/messages/{messageId}
|
||||
* Otherwise: DELETE /api/workflows/{workflowId}/messages/{messageId}
|
||||
* Delete a message from a workflow: DELETE {apiBaseUrl}/workflows/{workflowId}/messages/{messageId}
|
||||
*/
|
||||
export async function deleteMessageApi(
|
||||
request: ApiRequestFunction,
|
||||
|
|
@ -451,14 +448,13 @@ export async function deleteMessageApi(
|
|||
messageId: string,
|
||||
apiBaseUrl?: string
|
||||
): Promise<void> {
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}/messages/${messageId}` : `/api/workflows/${workflowId}/messages/${messageId}`;
|
||||
const base = _requireApiBaseUrl(apiBaseUrl, 'deleteMessageApi');
|
||||
const url = `${base}/workflows/${workflowId}/messages/${messageId}`;
|
||||
await request({ url, method: 'delete' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a file reference from a message
|
||||
* When apiBaseUrl provided: DELETE {apiBaseUrl}/workflows/{workflowId}/messages/{messageId}/files/{fileId}
|
||||
* Otherwise: DELETE /api/workflows/{workflowId}/messages/{messageId}/files/{fileId}
|
||||
* Delete a file reference from a message: DELETE {apiBaseUrl}/workflows/{workflowId}/messages/{messageId}/files/{fileId}
|
||||
*/
|
||||
export async function deleteFileFromMessageApi(
|
||||
request: ApiRequestFunction,
|
||||
|
|
@ -467,7 +463,8 @@ export async function deleteFileFromMessageApi(
|
|||
fileId: string,
|
||||
apiBaseUrl?: string
|
||||
): Promise<void> {
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}/messages/${messageId}/files/${fileId}` : `/api/workflows/${workflowId}/messages/${messageId}/files/${fileId}`;
|
||||
const base = _requireApiBaseUrl(apiBaseUrl, 'deleteFileFromMessageApi');
|
||||
const url = `${base}/workflows/${workflowId}/messages/${messageId}/files/${fileId}`;
|
||||
await request({ url, method: 'delete' });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ export function useDashboardInputForm(instanceId: string) {
|
|||
};
|
||||
}, []);
|
||||
|
||||
const { workflows, loading: workflowsLoading, refetch: refetchWorkflows } = useWorkflows();
|
||||
const { workflows, loading: workflowsLoading, refetch: refetchWorkflows } = useWorkflows(instanceId);
|
||||
|
||||
// Track processed log IDs to avoid reprocessing
|
||||
const processedLogIdsRef = useRef<Set<string>>(new Set());
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
||||
import { useApiRequest } from '../useApi';
|
||||
import {
|
||||
type Workflow,
|
||||
|
|
@ -11,6 +11,7 @@ import {
|
|||
import { useWorkflowOperations } from './useWorkflowOperations';
|
||||
import { sortMessages, sortLogs } from './playgroundUtils';
|
||||
import { useWorkflowPolling } from './useWorkflowPolling';
|
||||
import { getWorkflowApiBaseUrl } from '../useWorkflows';
|
||||
|
||||
interface UnifiedChatDataItem {
|
||||
type: 'message' | 'log' | 'stat';
|
||||
|
|
@ -65,6 +66,8 @@ interface UnifiedChatDataItem {
|
|||
*/
|
||||
|
||||
export function useWorkflowLifecycle(instanceId: string) {
|
||||
const apiBaseUrl = useMemo(() => getWorkflowApiBaseUrl(instanceId, 'chatplayground'), [instanceId]);
|
||||
|
||||
// === STATE ===
|
||||
const [workflowId, setWorkflowId] = useState<string | null>(null);
|
||||
const [workflowStatus, setWorkflowStatus] = useState<string>('idle');
|
||||
|
|
@ -333,7 +336,7 @@ export function useWorkflowLifecycle(instanceId: string) {
|
|||
const afterTimestamp = lastRenderedTimestampRef.current || undefined;
|
||||
|
||||
// Fetch workflow status
|
||||
const workflowData = await fetchWorkflowApi(request, id).catch(() => null);
|
||||
const workflowData = await fetchWorkflowApi(request, id, apiBaseUrl).catch(() => null);
|
||||
|
||||
if (workflowData) {
|
||||
const status = workflowData.status || 'idle';
|
||||
|
|
@ -366,7 +369,7 @@ export function useWorkflowLifecycle(instanceId: string) {
|
|||
} catch (error) {
|
||||
console.error('❌ Polling error:', error);
|
||||
}
|
||||
}, [request, instanceId, updateWorkflowStatus, processUnifiedChatData]);
|
||||
}, [request, instanceId, apiBaseUrl, updateWorkflowStatus, processUnifiedChatData]);
|
||||
|
||||
// === POLLING CONTROL EFFECT ===
|
||||
useEffect(() => {
|
||||
|
|
@ -517,7 +520,7 @@ export function useWorkflowLifecycle(instanceId: string) {
|
|||
setHasRenderedLastMessage(false);
|
||||
|
||||
// Fetch workflow data
|
||||
const workflowData = await fetchWorkflowApi(request, workflowIdToSelect).catch(() => null);
|
||||
const workflowData = await fetchWorkflowApi(request, workflowIdToSelect, apiBaseUrl).catch(() => null);
|
||||
|
||||
if (!workflowData) {
|
||||
setMessages([]);
|
||||
|
|
@ -578,7 +581,7 @@ export function useWorkflowLifecycle(instanceId: string) {
|
|||
} catch (error) {
|
||||
console.error('❌ Error selecting workflow:', error);
|
||||
}
|
||||
}, [request, instanceId, updateWorkflowStatus, processUnifiedChatData]);
|
||||
}, [request, instanceId, apiBaseUrl, updateWorkflowStatus, processUnifiedChatData]);
|
||||
|
||||
// === EXPOSE STATUS SETTER FOR OPTIMISTIC UPDATES ===
|
||||
const setWorkflowStatusOptimistic = useCallback((status: string) => {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import { useApiRequest } from '../useApi';
|
||||
import { useWorkflowSelection } from '../../contexts/WorkflowSelectionContext';
|
||||
import { fetchWorkflows as fetchWorkflowsApi, type Workflow } from '../../api/workflowApi';
|
||||
import { getWorkflowApiBaseUrl } from '../useWorkflows';
|
||||
|
||||
export function useWorkflows() {
|
||||
export function useWorkflows(instanceId?: string, featureCode: string = 'chatplayground') {
|
||||
const [workflows, setWorkflows] = useState<Workflow[]>([]);
|
||||
const [isRefetching, setIsRefetching] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
|
@ -11,13 +12,22 @@ export function useWorkflows() {
|
|||
const { request } = useApiRequest<null, Workflow[]>();
|
||||
const { selectedWorkflowId, clearWorkflow } = useWorkflowSelection();
|
||||
|
||||
const apiBaseUrl = useMemo(
|
||||
() => getWorkflowApiBaseUrl(instanceId, featureCode),
|
||||
[instanceId, featureCode]
|
||||
);
|
||||
|
||||
const fetchWorkflows = useCallback(async () => {
|
||||
if (!apiBaseUrl) {
|
||||
console.warn('⚠️ useWorkflows: No apiBaseUrl available (missing instanceId), skipping fetch');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
console.log('🔄 useWorkflows: Fetching workflows from API...');
|
||||
const workflowList = await fetchWorkflowsApi(request);
|
||||
console.log('🔄 useWorkflows: Fetching workflows from API...', { apiBaseUrl });
|
||||
const workflowList = await fetchWorkflowsApi(request, undefined, apiBaseUrl);
|
||||
console.log('✅ useWorkflows: Fetched workflows:', workflowList);
|
||||
|
||||
if (Array.isArray(workflowList)) {
|
||||
|
|
@ -34,7 +44,7 @@ export function useWorkflows() {
|
|||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [request]);
|
||||
}, [request, apiBaseUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchWorkflows();
|
||||
|
|
|
|||
|
|
@ -122,7 +122,11 @@ export function useUserWorkflows(options?: { instanceId?: string; featureCode?:
|
|||
}
|
||||
|
||||
let data: any;
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows` : '/api/workflows/';
|
||||
if (!apiBaseUrl) {
|
||||
console.error('useUserWorkflows: apiBaseUrl is required (missing instanceId/featureCode)');
|
||||
return;
|
||||
}
|
||||
const url = `${apiBaseUrl}/workflows`;
|
||||
if (Object.keys(requestParams).length > 0) {
|
||||
data = await request({ url, method: 'get', params: requestParams });
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export const WorkflowsPage: React.FC = () => {
|
|||
const { instanceId, featureCode } = useCurrentInstance();
|
||||
const workflowOptions = instanceId && featureCode ? { instanceId, featureCode } : undefined;
|
||||
const apiBaseUrl = getWorkflowApiBaseUrl(instanceId, featureCode);
|
||||
const apiEndpoint = apiBaseUrl ? `${apiBaseUrl}/workflows` : '/api/workflows/';
|
||||
const apiEndpoint = apiBaseUrl ? `${apiBaseUrl}/workflows` : '';
|
||||
|
||||
// Data hook - pass instance context when in feature route
|
||||
const {
|
||||
|
|
|
|||
Loading…
Reference in a new issue