LedgeKitDocs
Evaluations
Developer documentation

Evaluate a dataset

Execute current application code against saved inputs and preserve report provenance.

This guide uses Apple Evaluations in an iOS/macOS 27 test target. LedgeKit loads inputs and reports results; your evaluation owns execution and scoring.

Construct native samples

Use the SummaryInput type from the dataset guide. Preserve each item's ID, original JSON, and source in its native sample:

import Evaluations
import Foundation
import LedgeKit
import LedgeKitTesting

@available(iOS 27.0, macOS 27.0, *)
struct SummarySample: SampleProtocol {
    let id: String
    let source: LedgeDatasetSource
    let savedInput: LedgeJSONValue
    let arguments: SummaryInput
    var input: String { id }
    var expected: LedgeEvaluationResponse? { nil }

    init(_ item: LedgeDatasetInput<SummaryInput>) {
        id = item.id.uuidString.lowercased()
        source = item.source
        savedInput = item.input
        arguments = item.value
    }
}

The saved source describes where the input came from. A fresh model request made during evaluation has its own identity.

Define execution and scoring

The following evaluation calls the current summarize(_:client:) function from the Foundation Models guide. The completion metric reports execution status. Review semantic quality from the captured responses.

@available(iOS 27.0, macOS 27.0, *)
struct SummaryEvaluation: Evaluation {
    let samples: [SummarySample]
    var dataset: ArrayLoader<SummarySample> { .init(samples: samples) }

    func subject(from sample: SummarySample) async throws -> ModelSubject<LedgeEvaluationResponse> {
        let capture = LedgeEvaluationCapture()
        let directory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
        defer { try? FileManager.default.removeItem(at: directory) }
        let client = try LedgeClient(serviceName: "my-app", transport: capture, localDirectory: directory)
        // Execute the real implementation. Its application return value is not model output.
        _ = try await summarize(sample.arguments.text, client: client)
        await client.flush()
        guard let trace = await capture.envelopes.first?.trace else {
            throw LedgeEvaluationError.invalidReport("No session captured")
        }
        return ModelSubject(value: trace.response)
    }

    var evaluators: Evaluators {
        Evaluator<SummarySample> { _, subject in
            subject.value.trace?.status == .success
                ? Metric("completed").passing()
                : Metric("completed").failing()
        }
    }

    func aggregateMetrics(using aggregator: inout MetricsAggregator) {
        aggregator.computeMean(of: Metric("completed"))
    }
}

The SDK response contains the captured trace. For an isolated model call, use span.response. The viewer displays the actual text or structured response; requests, timing, errors and transcripts remain details. Pipelines retain every call, including retries and fallbacks. The uploader rejects arbitrary application values and outputs that differ from their captured transcript.

Run, save, and validate

let dataset = try await LedgeDatasetClient(readKey: readKey).load(datasetID)
let inputs = try dataset.decodeInputs(
    SummaryInput.self, sourceKind: .span, definitionName: "summarizer"
) { input in
    guard !input.text.isEmpty else { throw InputError.emptyText }
}
let evaluation = SummaryEvaluation(
    samples: inputs.map(SummarySample.init)
)
let result = try await evaluation.run(info: dataset.evaluationInfo())
let reportURL = try result.saveJSON(
    to: reportDirectory, includeReportMetadata: true
)
try dataset.validateResults(in: reportURL)
let reporter = LedgeEvaluationReporter(writeKey: writeKey)
try await reporter.submit(reportURL)

reportDirectory is a writable URL where Apple Evaluations exports the report; the reporter manages its own durable upload storage.

evaluationInfo() records the full expected dataset membership before execution. validateResults(in:) requires every loaded item ID exactly once in the report. It does not score outputs or assert that native execution succeeded. Verify that result.errors.hasFailures is false and assert your quality metrics in the test as well.

Save the report before validation so incomplete results remain inspectable. A delivery retry should submit the saved file instead of calling run again.

Review the result

Open Evaluations in the write key's app and environment. Inspect sample responses and metrics, then use the recorded dataset/source information to follow the result back to its inputs where available.

On this page