Show in-progress tool calls

This commit is contained in:
2026-06-05 22:20:56 -07:00
parent f71b69ca8b
commit fccc8110f4
14 changed files with 382 additions and 177 deletions

View File

@@ -1186,7 +1186,7 @@ final class SybilViewModel {
break
case let .toolCall(payload):
insertQuickQuestionToolCallMessage(payload)
upsertQuickQuestionToolCallMessage(payload)
case let .delta(payload):
guard !payload.text.isEmpty else { return }
@@ -2006,7 +2006,7 @@ final class SybilViewModel {
}
case let .toolCall(payload):
insertPendingToolCallMessage(payload, chatID: chatID)
upsertPendingToolCallMessage(payload, chatID: chatID)
case let .delta(payload):
guard !payload.text.isEmpty else { return }
@@ -2222,12 +2222,14 @@ final class SybilViewModel {
quickQuestionMessages[index].content = transform(quickQuestionMessages[index].content)
}
private func insertPendingToolCallMessage(_ payload: CompletionStreamToolCall, chatID: String) {
private func upsertPendingToolCallMessage(_ payload: CompletionStreamToolCall, chatID: String) {
guard var pending = pendingChatStates[chatID] else {
return
}
if pending.messages.contains(where: { $0.toolCallMetadata?.toolCallId == payload.toolCallId }) {
if let existingIndex = pending.messages.firstIndex(where: { $0.toolCallMetadata?.toolCallId == payload.toolCallId || $0.id == "temp-tool-\(payload.toolCallId)" }) {
pending.messages[existingIndex] = toolCallMessage(for: payload, id: pending.messages[existingIndex].id)
pendingChatStates[chatID] = pending
return
}
@@ -2242,8 +2244,9 @@ final class SybilViewModel {
pendingChatStates[chatID] = pending
}
private func insertQuickQuestionToolCallMessage(_ payload: CompletionStreamToolCall) {
if quickQuestionMessages.contains(where: { $0.toolCallMetadata?.toolCallId == payload.toolCallId }) {
private func upsertQuickQuestionToolCallMessage(_ payload: CompletionStreamToolCall) {
if let existingIndex = quickQuestionMessages.firstIndex(where: { $0.toolCallMetadata?.toolCallId == payload.toolCallId || $0.id == "temp-tool-\(payload.toolCallId)" }) {
quickQuestionMessages[existingIndex] = toolCallMessage(for: payload, id: quickQuestionMessages[existingIndex].id)
return
}
@@ -2255,8 +2258,8 @@ final class SybilViewModel {
}
}
private func toolCallMessage(for payload: CompletionStreamToolCall) -> Message {
let metadata: JSONValue = .object([
private func toolCallMessage(for payload: CompletionStreamToolCall, id: String? = nil) -> Message {
var metadataObject: [String: JSONValue] = [
"kind": .string("tool_call"),
"toolCallId": .string(payload.toolCallId),
"toolName": .string(payload.name),
@@ -2264,19 +2267,26 @@ final class SybilViewModel {
"summary": .string(payload.summary),
"args": .object(payload.args),
"startedAt": .string(payload.startedAt),
"completedAt": .string(payload.completedAt),
"durationMs": .number(Double(payload.durationMs)),
"error": payload.error.map { .string($0) } ?? .null,
"resultPreview": payload.resultPreview.map { .string($0) } ?? .null
])
]
if let completedAt = payload.completedAt {
metadataObject["completedAt"] = .string(completedAt)
}
if let durationMs = payload.durationMs {
metadataObject["durationMs"] = .number(Double(durationMs))
}
let metadata: JSONValue = .object(metadataObject)
let summary = payload.summary.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
? "Ran tool '\(payload.name)'."
: payload.summary
return Message(
id: "temp-tool-\(payload.toolCallId)",
createdAt: Date(),
id: id ?? "temp-tool-\(payload.toolCallId)",
createdAt: toolCallDate(from: payload.completedAt) ?? toolCallDate(from: payload.startedAt) ?? Date(),
role: .tool,
content: summary,
name: payload.name,
@@ -2284,6 +2294,19 @@ final class SybilViewModel {
)
}
private func toolCallDate(from value: String?) -> Date? {
guard let value else { return nil }
let fractionalFormatter = ISO8601DateFormatter()
fractionalFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
if let date = fractionalFormatter.date(from: value) {
return date
}
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime]
return formatter.date(from: value)
}
private var currentChatID: String? {
if draftKind == .chat {
return nil