Sample CodemacOSReviewed 2026-11-01View on Apple Developer

Evaluating a Core AI model

At a glance

Item Summary
Purpose Export an open-source language model with Core AI, run it through a Foundation Models LanguageModelSession, and use the Evaluations framework to compare its three-way sentiment classification with the system’s default on-device model.
App architecture Two Xcode targets. A small SwiftUI macOS app (CoreAIEvals): one view owns an @Observable screen model, which calls a stateless SentimentClassifier service. A unit-test bundle (CoreAIEvalsTests) hosted by the app reuses that same service as the evaluation subject.
Main patterns Service object (SentimentClassifier, shared by app and tests), Actor isolation (CoreAIModelStore caches the loaded model), Binding-based state propagation (@Bindable view bindings into the model)
Project style Feature folders inside the app target (Models/, Inference/, Views/), a separate test target for evaluations and the labeled dataset, and a shell script that exports the model into a bundled folder reference.
Execution model The app target compiles with SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor, so undeclared app types are main-actor isolated. The one explicit actor is CoreAIModelStore. The screen model creates one retained, cancellable Task. The test target has no default-isolation setting.
State/event model Observation (@Observable ClassifierModel, held in the view’s @State), plus a closed Phase enum for the classification lifecycle.
Key frameworks/packages Swift modules SwiftUI, Observation, Foundation, FoundationModels, and CoreAILanguageModels (app), plus Testing and Evaluations (tests). The CoreAILM product comes from the remote coreai-models package and is linked only into the app target.

Project structure

CoreAIEvals.xcodeproj           # 2 targets: app + hosted unit-test bundle
CoreAIEvals/                    # app target (synchronized folder)
├── CoreAIEvalsApp.swift        # @main, one WindowGroup
├── Models/
│   └── ClassifierModel.swift   # @Observable screen state + Task lifecycle
├── Inference/
│   ├── ClassifierBackend.swift # .coreAI / .onDeviceDefault selector
│   ├── CoreAIModelStore.swift  # actor: load-once model cache
│   ├── SentimentClassifier.swift # stateless service (app + tests)
│   └── SentimentVerdict.swift  # @Generable output schema
└── Views/
    ├── SentimentClassifierView.swift
    ├── ResultView.swift
    └── VerdictView.swift
CoreAIEvalsTests/               # test target, @testable import CoreAIEvals
├── SentimentClassificationEvaluationTests.swift
└── SentimentDataset.swift      # 60 labeled ModelSample values
scripts/prepare-model.sh        # exports Models/coreai-model (not committed)

Structure observations

  • The project has two native targets. CoreAIEvals is an application (CoreAIEvals.xcodeproj/project.pbxproj:133). CoreAIEvalsTests is a unit-test bundle (CoreAIEvals.xcodeproj/project.pbxproj:156) that depends on the app (CoreAIEvals.xcodeproj/project.pbxproj:146) and runs inside it through TEST_HOST (CoreAIEvals.xcodeproj/project.pbxproj:469).
  • The test target links no package product of its own. It reaches app code through @testable import CoreAIEvals (CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:11). That is why the evaluation exercises the shipping classification path instead of a copy.
  • The model is not source code. prepare-model.sh exports Qwen/Qwen3-0.6B into Models/coreai-model (scripts/prepare-model.sh:27, scripts/prepare-model.sh:28). The project adds that folder to the app’s Resources phase (CoreAIEvals.xcodeproj/project.pbxproj:206), and CoreAIModelStore looks it up by name at runtime.
  • The folders reflect responsibility: Inference/ holds everything that talks to models, Models/ holds UI-facing state, and Views/ holds rendering only.

Overall architecture

Reference code

CoreAIEvals/Inference/SentimentClassifier.swift:37 — makeSession(for:) is the single branch point between the two backends

private static func makeSession(for backend: ClassifierBackend) async throws -> LanguageModelSession {
    switch backend {
    case .coreAI:
        let coreAIModel = try await CoreAIModelStore.shared.model()
        return LanguageModelSession(model: coreAIModel, instructions: instructions)
    case .onDeviceDefault:
        return LanguageModelSession(model: .default, instructions: instructions)
    }
}

CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:113 — the evaluation calls the same entry point the UI uses

