diff --git a/Neon Vision Editor/GrokStreamClient.swift b/Neon Vision Editor/GrokStreamClient.swift new file mode 100644 index 0000000..0574bf7 --- /dev/null +++ b/Neon Vision Editor/GrokStreamClient.swift @@ -0,0 +1,92 @@ +import Foundation + +public struct GrokStreamClient { + public let apiKey: String + /// Update to the latest code-focused Grok model as per xAI docs + public var model: String = "grok-2-code" + private let endpoint = URL(string: "https://api.x.ai/v1/chat/completions")! + + private struct ChatDeltaChunk: Decodable { + struct Choice: Decodable { + struct Delta: Decodable { + let content: String? + } + let delta: Delta? + let finish_reason: String? + } + let choices: [Choice]? + } + + public func streamSuggestions(prompt: String) -> AsyncStream { + var request = URLRequest(url: endpoint) + request.httpMethod = "POST" + request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization") + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + request.setValue("text/event-stream", forHTTPHeaderField: "Accept") + + let body: [String: Any] = [ + "model": model, + "stream": true, + "messages": [ + ["role": "system", "content": "You are a helpful code assistant providing concise inline suggestions."], + ["role": "user", "content": prompt] + ] + ] + request.httpBody = try? JSONSerialization.data(withJSONObject: body) + + return AsyncStream { continuation in + Task { + do { + let (bytes, response) = try await URLSession.shared.bytes(for: request) + if let http = response as? HTTPURLResponse, http.statusCode >= 400 { + continuation.finish() + return + } + var buffer = "" + for try await chunk in bytes.lines { + buffer += chunk + "\n" + while let range = buffer.range(of: "\n\n") { + let event = String(buffer[..