(prompt: string, context?: CallApiContextParams)
| 65 | } |
| 66 | |
| 67 | async callApi(prompt: string, context?: CallApiContextParams): Promise<ProviderResponse> { |
| 68 | const scriptParts = parseScriptParts(this.scriptPath); |
| 69 | const fileHashes = getFileHashes(scriptParts); |
| 70 | |
| 71 | if (fileHashes.length === 0) { |
| 72 | logger.warn(`Could not find any valid files in the command: ${this.scriptPath}`); |
| 73 | } |
| 74 | |
| 75 | const cacheKey = `exec:${this.scriptPath}:${fileHashes.join(':')}:${prompt}:${JSON.stringify(this.options)}`; |
| 76 | |
| 77 | let cachedResult; |
| 78 | if (fileHashes.length > 0 && isCacheEnabled()) { |
| 79 | const cache = await getCache(); |
| 80 | cachedResult = await cache.get(cacheKey); |
| 81 | |
| 82 | if (cachedResult) { |
| 83 | logger.debug(`Returning cached result for script ${this.scriptPath}: ${cachedResult}`); |
| 84 | return { ...JSON.parse(cachedResult as string), cached: true }; |
| 85 | } |
| 86 | } else if (fileHashes.length === 0 && isCacheEnabled()) { |
| 87 | logger.warn( |
| 88 | `Could not hash any files for command ${this.scriptPath}, caching will not be used`, |
| 89 | ); |
| 90 | } |
| 91 | |
| 92 | return new Promise<ProviderResponse>((resolve, reject) => { |
| 93 | const command = scriptParts.shift(); |
| 94 | invariant(command, 'No command found in script path'); |
| 95 | // Remove properties not useful in shell scripts and non-serializable objects |
| 96 | // These can contain circular references (e.g., Timeout objects) that break JSON serialization |
| 97 | delete context?.getCache; |
| 98 | delete context?.logger; |
| 99 | delete context?.filters; // NunjucksFilterMap contains functions |
| 100 | delete context?.originalProvider; // ApiProvider object with methods |
| 101 | const scriptArgs = scriptParts.concat([ |
| 102 | prompt, |
| 103 | safeJsonStringify(this.options || {}) as string, |
| 104 | safeJsonStringify(context || {}) as string, |
| 105 | ]); |
| 106 | const options = this.options?.config.basePath ? { cwd: this.options.config.basePath } : {}; |
| 107 | |
| 108 | const child = execFile(command, scriptArgs, options, async (error, stdout, stderr) => { |
| 109 | if (error) { |
| 110 | logger.debug(`Error running script ${this.scriptPath}: ${error.message}`); |
| 111 | reject(error); |
| 112 | return; |
| 113 | } |
| 114 | const standardOutput = stripText(Buffer.from(stdout).toString('utf8').trim()); |
| 115 | const errorOutput = stripText(Buffer.from(stderr).toString('utf8').trim()); |
| 116 | if (errorOutput) { |
| 117 | logger.debug(`Error output from script ${this.scriptPath}: ${errorOutput}`); |
| 118 | if (!standardOutput) { |
| 119 | reject(new Error(errorOutput)); |
| 120 | return; |
| 121 | } |
| 122 | } |
| 123 | logger.debug(`Output from script ${this.scriptPath}: ${standardOutput}`); |
| 124 | const result = { output: standardOutput }; |
nothing calls this directly
no test coverage detected