Skip to content
Open
Show file tree
Hide file tree
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
18 changes: 12 additions & 6 deletions pkg/aiusechat/openaichat/openaichat-backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ func processChatStream(
) (*uctypes.WaveStopReason, *StoredChatMessage, error) {
decoder := eventsource.NewDecoder(body)
var textBuilder strings.Builder
var reasoningBuilder strings.Builder
msgID := uuid.New().String()
textID := uuid.New().String()
var finishReason string
Expand Down Expand Up @@ -128,7 +129,7 @@ func processChatStream(
break
}
if sseHandler.Err() != nil {
partialMsg := extractPartialTextMessage(msgID, textBuilder.String())
partialMsg := extractPartialTextMessage(msgID, textBuilder.String(), reasoningBuilder.String())
return &uctypes.WaveStopReason{
Kind: uctypes.StopKindCanceled,
ErrorType: "client_disconnect",
Expand Down Expand Up @@ -159,6 +160,9 @@ func processChatStream(
}

choice := chunk.Choices[0]
if choice.Delta.ReasoningContent != "" {
reasoningBuilder.WriteString(choice.Delta.ReasoningContent)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
if choice.Delta.Content != "" {
if !textStarted {
_ = sseHandler.AiMsgTextStart(textID)
Expand Down Expand Up @@ -239,7 +243,8 @@ func processChatStream(
assistantMsg := &StoredChatMessage{
MessageId: msgID,
Message: ChatRequestMessage{
Role: "assistant",
Role: "assistant",
ReasoningContent: reasoningBuilder.String(),
},
}

Expand All @@ -260,16 +265,17 @@ func processChatStream(
return stopReason, assistantMsg, nil
}

func extractPartialTextMessage(msgID string, text string) *StoredChatMessage {
if text == "" {
func extractPartialTextMessage(msgID string, text string, reasoning string) *StoredChatMessage {
if text == "" && reasoning == "" {
return nil
}

return &StoredChatMessage{
MessageId: msgID,
Message: ChatRequestMessage{
Role: "assistant",
Content: text,
Role: "assistant",
Content: text,
ReasoningContent: reasoning,
},
}
}
41 changes: 23 additions & 18 deletions pkg/aiusechat/openaichat/openaichat-types.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,29 +50,32 @@ type ChatImageUrl struct {
}

type ChatRequestMessage struct {
Role string `json:"role"` // "system","user","assistant","tool"
Content string `json:"-"` // plain text (used when ContentParts is nil)
ContentParts []ChatContentPart `json:"-"` // multimodal parts (used when images present)
ToolCalls []ToolCall `json:"tool_calls,omitempty"` // assistant tool-call message
ToolCallID string `json:"tool_call_id,omitempty"` // for role:"tool"
Name string `json:"name,omitempty"` // tool name on role:"tool"
Role string `json:"role"` // "system","user","assistant","tool"
Content string `json:"-"` // plain text (used when ContentParts is nil)
ContentParts []ChatContentPart `json:"-"` // multimodal parts (used when images present)
ReasoningContent string `json:"-"` // preserved for DeepSeek multi-turn (reasoning_content)
ToolCalls []ToolCall `json:"tool_calls,omitempty"` // assistant tool-call message
ToolCallID string `json:"tool_call_id,omitempty"` // for role:"tool"
Name string `json:"name,omitempty"` // tool name on role:"tool"
}

// chatRequestMessageJSON is the wire format for ChatRequestMessage
type chatRequestMessageJSON struct {
Role string `json:"role"`
Content json.RawMessage `json:"content"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
Name string `json:"name,omitempty"`
Role string `json:"role"`
Content json.RawMessage `json:"content"`
ReasoningContent string `json:"reasoning_content,omitempty"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
Name string `json:"name,omitempty"`
}

func (cm ChatRequestMessage) MarshalJSON() ([]byte, error) {
raw := chatRequestMessageJSON{
Role: cm.Role,
ToolCalls: cm.ToolCalls,
ToolCallID: cm.ToolCallID,
Name: cm.Name,
Role: cm.Role,
ReasoningContent: cm.ReasoningContent,
ToolCalls: cm.ToolCalls,
ToolCallID: cm.ToolCallID,
Name: cm.Name,
}
if len(cm.ContentParts) > 0 {
b, err := json.Marshal(cm.ContentParts)
Expand All @@ -96,6 +99,7 @@ func (cm *ChatRequestMessage) UnmarshalJSON(data []byte) error {
return err
}
cm.Role = raw.Role
cm.ReasoningContent = raw.ReasoningContent
cm.ToolCalls = raw.ToolCalls
cm.ToolCallID = raw.ToolCallID
cm.Name = raw.Name
Expand Down Expand Up @@ -193,9 +197,10 @@ type StreamChoice struct {

// This is the important part:
type ContentDelta struct {
Role string `json:"role,omitempty"`
Content string `json:"content,omitempty"`
ToolCalls []ToolCallDelta `json:"tool_calls,omitempty"`
Role string `json:"role,omitempty"`
Content string `json:"content,omitempty"`
ReasoningContent string `json:"reasoning_content,omitempty"`
ToolCalls []ToolCallDelta `json:"tool_calls,omitempty"`
}

type ToolCallDelta struct {
Expand Down