ac75faa0cc
CI / build-and-test (push) Has been cancelled
- E4B-MarkBase model (42 layers, 4.4GB) loaded successfully - All Phase 1-6 tests passed (model loading, forward pass, vision/audio towers, token generation, performance) - All stress tests passed (5/5 in 127.6s) - Concurrent inference - Memory stress (67.5 tok/s, 0 NaN) - Continuous generation - Batch processing - Long-running stability - Swift Metal inference engine with multimodal support
140 lines
4.5 KiB
Swift
140 lines
4.5 KiB
Swift
import Foundation
|
|
|
|
// ─────────────────────────────────────────────────────────────
|
|
// Cross-Device Client
|
|
// ─────────────────────────────────────────────────────────────
|
|
|
|
/// Cross-device client for making requests to other nodes
|
|
public final class CrossDeviceClient: @unchecked Sendable {
|
|
private let session: URLSession
|
|
private let loadBalancer: LoadBalancer
|
|
private let timeout: TimeInterval
|
|
|
|
public init(
|
|
loadBalancer: LoadBalancer,
|
|
timeout: TimeInterval = 30
|
|
) {
|
|
self.loadBalancer = loadBalancer
|
|
self.timeout = timeout
|
|
|
|
let config = URLSessionConfiguration.default
|
|
config.timeoutIntervalForRequest = timeout
|
|
self.session = URLSession(configuration: config)
|
|
}
|
|
|
|
/// Send request to cluster
|
|
public func sendToCluster(
|
|
endpoint: String,
|
|
method: String = "POST",
|
|
body: Data? = nil
|
|
) async throws -> CrossDeviceResponse {
|
|
guard let node = loadBalancer.getNextNode() else {
|
|
throw CrossDeviceError.noHealthyNodes
|
|
}
|
|
|
|
return try await sendToNode(
|
|
node: node,
|
|
endpoint: endpoint,
|
|
method: method,
|
|
body: body
|
|
)
|
|
}
|
|
|
|
/// Send request to specific node
|
|
public func sendToNode(
|
|
node: DeviceNode,
|
|
endpoint: String,
|
|
method: String = "POST",
|
|
body: Data? = nil
|
|
) async throws -> CrossDeviceResponse {
|
|
let url = URL(string: "\(node.baseURL)\(endpoint)")!
|
|
var request = URLRequest(url: url)
|
|
request.httpMethod = method
|
|
request.httpBody = body
|
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
request.timeoutInterval = timeout
|
|
|
|
let startTime = Date()
|
|
|
|
do {
|
|
let (data, response) = try await session.data(for: request)
|
|
let latency = Date().timeIntervalSince(startTime)
|
|
|
|
guard let httpResponse = response as? HTTPURLResponse else {
|
|
throw CrossDeviceError.invalidResponse
|
|
}
|
|
|
|
// Update node status
|
|
loadBalancer.updateNodeStatus(
|
|
id: node.id,
|
|
status: httpResponse.statusCode < 500 ? .healthy : .degraded,
|
|
load: nil
|
|
)
|
|
|
|
return CrossDeviceResponse(
|
|
requestId: UUID().uuidString,
|
|
statusCode: httpResponse.statusCode,
|
|
body: data,
|
|
latency: latency,
|
|
nodeId: node.id
|
|
)
|
|
} catch {
|
|
let latency = Date().timeIntervalSince(startTime)
|
|
|
|
// Update node status
|
|
loadBalancer.updateNodeStatus(id: node.id, status: .unhealthy)
|
|
|
|
throw CrossDeviceError.requestFailed(error)
|
|
}
|
|
}
|
|
|
|
/// Broadcast request to all healthy nodes
|
|
public func broadcastToAll(
|
|
endpoint: String,
|
|
method: String = "POST",
|
|
body: Data? = nil
|
|
) async throws -> [CrossDeviceResponse] {
|
|
let nodes = loadBalancer.getNodes().filter { $0.status == .healthy }
|
|
|
|
return try await withThrowingTaskGroup(of: CrossDeviceResponse.self) { group in
|
|
for node in nodes {
|
|
group.addTask {
|
|
try await self.sendToNode(
|
|
node: node,
|
|
endpoint: endpoint,
|
|
method: method,
|
|
body: body
|
|
)
|
|
}
|
|
}
|
|
|
|
var responses: [CrossDeviceResponse] = []
|
|
for try await response in group {
|
|
responses.append(response)
|
|
}
|
|
return responses
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Cross-device errors
|
|
public enum CrossDeviceError: Error, LocalizedError {
|
|
case noHealthyNodes
|
|
case invalidResponse
|
|
case requestFailed(Error)
|
|
case timeout
|
|
|
|
public var errorDescription: String? {
|
|
switch self {
|
|
case .noHealthyNodes:
|
|
return "No healthy nodes available"
|
|
case .invalidResponse:
|
|
return "Invalid response from node"
|
|
case .requestFailed(let error):
|
|
return "Request failed: \(error.localizedDescription)"
|
|
case .timeout:
|
|
return "Request timed out"
|
|
}
|
|
}
|
|
}
|