agent/tools: surface actionable web auth error (#17169)

This commit is contained in:
Parth Sareen
2026-07-14 13:35:16 -07:00
committed by GitHub
parent 123b1f2479
commit 3bd506bd1c
2 changed files with 60 additions and 7 deletions

View File

@@ -13,17 +13,14 @@ import (
internalcloud "github.com/ollama/ollama/internal/cloud"
)
var (
ErrWebSearchAuthRequired = errors.New("web search requires authentication")
ErrWebFetchAuthRequired = errors.New("web fetch requires authentication")
)
const (
maxWebFetchContentRunes = 60_000
webSearchTimeout = 15 * time.Second
webFetchTimeout = 30 * time.Second
)
var ErrWebAuthRequired = errors.New("Not authenticated. Run `ollama signin` and try again.")
type WebSearch struct{}
func (w *WebSearch) Name() string {
@@ -76,7 +73,7 @@ func (w *WebSearch) Execute(ctx context.Context, _ agent.ToolContext, args map[s
if err != nil {
var authErr api.AuthorizationError
if errors.As(err, &authErr) {
return agent.ToolResult{}, fmt.Errorf("%w: %s", ErrWebSearchAuthRequired, authErr)
return agent.ToolResult{}, ErrWebAuthRequired
}
return agent.ToolResult{}, err
}
@@ -160,7 +157,7 @@ func (w *WebFetch) Execute(ctx context.Context, _ agent.ToolContext, args map[st
if err != nil {
var authErr api.AuthorizationError
if errors.As(err, &authErr) {
return agent.ToolResult{}, fmt.Errorf("%w: %s", ErrWebFetchAuthRequired, authErr)
return agent.ToolResult{}, ErrWebAuthRequired
}
return agent.ToolResult{}, err
}

View File

@@ -2,6 +2,7 @@ package tools
import (
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"strings"
@@ -20,6 +21,61 @@ func TestWebToolsRequireApproval(t *testing.T) {
}
}
var webToolCases = []struct {
name string
tool coreagent.Tool
args map[string]any
path string
}{
{"search", &WebSearch{}, map[string]any{"query": "ollama"}, "/api/experimental/web_search"},
{"fetch", &WebFetch{}, map[string]any{"url": "https://ollama.com"}, "/api/experimental/web_fetch"},
}
// runWebTool executes tool against a stub server that responds to every
// request with status and body, returning the resulting error.
func runWebTool(t *testing.T, tool coreagent.Tool, args map[string]any, path string, status int, body string) error {
t.Helper()
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != path {
t.Fatalf("path = %q, want %q", r.URL.Path, path)
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_, _ = w.Write([]byte(body))
}))
t.Cleanup(ts.Close)
t.Setenv("OLLAMA_HOST", ts.URL)
_, err := tool.Execute(t.Context(), coreagent.ToolContext{}, args)
return err
}
func TestWebToolsReportAuthenticationError(t *testing.T) {
for _, tt := range webToolCases {
t.Run(tt.name, func(t *testing.T) {
err := runWebTool(t, tt.tool, tt.args, tt.path, http.StatusUnauthorized,
`{"error":"unauthorized","signin_url":"https://ollama.com/signin"}`)
if !errors.Is(err, ErrWebAuthRequired) {
t.Fatalf("error = %v, want %v", err, ErrWebAuthRequired)
}
})
}
}
func TestWebToolsPreserveNonAuthenticationErrors(t *testing.T) {
for _, tt := range webToolCases {
t.Run(tt.name, func(t *testing.T) {
err := runWebTool(t, tt.tool, tt.args, tt.path, http.StatusTooManyRequests,
`{"error":"web search quota exceeded"}`)
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "web search quota exceeded") {
t.Fatalf("error = %q, want original error message", err)
}
})
}
}
func TestWebFetchRejectsUnsupportedScheme(t *testing.T) {
tests := []struct {
name string