package cmd import ( "errors" "os" "path/filepath" "strconv" "strings" "testing" "time" "github.com/spf13/cobra" coreagent "github.com/ollama/ollama/agent" agenttools "github.com/ollama/ollama/agent/tools" "github.com/ollama/ollama/api" "github.com/ollama/ollama/cmd/config" agentchat "github.com/ollama/ollama/cmd/tui/chat" ) func TestAgentSystemPromptIncludesSessionWorkingDirOnce(t *testing.T) { workingDir := t.TempDir() prompt := agentSystemPromptAtWithWorkingDir( time.Date(2026, time.July, 14, 0, 0, 0, 0, time.UTC), "test-model", "model instruction", "caller instruction", workingDir, ) workingDirInstruction := "Current working directory: " + strconv.Quote(workingDir) + "." if got := strings.Count(prompt, workingDirInstruction); got != 1 { t.Fatalf("working directory instruction count = %d, want 1:\n%s", got, prompt) } for _, want := range []string{"model instruction", "caller instruction"} { if !strings.Contains(prompt, want) { t.Fatalf("prompt missing %q:\n%s", want, prompt) } } } func TestAgentWorkingDirIgnoresGetwdFailure(t *testing.T) { original := agentGetwd agentGetwd = func() (string, error) { return "", errors.New("getwd failed") } t.Cleanup(func() { agentGetwd = original }) if got := agentWorkingDir(); got != "" { t.Fatalf("working directory = %q, want empty on getwd failure", got) } } func TestAgentSystemPromptIncludesSkillCatalog(t *testing.T) { dir := t.TempDir() if err := os.Mkdir(filepath.Join(dir, "release-notes"), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "release-notes", "SKILL.md"), []byte("---\nname: release-notes\ndescription: Draft releases.\n---\nUse bullets."), 0o644); err != nil { t.Fatal(err) } catalog, err := coreagent.DiscoverSkills(dir) if err != nil { t.Fatal(err) } got := agentSystemPromptAtWithWorkingDir(time.Date(2026, 7, 14, 0, 0, 0, 0, time.UTC), "model", "", catalog.SystemContext(), "") if !strings.Contains(got, "release-notes: Draft releases.") || !strings.Contains(got, "normal approval rules") { t.Fatalf("system prompt missing skill context: %q", got) } } func TestAgentSkillSystemContextRequiresAvailableEnabledSkillTool(t *testing.T) { dir := t.TempDir() if err := os.Mkdir(filepath.Join(dir, "release-notes"), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "release-notes", "SKILL.md"), []byte("---\nname: release-notes\ndescription: Draft releases.\n---\nUse bullets."), 0o644); err != nil { t.Fatal(err) } catalog, err := coreagent.DiscoverSkills(dir) if err != nil { t.Fatal(err) } registry := &coreagent.Registry{} registry.Register(&agenttools.Skill{Catalog: catalog}) if got := agentSkillSystemContext(catalog, registry, false); !strings.Contains(got, "release-notes: Draft releases.") { t.Fatalf("enabled skill context = %q", got) } if got := agentSkillSystemContext(catalog, registry, true); got != "" { t.Fatalf("disabled tools should omit skill context, got %q", got) } if got := agentSkillSystemContext(catalog, &coreagent.Registry{}, false); got != "" { t.Fatalf("unavailable skill tool should omit skill context, got %q", got) } } func TestAgentSelectionItemsUseLaunchSections(t *testing.T) { items := agentSelectionItems([]agentchat.ModelOption{ {Name: "glm-5.2:cloud", Description: "cloud", Recommended: true, Cloud: true}, {Name: "llama3.2", Description: "local"}, }) if len(items) != 2 { t.Fatalf("items = %d, want 2", len(items)) } if !items[0].Recommended { t.Fatalf("cloud recommendation should be pinned: %#v", items[0]) } if items[1].Recommended { t.Fatalf("local selected model should stay in launch More section: %#v", items[1]) } if items[1].Description != "local" { t.Fatalf("selected model description = %q, want plain description", items[1].Description) } } func TestContextWindowFromRecommendationsMatchesCloudModel(t *testing.T) { got := contextWindowFromRecommendations("glm-5.2:cloud", []api.ModelRecommendation{ {Model: "gemma4:cloud", ContextLength: 32768}, {Model: "glm-5.2:cloud", ContextLength: 1048576}, }) if got != 1048576 { t.Fatalf("context window = %d, want 1048576", got) } } func TestShowResponseContextWindowReadsArchitectureContextLength(t *testing.T) { got := showResponseContextWindow(&api.ShowResponse{ ModelInfo: map[string]any{ "qwen3.context_length": uint32(262144), "qwen3.rope.scaling.original_context_length": uint32(32768), }, }) if got != 262144 { t.Fatalf("context window = %d, want 262144", got) } } func TestProcessContextWindowForModelMatchesLatestAlias(t *testing.T) { got := processContextWindowForModel("ornith", &api.ProcessResponse{ Models: []api.ProcessModelResponse{ {Name: "other:latest", Model: "other:latest", ContextLength: 32768}, {Name: "ornith:latest", Model: "ornith:latest", ContextLength: 262144}, }, }) if got != 262144 { t.Fatalf("context window = %d, want 262144", got) } } func TestSaveLastAgentModel(t *testing.T) { setCmdTestHome(t, t.TempDir()) if err := saveLastAgentModel(" qwen3:8b "); err != nil { t.Fatalf("saveLastAgentModel returned error: %v", err) } if got := config.LastModel(); got != "qwen3:8b" { t.Fatalf("last model = %q, want qwen3:8b", got) } if err := saveLastAgentModel(" "); err != nil { t.Fatalf("saveLastAgentModel blank returned error: %v", err) } if got := config.LastModel(); got != "qwen3:8b" { t.Fatalf("blank save changed last model to %q", got) } } func TestApplyAgentFlagsNoTools(t *testing.T) { cmd := &cobra.Command{} registerAgentFlags(cmd) if err := cmd.Flags().Set("no-tools", "true"); err != nil { t.Fatal(err) } var opts agentTUIOptions if _, err := applyAgentFlags(cmd, &opts); err != nil { t.Fatalf("applyAgentFlags returned error: %v", err) } if !opts.ToolsDisabled { t.Fatal("--no-tools should disable tools") } }