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
4 changes: 2 additions & 2 deletions Sources/Conduit/Core/Types/DeviceCapabilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ extension DeviceCapabilities {
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)
let bytes = buffer.prefix { $0 != 0 }.map { UInt8(bitPattern: $0) }
return String(decoding: bytes, as: UTF8.self)
}
return nil
#elseif os(Linux)
Expand Down
8 changes: 4 additions & 4 deletions Sources/Conduit/Core/Types/GenerationSchema.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ 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 {
case .invalidValue(let value, let context):
return "Invalid value during encoding: \(value). \(context.debugDescription)"
case .invalidValue(let valueDescription, let context):
return "Invalid value during encoding: \(valueDescription). \(context.debugDescription)"
}
}

Expand Down Expand Up @@ -59,7 +59,7 @@ public struct GenerationSchema: Sendable, Codable, CustomDebugStringConvertible
for (name, node) in obj.properties {
guard let key = DynamicCodingKey(stringValue: name) else {
throw EncodingError.invalidValue(
name,
String(describing: name),
EncodingError.Context(
codingPath: container.codingPath,
debugDescription: "Unable to create coding key for property '\(name)'"
Expand Down
30 changes: 21 additions & 9 deletions Tests/ConduitTests/Core/ModelIdentifierTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -414,17 +414,25 @@ final class ModelIdentifierTests: XCTestCase {
func testRegistryContainsAllExpectedModels() {
let allModels = ModelRegistry.allModels

// Should have 16 models total
XCTAssertEqual(allModels.count, 16)
// Ensure registry totals match provider buckets.
XCTAssertEqual(
allModels.count,
ModelRegistry.models(for: .mlx).count
+ ModelRegistry.models(for: .huggingFace).count
+ ModelRegistry.models(for: .foundationModels).count
+ ModelRegistry.models(for: .kimi).count
)

// 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 +459,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 +480,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) // Most models support text generation
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) // + Kimi K2.5 and Kimi K2
XCTAssertEqual(reasoningModels.count, 6) // + Kimi K2.5 and Kimi K1.5
XCTAssertEqual(transcriptionModels.count, 1) // Whisper Large V3

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

// Cloud models should be HuggingFace only
XCTAssertEqual(cloudModels.count, 5)
// Cloud models should 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 provider set should match expected cloud providers in the registry.
let providers = Set(cloudModels.map(\.identifier.provider))
XCTAssertEqual(providers, Set([.huggingFace, .kimi]))
}

// MARK: - ProviderType Tests
Expand Down
4 changes: 3 additions & 1 deletion Tests/ConduitTests/Core/ProtocolCompilationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -675,7 +675,7 @@ final class ProtocolCompilationTests: XCTestCase {

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 +685,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
Expand Up @@ -3,7 +3,7 @@
//
// This file requires the MLX trait (Hub) to be enabled.

#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))
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//
// This file requires the MLX trait to be enabled.

#if canImport(MLX)
#if CONDUIT_TRAIT_MLX && canImport(MLX)

import Foundation
import Testing
Expand Down Expand Up @@ -577,4 +577,4 @@ struct DiffusionModelRegistryTests {
}
}

#endif // canImport(MLX)
#endif // CONDUIT_TRAIT_MLX && canImport(MLX)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//
// This file requires the MLX trait to be enabled.

#if canImport(MLX)
#if CONDUIT_TRAIT_MLX && canImport(MLX)

import Foundation
import Testing
Expand Down Expand Up @@ -340,4 +340,4 @@ struct DiffusionVariantTests {
}
}

#endif // canImport(MLX)
#endif // CONDUIT_TRAIT_MLX && canImport(MLX)