Repository navigation
Release: Billing Optimization & Credential Modal UX - 2025.10.24 #640
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,45 +2,31 @@ | |
| * Utility functions for detecting and managing missing credentials in flows | ||
| */ | ||
|
|
||
| import { getCredentialCategory } from './getCredentialCategory' | ||
|
|
||
| /** | ||
| * Extract required credentials from flow data and identify missing ones | ||
| * @param {string|object} flowData - Flow data as JSON string or object | ||
| * @returns {any[]} Array of missing credentials | ||
| * @returns {object} Object containing missing credentials info | ||
| */ | ||
| export const extractMissingCredentials = (flowData: string | any) => { | ||
| try { | ||
| // Parse flow data if it's a string | ||
| const flow = typeof flowData === 'string' ? JSON.parse(flowData) : flowData | ||
|
|
||
| if (!flow.nodes || !Array.isArray(flow.nodes)) { | ||
| return [] | ||
| return { missingCredentials: [], hasCredentials: false } | ||
| } | ||
|
|
||
| const missingCredentials: any[] = [] | ||
| const credentialTypes = new Set<string>() | ||
|
|
||
| // Define commonly needed optional credentials that should be offered | ||
| const importantOptionalCredentials = ['redisCacheApi', 'redisCacheUrlApi', 'upstashRedisApi', 'upstashRedisMemoryApi'] | ||
|
|
||
| // Iterate through all nodes | ||
| flow.nodes.forEach((node: any, index: number) => { | ||
| flow.nodes.forEach((node: any) => { | ||
| if (node.data && node.data.inputParams) { | ||
| // Find credential input parameters (required OR important optional ones) | ||
| // Find all credential input parameters | ||
| const credentialParams = node.data.inputParams.filter((param: any) => { | ||
| if (param.type !== 'credential') return false | ||
|
|
||
| // Include required credentials | ||
| if (!param.optional) return true | ||
|
|
||
| // Include important optional credentials | ||
| if ( | ||
| param.credentialNames && | ||
| param.credentialNames.some((name: string) => importantOptionalCredentials.includes(name)) | ||
| ) { | ||
| return true | ||
| } | ||
|
|
||
| return false | ||
| return param.type === 'credential' | ||
| }) | ||
|
|
||
| credentialParams.forEach((credentialParam: any) => { | ||
|
|
@@ -57,13 +43,27 @@ export const extractMissingCredentials = (flowData: string | any) => { | |
| credentialNames.forEach((credentialName: string) => { | ||
| credentialTypes.add(credentialName) | ||
|
|
||
| // Get category information to determine if credential is core | ||
| const category = getCredentialCategory(node.data.category, credentialName) | ||
|
|
||
| // Credential is required if: | ||
| // 1. Category is core (Chat Models, Agents, etc), OR | ||
| // 2. Parameter explicitly marked as NOT optional (optional: false) | ||
| // Default to optional if not explicitly marked | ||
| const isRequired = category.isCore || (credentialParam.optional === false) | ||
|
Comment on lines
+46
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Similar to Useful? React with πΒ / π. |
||
|
|
||
| const missingCred = { | ||
| nodeId: node.id, | ||
| nodeName: node.data.name || 'Unknown Node', | ||
| nodeCategory: node.data.category || 'Unknown', | ||
| nodeType: node.data.type || node.type || 'Unknown', | ||
| credentialType: credentialName, | ||
| parameterName: credentialParam.name, | ||
| label: credentialParam.label || credentialParam.name, | ||
| isOptional: !!credentialParam.optional | ||
| isRequired: isRequired, | ||
| isCore: category.isCore, | ||
| categoryType: category.type, | ||
| categoryDisplayName: category.displayName | ||
| } | ||
|
|
||
| missingCredentials.push(missingCred) | ||
|
|
@@ -73,9 +73,12 @@ export const extractMissingCredentials = (flowData: string | any) => { | |
| } | ||
| }) | ||
|
|
||
| return missingCredentials | ||
| return { | ||
| missingCredentials, | ||
| hasCredentials: credentialTypes.size > 0 | ||
| } | ||
| } catch (error) { | ||
| console.error('Error in extractMissingCredentials:', error) | ||
| return [] | ||
| return { missingCredentials: [], hasCredentials: false } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| /** | ||
| * Determine credential category based on node category and type | ||
| * @param {string} nodeCategory - Node category (e.g., "Chat Models", "Tools", "MCP Servers") | ||
| * @param {string} credentialType - Credential type name | ||
| * @returns {object} Category info with type, display name, and isCore flag | ||
| */ | ||
| export const getCredentialCategory = (nodeCategory?: string, credentialType?: string) => { | ||
| // Normalize category | ||
| const category = (nodeCategory || '').toLowerCase() | ||
| const credType = (credentialType || '').toLowerCase() | ||
|
|
||
| // Chat Models - always required for basic functionality | ||
| if (category.includes('chat model') || category.includes('llm')) { | ||
| return { type: 'chatModel', displayName: 'Chat Model', isCore: true } | ||
| } | ||
|
|
||
| // MCP Servers - optional tools | ||
| if (category.includes('mcp server') || credType.includes('mcp')) { | ||
| return { type: 'mcpServer', displayName: 'MCP Server', isCore: false } | ||
| } | ||
|
|
||
| // Tools - optional enhancements | ||
| if (category.includes('tool')) { | ||
| return { type: 'tool', displayName: 'Tool', isCore: false } | ||
| } | ||
|
|
||
| // Agents - required for agent flows | ||
| if (category.includes('agent')) { | ||
| return { type: 'agent', displayName: 'Agent', isCore: true } | ||
| } | ||
|
|
||
| // Chains - required for chain flows | ||
| if (category.includes('chain')) { | ||
| return { type: 'chain', displayName: 'Chain', isCore: true } | ||
| } | ||
|
|
||
| // Document Loaders | ||
| if (category.includes('document loader')) { | ||
| return { type: 'documentLoader', displayName: 'Document Loader', isCore: false } | ||
| } | ||
|
|
||
| // Vector Stores | ||
| if (category.includes('vector store')) { | ||
| return { type: 'vectorStore', displayName: 'Vector Store', isCore: false } | ||
| } | ||
|
|
||
| // Cache - optional but important | ||
| if (credType.includes('redis') || credType.includes('cache')) { | ||
| return { type: 'cache', displayName: 'Cache', isCore: false } | ||
| } | ||
|
|
||
| // Default | ||
| return { type: 'other', displayName: 'Other', isCore: false } | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new
isRequiredcalculation only marks a credential as required whencredentialParam.optional === falseor the category is core. In Flowise nodes a credential parameter is required whenoptionalis omitted (undefined) and optional only when the flag is explicitly set totrue. With the current check, all nonβcore credentials that leaveoptionalundefined will be classified as optional, so the quick setup UI will let users skip API keys that are in fact mandatory and the flow will fail at runtime. Consider usingcredentialParam.optional !== trueso undefined values still count as required.Useful? React with πΒ / π.