func subject(from sample: ModelSample<SentimentVerdict>) async throws -> ModelSubject<SentimentVerdict> {
    ModelSubject(value: try await SentimentClassifier.classify(sample.prompt, using: backend))
}

Interpretation

Observed: both the UI (CoreAIEvals/Models/ClassifierModel.swift:96) and the evaluation (CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:114) call SentimentClassifier.classify(_:using:). The only difference between the two measured configurations is the ClassifierBackend value. Both backends go through the same LanguageModelSession API, with the same instructions and the same @Generable output type.

Inference: this is the main architectural lesson of the sample. Because the classifier boundary is the single seam that both production code and evaluation code call, the evaluation scores exactly what the app ships. Comparing two models becomes a one-parameter change, not two code paths.

Ownership and state

Ownership evidence

CoreAIEvals/Views/SentimentClassifierView.swift:12 — the view creates and owns the screen model

struct SentimentClassifierView: View {
    @State private var model: ClassifierModel

    /// Accepts a model so previews can supply one parked in a fixed phase;
    /// defaults to a live model for the running app.
    init(model: ClassifierModel = ClassifierModel()) {
        _model = State(initialValue: model)
    }
    // ...
}

CoreAIEvals/Inference/CoreAIModelStore.swift:29 — a process-wide actor owns the loaded model

actor CoreAIModelStore {
    static let shared = CoreAIModelStore()
    // ...
    private var cached: CoreAILanguageModel?
    // ...
}
Owner Object or state Relationship Mutation authority
SentimentClassifierView ClassifierModel Creates it by default (or accepts one for previews) and stores it in @State (CoreAIEvals/Views/SentimentClassifierView.swift:16) The view writes text and useOnDeviceDefault through bindings, and calls classify()
ClassifierModel text, useOnDeviceDefault Stored, implicitly internal vars (CoreAIEvals/Models/ClassifierModel.swift:29, CoreAIEvals/Models/ClassifierModel.swift:32) Any module code. In practice, the view’s $model bindings
ClassifierModel phase: Phase private(set) (CoreAIEvals/Models/ClassifierModel.swift:35) Only ClassifierModel, including its same-file #if DEBUG preview factory (CoreAIEvals/Models/ClassifierModel.swift:137)
ClassifierModel task: Task<Void, Never>? private. Retains the most recent task (CoreAIEvals/Models/ClassifierModel.swift:102) classify() only
CoreAIModelStore cached: CoreAILanguageModel? private, filled once on first use (CoreAIEvals/Inference/CoreAIModelStore.swift:50) The actor’s own model() method
SentimentClassifier LanguageModelSession Creates a fresh session on every call and returns it to classify without storing it (CoreAIEvals/Inference/SentimentClassifier.swift:60) No stored state. It is a caseless enum
SentimentClassificationEvaluationTests One SentimentClassificationEvaluation per backend static let values (CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:121, CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:129) Immutable after construction

Derived values are computed properties, not stored state. These are selectedBackend (CoreAIEvals/Models/ClassifierModel.swift:43), unavailableMessage (CoreAIEvals/Models/ClassifierModel.swift:52), and canClassify (CoreAIEvals/Models/ClassifierModel.swift:71). So the only lifecycle variable the view has to observe is phase.

Concurrency, scheduling, and thread safety

Evidence limit: actor isolation, async/await, or Task creation does not by itself prove background-thread execution. A Sendable conformance alone does not prove thread-safe mutation.

