|
454 | 454 | public extension GenerativeModelSession { |
455 | 455 | /// An asynchronous sequence of snapshots of the model's response. |
456 | 456 | struct ResponseStream<Content, PartialContent>: AsyncSequence { |
| 457 | + // TODO(#15962): Add unit tests for `ResponseStream`. |
| 458 | + |
457 | 459 | public typealias Element = Snapshot |
458 | 460 |
|
459 | 461 | /// A snapshot of the model's response at a point in time. |
|
495 | 497 | @available(iOS 18.0, macOS 15.0, macCatalyst 18.0, tvOS 18.0, watchOS 11.0, visionOS 2.0, *) |
496 | 498 | public mutating func next(isolation actor: isolated (any Actor)?) async throws |
497 | 499 | -> Snapshot? { |
498 | | - let rawResult = try await rawIterator.next(isolation: actor) |
499 | | - return try process(rawResult) |
| 500 | + var lastDecodingError: Error? = nil |
| 501 | + |
| 502 | + while let rawResult = try await rawIterator.next(isolation: actor) { |
| 503 | + do { |
| 504 | + // If it parses successfully, return the snapshot and discard any errors from previous |
| 505 | + // loop iterations. |
| 506 | + return try process(rawResult) |
| 507 | + } catch { |
| 508 | + // Intermediate failure (e.g., incomplete JSON that could not be parsed). |
| 509 | + // Hold onto the error and let the loop fetch the next chunk. |
| 510 | + lastDecodingError = error |
| 511 | + } |
| 512 | + } |
| 513 | + |
| 514 | + // If the last chunk processed resulted in an error, throw it. |
| 515 | + if let lastDecodingError { |
| 516 | + throw lastDecodingError |
| 517 | + } |
| 518 | + |
| 519 | + return nil |
500 | 520 | } |
501 | 521 |
|
502 | 522 | public mutating func next() async throws -> Snapshot? { |
503 | | - let rawResult = try await rawIterator.next() |
504 | | - return try process(rawResult) |
505 | | - } |
| 523 | + var lastDecodingError: Error? = nil |
| 524 | + |
| 525 | + while let rawResult = try await rawIterator.next() { |
| 526 | + do { |
| 527 | + // If it parses successfully, return the snapshot and discard any errors from previous |
| 528 | + // loop iterations. |
| 529 | + return try process(rawResult) |
| 530 | + } catch { |
| 531 | + // Intermediate failure (e.g., incomplete JSON that could not be parsed). |
| 532 | + // Hold onto the error and let the loop fetch the next chunk. |
| 533 | + lastDecodingError = error |
| 534 | + } |
| 535 | + } |
506 | 536 |
|
507 | | - private func process(_ rawResult: RawResult?) throws -> Snapshot? { |
508 | | - guard let rawResult else { return nil } |
| 537 | + // If the last chunk processed resulted in an error, throw it. |
| 538 | + if let lastDecodingError { |
| 539 | + throw lastDecodingError |
| 540 | + } |
| 541 | + |
| 542 | + return nil |
| 543 | + } |
509 | 544 |
|
| 545 | + private func process(_ rawResult: RawResult) throws -> Snapshot { |
510 | 546 | let partialContent: PartialContent = try GenerativeModelSession |
511 | 547 | .resolveContent(from: rawResult.rawContent) |
512 | 548 |
|
|
0 commit comments