Skip to content

Commit 7803f12

Browse files
committed
bug fixes, updates
1 parent d11dfba commit 7803f12

11 files changed

Lines changed: 97 additions & 59 deletions

File tree

frontend/app/aipanel/aimessage.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,12 @@ export const AIMessage = memo(({ message, isStreaming }: AIMessageProps) => {
127127
const fileParts = parts.filter(
128128
(part): part is WaveUIMessagePart & { type: "data-userfile" } => part.type === "data-userfile"
129129
);
130-
const hasTextContent = displayParts.length > 0 && displayParts.some((part) => part.type === "text" && part.text);
130+
const hasContent = displayParts.length > 0 && displayParts.some((part) =>
131+
(part.type === "text" && part.text) || part.type.startsWith("tool-")
132+
);
131133

132-
const showThinking = !hasTextContent && isStreaming && message.role === "assistant";
134+
const showThinkingOnly = !hasContent && isStreaming && message.role === "assistant";
135+
const showThinkingInline = hasContent && isStreaming && message.role === "assistant";
133136

134137
return (
135138
<div className={cn("flex", message.role === "user" ? "justify-end" : "justify-start")}>
@@ -141,16 +144,23 @@ export const AIMessage = memo(({ message, isStreaming }: AIMessageProps) => {
141144
: "bg-gray-800 text-gray-100"
142145
)}
143146
>
144-
{showThinking ? (
147+
{showThinkingOnly ? (
145148
<AIThinking />
146-
) : !hasTextContent && !isStreaming ? (
149+
) : !hasContent && !isStreaming ? (
147150
<div className="whitespace-pre-wrap break-words">(no text content)</div>
148151
) : (
149-
displayParts.map((part, index: number) => (
150-
<div key={index} className={cn(index > 0 && "mt-2")}>
151-
<AIMessagePart part={part} role={message.role} isStreaming={isStreaming} />
152-
</div>
153-
))
152+
<>
153+
{displayParts.map((part, index: number) => (
154+
<div key={index} className={cn(index > 0 && "mt-2")}>
155+
<AIMessagePart part={part} role={message.role} isStreaming={isStreaming} />
156+
</div>
157+
))}
158+
{showThinkingInline && (
159+
<div className="mt-2">
160+
<AIThinking />
161+
</div>
162+
)}
163+
</>
154164
)}
155165

156166
{message.role === "user" && <UserMessageFiles fileParts={fileParts} />}

frontend/app/aipanel/aipanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
import { WaveUIMessagePart } from "@/app/aipanel/aitypes";
55
import { ErrorBoundary } from "@/app/element/errorboundary";
66
import { atoms, getSettingsKeyAtom } from "@/app/store/global";
7-
import { workspaceLayoutModel } from "@/app/workspace/workspace-layout-model";
87
import { globalStore } from "@/app/store/jotaiStore";
8+
import { workspaceLayoutModel } from "@/app/workspace/workspace-layout-model";
99
import { getWebServerEndpoint } from "@/util/endpoints";
1010
import { checkKeyPressed, keydownWrapper } from "@/util/keyutil";
1111
import { cn } from "@/util/util";

frontend/app/aipanel/aipanelinput.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ export const AIPanelInput = memo(
4141

4242
useImperativeHandle(ref, () => ({
4343
focus: () => {
44-
console.log("calling FOCUS", textareaRef.current);
4544
textareaRef.current?.focus();
4645
},
4746
resize: resizeTextarea,

frontend/app/app.scss

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,5 @@ a {
143143
font-weight: bold;
144144
margin-bottom: 5px;
145145
}
146-
147-
.flash-error-message {
148-
}
149146
}
150147
}

frontend/app/tab/tabbar.scss

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,6 @@
102102
height: 27px;
103103
}
104104

105-
.workspace-switcher {
106-
}
107-
108105
.window-drag {
109106
height: 100%;
110107
width: var(--default-indent);

pkg/aiusechat/anthropic/anthropic-backend.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ func RunAnthropicChatStep(
406406
if chat.APIType != chatOpts.Config.APIType {
407407
return nil, nil, nil, fmt.Errorf("API type mismatch: chat has %s, chatOpts has %s", chat.APIType, chatOpts.Config.APIType)
408408
}
409-
if chat.Model != chatOpts.Config.Model {
409+
if !uctypes.AreModelsCompatible(chat.APIType, chat.Model, chatOpts.Config.Model) {
410410
return nil, nil, nil, fmt.Errorf("model mismatch: chat has %s, chatOpts has %s", chat.Model, chatOpts.Config.Model)
411411
}
412412
if chat.APIVersion != chatOpts.Config.APIVersion {

pkg/aiusechat/chatstore/chatstore.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func (cs *ChatStore) PostMessage(chatId string, aiOpts *uctypes.AIOptsType, mess
6868
if chat.APIType != aiOpts.APIType {
6969
return fmt.Errorf("API type mismatch: expected %s, got %s", chat.APIType, aiOpts.APIType)
7070
}
71-
if chat.Model != aiOpts.Model {
71+
if !uctypes.AreModelsCompatible(chat.APIType, chat.Model, aiOpts.Model) {
7272
return fmt.Errorf("model mismatch: expected %s, got %s", chat.Model, aiOpts.Model)
7373
}
7474
if chat.APIVersion != aiOpts.APIVersion {

pkg/aiusechat/openai/openai-backend.go

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ func RunOpenAIChatStep(
341341
if chat.APIType != chatOpts.Config.APIType {
342342
return nil, nil, nil, fmt.Errorf("API type mismatch: chat has %s, chatOpts has %s", chat.APIType, chatOpts.Config.APIType)
343343
}
344-
if !areModelsCompatible(chat.Model, chatOpts.Config.Model) {
344+
if !uctypes.AreModelsCompatible(chat.APIType, chat.Model, chatOpts.Config.Model) {
345345
return nil, nil, nil, fmt.Errorf("model mismatch: chat has %s, chatOpts has %s", chat.Model, chatOpts.Config.Model)
346346
}
347347
if chat.APIVersion != chatOpts.Config.APIVersion {
@@ -357,7 +357,7 @@ func RunOpenAIChatStep(
357357

358358
// Validate continuation if provided
359359
if cont != nil {
360-
if !areModelsCompatible(chatOpts.Config.Model, cont.Model) {
360+
if !uctypes.AreModelsCompatible(chat.APIType, chatOpts.Config.Model, cont.Model) {
361361
return nil, nil, nil, fmt.Errorf("cannot continue with a different model, model:%q, cont-model:%q", chatOpts.Config.Model, cont.Model)
362362
}
363363
}
@@ -383,7 +383,7 @@ func RunOpenAIChatStep(
383383
}
384384
}
385385

386-
req, err := buildOpenAIHTTPRequest(ctx, inputs, chatOpts)
386+
req, err := buildOpenAIHTTPRequest(ctx, inputs, chatOpts, cont)
387387
if err != nil {
388388
return nil, nil, nil, err
389389
}
@@ -594,12 +594,15 @@ func handleOpenAIEvent(
594594
case "message":
595595
// Message item - content parts will be handled in streaming events
596596
case "function_call":
597-
// Track function call info for later use
597+
// Track function call info and notify frontend
598+
id := uuid.New().String()
598599
state.blockMap[ev.Item.Id] = &openaiBlockState{
599600
kind: openaiBlockToolUse,
601+
localID: id,
600602
toolCallID: ev.Item.CallId,
601603
toolName: ev.Item.Name,
602604
}
605+
_ = sse.AiMsgToolInputStart(ev.Item.CallId, ev.Item.Name)
603606
}
604607
return nil, nil
605608

@@ -614,6 +617,9 @@ func handleOpenAIEvent(
614617
switch st.kind {
615618
case openaiBlockReasoning:
616619
_ = sse.AiMsgReasoningEnd(st.localID)
620+
case openaiBlockToolUse:
621+
// Tool input completion notification was already sent in function_call_arguments.done
622+
// This just marks the end of the tool item itself
617623
}
618624
}
619625
return nil, nil
@@ -839,20 +845,3 @@ func extractMessageAndToolsFromResponse(resp openaiResponse) ([]*OpenAIChatMessa
839845
return allMessages, toolCalls
840846
}
841847

842-
func areModelsCompatible(model1, model2 string) bool {
843-
if model1 == model2 {
844-
return true
845-
}
846-
847-
gpt5Models := map[string]bool{
848-
"gpt-5": true,
849-
"gpt-5-mini": true,
850-
"gpt-5-nano": true,
851-
}
852-
853-
if gpt5Models[model1] && gpt5Models[model2] {
854-
return true
855-
}
856-
857-
return false
858-
}

pkg/aiusechat/openai/openai-convertmessage.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,15 @@ func debugPrintReq(req *OpenAIRequest, endpoint string) {
111111
}
112112

113113
// buildOpenAIHTTPRequest creates a complete HTTP request for the OpenAI API
114-
func buildOpenAIHTTPRequest(ctx context.Context, inputs []any, chatOpts uctypes.WaveChatOpts) (*http.Request, error) {
114+
func buildOpenAIHTTPRequest(ctx context.Context, inputs []any, chatOpts uctypes.WaveChatOpts, cont *uctypes.WaveContinueResponse) (*http.Request, error) {
115115
opts := chatOpts.Config
116+
117+
// If continuing from premium rate limit, downgrade to default model and low thinking
118+
if cont != nil && cont.ContinueFromKind == uctypes.StopKindPremiumRateLimit {
119+
opts.Model = uctypes.DefaultOpenAIModel
120+
opts.ThinkingLevel = uctypes.ThinkingLevelLow
121+
}
122+
116123
if opts.Model == "" {
117124
return nil, errors.New("opts.model is required")
118125
}

pkg/aiusechat/uctypes/usechat-types.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import (
99
"strings"
1010
)
1111

12+
const DefaultAnthropicModel = "claude-sonnet-4-5"
13+
const DefaultOpenAIModel = "gpt-5-mini"
14+
const PremiumOpenAIModel = "gpt-5"
15+
1216
type UseChatRequest struct {
1317
Messages []UIMessage `json:"messages"`
1418
}
@@ -431,3 +435,23 @@ func ParseRateLimitHeader(header string) *RateLimitInfo {
431435

432436
return info
433437
}
438+
439+
func AreModelsCompatible(apiType, model1, model2 string) bool {
440+
if model1 == model2 {
441+
return true
442+
}
443+
444+
if apiType == "openai" {
445+
gpt5Models := map[string]bool{
446+
"gpt-5": true,
447+
"gpt-5-mini": true,
448+
"gpt-5-nano": true,
449+
}
450+
451+
if gpt5Models[model1] && gpt5Models[model2] {
452+
return true
453+
}
454+
}
455+
456+
return false
457+
}

0 commit comments

Comments
 (0)