using System.Text; using System.Text.Json; using System.Runtime.CompilerServices; namespace AIStudio.Tools.Services; public sealed partial class RustService { public async Task ReadArbitraryFileData(string path, int maxChunks, bool extractImages = false) { var streamId = Guid.NewGuid().ToString(); var requestUri = $"/retrieval/fs/extract?path={Uri.EscapeDataString(path)}&stream_id={streamId}&extract_images={extractImages}"; var request = new HttpRequestMessage(HttpMethod.Get, requestUri); var response = await this.http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); if (!response.IsSuccessStatusCode) return string.Empty; var resultBuilder = new StringBuilder(); try { await using var stream = await response.Content.ReadAsStreamAsync(); using var reader = new StreamReader(stream); var chunkCount = 0; while (!reader.EndOfStream && chunkCount < maxChunks) { var line = await reader.ReadLineAsync(); if (string.IsNullOrWhiteSpace(line)) continue; if (!line.StartsWith("data:", StringComparison.InvariantCulture)) continue; var jsonContent = line[5..]; try { var sseEvent = JsonSerializer.Deserialize(jsonContent); if (sseEvent is not null) { var content = ContentStreamSseHandler.ProcessEvent(sseEvent, extractImages); if (content is not null) resultBuilder.AppendLine(content); chunkCount++; } } catch (JsonException) { if (this.TryLogSseErrorMessage(jsonContent, path)) continue; this.logger?.LogError("Failed to deserialize SSE event: {JsonContent}", jsonContent); } } } catch(Exception e) { this.logger?.LogError(e, "Error reading file data from stream: {Path}", path); } finally { var finalContentChunk = ContentStreamSseHandler.Clear(streamId); if (!string.IsNullOrWhiteSpace(finalContentChunk)) resultBuilder.AppendLine(finalContentChunk); } return resultBuilder.ToString(); } public async IAsyncEnumerable StreamArbitraryFileData(string path, bool extractImages = false, [EnumeratorCancellation] CancellationToken token = default) { var streamId = Guid.NewGuid().ToString(); var requestUri = $"/retrieval/fs/extract?path={Uri.EscapeDataString(path)}&stream_id={streamId}&extract_images={extractImages}"; using var request = new HttpRequestMessage(HttpMethod.Get, requestUri); using var response = await this.http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token); if (!response.IsSuccessStatusCode) yield break; string? finalContentChunk = null; try { await using var stream = await response.Content.ReadAsStreamAsync(token); using var reader = new StreamReader(stream); while (!reader.EndOfStream && !token.IsCancellationRequested) { var line = await reader.ReadLineAsync(token); if (string.IsNullOrWhiteSpace(line)) continue; if (!line.StartsWith("data:", StringComparison.InvariantCulture)) continue; var jsonContent = line[5..]; ContentStreamSseEvent? sseEvent = null; try { sseEvent = JsonSerializer.Deserialize(jsonContent); } catch (JsonException) { if (this.TryLogSseErrorMessage(jsonContent, path)) continue; this.logger?.LogError("Failed to deserialize SSE event: {JsonContent}", jsonContent); } if (sseEvent is null) continue; var content = ContentStreamSseHandler.ProcessEvent(sseEvent, extractImages); if (!string.IsNullOrWhiteSpace(content)) yield return content; } } finally { finalContentChunk = ContentStreamSseHandler.Clear(streamId); } if (!string.IsNullOrWhiteSpace(finalContentChunk)) yield return finalContentChunk; } private bool TryLogSseErrorMessage(string jsonContent, string path) { try { var errorMessage = JsonSerializer.Deserialize(jsonContent); if (string.IsNullOrWhiteSpace(errorMessage)) return false; this.logger?.LogError("Rust retrieval stream error for '{Path}': {ErrorMessage}", path, errorMessage); return true; } catch (JsonException) { return false; } } }