ParaphrasingService.swift 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. import Foundation
  2. protocol ParaphrasingServicing {
  3. func paraphrase(_ text: String) async throws -> String
  4. }
  5. struct ParaphrasingService: ParaphrasingServicing {
  6. private static let chunkMaxWords = 280
  7. private static let maxConcurrentChunks = 4
  8. private static let systemPrompt = """
  9. You are a professional writing assistant. Paraphrase the user's text while preserving \
  10. the original meaning and tone. Return only the paraphrased text with no preamble or explanation.
  11. """
  12. private let openAI: OpenAIChatServicing
  13. init(openAI: OpenAIChatServicing = OpenAIChatService.shared) {
  14. self.openAI = openAI
  15. }
  16. func paraphrase(_ text: String) async throws -> String {
  17. let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
  18. guard !trimmed.isEmpty else {
  19. throw OpenAIServiceError.apiError("Enter some text to paraphrase.")
  20. }
  21. let chunks = trimmed.chunked(maxWords: Self.chunkMaxWords)
  22. if chunks.count == 1 {
  23. return try await openAI.completeText(
  24. systemPrompt: Self.systemPrompt,
  25. userPrompt: trimmed,
  26. maxTokens: 1_000
  27. )
  28. }
  29. var paraphrasedChunks: [String] = []
  30. for batchStart in stride(from: 0, to: chunks.count, by: Self.maxConcurrentChunks) {
  31. let batchEnd = min(batchStart + Self.maxConcurrentChunks, chunks.count)
  32. let batch = Array(chunks[batchStart..<batchEnd])
  33. let batchResults = try await withThrowingTaskGroup(of: (Int, String).self) { group in
  34. for (offset, chunk) in batch.enumerated() {
  35. let originalIndex = batchStart + offset
  36. group.addTask {
  37. let paraphrased = try await self.openAI.completeText(
  38. systemPrompt: Self.systemPrompt,
  39. userPrompt: chunk,
  40. maxTokens: 500
  41. )
  42. return (originalIndex, paraphrased)
  43. }
  44. }
  45. var indexedResults: [(Int, String)] = []
  46. for try await item in group {
  47. indexedResults.append(item)
  48. }
  49. return indexedResults.sorted { $0.0 < $1.0 }
  50. }
  51. paraphrasedChunks.append(contentsOf: batchResults.map(\.1))
  52. }
  53. return paraphrasedChunks.joined(separator: "\n\n")
  54. }
  55. }