fix launch model picker recovery (#17170)
This commit is contained in:
@@ -996,9 +996,11 @@ func (c *launcherClient) resolveSingleIntegrationTarget(ctx context.Context, nam
|
||||
target = selected
|
||||
} else if !skipReadiness {
|
||||
if err := c.ensureModelsReadyFor(ctx, []string{target}, runner.String(), name); err != nil {
|
||||
if !errors.Is(err, errDeprecatedLaunchModelDeclined) || req.ModelOverride != "" {
|
||||
if !errors.Is(err, errDeprecatedLaunchModelDeclined) {
|
||||
return "", false, err
|
||||
}
|
||||
// "Pick another model" is an interactive recovery path, including
|
||||
// when --model supplied the initial target.
|
||||
selected, err := c.selectSingleModelWithSelectorReady(ctx, fmt.Sprintf("Select model for %s:", runner), target, DefaultSingleSelector, true, runner.String(), name)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
|
||||
+40
-23
@@ -3,7 +3,6 @@ package launch
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -3556,21 +3555,17 @@ func TestLaunchIntegration_ClaudeModelOverrideSkipsSelector(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLaunchIntegration_ModelOverrideDeprecatedPromptsAndDeclineCancels(t *testing.T) {
|
||||
func TestLaunchIntegration_ClaudeModelOverrideDeprecatedDeclineOpensPicker(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
setLaunchTestHome(t, tmpDir)
|
||||
withLauncherHooks(t)
|
||||
withInteractiveSession(t, true)
|
||||
|
||||
binDir := t.TempDir()
|
||||
writeFakeBinary(t, binDir, "droid")
|
||||
writeFakeBinary(t, binDir, "claude")
|
||||
t.Setenv("PATH", binDir)
|
||||
|
||||
runner := &launcherSingleRunner{}
|
||||
withIntegrationOverride(t, "droid", runner)
|
||||
|
||||
var showCalls atomic.Int32
|
||||
var pullCalls atomic.Int32
|
||||
var prompt string
|
||||
var promptOptions ConfirmOptions
|
||||
DefaultConfirmPrompt = func(p string, options ConfirmOptions) (bool, error) {
|
||||
@@ -3578,6 +3573,26 @@ func TestLaunchIntegration_ModelOverrideDeprecatedPromptsAndDeclineCancels(t *te
|
||||
promptOptions = options
|
||||
return false, nil
|
||||
}
|
||||
var selectorCalls int
|
||||
DefaultSingleSelector = func(title string, items []SelectionItem, current string) (string, error) {
|
||||
selectorCalls++
|
||||
if title != "Select model for Claude Code:" {
|
||||
t.Fatalf("picker title = %q, want Claude Code model picker", title)
|
||||
}
|
||||
if current != "llama3.2" {
|
||||
t.Fatalf("picker current = %q, want deprecated override", current)
|
||||
}
|
||||
itemNames := selectionItemNames(items)
|
||||
if slices.Contains(itemNames, "llama3.2") {
|
||||
t.Fatalf("expected deprecated override to be hidden from picker, got %v", itemNames)
|
||||
}
|
||||
for _, model := range []string{"best-cloud:cloud", "best-local"} {
|
||||
if !slices.Contains(itemNames, model) {
|
||||
t.Fatalf("expected compatible model %q in picker, got %v", model, itemNames)
|
||||
}
|
||||
}
|
||||
return "best-local", nil
|
||||
}
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/api/experimental/model-recommendations":
|
||||
@@ -3585,12 +3600,11 @@ func TestLaunchIntegration_ModelOverrideDeprecatedPromptsAndDeclineCancels(t *te
|
||||
`{"model":"best-cloud:cloud","description":"Cloud rec","context_length":262144,"max_output_tokens":32768},`+
|
||||
`{"model":"best-local","description":"Local rec"}`+
|
||||
`]}`)
|
||||
case "/api/tags":
|
||||
fmt.Fprint(w, `{"models":[{"name":"llama3.2"},{"name":"best-local"}]}`)
|
||||
case "/api/show":
|
||||
showCalls.Add(1)
|
||||
fmt.Fprint(w, `{"model_info":{"general.context_length":131072}}`)
|
||||
case "/api/pull":
|
||||
pullCalls.Add(1)
|
||||
fmt.Fprint(w, `{"status":"success"}`)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
@@ -3598,14 +3612,13 @@ func TestLaunchIntegration_ModelOverrideDeprecatedPromptsAndDeclineCancels(t *te
|
||||
defer srv.Close()
|
||||
t.Setenv("OLLAMA_HOST", srv.URL)
|
||||
|
||||
err := LaunchIntegration(context.Background(), IntegrationLaunchRequest{
|
||||
Name: "droid",
|
||||
ModelOverride: "qwen2.5-coder:32b",
|
||||
})
|
||||
if !errors.Is(err, ErrCancelled) {
|
||||
t.Fatalf("expected deprecated model override decline to cancel, got %v", err)
|
||||
if err := LaunchIntegration(context.Background(), IntegrationLaunchRequest{
|
||||
Name: "claude",
|
||||
ModelOverride: "llama3.2",
|
||||
}); err != nil {
|
||||
t.Fatalf("LaunchIntegration returned error: %v", err)
|
||||
}
|
||||
for _, want := range []string{"qwen2.5-coder:32b does not work well with StubSingle", "best-cloud:cloud", "best-local", "ollama launch droid --model best-cloud:cloud"} {
|
||||
for _, want := range []string{"llama3.2 does not work well with Claude Code", "best-cloud:cloud", "best-local", "ollama launch claude --model best-cloud:cloud"} {
|
||||
if !strings.Contains(prompt, want) {
|
||||
t.Fatalf("prompt %q does not contain %q", prompt, want)
|
||||
}
|
||||
@@ -3613,14 +3626,18 @@ func TestLaunchIntegration_ModelOverrideDeprecatedPromptsAndDeclineCancels(t *te
|
||||
if promptOptions.YesLabel != "Launch anyway" || promptOptions.NoLabel != "Pick another model" || promptOptions.Default != ConfirmDefaultNo {
|
||||
t.Fatalf("unexpected deprecation prompt options: %+v", promptOptions)
|
||||
}
|
||||
if showCalls.Load() != 0 {
|
||||
t.Fatalf("deprecated override decline should stop before /api/show, got %d calls", showCalls.Load())
|
||||
if selectorCalls != 1 {
|
||||
t.Fatalf("expected picker to open after declining override, got %d calls", selectorCalls)
|
||||
}
|
||||
if pullCalls.Load() != 0 {
|
||||
t.Fatalf("deprecated override decline should stop before /api/pull, got %d calls", pullCalls.Load())
|
||||
if showCalls.Load() != 1 {
|
||||
t.Fatalf("expected only replacement model readiness to call /api/show, got %d calls", showCalls.Load())
|
||||
}
|
||||
if runner.ranModel != "" {
|
||||
t.Fatalf("expected integration not to run, got %q", runner.ranModel)
|
||||
saved, err := config.LoadIntegration("claude")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to reload saved config: %v", err)
|
||||
}
|
||||
if got := primaryModelFromConfig(saved); got != "best-local" {
|
||||
t.Fatalf("expected picked model to replace deprecated override, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user