Concern Source mechanism Verified placement or handoff Evidence
Main-actor isolation (target-wide) SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor on the app target App declarations without explicit isolation, including ClassifierModel, SentimentClassifier, and the views, are main-actor isolated. No source file writes @MainActor. CoreAIEvals.xcodeproj/project.pbxproj:402
Isolation opt-out nonisolated var displayName The one explicit opt-out in the app. The test target has no default-isolation setting, and it reads displayName synchronously in name and in @Test(... info:) arguments. CoreAIEvals/Inference/ClassifierBackend.swift:17, CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:40, CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:123
Task creation Plain Task { ... } in classify() Created from a main-actor-isolated method, so the closure inherits the main actor. Its writes to phase happen on the main actor. The model work happens across the await inside it. CoreAIEvals/Models/ClassifierModel.swift:87
Task retention and cancellation private var task, task?.cancel() ClassifierModel cancels any in-flight task before starting a new one, stores the new one, and returns it (@discardableResult). The view discards the return value. No deinit cancels the task. CoreAIEvals/Models/ClassifierModel.swift:84, CoreAIEvals/Models/ClassifierModel.swift:102, CoreAIEvals/Views/SentimentClassifierView.swift:35
Actor isolation actor CoreAIModelStore Serializes access to cached. Callers must await CoreAIModelStore.shared.model(). CoreAIEvals/Inference/CoreAIModelStore.swift:29, CoreAIEvals/Inference/SentimentClassifier.swift:40
Suspension inside the actor try await CoreAILanguageModel(resourcesAt:) model() suspends while loading the model, before it assigns cached. Actor reentrancy allows another caller in during that suspension. CoreAIEvals/Inference/CoreAIModelStore.swift:49
Framework async calls session.respond(to:generating:) An awaited Foundation Models call. Its executor is not established by the sample source. CoreAIEvals/Inference/SentimentClassifier.swift:61
Test-side async async throws subject(from:) and @Test functions These are nonisolated in the test target. Calling the main-actor SentimentClassifier.classify from here requires await. CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:113

No DispatchQueue, lock, Task.detached, MainActor.run, Combine, or completion handler appears in the source. The project enables SWIFT_APPROACHABLE_CONCURRENCY on both targets (CoreAIEvals.xcodeproj/project.pbxproj:401, CoreAIEvals.xcodeproj/project.pbxproj:465) under Swift 6 (CoreAIEvals.xcodeproj/project.pbxproj:405). The sample does not depend on any behavior of those settings beyond what the table shows.

Reference code

CoreAIEvals/Models/ClassifierModel.swift:83 — cancel the previous task, publish .classifying, then retain the new task

func classify() -> Task<Void, Never> {
    task?.cancel()

    phase = .classifying
    let task = Task {
        // ...
        if let unavailableMessage {
            phase = .failed(unavailableMessage)
            return
        }
        do {
            let verdict = try await SentimentClassifier.classify(Prompt(text), using: selectedBackend)
            phase = .classified(verdict)
        } catch {
            phase = .failed(Self.message(for: error))
        }
    }
    self.task = task
    return task
}

CoreAIEvals/Inference/CoreAIModelStore.swift:42 — the load-once path

func model() async throws -> CoreAILanguageModel {
    if let cached { return cached }

    guard let url = Bundle.main.url(forResource: Self.modelResourceName, withExtension: nil) else {
        throw CoreAIModelStoreError.modelNotBundled(resourceName: Self.modelResourceName)
    }

    let model = try await CoreAILanguageModel(resourcesAt: url)
    cached = model
    return model
}

Observations and inferences on these boundaries:

  • Observed: the task body checks neither Task.isCancelled nor which task is current. Inference: if a caller cancels a run that then finishes or throws, the old task can still write phase. In the shipped UI this cannot happen through the button: canClassify returns false while phase is .classifying (CoreAIEvals/Models/ClassifierModel.swift:72), and the button is disabled on that value (CoreAIEvals/Views/SentimentClassifierView.swift:37). The cancel() path matters only to programmatic callers of the returned task.
  • Observed: CoreAIModelStore.model() checks cached before the await and assigns it after. Inference: two concurrent first callers could each start a load. The actor keeps cached free of data races, but it does not deduplicate in-flight loads. The source does not show whether the Evaluations framework calls subject(from:) concurrently.
  • Inference: the explicit nonisolated on ClassifierBackend.displayName exists because the test target is nonisolated by default. The tests use the name synchronously when they build Evaluation.name and the @Test trait arguments.

State propagation, frameworks, and dependencies

Evidence limit: an import proves a source-level compilation dependency at the cited line. It does not prove runtime use, architectural adoption, or whether a Swift package is a direct application dependency.

