@@ -84,7 +84,7 @@ func (h *HTTPHandler) routeHandlers() map[string]http.HandlerFunc {
8484 "POST " + inference .ModelsPrefix + "/create" : h .handleCreateModel ,
8585 "POST " + inference .ModelsPrefix + "/load" : h .handleLoadModel ,
8686 "GET " + inference .ModelsPrefix : h .handleGetModels ,
87- "GET " + inference .ModelsPrefix + "/{name ...}" : h . handleGetModel ,
87+ "GET " + inference .ModelsPrefix + "/{nameAndAction ...}" : h . handleModelGetAction ,
8888 "DELETE " + inference .ModelsPrefix + "/{name...}" : h .handleDeleteModel ,
8989 "POST " + inference .ModelsPrefix + "/{nameAndAction...}" : h .handleModelAction ,
9090 "DELETE " + inference .ModelsPrefix + "/purge" : h .handlePurge ,
@@ -142,6 +142,35 @@ func (h *HTTPHandler) handleLoadModel(w http.ResponseWriter, r *http.Request) {
142142 }
143143}
144144
145+ func (h * HTTPHandler ) handleModelGetAction (w http.ResponseWriter , r * http.Request ) {
146+ nameAndAction := r .PathValue ("nameAndAction" )
147+ model , action := path .Split (nameAndAction )
148+ model = strings .TrimRight (model , "/" )
149+
150+ if action == "export" {
151+ h .handleExportModel (w , r , model )
152+ return
153+ }
154+
155+ h .handleGetModelByRef (w , r , nameAndAction )
156+ }
157+
158+ func (h * HTTPHandler ) handleExportModel (w http.ResponseWriter , r * http.Request , modelRef string ) {
159+ w .Header ().Set ("Content-Type" , "application/x-tar" )
160+ w .Header ().Set ("Content-Disposition" , fmt .Sprintf ("attachment; filename=%q" , modelRef + ".tar" ))
161+
162+ err := h .manager .Export (modelRef , w )
163+ if err != nil {
164+ if errors .Is (err , distribution .ErrModelNotFound ) {
165+ http .Error (w , err .Error (), http .StatusNotFound )
166+ return
167+ }
168+ h .log .Warnln ("Error while exporting model:" , err )
169+ http .Error (w , err .Error (), http .StatusInternalServerError )
170+ return
171+ }
172+ }
173+
145174// handleGetModels handles GET <inference-prefix>/models requests.
146175func (h * HTTPHandler ) handleGetModels (w http.ResponseWriter , r * http.Request ) {
147176 apiModels , err := h .manager .List ()
@@ -160,7 +189,10 @@ func (h *HTTPHandler) handleGetModels(w http.ResponseWriter, r *http.Request) {
160189// handleGetModel handles GET <inference-prefix>/models/{name} requests.
161190func (h * HTTPHandler ) handleGetModel (w http.ResponseWriter , r * http.Request ) {
162191 modelRef := r .PathValue ("name" )
192+ h .handleGetModelByRef (w , r , modelRef )
193+ }
163194
195+ func (h * HTTPHandler ) handleGetModelByRef (w http.ResponseWriter , r * http.Request , modelRef string ) {
164196 // Parse remote query parameter
165197 remote := false
166198 if r .URL .Query ().Has ("remote" ) {
@@ -355,10 +387,8 @@ func (h *HTTPHandler) handleOpenAIGetModel(w http.ResponseWriter, r *http.Reques
355387 }
356388}
357389
358- // handleTagModel handles POST <inference-prefix>/models/{nameAndAction} requests.
359- // Action is one of:
360- // - tag: tag the model with a repository and tag (e.g. POST <inference-prefix>/models/my-org/my-repo:latest/tag})
361- // - push: pushes a tagged model to the registry
390+ // handleModelAction handles POST <inference-prefix>/models/{nameAndAction} requests.
391+ // Actions: tag, push, repackage
362392func (h * HTTPHandler ) handleModelAction (w http.ResponseWriter , r * http.Request ) {
363393 model , action := path .Split (r .PathValue ("nameAndAction" ))
364394 model = strings .TrimRight (model , "/" )
@@ -368,6 +398,8 @@ func (h *HTTPHandler) handleModelAction(w http.ResponseWriter, r *http.Request)
368398 h .handleTagModel (w , r , model )
369399 case "push" :
370400 h .handlePushModel (w , r , model )
401+ case "repackage" :
402+ h .handleRepackageModel (w , r , model )
371403 default :
372404 http .Error (w , fmt .Sprintf ("unknown action %q" , action ), http .StatusNotFound )
373405 }
@@ -438,6 +470,49 @@ func (h *HTTPHandler) handlePushModel(w http.ResponseWriter, r *http.Request, mo
438470 }
439471}
440472
473+ type RepackageRequest struct {
474+ Target string `json:"target"`
475+ ContextSize * uint64 `json:"context_size,omitempty"`
476+ }
477+
478+ func (h * HTTPHandler ) handleRepackageModel (w http.ResponseWriter , r * http.Request , model string ) {
479+ var req RepackageRequest
480+ if err := json .NewDecoder (r .Body ).Decode (& req ); err != nil {
481+ http .Error (w , "invalid request body: " + err .Error (), http .StatusBadRequest )
482+ return
483+ }
484+
485+ if req .Target == "" {
486+ http .Error (w , "target is required" , http .StatusBadRequest )
487+ return
488+ }
489+
490+ opts := RepackageOptions {
491+ ContextSize : req .ContextSize ,
492+ }
493+
494+ if err := h .manager .Repackage (model , req .Target , opts ); err != nil {
495+ if errors .Is (err , distribution .ErrModelNotFound ) {
496+ http .Error (w , err .Error (), http .StatusNotFound )
497+ return
498+ }
499+ h .log .Warnf ("Failed to repackage model %q: %v" , utils .SanitizeForLog (model , - 1 ), err )
500+ http .Error (w , err .Error (), http .StatusInternalServerError )
501+ return
502+ }
503+
504+ w .Header ().Set ("Content-Type" , "application/json" )
505+ w .WriteHeader (http .StatusCreated )
506+ response := map [string ]string {
507+ "message" : fmt .Sprintf ("Model repackaged successfully as %q" , req .Target ),
508+ "source" : model ,
509+ "target" : req .Target ,
510+ }
511+ if err := json .NewEncoder (w ).Encode (response ); err != nil {
512+ h .log .Warnln ("Error while encoding repackage response:" , err )
513+ }
514+ }
515+
441516// handlePurge handles DELETE <inference-prefix>/models/purge requests.
442517func (h * HTTPHandler ) handlePurge (w http.ResponseWriter , _ * http.Request ) {
443518 err := h .manager .Purge ()
0 commit comments