[Manager] Optimize conflict detection with bulk API and version support (#4538)

This commit is contained in:
Jin Yi
2025-07-27 08:06:09 +09:00
parent b912416138
commit 9052496402
3 changed files with 112 additions and 34 deletions

View File

@@ -36,11 +36,29 @@ export const useInstalledPacks = (options: UseNodePacksOptions = {}) => {
cleanup() cleanup()
}) })
// Create a computed property that provides installed pack info with versions
const installedPacksWithVersions = computed(() => {
const result: Array<{ id: string; version: string }> = []
for (const pack of Object.values(comfyManagerStore.installedPacks)) {
const id = pack.cnr_id || pack.aux_id
if (id) {
result.push({
id,
version: pack.ver
})
}
}
return result
})
return { return {
error, error,
isLoading, isLoading,
isReady, isReady,
installedPacks: nodePacks, installedPacks: nodePacks,
installedPacksWithVersions,
startFetchInstalled, startFetchInstalled,
filterInstalledPack filterInstalledPack
} }

View File

@@ -40,6 +40,7 @@ export function useConflictDetection() {
const { const {
startFetchInstalled, startFetchInstalled,
installedPacks, installedPacks,
installedPacksWithVersions,
isReady: installedPacksReady isReady: installedPacksReady
} = useInstalledPacks() } = useInstalledPacks()
@@ -203,58 +204,67 @@ export function useConflictDetection() {
return [] return []
} }
// Step 2: Get Registry service for individual API calls // Step 2: Get Registry service for bulk API calls
const registryService = useComfyRegistryService() const registryService = useComfyRegistryService()
// Step 3: Setup abort controller for request cancellation // Step 3: Setup abort controller for request cancellation
abortController.value = new AbortController() abortController.value = new AbortController()
// Step 4: Fetch version-specific data in chunks to avoid overwhelming the Registry API // Step 4: Use bulk API to fetch all version data in a single request
// - Each chunk processes up to 30 packages concurrently
// - Results are stored in versionDataMap for later use
// - Use installedPacks from useInstalledPacks composable
const chunkSize = 30
const versionDataMap = new Map< const versionDataMap = new Map<
string, string,
components['schemas']['NodeVersion'] components['schemas']['NodeVersion']
>() >()
for (let i = 0; i < installedPacks.value.length; i += chunkSize) { // Prepare bulk request with actual installed versions from Manager API
const chunk = installedPacks.value.slice(i, i + chunkSize) const nodeVersions = installedPacksWithVersions.value.map((pack) => ({
node_id: pack.id,
version: pack.version
}))
const fetchTasks = chunk.map(async (pack) => { if (nodeVersions.length > 0) {
// Use pack.id which should be the normalized ID from Registry try {
const packageId = pack.id || '' const bulkResponse = await registryService.getBulkNodeVersions(
const version = pack.latest_version?.version || 'latest' nodeVersions,
abortController.value?.signal
)
try { if (bulkResponse && bulkResponse.node_versions) {
const versionData = await registryService.getPackByVersion( // Process bulk response
packageId, bulkResponse.node_versions.forEach((result) => {
version, if (result.status === 'success' && result.node_version) {
abortController.value?.signal versionDataMap.set(
) result.identifier.node_id,
result.node_version
if (versionData) { )
versionDataMap.set(packageId, versionData) } else if (result.status === 'error') {
} console.warn(
} catch (error) { `[ConflictDetection] Failed to fetch version data for ${result.identifier.node_id}@${result.identifier.version}:`,
console.warn( result.error_message
`[ConflictDetection] Failed to fetch version data for ${packageId}@${version}:`, )
error }
) })
} }
}) } catch (error) {
console.warn(
await Promise.allSettled(fetchTasks) '[ConflictDetection] Failed to fetch bulk version data:',
error
)
}
} }
// Step 5: Combine local installation data with Registry version data // Step 5: Combine local installation data with Registry version data
// Use installedPacks from useInstalledPacks composable
const requirements: NodePackRequirements[] = [] const requirements: NodePackRequirements[] = []
// Create a map for quick access to version info
const versionInfoMap = new Map(
installedPacksWithVersions.value.map((pack) => [pack.id, pack.version])
)
for (const pack of installedPacks.value) { for (const pack of installedPacks.value) {
const packageId = pack.id || '' const packageId = pack.id || ''
const versionData = versionDataMap.get(packageId) const versionData = versionDataMap.get(packageId)
const installedVersion = versionInfoMap.get(packageId) || 'unknown'
// Check if package is enabled using store method // Check if package is enabled using store method
const isEnabled = managerStore.isPackEnabled(packageId) const isEnabled = managerStore.isPackEnabled(packageId)
@@ -265,7 +275,7 @@ export function useConflictDetection() {
// Basic package info // Basic package info
package_id: packageId, package_id: packageId,
package_name: pack.name || packageId, package_name: pack.name || packageId,
installed_version: pack.latest_version?.version || 'unknown', installed_version: installedVersion,
is_enabled: isEnabled, is_enabled: isEnabled,
// Version-specific compatibility data // Version-specific compatibility data
@@ -297,7 +307,7 @@ export function useConflictDetection() {
const fallbackRequirement: NodePackRequirements = { const fallbackRequirement: NodePackRequirements = {
package_id: packageId, package_id: packageId,
package_name: pack.name || packageId, package_name: pack.name || packageId,
installed_version: pack.latest_version?.version || 'unknown', installed_version: installedVersion,
is_enabled: isEnabled, is_enabled: isEnabled,
is_banned: false, is_banned: false,
registry_fetch_time: new Date().toISOString(), registry_fetch_time: new Date().toISOString(),

View File

@@ -359,6 +359,55 @@ export const useComfyRegistryService = () => {
) )
} }
/**
* Get multiple pack versions in a single bulk request.
* This is more efficient than making individual requests for each pack version.
*
* @param nodeVersions - Array of node ID and version pairs to retrieve
* @param signal - Optional AbortSignal for request cancellation
* @returns Bulk response containing the requested node versions or null on error
*
* @example
* ```typescript
* const versions = await getBulkNodeVersions([
* { node_id: 'ComfyUI-Manager', version: '1.0.0' },
* { node_id: 'ComfyUI-Impact-Pack', version: '2.0.0' }
* ])
* if (versions) {
* versions.node_versions.forEach(result => {
* if (result.status === 'success' && result.node_version) {
* console.log(`Retrieved ${result.identifier.node_id}@${result.identifier.version}`)
* }
* })
* }
* ```
*/
const getBulkNodeVersions = async (
nodeVersions: components['schemas']['NodeVersionIdentifier'][],
signal?: AbortSignal
) => {
const endpoint = '/bulk/nodes/versions'
const errorContext = 'Failed to get bulk node versions'
const routeSpecificErrors = {
400: 'Bad request: Invalid node version identifiers provided'
}
const requestBody: components['schemas']['BulkNodeVersionsRequest'] = {
node_versions: nodeVersions
}
return executeApiRequest(
() =>
registryApiClient.post<
components['schemas']['BulkNodeVersionsResponse']
>(endpoint, requestBody, {
signal
}),
errorContext,
routeSpecificErrors
)
}
return { return {
isLoading, isLoading,
error, error,
@@ -372,6 +421,7 @@ export const useComfyRegistryService = () => {
listPacksForPublisher, listPacksForPublisher,
getNodeDefs, getNodeDefs,
postPackReview, postPackReview,
inferPackFromNodeName inferPackFromNodeName,
getBulkNodeVersions
} }
} }