diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 611b570e..64537485 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,10 +42,10 @@ jobs: ${{ runner.os }}-swift-${{ matrix.swift }}-spm- - name: Build - run: swift build -v + run: swift build --traits MLX,Llama,CoreML - name: Test - run: swift test -v + run: swift test test-linux: name: Swift ${{ matrix.swift-version }} on Linux @@ -66,7 +66,7 @@ jobs: toolchain: ${{ matrix.swift-version }} - name: Build - run: swift build -v + run: swift build - name: Test - run: swift test -v + run: swift test diff --git a/Sources/AnyLanguageModel/Models/CoreMLLanguageModel.swift b/Sources/AnyLanguageModel/Models/CoreMLLanguageModel.swift index c27bdc87..cde04e41 100644 --- a/Sources/AnyLanguageModel/Models/CoreMLLanguageModel.swift +++ b/Sources/AnyLanguageModel/Models/CoreMLLanguageModel.swift @@ -214,15 +214,7 @@ // MARK: - Image Validation private func validateNoImageSegments(in session: LanguageModelSession) throws { - // Check for image segments in instructions - if let instructions = session.instructions { - for segment in instructions.segments { - if case .image = segment { - throw CoreMLLanguageModelError.unsupportedFeature - } - } - } - + // Note: Instructions is a plain text type without segments, so no image check needed there. // Check for image segments in the most recent prompt for entry in session.transcript.reversed() { if case .prompt(let p) = entry { diff --git a/Sources/AnyLanguageModel/Models/MLXLanguageModel.swift b/Sources/AnyLanguageModel/Models/MLXLanguageModel.swift index 77accfb5..1f13824d 100644 --- a/Sources/AnyLanguageModel/Models/MLXLanguageModel.swift +++ b/Sources/AnyLanguageModel/Models/MLXLanguageModel.swift @@ -175,7 +175,8 @@ import Foundation let hub = self.hub let directory = self.directory - let stream: AsyncThrowingStream.Snapshot, any Error> = .init { continuation in + let stream: AsyncThrowingStream.Snapshot, any Error> = .init { + continuation in let task = Task { @Sendable in do { let context: ModelContext @@ -213,7 +214,8 @@ import Foundation case .chunk(let text): accumulatedText += text let raw = GeneratedContent(accumulatedText) - let content: Content.PartiallyGenerated = (accumulatedText as! Content).asPartiallyGenerated() + let content: Content.PartiallyGenerated = (accumulatedText as! Content) + .asPartiallyGenerated() continuation.yield(.init(content: content, rawContent: raw)) case .info, .toolCall: break @@ -357,19 +359,15 @@ import Foundation // MARK: - Tool Conversion private func convertToolToMLXSpec(_ tool: any Tool) -> ToolSpec { - // Convert AnyLanguageModel's GenerationSchema to JSON-compatible dictionary - let parametersDict: [String: Any] + // Convert AnyLanguageModel's GenerationSchema to Sendable dictionary + // using MLXLMCommon.JSONValue which is already Sendable + let parametersValue: JSONValue do { let resolvedSchema = tool.parameters.withResolvedRoot() ?? tool.parameters - let encoder = JSONEncoder() - let data = try encoder.encode(resolvedSchema) - if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] { - parametersDict = json - } else { - parametersDict = ["type": "object", "properties": [:], "required": []] - } + let data = try JSONEncoder().encode(resolvedSchema) + parametersValue = try JSONDecoder().decode(JSONValue.self, from: data) } catch { - parametersDict = ["type": "object", "properties": [:], "required": []] + parametersValue = .object(["type": .string("object"), "properties": .object([:]), "required": .array([])]) } return [ @@ -377,8 +375,8 @@ import Foundation "function": [ "name": tool.name, "description": tool.description, - "parameters": parametersDict, - ], + "parameters": parametersValue, + ] as [String: any Sendable], ] }