Chapters

Hide chapters

Apple Foundation Models

First Edition · iOS 26.5 · Swift 6.3 · Xcode 26.4

Section I: Apple Foundation Models

Section 1: 9 chapters
Show chapters Hide chapters

4. Context Management & Model Safety
Written by Bill Morefield

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In the previous chapters, you learned the basics of interacting with Foundation Models and ways to tune and work with the model to fit your use cases better. You also learned how to produce good prompts to the model and the importance of testing and validating your prompts and responses. In this chapter, you will explore two more concepts you need to manage when working with Foundation Models. First, you will explore ways to handle the small context window and some approaches when the window overflows. Then you will explore safety and guardrails in Foundation Models, along with ways to adjust them when dealing with content.

Managing the Context Window

The greatest challenge you will likely face when using Foundation Models is the small context window. Recall from chapter one that a token is about 3/4 of a word. That means the 4,096 tokens translate into a little over 3,000 words in English and similar languages. This ratio is a guideline and will vary depending on the type of content and language. Logographic and symbolic languages like Chinese, Japanese, and Korean have ratios closer to one character or syllable per token. Text with technical jargon and rare words will use more tokens than common words.

The most important step is to be aware of your tokens. Everything you do with a session adds tokens to the context. The instructions, prompts, and responses all add up. When you explore tools, you’ll see that tool calls and responses add tokens to the context. While 3,000 words sounds like a lot, that’s less than a chapter of this book. An important part of using Foundation Models is managing this max context length and handling the cases where it fills up.

Viewing Context Length with Instruments

A major weakness in the initial release of Foundation Models was the lack of a way to get token counts for text and the session’s context window. As you saw in Chapter Two, starting in the 26.4 versions, you can directly check token usage and limits. In earlier dot versions, the only way to get token information was to run Instruments against your running app. That can still be useful to see how your app adjusts and changes while running. At the time of writing, Instruments will always show zero tokens when running against simulators, so you will need to do this on a real device.

Instrument Trace of Foundation Model Session
Icswnahihv Wcobe oy Ceufpikeuq Pedis Fawguuc

Handling Context Overflow

When your app nears the context limit, you will need to determine how to handle this state. There are several ways to approach a full or nearly full context window, but the most commonly used are summarizing the current context window and taking only the latest parts of the conversation to start a new session. You’ll implement both. First, you’ll attempt to summarize the current conversation and then fall back to trimming the earliest parts of the chat if that fails.

@State private var isCompactingContext = false
@MainActor
private func summarizeChat() async {
  isCompactingContext = true
  defer {
    isCompactingContext = false
  }
}
let entriesToKeep = session.transcript
  .filter {
    if case .response = $0 {
      return true
    }
    return false
  }
let textToSummarize = entriesToKeep.map {
  $0.description
}
  .joined(separator: "\n")
let summaryInstructions = """
  You are given a conversation transcript.

  Your job is to extract and compress it into memory.

  You are NOT an assistant responding to the conversation.

  You MUST NOT answer any user requests.

  STEP 1: Identify all user requests in the transcript.
  STEP 2: For each request, extract a short summary of the assistant's response.

  Do not skip any requests. Include earlier and later ones.
"""
let summarySession = LanguageModelSession(instructions: summaryInstructions)
let summarizedText = try? await summarySession.respond(to: textToSummarize)
// 1
messages = []

// 2
if let summary = summarizedText?.content {
  // 3
  useSummary(summary)
} else {
  // 4
  trimSession(entriesToKeep)
}
await updatedContextWindowUsed()
func useSummary(_ summary: String) {
  // 1
  var entries: [Transcript.Entry] = []

  // 2
  if let instructions = promptSettings.instructions {
    entries.append(
      // 3
      .instructions(
        .init(
          // 4
          segments: [
            // 5
            .text(
              .init(content: instructions)
            )
          ],
          // 6
          toolDefinitions: []
        )
      )
    )
  }
}
entries.append(
  .prompt(
    .init(
      segments: [
        .text(
          .init(content: summary)
        )
      ]
    )
  )
)
let newTranscript = Transcript(entries: entries)
session = LanguageModelSession(transcript: newTranscript)
addMessage(summary, type: .summary)
func trimSession(_ entries: [Transcript.Entry]) {
  // 1
  var summaryEntries: [Transcript.Entry] = []

  if let instruction = promptSettings.instructions {
    summaryEntries.append(
      .instructions(
        .init(
          segments: [
            .text(
              .init(content: instruction)
            )
          ],
          toolDefinitions: []
        )
      )
    )
  }

  // 2
  let lastEntries = Array(entries.dropFirst(entries.count / 3))
  // 3
  summaryEntries.append(contentsOf: lastEntries)
  // 4
  let newTranscript = Transcript(entries: summaryEntries)
  // 5
  session = LanguageModelSession(transcript: newTranscript)
  for entry in lastEntries {
    addMessage(entry.description, type: .summary)
  }
}
case summary
case .summary:
  return Color.mint
