fix(windsurf): fix tool call for legacy-enum models + gateway logger
Three fixes: 1. Logger: windsurf_gateway_service used zap.L() (nop) instead of logger.L() — all gateway-level logs were silently dropped. 2. Tool mode routing: when tools are present in the request, force cascade mode even for legacy-enum models. Legacy mode ignores toolPreamble entirely, so tool calls were never injected. 3. Model enum hint: pass meta.EnumValue through to SendUserCascadeMessage/buildCascadeConfig as a fallback when modelUID-based enum resolution returns 0. Prevents 'neither PlanModel nor RequestedModel specified' gRPC errors. Tested: claude-sonnet-4-6 with tool definitions returns proper tool_use content blocks in both streaming and non-streaming modes. Tool result round-trip verified.
This commit is contained in:
parent
9112645bf9
commit
8b446ffef8
@ -279,7 +279,7 @@ func main() {
|
||||
// SendUserCascadeMessage
|
||||
{
|
||||
ctx, cancel := context.WithTimeout(context.Background(), f.timeout)
|
||||
newCID, err := lsClient.SendUserCascadeMessage(ctx, f.jwt, cascadeID, f.prompt, pickedModel, "")
|
||||
newCID, err := lsClient.SendUserCascadeMessage(ctx, f.jwt, cascadeID, f.prompt, pickedModel, "", 0)
|
||||
if err == nil && newCID != "" {
|
||||
cascadeID = newCID
|
||||
}
|
||||
|
||||
@ -159,8 +159,11 @@ func (l *LocalLSClient) StartCascade(ctx context.Context, token string) (string,
|
||||
// SendUserCascadeMessage sends a message into an existing cascade session.
|
||||
// Returns the (possibly new) cascadeID — it changes if panel-state retry triggers a new StartCascade.
|
||||
// toolPreamble, if non-empty, is injected into the tool_calling_section override.
|
||||
func (l *LocalLSClient) SendUserCascadeMessage(ctx context.Context, token, cascadeID, text, modelUID, toolPreamble string) (string, error) {
|
||||
func (l *LocalLSClient) SendUserCascadeMessage(ctx context.Context, token, cascadeID, text, modelUID, toolPreamble string, modelEnumHint int) (string, error) {
|
||||
modelEnum := resolveModelEnum(modelUID)
|
||||
if modelEnum == 0 && modelEnumHint > 0 {
|
||||
modelEnum = modelEnumHint
|
||||
}
|
||||
|
||||
doSend := func(cid string) error {
|
||||
body := encodeStringField(1, cid)
|
||||
@ -369,7 +372,7 @@ func (e *CascadeModelError) Error() string { return e.Msg }
|
||||
// StreamCascadeChat performs the full Cascade chat flow and returns accumulated text + thinking.
|
||||
// Includes cold/warm stall detection, step error handling, and final sweep (aligned with JS v1.9).
|
||||
// If reuseCascadeID is non-empty, skips StartCascade and reuses the existing cascade session.
|
||||
func (l *LocalLSClient) StreamCascadeChat(ctx context.Context, token, modelUID, userText, toolPreamble, reuseCascadeID string) (*CascadeChatResult, error) {
|
||||
func (l *LocalLSClient) StreamCascadeChat(ctx context.Context, token, modelUID, userText, toolPreamble, reuseCascadeID string, modelEnumHint int) (*CascadeChatResult, error) {
|
||||
if err := l.WarmupCascade(ctx, token); err != nil {
|
||||
return nil, fmt.Errorf("warmup: %w", err)
|
||||
}
|
||||
@ -385,7 +388,7 @@ func (l *LocalLSClient) StreamCascadeChat(ctx context.Context, token, modelUID,
|
||||
}
|
||||
}
|
||||
|
||||
cascadeID, err = l.SendUserCascadeMessage(ctx, token, cascadeID, userText, modelUID, toolPreamble)
|
||||
cascadeID, err = l.SendUserCascadeMessage(ctx, token, cascadeID, userText, modelUID, toolPreamble, modelEnumHint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("SendUserCascadeMessage: %w", err)
|
||||
}
|
||||
@ -578,6 +581,17 @@ func (l *LocalLSClient) StreamCascadeChat(ctx context.Context, token, modelUID,
|
||||
}
|
||||
}
|
||||
|
||||
slog.Info("windsurf_cascade_poll_result",
|
||||
"cascade_id", cascadeID[:min(8, len(cascadeID))],
|
||||
"acc_text_len", len(accText),
|
||||
"acc_thinking_len", len(accThinking),
|
||||
"native_tool_calls", len(nativeToolCalls),
|
||||
"saw_active", sawActive,
|
||||
"saw_text", sawText,
|
||||
"steps_seen", len(textCursors),
|
||||
"idle_count", idleCount,
|
||||
)
|
||||
|
||||
// Aggregate step usage
|
||||
var aggUsage *StepUsage
|
||||
for _, u := range usageByStep {
|
||||
|
||||
@ -61,6 +61,10 @@ func (s *WindsurfChatService) Chat(ctx context.Context, req *WindsurfChatRequest
|
||||
meta := windsurf.GetModelInfo(modelKey)
|
||||
|
||||
mode := s.resolveMode(meta)
|
||||
// Tool emulation requires cascade mode for proto section injection
|
||||
if mode == "legacy" && req.ToolPreamble != "" {
|
||||
mode = "cascade"
|
||||
}
|
||||
|
||||
var lease *windsurf.LSLease
|
||||
if token.LSBinding.ContainerID != "" || token.LSBinding.ContainerName != "" {
|
||||
@ -109,8 +113,10 @@ func (s *WindsurfChatService) resolveMode(meta *windsurf.ModelMeta) string {
|
||||
|
||||
func (s *WindsurfChatService) chatCascade(ctx context.Context, client *windsurf.LocalLSClient, apiKey string, meta *windsurf.ModelMeta, messages []windsurf.ChatMessage, toolPreamble string, modelKey string, lsEndpoint string) (*WindsurfChatResponse, error) {
|
||||
modelUID := ""
|
||||
modelEnumHint := 0
|
||||
if meta != nil {
|
||||
modelUID = meta.ModelUID
|
||||
modelEnumHint = meta.EnumValue
|
||||
}
|
||||
|
||||
fpBefore := windsurf.FingerprintBefore(messages, modelKey)
|
||||
@ -125,11 +131,11 @@ func (s *WindsurfChatService) chatCascade(ctx context.Context, client *windsurf.
|
||||
|
||||
userText := buildCascadeText(messages, modelUID, isResume)
|
||||
|
||||
result, err := client.StreamCascadeChat(ctx, apiKey, modelUID, userText, toolPreamble, reuseCascadeID)
|
||||
result, err := client.StreamCascadeChat(ctx, apiKey, modelUID, userText, toolPreamble, reuseCascadeID, modelEnumHint)
|
||||
if err != nil && isResume {
|
||||
slog.Warn("windsurf_cascade_reuse_failed", "error", err, "model", modelKey)
|
||||
userText = buildCascadeText(messages, modelUID, false)
|
||||
result, err = client.StreamCascadeChat(ctx, apiKey, modelUID, userText, toolPreamble, "")
|
||||
result, err = client.StreamCascadeChat(ctx, apiKey, modelUID, userText, toolPreamble, "", modelEnumHint)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/config"
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/logger"
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/windsurf"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@ -674,7 +675,7 @@ func windsurfExtractContentTextFromRaw(raw json.RawMessage) string {
|
||||
}
|
||||
|
||||
func windsurfLogger(c *gin.Context, component string, fields ...zap.Field) *zap.Logger {
|
||||
l := zap.L().With(zap.String("component", component))
|
||||
l := logger.L().With(zap.String("component", component))
|
||||
if c != nil {
|
||||
if reqID := c.GetHeader("X-Request-ID"); reqID != "" {
|
||||
l = l.With(zap.String("request_id", reqID))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user