Add dedicated quick question API
TestFlight / Build and upload (push) Successful in 1m42s

This commit is contained in:
2026-08-27 19:31:23 -07:00
parent 5f9fc82b86
commit 922172fc60
13 changed files with 269 additions and 44 deletions
@@ -22,6 +22,7 @@ private struct MockClientCallSnapshot: Sendable {
var getSearch = 0
var getActiveRuns = 0
var runCompletionStream = 0
var runQuickQuestionStream = 0
var attachCompletionStream = 0
var attachSearchStream = 0
}
@@ -50,7 +51,7 @@ private actor MockSybilClient: SybilAPIClienting {
private var snapshot = MockClientCallSnapshot()
private var lastCreateChatCall: ChatCreateCallSnapshot?
private var lastCompletionStreamBody: CompletionStreamRequest?
private var lastQuickQuestionStreamBody: QuickQuestionStreamRequest?
private var completionStreamEvents: [CompletionStreamEvent]?
private var listChatsDelayNanoseconds: UInt64 = 0
private var listSearchesDelayNanoseconds: UInt64 = 0
@@ -103,8 +104,8 @@ private actor MockSybilClient: SybilAPIClienting {
lastCreateChatCall
}
func currentCompletionStreamBody() -> CompletionStreamRequest? {
lastCompletionStreamBody
func currentQuickQuestionStreamBody() -> QuickQuestionStreamRequest? {
lastQuickQuestionStreamBody
}
func setCompletionStreamEvents(_ events: [CompletionStreamEvent], delayNanoseconds: UInt64 = 0) {
@@ -287,7 +288,27 @@ private actor MockSybilClient: SybilAPIClienting {
onEvent: @escaping @Sendable (CompletionStreamEvent) async -> Void
) async throws {
snapshot.runCompletionStream += 1
lastCompletionStreamBody = body
if completionStreamDelayNanoseconds > 0 {
try await Task.sleep(nanoseconds: completionStreamDelayNanoseconds)
}
if let completionStreamNetworkErrorMessage {
throw APIError.networkError(message: completionStreamNetworkErrorMessage)
}
if let completionStreamEvents {
for event in completionStreamEvents {
await onEvent(event)
}
return
}
throw UnexpectedClientCall()
}
func runQuickQuestionStream(
body: QuickQuestionStreamRequest,
onEvent: @escaping @Sendable (CompletionStreamEvent) async -> Void
) async throws {
snapshot.runQuickQuestionStream += 1
lastQuickQuestionStreamBody = body
if completionStreamDelayNanoseconds > 0 {
try await Task.sleep(nanoseconds: completionStreamDelayNanoseconds)
}
@@ -1005,7 +1026,8 @@ private func makeToolCallMessage(id: String, date: Date, summary: String = "Ran
await first?.value
let calls = await client.currentSnapshot()
#expect(calls.runCompletionStream == 1)
#expect(calls.runQuickQuestionStream == 1)
#expect(calls.runCompletionStream == 0)
#expect(viewModel.quickQuestionAnswerText == "One answer.")
#expect(!viewModel.isQuickQuestionSending)
}
@@ -1021,12 +1043,12 @@ private func makeToolCallMessage(id: String, date: Date, summary: String = "Ran
await task?.value
let calls = await client.currentSnapshot()
#expect(calls.runCompletionStream == 0)
#expect(calls.runQuickQuestionStream == 0)
#expect(!viewModel.isQuickQuestionSending)
}
@MainActor
@Test func quickQuestionRunsNonPersistentCompletionStream() async throws {
@Test func quickQuestionUsesDedicatedServerEndpoint() async throws {
let client = MockSybilClient()
await client.setCompletionStreamEvents([
.delta(CompletionStreamDelta(text: "Reset it from ")),
@@ -1041,13 +1063,11 @@ private func makeToolCallMessage(id: String, date: Date, summary: String = "Ran
await task?.value
let snapshot = await client.currentSnapshot()
let body = await client.currentCompletionStreamBody()
#expect(snapshot.runCompletionStream == 1)
#expect(body?.persist == false)
#expect(body?.chatId == nil)
let body = await client.currentQuickQuestionStreamBody()
#expect(snapshot.runQuickQuestionStream == 1)
#expect(snapshot.runCompletionStream == 0)
#expect(body?.provider == .openai)
#expect(body?.messages.first?.role == .user)
#expect(body?.messages.first?.content == "How do I reset my password?")
#expect(body?.question == "How do I reset my password?")
#expect(viewModel.quickQuestionAnswerText == "Reset it from Settings.")
#expect(!viewModel.isQuickQuestionSending)
}
@@ -1572,7 +1592,7 @@ private final class MockQuickQuestionLifecycle {
#expect(panel.state.prompt == "Keep this draft")
#expect(panel.state.answer == "An answer arrived while hidden.")
let calls = await client.currentSnapshot()
#expect(calls.runCompletionStream == 0)
#expect(calls.runQuickQuestionStream == 0)
controller.stop()
}
@@ -1630,7 +1650,7 @@ private final class MockQuickQuestionLifecycle {
#expect(panel.presentationCount == 2)
#expect(panel.state.canSend)
let calls = await client.currentSnapshot()
#expect(calls.runCompletionStream == 0)
#expect(calls.runQuickQuestionStream == 0)
controller.stop()
}
@@ -1652,7 +1672,7 @@ private final class MockQuickQuestionLifecycle {
#expect(panel.state.prompt == "A signed-out question")
#expect(!viewModel.isQuickQuestionSending)
let calls = await client.currentSnapshot()
#expect(calls.runCompletionStream == 0)
#expect(calls.runQuickQuestionStream == 0)
controller.stop()
}
#endif