DropCompletedToolCalls.swift 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. //===----------------------------------------------------------------------===//
  2. //
  3. // This source file is part of the Foundation Models open source project.
  4. //
  5. // Copyright © 2024-2027 Apple Inc. and the Foundation Models project authors.
  6. //
  7. // Licensed under the Apache License v2.0
  8. //
  9. // See LICENSE.txt for license information
  10. //
  11. //===----------------------------------------------------------------------===//
  12. public import FoundationModels
  13. extension LanguageModelSession.DynamicProfile {
  14. /// Returns a modified profile that removes completed tool-call and
  15. /// tool-output entries from the transcript before each generation.
  16. ///
  17. /// Tool calls that have already been fulfilled add bulk to the
  18. /// transcript and are not always useful context for future responses.
  19. /// This modifier strips them out, keeping only the most recent
  20. /// tool-call exchange and all non-tool entries.
  21. ///
  22. /// It composes well with other history modifiers. For example, applying
  23. /// it outermost ensures tool-call entries are cleaned up before a
  24. /// rolling window or summarization step runs:
  25. ///
  26. /// ```swift
  27. /// Profile {
  28. /// Instructions("A helpful assistant.")
  29. /// }
  30. /// .summarizeHistory(entryThreshold: 50, model: model)
  31. /// .rollingWindow(entries: 10)
  32. /// .droppingCompletedToolCalls()
  33. /// ```
  34. ///
  35. /// - Returns: A profile that prunes completed tool-call entries from its
  36. /// transcript before each generation.
  37. public func droppingCompletedToolCalls() -> some DynamicProfile {
  38. modifier(DropCompletedToolCallsModifier())
  39. }
  40. }
  41. private struct DropCompletedToolCallsModifier: LanguageModelSession.DynamicProfileModifier {
  42. @SessionProperty(\.history)
  43. private var history
  44. func body(content: Content) -> some DynamicProfile {
  45. content.onPrompt {
  46. let lastOutputIndex =
  47. history.lastIndex(where: { entry in
  48. if case .response = entry { return true }
  49. if case .toolCalls = entry { return true }
  50. return false
  51. }) ?? history.startIndex
  52. let prefix = history.prefix(upTo: lastOutputIndex).filter { entry in
  53. if case .toolCalls = entry { return false }
  54. if case .toolOutput = entry { return false }
  55. return true
  56. }
  57. let suffix = history.suffix(from: lastOutputIndex)
  58. history = prefix + suffix
  59. }
  60. }
  61. }