Expose chat-latest and default Quick Question to it
TestFlight / Build and upload (push) Failing after 45s
TestFlight / Build and upload (push) Failing after 45s
This commit is contained in:
@@ -52,9 +52,9 @@ final class SybilSettingsStore {
|
||||
self.preferredModelByProvider = preferredModels
|
||||
|
||||
self.quickQuestionPreferredProvider =
|
||||
defaults.string(forKey: Keys.quickQuestionPreferredProvider).flatMap(Provider.init(rawValue:)) ?? provider
|
||||
defaults.string(forKey: Keys.quickQuestionPreferredProvider).flatMap(Provider.init(rawValue:)) ?? .openai
|
||||
self.quickQuestionPreferredModelByProvider = [
|
||||
.openai: defaults.string(forKey: Keys.quickQuestionPreferredOpenAIModel) ?? preferredModels[.openai] ?? "gpt-4.1-mini",
|
||||
.openai: defaults.string(forKey: Keys.quickQuestionPreferredOpenAIModel) ?? "chat-latest",
|
||||
.anthropic: defaults.string(forKey: Keys.quickQuestionPreferredAnthropicModel) ?? preferredModels[.anthropic] ?? "claude-3-5-sonnet-latest",
|
||||
.xai: defaults.string(forKey: Keys.quickQuestionPreferredXAIModel) ?? preferredModels[.xai] ?? "grok-3-mini",
|
||||
.gemini: defaults.string(forKey: Keys.quickQuestionPreferredGeminiModel) ?? preferredModels[.gemini] ?? "gemini-3.5-flash",
|
||||
|
||||
@@ -222,7 +222,7 @@ final class SybilViewModel {
|
||||
private let clientFactory: (APIConfiguration) -> any SybilAPIClienting
|
||||
|
||||
private let fallbackModels: [Provider: [String]] = [
|
||||
.openai: ["gpt-4.1-mini"],
|
||||
.openai: ["gpt-4.1-mini", "chat-latest"],
|
||||
.anthropic: ["claude-3-5-sonnet-latest"],
|
||||
.xai: ["grok-3-mini"],
|
||||
.gemini: ["gemini-3.5-flash", "gemini-flash-latest"],
|
||||
|
||||
@@ -52,6 +52,7 @@ private actor MockSybilClient: SybilAPIClienting {
|
||||
private let updateChatStarResponses: [String: ChatSummary]
|
||||
private let updateSearchStarResponses: [String: SearchSummary]
|
||||
private let activeRunsResponse: ActiveRunsResponse
|
||||
private let modelCatalogResponse: ModelCatalogResponse
|
||||
|
||||
private var snapshot = MockClientCallSnapshot()
|
||||
private var lastCreateChatCall: ChatCreateCallSnapshot?
|
||||
@@ -84,7 +85,8 @@ private actor MockSybilClient: SybilAPIClienting {
|
||||
updateChatStarResponses: [String: ChatSummary] = [:],
|
||||
updateSearchStarResponses: [String: SearchSummary] = [:],
|
||||
activeRunsResponse: ActiveRunsResponse = ActiveRunsResponse(),
|
||||
workspaceItemsResponse: [WorkspaceItem]? = nil
|
||||
workspaceItemsResponse: [WorkspaceItem]? = nil,
|
||||
modelCatalogResponse: ModelCatalogResponse = ModelCatalogResponse(providers: [:])
|
||||
) {
|
||||
self.chatsResponse = chatsResponse
|
||||
self.searchesResponse = searchesResponse
|
||||
@@ -98,6 +100,7 @@ private actor MockSybilClient: SybilAPIClienting {
|
||||
self.updateChatStarResponses = updateChatStarResponses
|
||||
self.updateSearchStarResponses = updateSearchStarResponses
|
||||
self.activeRunsResponse = activeRunsResponse
|
||||
self.modelCatalogResponse = modelCatalogResponse
|
||||
}
|
||||
|
||||
private static func makeWorkspaceItems(chats: [ChatSummary], searches: [SearchSummary]) -> [WorkspaceItem] {
|
||||
@@ -306,7 +309,7 @@ private actor MockSybilClient: SybilAPIClienting {
|
||||
}
|
||||
|
||||
func listModels() async throws -> ModelCatalogResponse {
|
||||
ModelCatalogResponse(providers: [:])
|
||||
modelCatalogResponse
|
||||
}
|
||||
|
||||
func getActiveRuns() async throws -> ActiveRunsResponse {
|
||||
@@ -1257,6 +1260,7 @@ private func makeCompletionToolCall(
|
||||
#expect(snapshot.runQuickQuestionStream == 1)
|
||||
#expect(snapshot.runCompletionStream == 0)
|
||||
#expect(body?.provider == .openai)
|
||||
#expect(body?.model == "chat-latest")
|
||||
#expect(body?.question == "How do I reset my password?")
|
||||
#expect(viewModel.quickQuestionAnswerText == "Reset it from Settings.")
|
||||
#expect(!viewModel.isQuickQuestionSending)
|
||||
@@ -1370,6 +1374,42 @@ private func makeCompletionToolCall(
|
||||
#expect(viewModel.quickQuestionPrompt.isEmpty)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Test func quickQuestionDefaultsToChatLatestIndependentlyOfRegularChatPreferences() async {
|
||||
let defaults = UserDefaults(suiteName: #function)!
|
||||
defaults.removePersistentDomain(forName: #function)
|
||||
defer { defaults.removePersistentDomain(forName: #function) }
|
||||
defaults.set("anthropic", forKey: "sybil.ios.preferredProvider")
|
||||
defaults.set("gpt-4o", forKey: "sybil.ios.preferredOpenAIModel")
|
||||
let settings = SybilSettingsStore(defaults: defaults)
|
||||
let client = MockSybilClient(modelCatalogResponse: ModelCatalogResponse(providers: [
|
||||
.openai: ProviderModelInfo(models: ["gpt-4.1-mini", "chat-latest"], loadedAt: nil, error: nil),
|
||||
.anthropic: ProviderModelInfo(models: ["claude-3-5-sonnet-latest"], loadedAt: nil, error: nil)
|
||||
]))
|
||||
let viewModel = SybilViewModel(settings: settings) { _ in client }
|
||||
|
||||
#expect(viewModel.provider == .anthropic)
|
||||
#expect(settings.preferredModelByProvider[.openai] == "gpt-4o")
|
||||
#expect(viewModel.quickQuestionProvider == .openai)
|
||||
#expect(viewModel.quickQuestionModel == "chat-latest")
|
||||
#expect(viewModel.quickQuestionProviderModelOptions.contains("chat-latest"))
|
||||
|
||||
viewModel.setQuickQuestionProvider(.anthropic)
|
||||
viewModel.setQuickQuestionProvider(.openai)
|
||||
#expect(viewModel.quickQuestionModel == "chat-latest")
|
||||
|
||||
await viewModel.bootstrap()
|
||||
#expect(viewModel.quickQuestionModel == "chat-latest")
|
||||
#expect(viewModel.quickQuestionProviderModelOptions == ["gpt-4.1-mini", "chat-latest"])
|
||||
|
||||
viewModel.setQuickQuestionProvider(.anthropic)
|
||||
viewModel.setQuickQuestionProvider(.openai)
|
||||
#expect(viewModel.quickQuestionModel == "chat-latest")
|
||||
#expect(SybilSettingsStore(defaults: defaults).quickQuestionPreferredModelByProvider[.openai] == "chat-latest")
|
||||
#expect(settings.preferredProvider == .anthropic)
|
||||
#expect(settings.preferredModelByProvider[.openai] == "gpt-4o")
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Test func quickQuestionProviderAndModelSelectionPersistSeparately() async throws {
|
||||
let defaults = UserDefaults(suiteName: #function)!
|
||||
|
||||
@@ -16,6 +16,9 @@ second **Option+Space**, the close button, or clicking outside dismisses it.
|
||||
Hiding the panel preserves the draft and answer and lets an in-flight answer finish.
|
||||
**Open in Chat** saves the question and answer in the regular workspace.
|
||||
|
||||
Quick Question defaults to OpenAI `chat-latest` on iPhone, iPad, and Mac. Its saved
|
||||
provider/model selection takes precedence and is separate from regular chat preferences.
|
||||
|
||||
The regular Mac chat/search composer uses the same Return/Shift+Return rules.
|
||||
Holding Return does not repeatedly submit. On iOS, Return submits from Quick
|
||||
Question, while the normal chat/search composer continues to use Return for a
|
||||
|
||||
Reference in New Issue
Block a user