From 7bef37f458dcf2dc025c1a78733808436f42ca1f Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Fri, 24 Apr 2026 01:41:59 +0700 Subject: [PATCH] chore(lint): parallelize safe awaits in server (no-await-in-loop) Converts three sequential `for ... await` loops to `Promise.all`: - OpenCode: configure MCP servers in parallel - Sherpa: download model files and ensure multiple models concurrently - Speech runtime: check required model files across models in parallel --- .../server/agent/providers/opencode-agent.ts | 9 +-- .../local/sherpa/model-downloader.ts | 68 ++++++++++--------- .../src/server/speech/speech-runtime.ts | 27 ++++---- 3 files changed, 55 insertions(+), 49 deletions(-) diff --git a/packages/server/src/server/agent/providers/opencode-agent.ts b/packages/server/src/server/agent/providers/opencode-agent.ts index 31aebbbf7..b68cae6db 100644 --- a/packages/server/src/server/agent/providers/opencode-agent.ts +++ b/packages/server/src/server/agent/providers/opencode-agent.ts @@ -2530,10 +2530,11 @@ class OpenCodeAgentSession implements AgentSession { } private async configureMcpServers(mcpServers: Record): Promise { - for (const [name, serverConfig] of Object.entries(mcpServers)) { - const mappedConfig = toOpenCodeMcpConfig(serverConfig); - await this.registerMcpServer(name, mappedConfig); - } + await Promise.all( + Object.entries(mcpServers).map(([name, serverConfig]) => + this.registerMcpServer(name, toOpenCodeMcpConfig(serverConfig)), + ), + ); } private async registerMcpServer(name: string, config: OpenCodeMcpConfig): Promise { diff --git a/packages/server/src/server/speech/providers/local/sherpa/model-downloader.ts b/packages/server/src/server/speech/providers/local/sherpa/model-downloader.ts index 5430a229e..504f8ebd3 100644 --- a/packages/server/src/server/speech/providers/local/sherpa/model-downloader.ts +++ b/packages/server/src/server/speech/providers/local/sherpa/model-downloader.ts @@ -20,22 +20,21 @@ export function getSherpaOnnxModelDir(modelsDir: string, modelId: SherpaOnnxMode } async function hasRequiredFiles(modelDir: string, requiredFiles: string[]): Promise { - for (const rel of requiredFiles) { - const abs = path.join(modelDir, rel); - try { - const s = await stat(abs); - if (s.isDirectory()) { - continue; + const results = await Promise.all( + requiredFiles.map(async (rel) => { + const abs = path.join(modelDir, rel); + try { + const s = await stat(abs); + if (s.isDirectory()) { + return true; + } + return s.isFile() && s.size > 0; + } catch { + return false; } - if (s.isFile() && s.size > 0) { - continue; - } - return false; - } catch { - return false; - } - } - return true; + }), + ); + return results.every((present) => present); } interface DownloadToFileOptions { @@ -163,16 +162,18 @@ export async function ensureSherpaOnnxModel( if (spec.downloadFiles && spec.downloadFiles.length > 0) { await mkdir(modelDir, { recursive: true }); - for (const file of spec.downloadFiles) { - const dst = path.join(modelDir, file.relPath); - if (await isNonEmptyFile(dst)) { - continue; - } - await downloadToFile({ - url: file.url, - outputPath: dst, - }); - } + await Promise.all( + spec.downloadFiles.map(async (file) => { + const dst = path.join(modelDir, file.relPath); + if (await isNonEmptyFile(dst)) { + return; + } + await downloadToFile({ + url: file.url, + outputPath: dst, + }); + }), + ); logger.info( { @@ -205,12 +206,17 @@ export async function ensureSherpaOnnxModels(options: { }): Promise> { const uniq = Array.from(new Set(options.modelIds)); const out: Partial> = {}; - for (const id of uniq) { - out[id] = await ensureSherpaOnnxModel({ - modelsDir: options.modelsDir, - modelId: id, - logger: options.logger, - }); + const paths = await Promise.all( + uniq.map((id) => + ensureSherpaOnnxModel({ + modelsDir: options.modelsDir, + modelId: id, + logger: options.logger, + }), + ), + ); + for (let i = 0; i < uniq.length; i += 1) { + out[uniq[i]!] = paths[i]!; } return out as Record; } diff --git a/packages/server/src/server/speech/speech-runtime.ts b/packages/server/src/server/speech/speech-runtime.ts index 5009be3ac..5a9236ce1 100644 --- a/packages/server/src/server/speech/speech-runtime.ts +++ b/packages/server/src/server/speech/speech-runtime.ts @@ -100,20 +100,19 @@ async function findMissingRequiredLocalModels(params: { const specsById = new Map(listLocalSpeechModels().map((model) => [model.id, model])); const missing = new Set(); - for (const modelId of requiredModelIds) { - const spec = specsById.get(modelId); - if (!spec) { - missing.add(modelId); - continue; - } - const modelDir = getLocalSpeechModelDir(modelsDir, modelId); - for (const relPath of spec.requiredFiles) { - const filePath = join(modelDir, relPath); - if (!(await hasRequiredLocalModelFile(filePath))) { - missing.add(modelId); - break; - } - } + const checks = await Promise.all( + requiredModelIds.map(async (modelId) => { + const spec = specsById.get(modelId); + if (!spec) return { modelId, missing: true }; + const modelDir = getLocalSpeechModelDir(modelsDir, modelId); + const filePresence = await Promise.all( + spec.requiredFiles.map((relPath) => hasRequiredLocalModelFile(join(modelDir, relPath))), + ); + return { modelId, missing: !filePresence.every((present) => present) }; + }), + ); + for (const check of checks) { + if (check.missing) missing.add(check.modelId); } return Array.from(missing);