|
@@ -719,62 +719,65 @@ func GetModelInfo(req api.ShowRequest) (*api.ShowResponse, error) {
|
|
|
}
|
|
|
|
|
|
func (s *Server) ListModelsHandler(c *gin.Context) {
|
|
|
- models := make([]api.ModelResponse, 0)
|
|
|
- manifestsPath, err := GetManifestPath()
|
|
|
+ manifests, err := GetManifestPath()
|
|
|
if err != nil {
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- modelResponse := func(modelName string) (api.ModelResponse, error) {
|
|
|
- model, err := GetModel(modelName)
|
|
|
- if err != nil {
|
|
|
- return api.ModelResponse{}, err
|
|
|
- }
|
|
|
-
|
|
|
- modelDetails := api.ModelDetails{
|
|
|
- Format: model.Config.ModelFormat,
|
|
|
- Family: model.Config.ModelFamily,
|
|
|
- Families: model.Config.ModelFamilies,
|
|
|
- ParameterSize: model.Config.ModelType,
|
|
|
- QuantizationLevel: model.Config.FileType,
|
|
|
- }
|
|
|
-
|
|
|
- return api.ModelResponse{
|
|
|
- Model: model.ShortName,
|
|
|
- Name: model.ShortName,
|
|
|
- Size: model.Size,
|
|
|
- Digest: model.Digest,
|
|
|
- Details: modelDetails,
|
|
|
- }, nil
|
|
|
- }
|
|
|
-
|
|
|
- walkFunc := func(path string, info os.FileInfo, _ error) error {
|
|
|
+ var models []api.ModelResponse
|
|
|
+ if err := filepath.Walk(manifests, func(path string, info os.FileInfo, _ error) error {
|
|
|
if !info.IsDir() {
|
|
|
- path, tag := filepath.Split(path)
|
|
|
- model := strings.Trim(strings.TrimPrefix(path, manifestsPath), string(os.PathSeparator))
|
|
|
- modelPath := strings.Join([]string{model, tag}, ":")
|
|
|
- canonicalModelPath := strings.ReplaceAll(modelPath, string(os.PathSeparator), "/")
|
|
|
+ rel, err := filepath.Rel(manifests, path)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
|
|
|
- resp, err := modelResponse(canonicalModelPath)
|
|
|
+ n := model.ParseNameFromFilepath(rel)
|
|
|
+ m, err := ParseNamedManifest(n)
|
|
|
if err != nil {
|
|
|
- slog.Info(fmt.Sprintf("skipping file: %s", canonicalModelPath))
|
|
|
- // nolint: nilerr
|
|
|
- return nil
|
|
|
+ return err
|
|
|
}
|
|
|
|
|
|
- resp.ModifiedAt = info.ModTime()
|
|
|
- models = append(models, resp)
|
|
|
+ f, err := m.Config.Open()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ defer f.Close()
|
|
|
+
|
|
|
+ var c ConfigV2
|
|
|
+ if err := json.NewDecoder(f).Decode(&c); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ // tag should never be masked
|
|
|
+ models = append(models, api.ModelResponse{
|
|
|
+ Model: n.DisplayShortest(),
|
|
|
+ Name: n.DisplayShortest(),
|
|
|
+ Size: m.Size(),
|
|
|
+ Digest: m.Digest,
|
|
|
+ ModifiedAt: info.ModTime(),
|
|
|
+ Details: api.ModelDetails{
|
|
|
+ Format: c.ModelFormat,
|
|
|
+ Family: c.ModelFamily,
|
|
|
+ Families: c.ModelFamilies,
|
|
|
+ ParameterSize: c.ModelType,
|
|
|
+ QuantizationLevel: c.FileType,
|
|
|
+ },
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
- }
|
|
|
-
|
|
|
- if err := filepath.Walk(manifestsPath, walkFunc); err != nil {
|
|
|
+ }); err != nil {
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
|
return
|
|
|
}
|
|
|
|
|
|
+ slices.SortStableFunc(models, func(i, j api.ModelResponse) int {
|
|
|
+ // most recently modified first
|
|
|
+ return cmp.Compare(j.ModifiedAt.Unix(), i.ModifiedAt.Unix())
|
|
|
+ })
|
|
|
+
|
|
|
c.JSON(http.StatusOK, api.ListResponse{Models: models})
|
|
|
}
|
|
|
|