Category Mechanism or module Verified role Evidence
State propagation @Observable macro ClassifierModel publishes reads of text, useOnDeviceDefault, and phase to SwiftUI CoreAIEvals/Models/ClassifierModel.swift:14
State propagation @State + @Bindable The view owns the model in @State and creates a local @Bindable to derive $model.text and $model.useOnDeviceDefault CoreAIEvals/Views/SentimentClassifierView.swift:21, CoreAIEvals/Views/SentimentClassifierView.swift:26, CoreAIEvals/Views/SentimentClassifierView.swift:32
State propagation Value passing ResultView receives phase and unavailableMessage as let values. VerdictView receives a SentimentVerdict. CoreAIEvals/Views/ResultView.swift:13, CoreAIEvals/Views/VerdictView.swift:12
Swift module import Foundation, Observation, FoundationModels Screen model. FoundationModels supplies SystemLanguageModel availability and LanguageModelError CoreAIEvals/Models/ClassifierModel.swift:9, CoreAIEvals/Models/ClassifierModel.swift:10, CoreAIEvals/Models/ClassifierModel.swift:11
Swift module import FoundationModels LanguageModelSession, @Generable, @Guide CoreAIEvals/Inference/SentimentClassifier.swift:9, CoreAIEvals/Inference/SentimentVerdict.swift:9
Swift module import CoreAILanguageModels Supplies CoreAILanguageModel(resourcesAt:) CoreAIEvals/Inference/CoreAIModelStore.swift:9
Swift module import SwiftUI App entry and views CoreAIEvals/CoreAIEvalsApp.swift:8
Swift module import (tests) Testing, Evaluations, @testable CoreAIEvals Test target. CoreAIEvals is the local app target’s module, not a package CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:9, CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:10, CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:11
Package declaration Remote coreai-models, branch main, product CoreAILM The product is a direct package dependency of the app target only. The project file does not say which Swift module(s) the product vends; the app imports CoreAILanguageModels. CoreAIEvals.xcodeproj/project.pbxproj:129, CoreAIEvals.xcodeproj/project.pbxproj:534, CoreAIEvals.xcodeproj/project.pbxproj:546
Resource dependency Folder reference Models/coreai-model Copied into the app bundle. Found by name with Bundle.main.url(forResource:withExtension:) CoreAIEvals.xcodeproj/project.pbxproj:29, CoreAIEvals/Inference/CoreAIModelStore.swift:45

The test code reaches the bundled model through Bundle.main, which works because the bundle is hosted in the app (TEST_HOST). Inference: this is why the tests need no resource or package setup of their own.

Class and protocol design

Reference code

CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:37 — one evaluation type, parameterized by backend

struct SentimentClassificationEvaluation: Evaluation {
    let backend: ClassifierBackend

    var name: String { backend.displayName }

    let dataset = SentimentDataset.all

    /// Fraction of all labeled samples whose predicted sentiment matched the label.
    let accuracy = Metric("Accuracy")
    // ...
}

CoreAIEvals/Inference/SentimentVerdict.swift:23 — the guided-generation schema is also the evaluation’s expected-value type

@Generable
struct SentimentVerdict: Codable, Equatable {
    @Guide(description: "One short sentence explaining why this sentiment was chosen.")
    let reasoning: String
    @Guide(description: "How confident you are in the sentiment choice.", .range(0...100))
    let confidence: Int
    @Guide(description: "The overall sentiment: negative, neutral, or positive.")
    let sentiment: Sentiment
}
Type Responsibility Depends on
CoreAIEvalsApp @main entry. Hosts one WindowGroup SentimentClassifierView
SentimentClassifierView Input, backend toggle, Classify button, result area ClassifierModel
ClassifierModel UI-facing state, availability gate, task lifecycle, error-to-copy mapping SentimentClassifier, SystemLanguageModel, LanguageModelError
SentimentClassifier Stateless classification service. Builds a session per call and requests a SentimentVerdict CoreAIModelStore, LanguageModelSession
CoreAIModelStore Locates the bundled model folder and caches the loaded model CoreAILanguageModel, Bundle.main
ClassifierBackend Closed choice of model. Supplies display names to the UI and to reports —
SentimentVerdict / Sentiment @Generable output schema, also the dataset’s expected-value type FoundationModels
SentimentClassificationEvaluation Evaluation conformance: dataset, 10 metrics, evaluators, aggregation, subject SentimentClassifier, SentimentDataset
SentimentDataset 60 labeled ModelSample values in an ArrayLoader Evaluations

The only protocol conformances are to framework protocols: App, View, Evaluation, LocalizedError, Codable, and Equatable. The sample declares no local protocol. Backend selection uses an enum switch, not protocol substitution, so this analysis does not claim protocol-oriented design or a Strategy object.

Access control

