Initial commit: E4B-MarkBase model integration with passing tests
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
This commit is contained in:
MarkBase Admin
2026-06-23 18:12:35 +08:00
commit ac75faa0cc
301 changed files with 63426 additions and 0 deletions
+310
View File
@@ -0,0 +1,310 @@
import Foundation
//
// Unified Error Handling for MarkBase
//
/// MarkBase error types
public enum MarkBaseError: Error, LocalizedError {
case modelNotFound(String)
case invalidRequest(String)
case modelLoadingFailed(String)
case inferenceFailed(String)
case tokenLimitExceeded(current: Int, max: Int)
case invalidParameter(parameter: String, message: String)
case internalError(String)
case multimodalNotSupported
case imageProcessingFailed
case audioProcessingFailed
public var errorDescription: String? {
switch self {
case .modelNotFound(let path):
return "Model not found at: \(path)"
case .invalidRequest(let message):
return "Invalid request: \(message)"
case .modelLoadingFailed(let detail):
return "Failed to load model: \(detail)"
case .inferenceFailed(let detail):
return "Inference failed: \(detail)"
case .tokenLimitExceeded(let current, let max):
return "Token limit exceeded: \(current) > \(max)"
case .invalidParameter(let parameter, let message):
return "Invalid parameter '\(parameter)': \(message)"
case .internalError(let detail):
return "Internal error: \(detail)"
case .multimodalNotSupported:
return "Multimodal inference not supported for this model"
case .imageProcessingFailed:
return "Image processing failed"
case .audioProcessingFailed:
return "Audio processing failed"
}
}
/// HTTP status code for this error
public var httpStatus: Int {
switch self {
case .modelNotFound:
return 404
case .invalidRequest, .invalidParameter:
return 400
case .modelLoadingFailed, .internalError:
return 500
case .inferenceFailed:
return 500
case .tokenLimitExceeded:
return 400
case .multimodalNotSupported:
return 400
case .imageProcessingFailed, .audioProcessingFailed:
return 500
}
}
/// OpenAI-compatible error type
public var errorType: String {
switch self {
case .modelNotFound:
return "model_not_found"
case .invalidRequest, .invalidParameter, .tokenLimitExceeded:
return "invalid_request_error"
case .modelLoadingFailed, .internalError:
return "server_error"
case .inferenceFailed:
return "server_error"
case .multimodalNotSupported:
return "invalid_request_error"
case .imageProcessingFailed, .audioProcessingFailed:
return "server_error"
}
}
/// Convert to OpenAI error response format
public func toErrorResponse(param: String? = nil) -> ErrorResponse {
ErrorResponse(
error: toErrorDetail(param: param)
)
}
/// Convert to ErrorDetail
public func toErrorDetail(param: String? = nil) -> ErrorDetail {
ErrorDetail(
message: localizedDescription,
type: errorType,
code: httpStatus,
param: param
)
}
}
/// OpenAI-compatible error response
public struct ErrorResponse: Codable {
public let error: ErrorDetail
public init(error: ErrorDetail) {
self.error = error
}
/// Create error response from MarkBaseError
public static func from(_ error: MarkBaseError) -> ErrorResponse {
ErrorResponse(error: error.toErrorDetail())
}
}
public struct ErrorDetail: Codable {
public let message: String
public let type: String
public let code: Int
public let param: String?
public init(message: String, type: String, code: Int, param: String? = nil) {
self.message = message
self.type = type
self.code = code
self.param = param
}
}
/// Validation helpers
public enum Validator {
/// Validate model path exists
public static func validateModelPath(_ path: String) throws {
guard FileManager.default.fileExists(atPath: path) else {
throw MarkBaseError.modelNotFound(path)
}
// Check for required files (support both single-file and sharded safetensors)
let hasSingleFile = FileManager.default.fileExists(atPath: (path as NSString).appendingPathComponent("model.safetensors"))
let hasIndexFile = FileManager.default.fileExists(atPath: (path as NSString).appendingPathComponent("model.safetensors.index.json"))
guard hasSingleFile || hasIndexFile else {
throw MarkBaseError.modelLoadingFailed("Missing required file: model.safetensors or model.safetensors.index.json")
}
guard FileManager.default.fileExists(atPath: (path as NSString).appendingPathComponent("config.json")) else {
throw MarkBaseError.modelLoadingFailed("Missing required file: config.json")
}
}
/// Validate generation parameters
public static func validateGenerationParams(
maxTokens: Int?,
temperature: Float?,
topP: Float?,
topK: Int?
) throws {
if let maxTokens = maxTokens {
guard maxTokens > 0 && maxTokens <= 4096 else {
throw MarkBaseError.invalidParameter(
parameter: "max_tokens",
message: "Must be between 1 and 4096"
)
}
}
if let temperature = temperature {
guard temperature >= 0.0 && temperature <= 2.0 else {
throw MarkBaseError.invalidParameter(
parameter: "temperature",
message: "Must be between 0.0 and 2.0"
)
}
}
if let topP = topP {
guard topP >= 0.0 && topP <= 1.0 else {
throw MarkBaseError.invalidParameter(
parameter: "top_p",
message: "Must be between 0.0 and 1.0"
)
}
}
if let topK = topK {
guard topK > 0 else {
throw MarkBaseError.invalidParameter(
parameter: "top_k",
message: "Must be greater than 0"
)
}
}
}
/// Validate prompt
public static func validatePrompt(_ prompt: String, maxLength: Int = 4096) throws {
guard !prompt.isEmpty else {
throw MarkBaseError.invalidRequest("Prompt cannot be empty")
}
guard prompt.count <= maxLength else {
throw MarkBaseError.invalidParameter(
parameter: "prompt",
message: "Prompt too long (max \(maxLength) characters)"
)
}
}
/// Validate messages
public static func validateMessages(_ messages: [ChatMessage]) throws {
guard !messages.isEmpty else {
throw MarkBaseError.invalidRequest("Messages array cannot be empty")
}
for (index, message) in messages.enumerated() {
guard let content = message.content, !content.isEmpty else {
throw MarkBaseError.invalidParameter(
parameter: "messages[\(index)].content",
message: "Content cannot be empty"
)
}
guard ["system", "user", "assistant"].contains(message.role) else {
throw MarkBaseError.invalidParameter(
parameter: "messages[\(index)].role",
message: "Role must be 'system', 'user', or 'assistant'"
)
}
}
}
/// Validate multimodal messages
public static func validateMultimodalMessages(_ messages: [MultimodalMessage]) throws {
guard !messages.isEmpty else {
throw MarkBaseError.invalidRequest("Messages array cannot be empty")
}
for (index, message) in messages.enumerated() {
guard !message.content.isEmpty else {
throw MarkBaseError.invalidParameter(
parameter: "messages[\(index)].content",
message: "Content array cannot be empty"
)
}
// Validate content parts
for (partIndex, part) in message.content.enumerated() {
switch part {
case .text(let text):
guard !text.isEmpty else {
throw MarkBaseError.invalidParameter(
parameter: "messages[\(index)].content[\(partIndex)]",
message: "Text content cannot be empty"
)
}
case .imageUrl(let imageUrl):
guard !imageUrl.url.isEmpty else {
throw MarkBaseError.invalidParameter(
parameter: "messages[\(index)].content[\(partIndex)].image_url.url",
message: "Image URL cannot be empty"
)
}
case .audioUrl(let audioUrl):
guard !audioUrl.url.isEmpty else {
throw MarkBaseError.invalidParameter(
parameter: "messages[\(index)].content[\(partIndex)].audio_url.url",
message: "Audio URL cannot be empty"
)
}
case .videoUrl(let videoUrl):
guard !videoUrl.url.isEmpty else {
throw MarkBaseError.invalidParameter(
parameter: "messages[\(index)].content[\(partIndex)].video_url.url",
message: "Video URL cannot be empty"
)
}
}
}
// Validate role
guard ["system", "user", "assistant"].contains(message.role) else {
throw MarkBaseError.invalidParameter(
parameter: "messages[\(index)].role",
message: "Role must be 'system', 'user', or 'assistant'"
)
}
}
}
}
/// Result type with error handling
public enum Result<T> {
case success(T)
case failure(MarkBaseError)
public func get() throws -> T {
switch self {
case .success(let value):
return value
case .failure(let error):
throw error
}
}
public func map<U>(_ transform: (T) -> U) -> Result<U> {
switch self {
case .success(let value):
return .success(transform(value))
case .failure(let error):
return .failure(error)
}
}
}