Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion desktop/Desktop/Sources/CalendarReaderService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,27 @@ actor CalendarReaderService {
onToolActivity: { @Sendable _, _, _, _ in }
)

let responseText = result.text
var responseText = result.text
log("CalendarReaderService: Synthesis raw response (\(responseText.count) chars): \(responseText.prefix(300))")

// Extract JSON from response — handle markdown code fences and leading text
if let jsonStart = responseText.range(of: "```json") {
responseText = String(responseText[jsonStart.upperBound...])
if let jsonEnd = responseText.range(of: "```") {
responseText = String(responseText[..<jsonEnd.lowerBound])
}
} else if let jsonStart = responseText.range(of: "```") {
responseText = String(responseText[jsonStart.upperBound...])
if let jsonEnd = responseText.range(of: "```") {
responseText = String(responseText[..<jsonEnd.lowerBound])
}
}
// Also try finding raw JSON object if there's leading text
if let braceStart = responseText.firstIndex(of: "{") {
responseText = String(responseText[braceStart...])
}
Comment on lines +180 to +182
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Trailing text not stripped — parsing can still fail

The braceStart approach correctly removes content before the first {, but leaves anything after the closing } intact. If the LLM appends trailing prose (e.g., \n\nLet me know if you need more info.) after the JSON object and outside of any code fence, JSONSerialization will still fail because of the unexpected trailing text.

Since the PR description explicitly mentions fixing the "leading text" case, this gap should also be closed. A simple, more robust approach is to pair firstIndex(of: "{") with lastIndex(of: "}"):

Suggested change
if let braceStart = responseText.firstIndex(of: "{") {
responseText = String(responseText[braceStart...])
}
if let braceStart = responseText.firstIndex(of: "{"),
let braceEnd = responseText.lastIndex(of: "}"),
braceStart <= braceEnd {
responseText = String(responseText[braceStart...braceEnd])
}

This handles responses that have both a preamble and a postamble around the raw JSON object.

responseText = responseText.trimmingCharacters(in: .whitespacesAndNewlines)

guard let jsonData = responseText.data(using: .utf8),
let parsed = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any]
else {
Expand Down