Skip to content
Merged
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
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ recompiling. A breaking change waits for 2.0. (Before RC.1, `1.0.0-beta.N` and e
beta.4 → RC.1 changes that can break a build are listed under *Changed — breaking (pre-GA)* in the
RC.1 entry, and each entry's own "Beta caveats" apply to the betas.)

## 1.0.0 — GA (unreleased)

Fixes only; no API change.

### Fixed

- **Preflight rejected models that fit in memory** (`MLXModelProvider.validate(_:)`, stages
`sizeVsMemory` and `diskSpace`). The download size came from the Hub's repo-level `usedStorage`, which
counts blobs from every revision, so a re-uploaded repo reported about twice its real size —
`mlx-community/Qwen3.8-27B-8bit` read as 58.1 GB against 29.5 GB of actual files and failed on a 64 GB
Mac. The SDK now sums the current revision's file sizes and uses `usedStorage` only when no file reports
a size. `PreflightResult.weightBytes` reports the corrected figure, and the free-disk requirement drops
with it.

## 1.0.0-RC.1 — 2026-09-15 (binaries re-published 2026-09-18 and 2026-09-19)

Everything below is in `LocalLMLabSDKInference` unless noted (the 2026-09-19 re-publish also adds the
Expand Down
10 changes: 5 additions & 5 deletions Components/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ struct SDKRelease {
// or edit `defaultSDKVersion`. For a release not listed, add its entry (URL follows the pattern
// below; checksum is the `.sha256` next to the zip on that GitHub release). Keep this in step
// with `examples/model-switch/Package.swift`, which consumes Core through this package.
let defaultSDKVersion = "1.0.0-RC.1"
let defaultSDKVersion = "1.0.0-GA"

let knownSDKReleases: [String: SDKRelease] = [
"1.0.0-beta.4": SDKRelease(
url: "https://github.com/ancientcomputing/locallm/releases/download/v1.0.0-beta.4/LocalLMLabSDKCore-1.0.0-beta.4.xcframework.zip",
checksum: "3ed0e79b6914e6b48b7ae27f3fdda139f71e3d60f603daf54901716c8c972cb3"
),
"1.0.0-RC.1": SDKRelease(
url: "https://github.com/ancientcomputing/locallm/releases/download/v1.0.0-RC.1/LocalLMLabSDKCore-1.0.0-RC.1.xcframework.zip",
checksum: "397e7b5f7efd1076293a3d5d06c41d75043bffa23cffdb821d71d21ee41e68de"
),
"1.0.0-GA": SDKRelease(
url: "https://github.com/ancientcomputing/locallm/releases/download/v1.0.0-GA/LocalLMLabSDKCore-1.0.0-GA.xcframework.zip",
checksum: "7d77a9c2e37dfb2f7925f01ed011262ee5a2aebc561a3ae7a93524acc0285b3e"
),
]

func failManifest(_ message: String) -> Never {
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ an Apple entitlement that is still pending, so it is inert in this beta.

**Product page:** [thisbrain.ai/locallm](https://thisbrain.ai/locallm) — or
get the app directly:
[LocalLM Lab 1.0.0-RC.1 (arm64 DMG)](https://github.com/ancientcomputing/locallm-releases/releases/download/1.0.0-RC.1/LocalLM.Lab-1.0.0-RC.1-arm64.dmg).
[LocalLM Lab 1.0.0-GA (arm64 DMG)](https://github.com/ancientcomputing/locallm/releases/download/v1.0.0-GA/LocalLM.Lab-1.0.0-GA-arm64.dmg).

## LocalLM Lab SDK

Expand Down Expand Up @@ -63,11 +63,12 @@ what's public here is:
OpenAI-compatible API Lab endpoint. Full list with descriptions in the
[Building on the SDK](#building-on-the-sdk) section below.
- **[toolkit/](toolkit/)** — the `localai-cli` toolkit (zip + `.sha256`).
`0.6`–`1.0.0-beta.3` are checked in here; from `1.0.0-beta.4` on it ships as a
release asset on
[`ancientcomputing/locallm-releases`](https://github.com/ancientcomputing/locallm-releases/releases)
alongside the app DMG. See that folder's README to download and verify; full
CLI reference at
`0.6`–`1.0.0-beta.3` are checked in here; `1.0.0-beta.4` through `1.0.0-RC.1`
shipped as a release asset on
[`ancientcomputing/locallm-releases`](https://github.com/ancientcomputing/locallm-releases/releases);
from **`1.0.0-GA`** on it ships as a release asset on this repo instead,
alongside the app DMG and the SDK xcframeworks. See that folder's README to
download and verify; full CLI reference at
[thisbrain.ai/locallm/cli.html](https://thisbrain.ai/locallm/cli.html).
- **[Components/](Components/)** — `LocalLMLabSDKComponents`, prebuilt SwiftUI for
managing MCP servers and models, built on the SDK's public API.
Expand Down
32 changes: 14 additions & 18 deletions docs/annotated-examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ below for the same app rebuilt on Core's ready-made "Path A" `Tool`s instead.

```swift
// "What's on my plate today" — v1: Todoist (via Core's MCP client) + Calendar + Reminders (also
// via Core, through CalendarAccess/RemindersAccess — see below). Linear is a planned v2 addition,
// deliberately deferred.
// via Core, through CalendarAccess/RemindersAccess — see below). Linear is intentionally out of
// scope for this example — it demonstrates Calendar/Reminders/Todoist only.
//
// SwiftUI app shape (not a bare CLI): launch -> request Calendar/Reminders/Todoist access on
// first run -> pull + synthesize -> show result -> Done closes the app. Packaged as a real signed
Expand All @@ -79,10 +79,9 @@ struct TodaysEventsTool: Tool {
let description = "Retrieve the user's calendar events for today"

// Zero-property Arguments is valid and correct for a no-input tool (proven by Core's
// ClockTool) -- an earlier "unused placeholder" field here was a fragile workaround that
// actively caused decode failures: FoundationModels sometimes calls a tool with genuinely
// empty generated content when no argument makes sense, and a required-but-unused field then
// fails to decode from that empty content.
// ClockTool). Don't add a placeholder field to satisfy the schema: FoundationModels
// sometimes calls a tool with genuinely empty generated content when no argument makes
// sense, and a required-but-unused field then fails to decode from that empty content.
@Generable
struct Arguments {}

Expand All @@ -106,10 +105,9 @@ struct TodaysRemindersTool: Tool {
let description = "Retrieve the user's incomplete reminders due today"

// Zero-property Arguments is valid and correct for a no-input tool (proven by Core's
// ClockTool) -- an earlier "unused placeholder" field here was a fragile workaround that
// actively caused decode failures: FoundationModels sometimes calls a tool with genuinely
// empty generated content when no argument makes sense, and a required-but-unused field then
// fails to decode from that empty content.
// ClockTool). Don't add a placeholder field to satisfy the schema: FoundationModels
// sometimes calls a tool with genuinely empty generated content when no argument makes
// sense, and a required-but-unused field then fails to decode from that empty content.
@Generable
struct Arguments {}

Expand Down Expand Up @@ -137,10 +135,9 @@ struct TodaysLocationTool: Tool {
let description = "Returns the user's current one-shot location (place name, if available) — useful as input to the weather tool."

// Zero-property Arguments is valid and correct for a no-input tool (proven by Core's
// ClockTool) -- an earlier "unused placeholder" field here was a fragile workaround that
// actively caused decode failures: FoundationModels sometimes calls a tool with genuinely
// empty generated content when no argument makes sense, and a required-but-unused field then
// fails to decode from that empty content.
// ClockTool). Don't add a placeholder field to satisfy the schema: FoundationModels
// sometimes calls a tool with genuinely empty generated content when no argument makes
// sense, and a required-but-unused field then fails to decode from that empty content.
@Generable
struct Arguments {}

Expand Down Expand Up @@ -171,10 +168,9 @@ struct TodoistTasksTool: Tool {
let serverURL: URL

// Zero-property Arguments is valid and correct for a no-input tool (proven by Core's
// ClockTool) -- an earlier "unused placeholder" field here was a fragile workaround that
// actively caused decode failures: FoundationModels sometimes calls a tool with genuinely
// empty generated content when no argument makes sense, and a required-but-unused field then
// fails to decode from that empty content.
// ClockTool). Don't add a placeholder field to satisfy the schema: FoundationModels
// sometimes calls a tool with genuinely empty generated content when no argument makes
// sense, and a required-but-unused field then fails to decode from that empty content.
@Generable
struct Arguments {}

Expand Down
91 changes: 90 additions & 1 deletion docs/api-surface.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> Machine-generated by `scripts/dump-public-api.sh` from the built `.swiftinterface`s.
> Do not hand-edit. `sdk-guide.md` §12 is the *narrative* reference; this is the check.
> SDK version: `"1.0.0-RC.1"` · generated 2026-09-19T18:42Z
> SDK version: `"1.0.0-GA"` · generated 2026-09-27T18:00Z

## LocalLMLabSDKCore

Expand Down Expand Up @@ -2056,31 +2056,65 @@ public func turnArtifacts(in transcript: Transcript) -> TurnArtifacts
> declarations in `Components/Sources/`, not a compiler-derived interface.

```swift
public enum Kind: Sendable, Equatable { case added, removed, modified }
public enum ModelOnboardingStepState: Sendable, Equatable
public enum ModelUpdateOwnership: Sendable, Equatable
public enum RemoteProviderKind: String, Sendable, Hashable, CaseIterable
public enum State: Equatable
public extension RemoteProviderDraft
public extension View
public final class MCPElicitationPresenter: ObservableObject, MCPElicitationHandler
public final class MCPServerManagerObservable: ObservableObject
public final class ModelOnboardingModel
public final class ModelUpdateModel
public final class ModelVersionsModel
public final class ToolConfirmationPresenter: ObservableObject, ToolConfirmationChannel
public func cancel()
public func checkForUpdate() async
public func handleElicitation(_ request: MCPElicitationRequest) async -> MCPElicitationResponse
public func refresh() async
public func remove(_ row: ModelVersionRow) async
public func reset()
public func resolve(_ id: MCPElicitationPrompt.ID, _ response: MCPElicitationResponse)
public func resolve(_ id: ToolConfirmationRequest.ID, allow: Bool)
public func revertToShippedVersion() async
public func rollBack() async
public func start()
public func update() async
public init(
public init(apiKey: Binding<String>, onCommit: @escaping () -> Void = {})
public init(core: MCPServerManager)
public init(current: String, available: String, changes: [ModelFileChange] = [])
public init(fallbackTimeout: Duration = .seconds(120))
public init(manager: MCPServerManagerObservable)
public init(manager: MCPServerManagerObservable, onAttach: @escaping (MCPResourceDescriptor, MCPResourceContent) -> Void)
public init(manager: MCPServerManagerObservable, onUse: @escaping (MCPPromptDescriptor, [MCPPromptMessage]) -> Void)
public init(model: ModelUpdateModel) { self.model = model }
public init(model: ModelVersionsModel) { self.model = model }
public init(modelId: String, ok: Bool, detail: String)
public init(path: String, kind: Kind, oldSize: Int64? = nil, newSize: Int64? = nil)
public init(provider: any DownloadableModelProvider)
public init(registry: ModelRegistry)
public init(registry: ModelRegistry, selection: Binding<ModelID?>, show27OnlyModels: Bool = true)
public init(requests: [ModelOnboardingRequest], source: ModelOnboardingSource)
public init(results: [ModelResult], message: String? = nil)
public init(revision: String, isCurrent: Bool, isComplete: Bool = true, freesBytes: Int64, sharedBytes: Int64 = 0)
public init(role: String = "Model", repoID: String, expectedRevision: String? = nil, validates: Bool = true)
public init(serverName: String, onCancel: @escaping () -> Void)
public let call: PendingToolCall
public let core: MCPServerManager
public let id: UUID
public let request: MCPElicitationRequest
public nonisolated func requestDecision(for call: PendingToolCall) async -> Bool
public private(set) var completed: [InstalledModel] = []
public private(set) var currentRevision: String?
public private(set) var isLoading = false
public private(set) var isRunning = false
public private(set) var items: [ModelOnboardingItem]
public private(set) var message: String?
public private(set) var rollbackRevision: String?
public private(set) var state: State = .idle
public private(set) var versions: [ModelVersionRow] = []
public static func unableToRun(_ message: String) -> ProviderTestOutcome
public struct AIModelsSettingsView: View
public struct ClaudeAuthField: View
Expand All @@ -2089,32 +2123,87 @@ public struct MCPOAuthWaitingView: View
public struct MCPPromptsView: View
public struct MCPResourcesView: View
public struct MCPServerPickerView: View
public struct ModelFileChange: Sendable, Equatable, Identifiable
public struct ModelOnboardingItem: Sendable, Equatable, Identifiable
public struct ModelOnboardingRequest: Sendable, Equatable, Identifiable
public struct ModelOnboardingSource: Sendable
public struct ModelOnboardingView: View
public struct ModelPickerView: View
public struct ModelResult: Sendable, Equatable, Identifiable
public struct ModelUpdateActions: Sendable
public struct ModelUpdateOffer: Sendable, Equatable
public struct ModelUpdateView: View
public struct ModelVersionRow: Sendable, Equatable, Identifiable
public struct ModelVersionsView: View
public struct ProviderSettingsSection: View
public struct ProviderTestOutcome: Sendable, Equatable
public struct RemoteProviderDraft: Identifiable, Hashable, Sendable
public struct ToolConfirmationRequest: Identifiable, Sendable
public var addMenuLabel: String
public var apiKey: String
public var apply: @Sendable (
public var available: String
public var baseURL: String
public var body: some View
public var canUpdate: Bool
public var cancel: @Sendable (String) -> Void
public var changes: [ModelFileChange]
public var check: @Sendable () async throws -> ModelUpdateOffer
public var configured: Bool
public var current: String
public var detail: String
public var displayName: String
public var download: @Sendable (String, @escaping @Sendable (DownloadProgress) -> Void) async throws -> InstalledModel
public var download: ModelOnboardingStepState = .pending
public var downloadFraction: Double?
public var downloadedBytes: Int64 = 0
public var errorDescription: String?
public var expectedRevision: String?
public var freesBytes: Int64
public var hasFailed: Bool { [validate, download, pin].contains { if case .failed = $0 { true } else { false } } }
public var hasFailed: Bool { items.contains { $0.hasFailed } }
public var hasStarted: Bool { items.contains { $0.validate != .pending } }
public var id: String { modelId }
public var id: String { path }
public var id: String { repoID }
public var id: String { request.repoID }
public var id: String { revision }
public var id: String { scheme }
public var isBusy: Bool
public var isComplete: Bool
public var isComplete: Bool { if case .done = pin { true } else { false } }
public var isCurrent: Bool
public var isFinished: Bool { !isRunning && !items.isEmpty && items.allSatisfy(\.isComplete) }
public var isSwitching: Bool { state == .switching }
public var isUpToDate: Bool { current == available }
public var kind: Kind
public var kind: RemoteProviderKind
public var maxSearches: Int
public var message: String?
public var modelId: String
public var models: [String]
public var newSize: Int64?
public var ok: Bool
public var oldSize: Int64?
public var ownership: ModelUpdateOwnership
public var path: String
public var pauseInference: (@Sendable () async throws -> Void)?
public var pin: ModelOnboardingStepState = .pending
public var repoID: String
public var request: ModelOnboardingRequest
public var results: [ModelResult]
public var revision: String
public var role: String
public var scheme: String
public var sharedBytes: Int64
public var shippedRevision: String?
public var sortedServers: [MCPServerState]
public var statusText: String?
public var summary: String
public var totalBytes: Int64 = 0
public var validate: @Sendable (String) async throws -> PreflightResult?
public var validate: ModelOnboardingStepState = .pending
public var validates: Bool
public var webSearchEnabled: Bool
public var webSearchSupported: Bool
```
4 changes: 2 additions & 2 deletions docs/migrating-to-1.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ The `examples/*/Package.swift` files resolve each SDK module as a `binaryTarget`
checksum, keyed by version. A newer version is a new entry:

```swift
"1.0.0-beta.3": SDKRelease(
url: "https://github.com/ancientcomputing/locallm/releases/download/v1.0.0-beta.3/LocalLMLabSDKCore-1.0.0-beta.3.xcframework.zip",
"1.0.0-GA": SDKRelease(
url: "https://github.com/ancientcomputing/locallm/releases/download/v1.0.0-GA/LocalLMLabSDKCore-1.0.0-GA.xcframework.zip",
checksum: "<the .sha256 asset next to the zip on that release>"
),
```
Expand Down
Loading
Loading