> */ public function listFiles(): array { $files = []; $after = null; do { $query = ['limit' => 100]; if (is_string($after) && $after !== '') { $query['after'] = $after; } $response = $this->client() ->get(self::BASE_URL . '/files', $query); if ($response->failed()) { throw new OpenAiFileException('OpenAI file list request failed: ' . $response->body()); } $payload = $response->json(); $batch = $payload['data'] ?? []; foreach ($batch as $file) { if (is_array($file)) { $files[] = $file; } } $hasMore = (bool) ($payload['has_more'] ?? false); $after = $payload['last_id'] ?? null; } while ($hasMore && is_string($after) && $after !== ''); return $files; } /** * Delete one OpenAI API file by ID. * * @return array */ public function deleteFile(string $fileId): array { $response = $this->client() ->delete(self::BASE_URL . '/files/' . rawurlencode($fileId)); if ($response->failed()) { throw new OpenAiFileException("OpenAI file delete request failed for {$fileId}: " . $response->body()); } return $response->json() ?? []; } /** * Delete every OpenAI API file visible to the configured API key. * * @return array{listed:int, deleted:int, failed:int, failures:array} */ public function deleteAllFiles(): array { $files = $this->listFiles(); $deleted = 0; $failures = []; foreach ($files as $file) { $fileId = (string) ($file['id'] ?? ''); if ($fileId === '') { continue; } try { $this->deleteFile($fileId); $deleted++; } catch (OpenAiFileException $exception) { $failures[] = [ 'file_id' => $fileId, 'error' => $exception->getMessage(), ]; } } return [ 'listed' => count($files), 'deleted' => $deleted, 'failed' => count($failures), 'failures' => $failures, ]; } /** * Build the authenticated OpenAI HTTP client. */ private function client(): PendingRequest { return Http::withToken($this->apiKey) ->acceptJson() ->timeout(60) ->retry(2, 500); } }