131 lines
3.9 KiB
TypeScript
131 lines
3.9 KiB
TypeScript
/**
|
|
* useOracleExecution — manages prompt submission and durable execution history.
|
|
*/
|
|
import { useState, useCallback, useRef } from 'react';
|
|
import type { PromptExecution, CanvasComponent, PlacementMode } from '../types/canvas';
|
|
import { submitPrompt } from '../lib/oracleApiClient';
|
|
|
|
export interface ExecutionEntry {
|
|
execution: PromptExecution;
|
|
componentsCreated: string[];
|
|
}
|
|
|
|
export interface OracleExecutionState {
|
|
history: ExecutionEntry[];
|
|
inFlight: PromptExecution | null;
|
|
lastError: string | null;
|
|
submit: (params: {
|
|
pageId: string;
|
|
branchId: string;
|
|
prompt: string;
|
|
tenantId: string;
|
|
actorId: string;
|
|
placementMode?: PlacementMode;
|
|
conversationContext?: Array<{ role: 'user' | 'assistant'; content: string }>;
|
|
onExecutionCommitted?: (commit: {
|
|
headRevision: number;
|
|
components: CanvasComponent[];
|
|
execution: PromptExecution;
|
|
}) => void;
|
|
}) => Promise<void>;
|
|
clearError: () => void;
|
|
}
|
|
|
|
export function useOracleExecution(): OracleExecutionState {
|
|
const [history, setHistory] = useState<ExecutionEntry[]>([]);
|
|
const [inFlight, setInFlight] = useState<PromptExecution | null>(null);
|
|
const [lastError, setLastError] = useState<string | null>(null);
|
|
const requestIdRef = useRef(0);
|
|
|
|
const submit = useCallback(
|
|
async ({
|
|
pageId,
|
|
branchId,
|
|
prompt,
|
|
tenantId,
|
|
actorId,
|
|
placementMode = 'append_after_last_visible_component',
|
|
conversationContext = [],
|
|
onExecutionCommitted,
|
|
}: {
|
|
pageId: string;
|
|
branchId: string;
|
|
prompt: string;
|
|
tenantId: string;
|
|
actorId: string;
|
|
placementMode?: PlacementMode;
|
|
conversationContext?: Array<{ role: 'user' | 'assistant'; content: string }>;
|
|
onExecutionCommitted?: (commit: {
|
|
headRevision: number;
|
|
components: CanvasComponent[];
|
|
execution: PromptExecution;
|
|
}) => void;
|
|
}) => {
|
|
const clientRequestId = `cli_${Date.now()}_${++requestIdRef.current}`;
|
|
const now = new Date().toISOString();
|
|
const optimistic: PromptExecution = {
|
|
executionId: `pex_${clientRequestId}`,
|
|
tenantId,
|
|
pageId,
|
|
branchId,
|
|
actorId,
|
|
prompt,
|
|
intentClass: 'analytical',
|
|
status: 'planning',
|
|
modelRuntime: 'oracle_runtime',
|
|
semanticModelVersion: 'oracle_semantic_v2026_04_08_01',
|
|
warnings: [],
|
|
createdAt: now,
|
|
};
|
|
|
|
setInFlight(optimistic);
|
|
setLastError(null);
|
|
|
|
try {
|
|
setInFlight((prev) => (prev ? { ...prev, status: 'executing' } : prev));
|
|
|
|
const response = await submitPrompt(pageId, {
|
|
clientRequestId,
|
|
branchId,
|
|
prompt,
|
|
conversationContext,
|
|
placementMode,
|
|
});
|
|
|
|
const completed: PromptExecution = {
|
|
...optimistic,
|
|
executionId: response.executionId,
|
|
status: response.status,
|
|
summary: response.summary,
|
|
warnings: response.warnings,
|
|
componentsCreated: response.componentsCreated,
|
|
completedAt: new Date().toISOString(),
|
|
};
|
|
|
|
onExecutionCommitted?.({
|
|
headRevision: response.headRevision,
|
|
components: response.components,
|
|
execution: completed,
|
|
});
|
|
|
|
setHistory((prev) => [...prev, { execution: completed, componentsCreated: response.componentsCreated }]);
|
|
setInFlight(null);
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : 'Prompt execution failed';
|
|
const failed: PromptExecution = {
|
|
...optimistic,
|
|
status: 'failed',
|
|
warnings: [msg],
|
|
completedAt: new Date().toISOString(),
|
|
};
|
|
setHistory((prev) => [...prev, { execution: failed, componentsCreated: [] }]);
|
|
setInFlight(null);
|
|
setLastError(msg);
|
|
}
|
|
},
|
|
[],
|
|
);
|
|
|
|
return { history, inFlight, lastError, submit, clearError: () => setLastError(null) };
|
|
}
|