Skip to content

Commit 0051a26

Browse files
committed
Add option requiresBeginWithUserMessage
1 parent dc2d2fc commit 0051a26

File tree

4 files changed

+29
-4
lines changed

4 files changed

+29
-4
lines changed

Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEdit.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ struct ChatModelEdit {
3333
var openAIProjectID: String = ""
3434
var customHeaders: [ChatModel.Info.CustomHeaderInfo.HeaderField] = []
3535
var openAICompatibleSupportsMultipartMessageContent = true
36+
var requiresBeginWithUserMessage = false
3637
}
3738

3839
enum Action: Equatable, BindableAction {
@@ -298,7 +299,8 @@ extension ChatModel {
298299
openAICompatibleInfo: .init(
299300
enforceMessageOrder: state.enforceMessageOrder,
300301
supportsMultipartMessageContent: state
301-
.openAICompatibleSupportsMultipartMessageContent
302+
.openAICompatibleSupportsMultipartMessageContent,
303+
requiresBeginWithUserMessage: state.requiresBeginWithUserMessage
302304
),
303305
customHeaderInfo: .init(headers: state.customHeaders)
304306
)
@@ -325,7 +327,8 @@ extension ChatModel {
325327
openAIProjectID: info.openAIInfo.projectID,
326328
customHeaders: info.customHeaderInfo.headers,
327329
openAICompatibleSupportsMultipartMessageContent: info.openAICompatibleInfo
328-
.supportsMultipartMessageContent
330+
.supportsMultipartMessageContent,
331+
requiresBeginWithUserMessage: info.openAICompatibleInfo.requiresBeginWithUserMessage
329332
)
330333
}
331334
}

Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,10 @@ struct ChatModelEditView: View {
340340
Toggle(isOn: $store.openAICompatibleSupportsMultipartMessageContent) {
341341
Text("Support multi-part message content")
342342
}
343+
344+
Toggle(isOn: $store.requiresBeginWithUserMessage) {
345+
Text("Requires the first message to be from the user")
346+
}
343347

344348
Button("Custom Headers") {
345349
isEditingCustomHeader.toggle()

Tool/Sources/AIModel/ChatModel.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,17 @@ public struct ChatModel: Codable, Equatable, Identifiable {
5353
public var enforceMessageOrder: Bool
5454
@FallbackDecoding<EmptyTrue>
5555
public var supportsMultipartMessageContent: Bool
56+
@FallbackDecoding<EmptyBool>
57+
public var requiresBeginWithUserMessage: Bool
5658

5759
public init(
5860
enforceMessageOrder: Bool = false,
59-
supportsMultipartMessageContent: Bool = true
61+
supportsMultipartMessageContent: Bool = true,
62+
requiresBeginWithUserMessage: Bool = false
6063
) {
6164
self.enforceMessageOrder = enforceMessageOrder
6265
self.supportsMultipartMessageContent = supportsMultipartMessageContent
66+
self.requiresBeginWithUserMessage = requiresBeginWithUserMessage
6367
}
6468
}
6569

Tool/Sources/OpenAIService/APIs/OpenAIChatCompletionsService.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,8 @@ actor OpenAIChatCompletionsService: ChatCompletionsStreamAPI, ChatCompletionsAPI
307307
enforceMessageOrder: model.info.openAICompatibleInfo.enforceMessageOrder,
308308
supportsMultipartMessageContent: model.info.openAICompatibleInfo
309309
.supportsMultipartMessageContent,
310+
requiresBeginWithUserMessage: model.info.openAICompatibleInfo
311+
.requiresBeginWithUserMessage,
310312
canUseTool: model.info.supportsFunctionCalling,
311313
supportsImage: model.info.supportsImage,
312314
supportsAudio: model.info.supportsAudio
@@ -709,6 +711,7 @@ extension OpenAIChatCompletionsService.RequestBody {
709711
endpoint: URL,
710712
enforceMessageOrder: Bool,
711713
supportsMultipartMessageContent: Bool,
714+
requiresBeginWithUserMessage: Bool,
712715
canUseTool: Bool,
713716
supportsImage: Bool,
714717
supportsAudio: Bool
@@ -732,10 +735,21 @@ extension OpenAIChatCompletionsService.RequestBody {
732735

733736
model = body.model
734737

738+
var body = body
739+
740+
if requiresBeginWithUserMessage {
741+
let firstUserIndex = body.messages.firstIndex(where: { $0.role == .user }) ?? 0
742+
let endIndex = firstUserIndex
743+
for i in stride(from: endIndex - 1, to: 0, by: -1)
744+
where i >= 0 && body.messages.endIndex > i
745+
{
746+
body.messages.remove(at: i)
747+
}
748+
}
749+
735750
// Special case for Claude through OpenRouter
736751

737752
if endpoint.absoluteString.contains("openrouter.ai"), model.hasPrefix("anthropic/") {
738-
var body = body
739753
body.model = model.replacingOccurrences(of: "anthropic/", with: "")
740754
let claudeRequestBody = ClaudeChatCompletionsService.RequestBody(body)
741755
messages = claudeRequestBody.system.map {

0 commit comments

Comments
 (0)