Browse Source

feat(03-03): update executeWorkflow for multi-provider dispatch

- Add API key headers (X-Replicate-API-Key, X-Fal-API-Key) for external providers
- Include selectedModel in request payload for provider routing
- Update logging to show provider-specific info
- Apply same changes to regenerateNode for consistency
handoff-20260429-1057
shrimbly 6 months ago
parent
commit
da326c90d2
  1. 75
      src/store/workflowStore.ts

75
src/store/workflowStore.ts

@ -1030,6 +1030,7 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
try { try {
const nodeData = node.data as NanoBananaNodeData; const nodeData = node.data as NanoBananaNodeData;
const providerSettingsState = get().providerSettings;
const requestPayload = { const requestPayload = {
images, images,
@ -1038,11 +1039,30 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
resolution: nodeData.resolution, resolution: nodeData.resolution,
model: nodeData.model, model: nodeData.model,
useGoogleSearch: nodeData.useGoogleSearch, useGoogleSearch: nodeData.useGoogleSearch,
selectedModel: nodeData.selectedModel,
}; };
logger.info('api.gemini', 'Calling Gemini API for image generation', { // Build headers with API keys for external providers
const headers: Record<string, string> = {
"Content-Type": "application/json",
};
if (nodeData.selectedModel?.provider === "replicate") {
const replicateConfig = providerSettingsState.providers.replicate;
if (replicateConfig?.apiKey) {
headers["X-Replicate-API-Key"] = replicateConfig.apiKey;
}
} else if (nodeData.selectedModel?.provider === "fal") {
const falConfig = providerSettingsState.providers.fal;
if (falConfig?.apiKey) {
headers["X-Fal-API-Key"] = falConfig.apiKey;
}
}
const provider = nodeData.selectedModel?.provider || "gemini";
logger.info('node.execution', `Calling ${provider} API for image generation`, {
nodeId: node.id, nodeId: node.id,
model: nodeData.model, provider,
model: nodeData.selectedModel?.modelId || nodeData.model,
aspectRatio: nodeData.aspectRatio, aspectRatio: nodeData.aspectRatio,
resolution: nodeData.resolution, resolution: nodeData.resolution,
imageCount: images.length, imageCount: images.length,
@ -1051,9 +1071,7 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
const response = await fetch("/api/generate", { const response = await fetch("/api/generate", {
method: "POST", method: "POST",
headers: { headers,
"Content-Type": "application/json",
},
body: JSON.stringify(requestPayload), body: JSON.stringify(requestPayload),
}); });
@ -1067,8 +1085,9 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
if (errorText) errorMessage += ` - ${errorText.substring(0, 200)}`; if (errorText) errorMessage += ` - ${errorText.substring(0, 200)}`;
} }
logger.error('api.error', 'Gemini API request failed', { logger.error('api.error', `${provider} API request failed`, {
nodeId: node.id, nodeId: node.id,
provider,
status: response.status, status: response.status,
statusText: response.statusText, statusText: response.statusText,
errorMessage, errorMessage,
@ -1137,8 +1156,9 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
}); });
} }
} else { } else {
logger.error('api.error', 'Gemini API generation failed', { logger.error('api.error', `${provider} API generation failed`, {
nodeId: node.id, nodeId: node.id,
provider,
error: result.error, error: result.error,
}); });
updateNodeData(node.id, { updateNodeData(node.id, {
@ -1161,8 +1181,11 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
errorMessage = error.message; errorMessage = error.message;
} }
logger.error('node.error', 'nanoBanana node execution failed', { const nodeData = node.data as NanoBananaNodeData;
const errorProvider = nodeData.selectedModel?.provider || "gemini";
logger.error('node.error', 'Generate node execution failed', {
nodeId: node.id, nodeId: node.id,
provider: errorProvider,
errorMessage, errorMessage,
}, error instanceof Error ? error : undefined); }, error instanceof Error ? error : undefined);
@ -1439,15 +1462,18 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
try { try {
if (node.type === "nanoBanana") { if (node.type === "nanoBanana") {
const nodeData = node.data as NanoBananaNodeData; const nodeData = node.data as NanoBananaNodeData;
const providerSettingsState = get().providerSettings;
const provider = nodeData.selectedModel?.provider || "gemini";
// Always get fresh connected inputs first, fall back to stored inputs only if not connected // Always get fresh connected inputs first, fall back to stored inputs only if not connected
const inputs = getConnectedInputs(nodeId); const inputs = getConnectedInputs(nodeId);
let images = inputs.images.length > 0 ? inputs.images : nodeData.inputImages; const images = inputs.images.length > 0 ? inputs.images : nodeData.inputImages;
let text = inputs.text ?? nodeData.inputPrompt; const text = inputs.text ?? nodeData.inputPrompt;
if (!text) { if (!text) {
logger.error('node.error', 'nanoBanana regeneration failed: missing text input', { logger.error('node.error', 'Generate node regeneration failed: missing text input', {
nodeId, nodeId,
provider,
}); });
updateNodeData(nodeId, { updateNodeData(nodeId, {
status: "error", status: "error",
@ -1463,9 +1489,26 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
error: null, error: null,
}); });
logger.info('api.gemini', 'Calling Gemini API for node regeneration', { // Build headers with API keys for external providers
const headers: Record<string, string> = {
"Content-Type": "application/json",
};
if (nodeData.selectedModel?.provider === "replicate") {
const replicateConfig = providerSettingsState.providers.replicate;
if (replicateConfig?.apiKey) {
headers["X-Replicate-API-Key"] = replicateConfig.apiKey;
}
} else if (nodeData.selectedModel?.provider === "fal") {
const falConfig = providerSettingsState.providers.fal;
if (falConfig?.apiKey) {
headers["X-Fal-API-Key"] = falConfig.apiKey;
}
}
logger.info('node.execution', `Calling ${provider} API for node regeneration`, {
nodeId, nodeId,
model: nodeData.model, provider,
model: nodeData.selectedModel?.modelId || nodeData.model,
aspectRatio: nodeData.aspectRatio, aspectRatio: nodeData.aspectRatio,
resolution: nodeData.resolution, resolution: nodeData.resolution,
imageCount: images.length, imageCount: images.length,
@ -1474,7 +1517,7 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
const response = await fetch("/api/generate", { const response = await fetch("/api/generate", {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers,
body: JSON.stringify({ body: JSON.stringify({
images, images,
prompt: text, prompt: text,
@ -1482,6 +1525,7 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
resolution: nodeData.resolution, resolution: nodeData.resolution,
model: nodeData.model, model: nodeData.model,
useGoogleSearch: nodeData.useGoogleSearch, useGoogleSearch: nodeData.useGoogleSearch,
selectedModel: nodeData.selectedModel,
}), }),
}); });
@ -1494,8 +1538,9 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
} catch { } catch {
if (errorText) errorMessage += ` - ${errorText.substring(0, 200)}`; if (errorText) errorMessage += ` - ${errorText.substring(0, 200)}`;
} }
logger.error('api.error', 'Gemini API regeneration failed', { logger.error('api.error', `${provider} API regeneration failed`, {
nodeId, nodeId,
provider,
status: response.status, status: response.status,
errorMessage, errorMessage,
}); });

Loading…
Cancel
Save