Skip to content

Commit 44f4df5

Browse files
committed
Start implementing /api
From ollama, this now works: MODEL_RUNNER_PORT=13434 make run OLLAMA_HOST="127.0.0.1:13434" ollama run ai/smollm2 OLLAMA_HOST="127.0.0.1:13434" ollama ls OLLAMA_HOST="127.0.0.1:13434" ollama ps OLLAMA_HOST="127.0.0.1:13434" ollama stop ai/smollm2 OLLAMA_HOST="127.0.0.1:13434" ollama pull ai/smollm2:135M-Q4_0 Signed-off-by: Eric Curtin <eric.curtin@docker.com>
1 parent 66f4b9d commit 44f4df5

File tree

6 files changed

+1051
-18
lines changed

6 files changed

+1051
-18
lines changed

cmd/cli/commands/list.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func newListCmd() *cobra.Command {
4747
if err != nil {
4848
return err
4949
}
50-
cmd.Print(models)
50+
fmt.Fprint(cmd.OutOrStdout(), models)
5151
return nil
5252
},
5353
ValidArgsFunction: completion.ModelNamesAndTags(getDesktopClient, 1),

main.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/docker/model-runner/pkg/inference/scheduling"
2121
"github.com/docker/model-runner/pkg/metrics"
2222
"github.com/docker/model-runner/pkg/middleware"
23+
"github.com/docker/model-runner/pkg/ollama"
2324
"github.com/docker/model-runner/pkg/routing"
2425
"github.com/sirupsen/logrus"
2526
)
@@ -160,6 +161,21 @@ func main() {
160161
router.Handle("/rerank", aliasHandler)
161162
router.Handle("/score", aliasHandler)
162163

164+
// Add Ollama API compatibility layer (only register with trailing slash to catch sub-paths)
165+
ollamaHandler := ollama.NewHandler(log, modelManager, scheduler, nil)
166+
router.Handle(ollama.APIPrefix+"/", ollamaHandler)
167+
168+
// Register root handler LAST - it will only catch exact "/" requests that don't match other patterns
169+
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
170+
// Only respond to exact root path
171+
if r.URL.Path != "/" {
172+
http.NotFound(w, r)
173+
return
174+
}
175+
w.WriteHeader(http.StatusOK)
176+
w.Write([]byte("Docker Model Runner is running"))
177+
})
178+
163179
// Add metrics endpoint if enabled
164180
if os.Getenv("DISABLE_METRICS") != "1" {
165181
metricsHandler := metrics.NewAggregatedMetricsHandler(

pkg/distribution/distribution/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ func (c *Client) GetModel(reference string) (types.Model, error) {
343343
model, err := c.store.Read(reference)
344344
if err != nil {
345345
c.log.Errorln("Failed to get model:", err, "reference:", utils.SanitizeForLog(reference))
346-
return nil, fmt.Errorf("get model '%q': %w", reference, err)
346+
return nil, fmt.Errorf("get model '%q': %w", utils.SanitizeForLog(reference), err)
347347
}
348348

349349
return model, nil

pkg/inference/models/manager.go

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -264,27 +264,12 @@ func (m *Manager) handleLoadModel(w http.ResponseWriter, r *http.Request) {
264264

265265
// handleGetModels handles GET <inference-prefix>/models requests.
266266
func (m *Manager) handleGetModels(w http.ResponseWriter, r *http.Request) {
267-
if m.distributionClient == nil {
268-
http.Error(w, "model distribution service unavailable", http.StatusServiceUnavailable)
269-
return
270-
}
271-
272-
// Query models.
273-
models, err := m.distributionClient.ListModels()
267+
apiModels, err := m.GetModels()
274268
if err != nil {
275269
http.Error(w, err.Error(), http.StatusInternalServerError)
276270
return
277271
}
278272

279-
apiModels := make([]*Model, len(models))
280-
for i, model := range models {
281-
apiModels[i], err = ToModel(model)
282-
if err != nil {
283-
http.Error(w, err.Error(), http.StatusInternalServerError)
284-
return
285-
}
286-
}
287-
288273
// Write the response.
289274
w.Header().Set("Content-Type", "application/json")
290275
if err := json.NewEncoder(w).Encode(apiModels); err != nil {
@@ -884,6 +869,31 @@ func (m *Manager) IsModelInStore(ref string) (bool, error) {
884869
return m.distributionClient.IsModelInStore(ref)
885870
}
886871

872+
// GetModels returns all models.
873+
func (m *Manager) GetModels() ([]*Model, error) {
874+
if m.distributionClient == nil {
875+
return nil, fmt.Errorf("model distribution service unavailable")
876+
}
877+
878+
// Query models.
879+
models, err := m.distributionClient.ListModels()
880+
if err != nil {
881+
return nil, fmt.Errorf("error while listing models: %w", err)
882+
}
883+
884+
apiModels := make([]*Model, 0, len(models))
885+
for _, model := range models {
886+
apiModel, err := ToModel(model)
887+
if err != nil {
888+
m.log.Warnf("error while converting model, skipping: %v", err)
889+
continue
890+
}
891+
apiModels = append(apiModels, apiModel)
892+
}
893+
894+
return apiModels, nil
895+
}
896+
887897
// GetModel returns a single model.
888898
func (m *Manager) GetModel(ref string) (types.Model, error) {
889899
model, err := m.distributionClient.GetModel(ref)

pkg/inference/scheduling/scheduler.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,11 @@ func (s *Scheduler) GetRunningBackends(w http.ResponseWriter, r *http.Request) {
321321
}
322322
}
323323

324+
// GetRunningBackendsInfo returns information about all running backends as a slice
325+
func (s *Scheduler) GetRunningBackendsInfo(ctx context.Context) []BackendStatus {
326+
return s.getLoaderStatus(ctx)
327+
}
328+
324329
// getLoaderStatus returns information about all running backends managed by the loader
325330
func (s *Scheduler) getLoaderStatus(ctx context.Context) []BackendStatus {
326331
if !s.loader.lock(ctx) {

0 commit comments

Comments
 (0)