Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions Sources/Conduit/Core/Types/DeviceCapabilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -181,15 +181,17 @@ extension DeviceCapabilities {
private static func getChipType() -> String? {
#if os(macOS) || os(iOS)
var size = 0
sysctlbyname("machdep.cpu.brand_string", nil, &size, nil, 0)
guard sysctlbyname("machdep.cpu.brand_string", nil, &size, nil, 0) == 0, size > 1 else {
return nil
}

if size > 0 {
var buffer = [CChar](repeating: 0, count: size)
sysctlbyname("machdep.cpu.brand_string", &buffer, &size, nil, 0)
// Use failable String initializer with null-terminated C string
return String(cString: buffer)
var buffer = [CChar](repeating: 0, count: size)
guard sysctlbyname("machdep.cpu.brand_string", &buffer, &size, nil, 0) == 0 else {
return nil
}
return nil

let bytes = buffer.prefix { $0 != 0 }.map { UInt8(bitPattern: $0) }
return bytes.isEmpty ? nil : String(decoding: bytes, as: UTF8.self)
#elseif os(Linux)
// Linux: Read from /proc/cpuinfo
// NOTE: Returns the "model name" field which contains CPU description
Expand Down
2 changes: 1 addition & 1 deletion Sources/Conduit/Core/Types/GenerationSchema.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import struct Foundation.Decimal

/// Error that occurs during schema encoding.
private enum EncodingError: Error, LocalizedError {
case invalidValue(Any, Context)
case invalidValue(String, Context)

var errorDescription: String? {
switch self {
Expand Down
24 changes: 15 additions & 9 deletions Tests/ConduitTests/Core/ModelIdentifierTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -414,17 +414,19 @@ final class ModelIdentifierTests: XCTestCase {
func testRegistryContainsAllExpectedModels() {
let allModels = ModelRegistry.allModels

// Should have 16 models total
XCTAssertEqual(allModels.count, 16)
// Should have 19 models total
XCTAssertEqual(allModels.count, 19)

// Count by provider
let mlxModels = allModels.filter { $0.identifier.provider == .mlx }
let hfModels = allModels.filter { $0.identifier.provider == .huggingFace }
let appleModels = allModels.filter { $0.identifier.provider == .foundationModels }
let kimiModels = allModels.filter { $0.identifier.provider == .kimi }

XCTAssertEqual(mlxModels.count, 10) // 7 text gen + 3 embedding
XCTAssertEqual(hfModels.count, 5)
XCTAssertEqual(appleModels.count, 1)
XCTAssertEqual(kimiModels.count, 3)
}

func testRegistryInfoLookup() {
Expand All @@ -451,15 +453,18 @@ final class ModelIdentifierTests: XCTestCase {
let mlxModels = ModelRegistry.models(for: .mlx)
let hfModels = ModelRegistry.models(for: .huggingFace)
let appleModels = ModelRegistry.models(for: .foundationModels)
let kimiModels = ModelRegistry.models(for: .kimi)

XCTAssertEqual(mlxModels.count, 10)
XCTAssertEqual(hfModels.count, 5)
XCTAssertEqual(appleModels.count, 1)
XCTAssertEqual(kimiModels.count, 3)

// Verify all MLX models are actually MLX
XCTAssertTrue(mlxModels.allSatisfy { $0.identifier.provider == .mlx })
XCTAssertTrue(hfModels.allSatisfy { $0.identifier.provider == .huggingFace })
XCTAssertTrue(appleModels.allSatisfy { $0.identifier.provider == .foundationModels })
XCTAssertTrue(kimiModels.allSatisfy { $0.identifier.provider == .kimi })
}

func testRegistryModelsByCapability() {
Expand All @@ -469,10 +474,10 @@ final class ModelIdentifierTests: XCTestCase {
let reasoningModels = ModelRegistry.models(with: .reasoning)
let transcriptionModels = ModelRegistry.models(with: .transcription)

XCTAssertEqual(textGenModels.count, 12) // Most models support text generation
XCTAssertEqual(textGenModels.count, 15) // Includes Kimi text generation models
XCTAssertEqual(embeddingModels.count, 3) // BGE small, BGE large, Nomic
XCTAssertEqual(codeGenModels.count, 3) // Phi-3 Mini, Phi-4, Llama 3.1 70B
XCTAssertEqual(reasoningModels.count, 4) // Phi-3 Mini, Phi-4, Llama 3.1 70B, DeepSeek R1
XCTAssertEqual(codeGenModels.count, 5) // Adds Kimi K2.5 + Kimi K2
XCTAssertEqual(reasoningModels.count, 6) // Adds Kimi K2.5 + Kimi K1.5
XCTAssertEqual(transcriptionModels.count, 1) // Whisper Large V3

// Verify all embedding models actually have the capability
Expand Down Expand Up @@ -517,15 +522,16 @@ final class ModelIdentifierTests: XCTestCase {
func testRegistryCloudModels() {
let cloudModels = ModelRegistry.cloudModels()

// Cloud models should be HuggingFace only
XCTAssertEqual(cloudModels.count, 5)
// Cloud models include HuggingFace + Kimi
XCTAssertEqual(cloudModels.count, 8)

// All should require network
XCTAssertTrue(cloudModels.allSatisfy { $0.identifier.requiresNetwork })
XCTAssertTrue(cloudModels.allSatisfy { !$0.identifier.isLocal })

// All should be HuggingFace
XCTAssertTrue(cloudModels.allSatisfy { $0.identifier.provider == .huggingFace })
// Cloud providers in registry should be HuggingFace or Kimi
let providers = Set(cloudModels.map { $0.identifier.provider })
XCTAssertEqual(providers, [.huggingFace, .kimi])
}

// MARK: - ProviderType Tests
Expand Down
10 changes: 9 additions & 1 deletion Tests/ConduitTests/Core/ProtocolCompilationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -659,23 +659,29 @@ final class ProtocolCompilationTests: XCTestCase {
let llama = ProviderType.llama
let huggingFace = ProviderType.huggingFace
let foundationModels = ProviderType.foundationModels
let kimi = ProviderType.kimi
let minimax = ProviderType.minimax

XCTAssertEqual(mlx.displayName, "MLX (Local)")
XCTAssertEqual(coreml.displayName, "Core ML (Local)")
XCTAssertEqual(llama.displayName, "llama.cpp (Local)")
XCTAssertEqual(huggingFace.displayName, "HuggingFace (Cloud)")
XCTAssertEqual(foundationModels.displayName, "Apple Foundation Models")
XCTAssertEqual(kimi.displayName, "Kimi")
XCTAssertEqual(minimax.displayName, "MiniMax")

XCTAssertFalse(mlx.requiresNetwork)
XCTAssertFalse(coreml.requiresNetwork)
XCTAssertFalse(llama.requiresNetwork)
XCTAssertTrue(huggingFace.requiresNetwork)
XCTAssertFalse(foundationModels.requiresNetwork)
XCTAssertTrue(kimi.requiresNetwork)
XCTAssertTrue(minimax.requiresNetwork)
}

func testProviderTypeIsCaseIterable() {
let allCases = ProviderType.allCases
XCTAssertEqual(allCases.count, 10)
XCTAssertEqual(allCases.count, 12)
XCTAssertTrue(allCases.contains(.mlx))
XCTAssertTrue(allCases.contains(.coreml))
XCTAssertTrue(allCases.contains(.llama))
Expand All @@ -685,6 +691,8 @@ final class ProtocolCompilationTests: XCTestCase {
XCTAssertTrue(allCases.contains(.openRouter))
XCTAssertTrue(allCases.contains(.ollama))
XCTAssertTrue(allCases.contains(.anthropic))
XCTAssertTrue(allCases.contains(.kimi))
XCTAssertTrue(allCases.contains(.minimax))
XCTAssertTrue(allCases.contains(.azure))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// DiffusionModelDownloaderTests.swift
// Conduit
//
// This file requires the MLX trait (Hub) to be enabled.
// This file requires MLX trait and at least one download backend.

#if canImport(Hub)
#if CONDUIT_TRAIT_MLX && canImport(MLX) && (canImport(Hub) || canImport(HuggingFace))

import Foundation
import Testing
Expand Down Expand Up @@ -773,4 +773,4 @@ struct DiffusionModelDownloaderTests {
}
}

#endif // canImport(Hub)
#endif // CONDUIT_TRAIT_MLX && canImport(MLX) && (canImport(Hub) || canImport(HuggingFace))