Symbol Access Verified effect Likely rationale
ClassifierModel.phase private(set) Code outside ClassifierModel’s file can read but not assign it. The same-file preview(text:phase:) extension can assign it (CoreAIEvals/Models/ClassifierModel.swift:137). Inference: classify() stays the only production path that moves the lifecycle, while previews can still park a phase.
ClassifierModel.task private Only ClassifierModel can read, cancel, or replace the in-flight task (CoreAIEvals/Models/ClassifierModel.swift:38). Inference: the model has sole authority over cancellation.
SentimentClassifierView.model private (@State) Only the view can reach the storage (CoreAIEvals/Views/SentimentClassifierView.swift:12). Inference: the view owns the model’s lifetime. Previews inject a model through init instead.
CoreAIModelStore.cached, modelResourceName private, private static Callers cannot bypass model() or change the resource name (CoreAIEvals/Inference/CoreAIModelStore.swift:34, CoreAIEvals/Inference/CoreAIModelStore.swift:37). Inference: keep the load-once invariant inside the actor.
SentimentClassifier.instructions, makeSession(for:) private static Callers must go through classify(_:using:) (CoreAIEvals/Inference/SentimentClassifier.swift:14, CoreAIEvals/Inference/SentimentClassifier.swift:37). Inference: the app and the tests cannot drift on prompt or session setup.
recallEvaluator, precisionEvaluator, SentimentEvaluator file private Visible only in the test file (CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:15, CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:18, CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:28). Inference: scoring helpers are local implementation details.
VerdictView.color private Visible only inside VerdictView (CoreAIEvals/Views/VerdictView.swift:35). Inference: a presentation detail.

The remaining app types are implicitly internal. The test target can see them only because it uses @testable import (CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:11). The sample declares nothing public or open.

Reference code

CoreAIEvals/Models/ClassifierModel.swift:34 — read-only lifecycle and private task handle

@Observable
final class ClassifierModel {
    // ...
    /// The current stage of the classification lifecycle.
    private(set) var phase: Phase = .idle

    /// Any in-flight classification task.
    private var task: Task<Void, Never>?
    // ...
}

Logic ownership and placement

Logic Owning type or file Placement rationale
Prompt instructions, session construction, guided generation SentimentClassifier Observed: kept in one private place and called by both the app and the evaluation. Inference: one definition of “the classifier” for both.
Model location and one-time loading CoreAIModelStore Observed: an actor with a shared instance. Inference: loading is expensive (the comment says so, CoreAIEvals/Inference/CoreAIModelStore.swift:5), so it is cached process-wide.
Availability gating for the default model ClassifierModel.unavailableMessage Observed: only .onDeviceDefault is gated, through SystemLanguageModel.default.availability (CoreAIEvals/Models/ClassifierModel.swift:54). Core AI load failures surface as thrown errors instead.
Error-to-user-copy mapping ClassifierModel.message(for:) Observed: maps LanguageModelError cases to strings (CoreAIEvals/Models/ClassifierModel.swift:109). The evaluation does not use this mapping.
Result rendering per phase ResultView, VerdictView Observed: pure functions of the values passed in.
Metrics (accuracy, per-class precision/recall, confidence splits) and aggregation SentimentClassificationEvaluation Observed: evaluators and aggregateMetrics(using:) (CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:62, CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift:98).
Ground-truth data SentimentDataset.all Observed: an inline ArrayLoader<ModelSample<SentimentVerdict>> (CoreAIEvalsTests/SentimentDataset.swift:14).
Model export scripts/prepare-model.sh Observed: outside the build. It runs uv run coreai.llm.export and writes to Models/coreai-model.

Design patterns

Pattern Source evidence Purpose or tradeoff
Service object CoreAIEvals/Inference/SentimentClassifier.swift:12 (enum SentimentClassifier). The file abstract calls it “The classification service” and says it is “shared by the app and the evaluations” (CoreAIEvals/Inference/SentimentClassifier.swift:5) A stateless caseless-enum service gives the UI and the evaluation one shared seam. The tradeoff: its static methods cannot be swapped by injection. The backend enum is the only variation point.
Actor isolation CoreAIEvals/Inference/CoreAIModelStore.swift:29 (actor CoreAIModelStore) Protects the cached model from data races. It does not deduplicate concurrent first loads (see Concurrency).
Binding-based state propagation CoreAIEvals/Views/SentimentClassifierView.swift:21 (@Bindable var model = model) and the $model.text / $model.useOnDeviceDefault bindings The view edits model-owned state directly. ClassifierModel stays the source of truth.

