Record your first request
Record a native Foundation Models response and inspect its span.
1. Add LedgeKit
Add https://github.com/noahtovares/ledgekit-ios to your Xcode project and link the
LedgeKit product. See
installation for the exact revision and toolchain requirements.
2. Create an app and write key
In the console, create an app with service name
my-app. Select its Test environment and create a write key. Supply
that key through local configuration. The key selects the app and environment;
serviceName must match the app. Read keys cannot upload traces.
3. Record a summary
import Foundation
import FoundationModels
import LedgeKit
struct SummaryArguments: Codable, Sendable { let text: String }
enum SummaryError: Error { case modelUnavailable }
@available(iOS 27.0, macOS 27.0, *)
func recordSummary(writeKey: String) async throws -> String {
guard SystemLanguageModel.default.availability == .available else {
throw SummaryError.modelUnavailable
}
let client = try LedgeClient(serviceName: "my-app", writeKey: writeKey)
let agent = try LedgeAgent(name: "summarizer", version: 1)
let session = client.session(
instructions: "Summarize the text in one sentence.",
agent: agent
)
let input = SummaryArguments(text: "A durable outbox saves spans before uploading them.")
let response = try await session.respond(
to: "Summarize: \(input.text)",
recording: .init(input: input)
)
let delivery = await client.flush()
print("Delivered: \(delivery.delivered); pending: \(delivery.pending)")
return response.content
}The native response passes through unchanged. This standalone call creates one
trace named summarizer, containing one span for the same agent. The adapter
finishes the trace on success, failure, or cancellation. Startup and recovery are
automatic. Keep one client alive at application scope for repeated calls.
4. Inspect the result
Open Traces in the same app's Test environment. Select summarizer and
its span to inspect model input, model output, original application input, agent
version, and timing. Full provider capture retains the native transcript.
flush() reports delivery state; a temporary failure leaves the payload pending.
See delivery if the trace has not arrived.
Continue with structured responses, grouped traces, or datasets.