Repository: ollama/ollama
Pull request: Add ModifiedAt Field to /api/show

Changed `model` variable name to `m` due to `ParseName` function from `model `package

E.g.

...

```
  "template": "
 {{ if .System }}{{ .System }} {{ end }}{{ .Prompt }} 
",
  "details": {
    "parent_model": "",
    "format": "gguf",
    "family": "llama",
    "families": [
      "llama",
      "clip"
    ],
    "parameter_size": "7B",
    "quantization_level": "Q4_0"
  },
  "modified_at": "2024-06-10T13:01:22.096005938-07:00"
}
```

Changed files:
filename: api/types.go
patch: @@ -238,6 +238,7 @@ type ShowResponse struct {
 	System     string       `json:"system,omitempty"`
 	Details    ModelDetails `json:"details,omitempty"`
 	Messages   []Message    `json:"messages,omitempty"`
+	ModifiedAt time.Time    `json:"modified_at,omitempty"`
 }
 
 // CopyRequest is the request passed to [Client.Copy].

filename: server/routes.go
patch: @@ -646,9 +646,12 @@ func (s *Server) ShowModelHandler(c *gin.Context) {
 
 	resp, err := GetModelInfo(req)
 	if err != nil {
-		if os.IsNotExist(err) {
+		switch {
+		case os.IsNotExist(err):
 			c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("model '%s' not found", req.Model)})
-		} else {
+		case err.Error() == "invalid model name":
+			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+		default:
 			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
 		}
 		return
@@ -658,44 +661,55 @@ func (s *Server) ShowModelHandler(c *gin.Context) {
 }
 
 func GetModelInfo(req api.ShowRequest) (*api.ShowResponse, error) {
-	model, err := GetModel(req.Model)
+	m, err := GetModel(req.Model)
 	if err != nil {
 		return nil, err
 	}
 
 	modelDetails := api.ModelDetails{
-		ParentModel:       model.ParentModel,
-		Format:            model.Config.ModelFormat,
-		Family:            model.Config.ModelFamily,
-		Families:          model.Config.ModelFamilies,
-		ParameterSize:     model.Config.ModelType,
-		QuantizationLevel: model.Config.FileType,
+		ParentModel:       m.ParentModel,
+		Format:            m.Config.ModelFormat,
+		Family:            m.Config.ModelFamily,
+		Families:          m.Config.ModelFamilies,
+		ParameterSize:     m.Config.ModelType,
+		QuantizationLevel: m.Config.FileType,
 	}
 
 	if req.System != "" {
-		model.System = req.System
+		m.System = req.System
 	}
 
 	if req.Template != "" {
-		model.Template = req.Template
+		m.Template = req.Template
 	}
 
 	msgs := make([]api.Message, 0)
-	for _, msg := range model.Messages {
+	for _, msg := range m.Messages {
 		msgs = append(msgs, api.Message{Role: msg.Role, Content: msg.Content})
 	}
 
+	n := model.ParseName(req.Model)
+	if !n.IsValid() {
+		return nil, fmt.Errorf("invalid model name")
+	}
+
+	manifest, err := ParseNamedManifest(n)
+	if err != nil {
+		return nil, err
+	}
+
 	resp := &api.ShowResponse{
-		License:  strings.Join(model.License, "\n"),
-		System:   model.System,
-		Template: model.Template,
-		Details:  modelDetails,
-		Messages: msgs,
+		License:    strings.Join(m.License, "\n"),
+		System:     m.System,
+		Template:   m.Template,
+		Details:    modelDetails,
+		Messages:   msgs,
+		ModifiedAt: manifest.fi.ModTime(),
 	}
 
 	var params []string
 	cs := 30
-	for k, v := range model.Options {
+	for k, v := range m.Options {
 		switch val := v.(type) {
 		case []interface{}:
 			for _, nv := range val {
@@ -709,15 +723,15 @@ func GetModelInfo(req api.ShowRequest) (*api.ShowResponse, error) {
 
 	for k, v := range req.Options {
 		if _, ok := req.Options[k]; ok {
-			model.Options[k] = v
+			m.Options[k] = v
 		}
 	}
 
 	var sb strings.Builder
 	fmt.Fprintln(&sb, "# Modelfile generated by \"ollama show\"")
 	fmt.Fprintln(&sb, "# To build a new Modelfile based on this, replace FROM with:")
-	fmt.Fprintf(&sb, "# FROM %s\n\n", model.ShortName)
-	fmt.Fprint(&sb, model.String())
+	fmt.Fprintf(&sb, "# FROM %s\n\n", m.ShortName)
+	fmt.Fprint(&sb, m.String())
 	resp.Modelfile = sb.String()
 
 	return resp, nil