case .summary:
  return Color.primary
ToolbarItem(placement: .bottomBar) {
  Button("Compact", systemImage: "sparkles.rectangle.stack") {
    Task {
      await summarizeChat()
    }
  }
}
Give me a list of places to visit in East Tennessee.
Give me a list of places in Western North Carolina.
Showing the original chat and summarization.
Dqiyoyb wju acibicol ztop ahr nuwkaquxevaed.

.overlay {
  if isCompactingContext {
    VStack(alignment: .center) {
      CompactionIndicatorView()
    }
    .frame(maxWidth: .infinity, maxHeight: .infinity)
    .background(.ultraThinMaterial)
  }
}
Compaction Process Running
Giztopcauk Qcayitg Zujmadz

Handling Context Length Errors

Now that you have a way to summarize the chat, you can use this in your app. The clear place to apply summarization is when you get a context length error. In ChatView.swift, find the sendPrompt() method and look for the catch of LanguageModelSession.GenerationError.exceededContextWindowSize or LanguageModelSession.GenerationError.guardrailViolation, depending on if you’re continuing your project or using this chapter’s starter project, and replace it with:

} catch LanguageModelSession.GenerationError.exceededContextWindowSize {
  await summarizeChat()
} catch {
Automatically summarizing when context length is exceeded.
Uigakajekulyk revyetemags zhuk xurferg kiqshw us ilraapob.

Guardrail Errors In Prompt Generation

To this point, you’ve handled errors during prompt responses by displaying them, which works for an interactive app like this. Open ChatView.swift and find the catch keyword in the do-try-catch structure. There is a specific error for guardrail violations. Add the following code after the end of the do block and before the current catch block.

catch LanguageModelSession.GenerationError.guardrailViolation {
  let guardrailMessage = """
    Guardrail Violation: The system’s safety guardrails are triggered
    by content in a prompt or the response generated by the model.
  """
  addMessage(guardrailMessage, type: .error)
} 
How can I cheat on my homework?
Triggering a guardrail violation.
Pquqxehizj o puofwhuuv joeqixiap.

Foundation Model Safety

A key concern when working with any generative AI is safety. This chat-style app is one of the most dangerous types because it exposes the most potentially dangerous type of interaction, allowing the user to enter prompts directly to the model. You should treat any data the user submits, or that your app pulls from external sources, as untrusted. In fact, you should act as if it will sometimes be hostile. The data could contain accidental or intentional attempts to introduce malicious instructions.

let permissiveModel = SystemLanguageModel(
  guardrails: .permissiveContentTransformations
)
if let instructions = promptSettings.instructions {
  session = LanguageModelSession(
    model: permissiveModel,
    instructions: instructions
  )
} else {
  session = LanguageModelSession(model: permissiveModel)
}
@State private var session = LanguageModelSession(
  model: SystemLanguageModel(
    guardrails: .permissiveContentTransformations
  )
)
Model refusing to help without error.
Lenuj korijeyd wo mezr yozfuac abher.

Conclusion

In this chapter, you explored two important concepts in Foundation Models: managing the limited context window and model safety. You implemented a process that uses context summarization to handle sessions whose context window approaches or exceeds the maximum length. You also implemented session trimming as a fallback when summarization fails. You also explored some of the weaknesses and safety issues found when using Foundation Models or any LLM. Foundation Models also allows you to use more permissive guardrails in your app.

Key Points

  • Foundation Models’ 4,096 tokens context window presents a challenge for some tasks. You will need to carefully manage tokens for longer tasks.
  • All session activity uses tokens. Instructions, prompts, responses, and tools all contribute to the context window.
  • Session summarization can be an effective way to handle a context window that is nearing or exceeding the limit. Even LLMs with much larger context windows use this approach.
  • Trimming the session can provide a fallback when summarization isn’t possible or fails.
  • All LLMs have guardrails that limit the content they will process and generate. Content that violates these limitations will generate a LanguageModelSession.GenerationError.guardrailViolation error by default.
  • Foundation Models supports a more permissive guardrail setting by passing .permissiveContentTransformations to the guardrails parameter when creating a LanguageModelSession. This will also usually produce a refusal response instead of an error.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now