Removed from the draft: “Central store”. CoreAIModelStore is a load-once cache for one framework object, not a store of application state, and it has no dispatch or reducer path. The Store suffix describes its caching role only. The sample is not labeled MVVM. ClassifierModel does play a screen-model role, but the source has only one view and one model and declares no view-model abstraction.

Main application flow

Reference code

CoreAIEvals/Inference/SentimentClassifier.swift:56 — session per call, typed result

    static func classify(
        _ prompt: Prompt,
        using backend: ClassifierBackend
    ) async throws -> SentimentVerdict {
        let session = try await makeSession(for: backend)
        let response = try await session.respond(to: prompt, generating: SentimentVerdict.self)
        return response.content
    }

The evaluation path is the same from Svc onward. Evaluations calls subject(from:) for each dataset sample, and the evaluators score subject.value against input.expected.

Naming conventions

  • Types: role suffixes match observed roles. …App is the entry, …View renders, ClassifierModel holds UI state, CoreAIModelStore caches (a cache, not a state store), SentimentClassifier is the service, ClassifierBackend is the choice enum, SentimentVerdict is the result value, and …Evaluation/…Tests/…Dataset are the test roles. CoreAIModelStoreError pairs its error type with its owner.
  • Protocols: no local protocol. Conformances use framework names (Evaluation, LocalizedError).
  • Methods: verbs for commands (classify(), classify(_:using:)), make… for construction (makeSession(for:)), noun-style async accessors (model()), and can… for Boolean gates (canClassify). recallEvaluator(for:metric:) / precisionEvaluator(for:metric:) name what they build.
  • Enum cases: lifecycle states read as adjectives or past participles (idle, classifying, classified, failed).
  • Files and extensions: one primary type per file, named after it. The #if DEBUG preview factory sits in an extension in the model’s own file, so it can reach the private(set) setter.

Architecture takeaways

  • Put the model call behind one seam (SentimentClassifier.classify(_:using:)) and have the UI and the evaluation both call it. Then the evaluation measures the shipping path, and comparing models is a one-enum-case change.
  • Use the guided-generation output type (SentimentVerdict) as the evaluation’s expected type too. That way the schema, the UI, and the scoring share one definition.
  • Target-wide MainActor default isolation removes most annotations. But it creates a boundary with a test target that is nonisolated by default, and the sample handles that with an explicit nonisolated member.
  • A retained, cancellable Task plus a closed Phase enum is enough lifecycle machinery for a single-request screen. The UI guard (canClassify), not task identity, is what prevents overlapping runs.
  • An actor makes a cache race-free, but not deduplicated. If you need single-flight loading, store the in-flight Task too.

Source map

Source file Relevant symbols
CoreAIEvals/CoreAIEvalsApp.swift CoreAIEvalsApp
CoreAIEvals/Views/SentimentClassifierView.swift SentimentClassifierView, @State model, @Bindable
CoreAIEvals/Views/ResultView.swift ResultView
CoreAIEvals/Views/VerdictView.swift VerdictView, color
CoreAIEvals/Models/ClassifierModel.swift ClassifierModel, Phase, classify(), unavailableMessage, canClassify, message(for:), preview(text:phase:)
CoreAIEvals/Inference/SentimentClassifier.swift SentimentClassifier, instructions, makeSession(for:), classify(_:using:)
CoreAIEvals/Inference/CoreAIModelStore.swift CoreAIModelStore, shared, cached, model(), CoreAIModelStoreError
CoreAIEvals/Inference/ClassifierBackend.swift ClassifierBackend, displayName
CoreAIEvals/Inference/SentimentVerdict.swift Sentiment, SentimentVerdict
CoreAIEvalsTests/SentimentClassificationEvaluationTests.swift SentimentClassificationEvaluation, SentimentClassificationEvaluationTests, recallEvaluator, precisionEvaluator
CoreAIEvalsTests/SentimentDataset.swift SentimentDataset.all
CoreAIEvals.xcodeproj/project.pbxproj Targets, SWIFT_DEFAULT_ACTOR_ISOLATION, TEST_HOST, CoreAILM package product, coreai-model resource
scripts/prepare-model.sh HF_MODEL_ID, MODEL_NAME, export command