mirror of
https://github.com/ollama/ollama.git
synced 2026-03-27 02:58:43 +07:00
api/show: overwrite basename for copilot chat (#15062)
Copilot Chat prefers to use `general.basename` in the built-in Ollama integration, but this name isn't usually shown directly to users (and there may be many models that share this name). Instead we pass back `req.Model`, which for this extension is the value that we return from `/api/tags`
This commit is contained in:
@@ -63,6 +63,7 @@ const (
|
|||||||
cloudErrRemoteModelDetailsUnavailable = "remote model details are unavailable"
|
cloudErrRemoteModelDetailsUnavailable = "remote model details are unavailable"
|
||||||
cloudErrWebSearchUnavailable = "web search is unavailable"
|
cloudErrWebSearchUnavailable = "web search is unavailable"
|
||||||
cloudErrWebFetchUnavailable = "web fetch is unavailable"
|
cloudErrWebFetchUnavailable = "web fetch is unavailable"
|
||||||
|
copilotChatUserAgentPrefix = "GitHubCopilotChat/"
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeModelRefParseError(c *gin.Context, err error, fallbackStatus int, fallbackMessage string) {
|
func writeModelRefParseError(c *gin.Context, err error, fallbackStatus int, fallbackMessage string) {
|
||||||
@@ -1158,6 +1159,17 @@ func (s *Server) ShowHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userAgent := c.Request.UserAgent()
|
||||||
|
if strings.HasPrefix(userAgent, copilotChatUserAgentPrefix) {
|
||||||
|
if resp.ModelInfo == nil {
|
||||||
|
resp.ModelInfo = map[string]any{}
|
||||||
|
}
|
||||||
|
// Copilot Chat prefers `general.basename`, but this is usually not what
|
||||||
|
// users are familiar with, so let's just echo back what we had returned in
|
||||||
|
// `/api/tags`
|
||||||
|
resp.ModelInfo["general.basename"] = req.Model
|
||||||
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, resp)
|
c.JSON(http.StatusOK, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -721,6 +721,111 @@ func TestShow(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShowCopilotUserAgentOverwritesExistingBasename(t *testing.T) {
|
||||||
|
t.Setenv("OLLAMA_MODELS", t.TempDir())
|
||||||
|
|
||||||
|
var s Server
|
||||||
|
|
||||||
|
w := createRequest(t, s.CreateHandler, api.CreateRequest{
|
||||||
|
Model: "show-model",
|
||||||
|
From: "bob",
|
||||||
|
RemoteHost: "https://ollama.com",
|
||||||
|
Info: map[string]any{
|
||||||
|
"model_family": "gptoss",
|
||||||
|
"base_name": "upstream-base-name",
|
||||||
|
},
|
||||||
|
Stream: &stream,
|
||||||
|
})
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected status code 200 creating model, actual %d", w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
h, err := s.GenerateRoutes(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
makeRequest := func(userAgent string) api.ShowResponse {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/api/show", strings.NewReader(`{"model":"show-model"}`))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
if userAgent != "" {
|
||||||
|
req.Header.Set("User-Agent", userAgent)
|
||||||
|
}
|
||||||
|
h.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected status code 200, actual %d", w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp api.ShowResponse
|
||||||
|
if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
withoutCopilot := makeRequest("")
|
||||||
|
if withoutCopilot.ModelInfo["general.basename"] != "upstream-base-name" {
|
||||||
|
t.Fatalf("expected general.basename to be %q, got %v", "upstream-base-name", withoutCopilot.ModelInfo["general.basename"])
|
||||||
|
}
|
||||||
|
|
||||||
|
withCopilot := makeRequest("GitHubCopilotChat/0.41.1")
|
||||||
|
if withCopilot.ModelInfo["general.basename"] != "show-model" {
|
||||||
|
t.Fatalf("expected general.basename to be %q, got %v", "show-model", withCopilot.ModelInfo["general.basename"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if withCopilot.ModelInfo["general.architecture"] != "gptoss" {
|
||||||
|
t.Fatalf("expected general.architecture to be %q, got %v", "gptoss", withCopilot.ModelInfo["general.architecture"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShowCopilotUserAgentSetsBasenameWhenModelInfoIsEmpty(t *testing.T) {
|
||||||
|
t.Setenv("OLLAMA_MODELS", t.TempDir())
|
||||||
|
|
||||||
|
var s Server
|
||||||
|
|
||||||
|
w := createRequest(t, s.CreateHandler, api.CreateRequest{
|
||||||
|
Model: "show-remote",
|
||||||
|
From: "bob",
|
||||||
|
RemoteHost: "https://ollama.com",
|
||||||
|
Stream: &stream,
|
||||||
|
})
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected status code 200 creating model, actual %d", w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
h, err := s.GenerateRoutes(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/api/show", strings.NewReader(`{"model":"show-remote"}`))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("User-Agent", "GitHubCopilotChat/0.41.1")
|
||||||
|
h.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected status code 200, actual %d", w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp api.ShowResponse
|
||||||
|
if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.ModelInfo["general.basename"] != "show-remote" {
|
||||||
|
t.Fatalf("expected general.basename to be %q, got %v", "show-remote", resp.ModelInfo["general.basename"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.ModelInfo) != 1 {
|
||||||
|
t.Fatalf("expected model_info to contain only general.basename, got %#v", resp.ModelInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNormalize(t *testing.T) {
|
func TestNormalize(t *testing.T) {
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
input []float32
|
input []float32
|
||||||
|
|||||||
Reference in New Issue
Block a user