2023-07-03 15:22:44 -04:00
package server
import (
2023-07-25 17:08:51 -04:00
"context"
2023-07-06 10:40:11 -07:00
"encoding/json"
2023-10-06 16:06:20 -04:00
"errors"
2023-07-21 23:02:12 -07:00
"fmt"
2023-07-03 15:22:44 -04:00
"io"
2023-10-06 16:06:20 -04:00
"io/fs"
2023-07-03 15:22:44 -04:00
"log"
"net"
"net/http"
2023-07-07 15:27:43 -04:00
"os"
2023-08-30 16:35:03 -04:00
"os/signal"
2023-07-14 17:27:14 -07:00
"path/filepath"
2023-07-31 21:35:18 -04:00
"reflect"
2023-09-12 11:04:35 -04:00
"runtime"
2023-09-06 11:04:17 -07:00
"strconv"
2023-07-06 10:40:11 -07:00
"strings"
2023-07-18 11:59:42 -07:00
"sync"
2023-08-30 16:35:03 -04:00
"syscall"
2023-07-12 18:18:06 -07:00
"time"
2023-07-03 15:22:44 -04:00
2023-07-21 18:01:24 -07:00
"github.com/gin-contrib/cors"
2023-07-03 15:22:44 -04:00
"github.com/gin-gonic/gin"
2023-07-03 16:32:48 -04:00
"github.com/jmorganca/ollama/api"
2023-07-21 13:33:56 -07:00
"github.com/jmorganca/ollama/llm"
2023-10-13 16:08:35 -07:00
"github.com/jmorganca/ollama/version"
2023-07-03 15:22:44 -04:00
)
2023-08-22 09:48:35 -07:00
var mode string = gin . DebugMode
func init ( ) {
switch mode {
case gin . DebugMode :
case gin . ReleaseMode :
case gin . TestMode :
default :
mode = gin . DebugMode
}
gin . SetMode ( mode )
}
2023-07-31 21:35:18 -04:00
var loaded struct {
2023-07-19 15:00:28 -07:00
mu sync . Mutex
2023-10-19 10:39:58 -04:00
runner llm . LLM
2023-07-19 15:00:28 -07:00
expireAt time . Time
expireTimer * time . Timer
2023-07-31 21:35:18 -04:00
2023-10-19 10:39:58 -04:00
* Model
* api . Options
2023-07-18 11:59:42 -07:00
}
2023-08-15 10:35:39 -03:00
var defaultSessionDuration = 5 * time . Minute
2023-08-08 15:13:22 -04:00
// load a model into memory if it is not already loaded, it is up to the caller to lock loaded.mu before calling this function
2023-09-21 20:38:49 +01:00
func load ( ctx context . Context , workDir string , model * Model , reqOpts map [ string ] interface { } , sessionDuration time . Duration ) error {
2023-08-03 15:55:35 -04:00
opts := api . DefaultOptions ( )
if err := opts . FromMap ( model . Options ) ; err != nil {
log . Printf ( "could not load model options: %v" , err )
2023-08-08 15:13:22 -04:00
return err
2023-08-03 15:55:35 -04:00
}
2023-08-08 15:13:22 -04:00
if err := opts . FromMap ( reqOpts ) ; err != nil {
return err
2023-08-03 15:55:35 -04:00
}
2023-08-30 16:35:03 -04:00
// check if the loaded model is still running in a subprocess, in case something unexpected happened
2023-10-19 10:39:58 -04:00
if loaded . runner != nil {
if err := loaded . runner . Ping ( ctx ) ; err != nil {
2023-08-30 16:35:03 -04:00
log . Print ( "loaded llm process not responding, closing now" )
// the subprocess is no longer running, so close it
2023-10-19 10:39:58 -04:00
loaded . runner . Close ( )
loaded . runner = nil
loaded . Model = nil
loaded . Options = nil
2023-08-30 16:35:03 -04:00
}
}
2023-10-19 10:39:58 -04:00
needLoad := loaded . runner == nil || // is there a model loaded?
loaded . ModelPath != model . ModelPath || // has the base model changed?
! reflect . DeepEqual ( loaded . AdapterPaths , model . AdapterPaths ) || // have the adapters changed?
! reflect . DeepEqual ( loaded . Options . Runner , opts . Runner ) // have the runner options changed?
if needLoad {
if loaded . runner != nil {
2023-08-30 16:35:03 -04:00
log . Println ( "changing loaded model" )
2023-10-19 10:39:58 -04:00
loaded . runner . Close ( )
loaded . runner = nil
loaded . Model = nil
loaded . Options = nil
2023-07-18 11:59:42 -07:00
}
2023-07-17 12:08:10 -07:00
2023-10-19 10:39:58 -04:00
llmRunner , err := llm . New ( workDir , model . ModelPath , model . AdapterPaths , opts )
2023-07-18 11:59:42 -07:00
if err != nil {
2023-10-19 14:50:45 -04:00
// some older models are not compatible with newer versions of llama.cpp
// show a generalized compatibility error until there is a better way to
// check for model compatibility
if strings . Contains ( err . Error ( ) , "failed to load model" ) {
err = fmt . Errorf ( "%v: this model may be incompatible with your version of Ollama. If you previously pulled this model, try updating it by running `ollama pull %s`" , err , model . ShortName )
}
2023-08-08 15:13:22 -04:00
return err
2023-07-18 11:59:42 -07:00
}
2023-10-19 10:39:58 -04:00
loaded . Model = model
loaded . runner = llmRunner
loaded . Options = & opts
2023-07-19 15:00:28 -07:00
}
2023-09-21 20:38:49 +01:00
2023-10-20 18:17:14 -07:00
// update options for the loaded llm
// TODO(mxyng): this isn't thread safe, but it should be fine for now
loaded . runner . SetOptions ( opts )
2023-07-31 21:35:18 -04:00
loaded . expireAt = time . Now ( ) . Add ( sessionDuration )
2023-08-08 15:13:22 -04:00
2023-07-31 21:35:18 -04:00
if loaded . expireTimer == nil {
loaded . expireTimer = time . AfterFunc ( sessionDuration , func ( ) {
loaded . mu . Lock ( )
defer loaded . mu . Unlock ( )
2023-07-19 15:00:28 -07:00
2023-07-31 21:35:18 -04:00
if time . Now ( ) . Before ( loaded . expireAt ) {
2023-07-19 15:00:28 -07:00
return
}
2023-10-19 10:39:58 -04:00
if loaded . runner != nil {
loaded . runner . Close ( )
2023-07-19 15:00:28 -07:00
}
2023-10-19 10:39:58 -04:00
loaded . runner = nil
loaded . Model = nil
loaded . Options = nil
2023-07-19 15:00:28 -07:00
} )
2023-07-06 10:40:11 -07:00
}
2023-09-21 20:38:49 +01:00
2023-07-31 21:35:18 -04:00
loaded . expireTimer . Reset ( sessionDuration )
2023-08-08 15:13:22 -04:00
return nil
}
func GenerateHandler ( c * gin . Context ) {
loaded . mu . Lock ( )
defer loaded . mu . Unlock ( )
checkpointStart := time . Now ( )
var req api . GenerateRequest
2023-10-18 16:08:42 -07:00
err := c . ShouldBindJSON ( & req )
switch {
case errors . Is ( err , io . EOF ) :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "missing request body" } )
return
case err != nil :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : err . Error ( ) } )
2023-08-08 15:13:22 -04:00
return
}
2023-10-18 15:56:34 -07:00
if req . Model == "" {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "model is required" } )
return
}
2023-08-08 15:13:22 -04:00
model , err := GetModel ( req . Model )
if err != nil {
2023-10-06 16:06:20 -04:00
var pErr * fs . PathError
if errors . As ( err , & pErr ) {
c . JSON ( http . StatusNotFound , gin . H { "error" : fmt . Sprintf ( "model '%s' not found, try pulling it first" , req . Model ) } )
return
}
2023-08-08 15:13:22 -04:00
c . JSON ( http . StatusBadRequest , gin . H { "error" : err . Error ( ) } )
return
}
2023-09-21 20:38:49 +01:00
workDir := c . GetString ( "workDir" )
// TODO: set this duration from the request if specified
sessionDuration := defaultSessionDuration
if err := load ( c . Request . Context ( ) , workDir , model , req . Options , sessionDuration ) ; err != nil {
2023-10-12 11:18:11 -04:00
if errors . Is ( err , api . ErrInvalidOpts ) {
c . JSON ( http . StatusBadRequest , gin . H { "error" : err . Error ( ) } )
return
}
2023-08-08 15:13:22 -04:00
c . JSON ( http . StatusInternalServerError , gin . H { "error" : err . Error ( ) } )
return
}
2023-07-06 10:40:11 -07:00
2023-07-18 12:02:02 -07:00
checkpointLoaded := time . Now ( )
2023-10-16 11:07:37 -04:00
prompt , err := model . Prompt ( req )
2023-07-11 14:57:17 -07:00
if err != nil {
c . JSON ( http . StatusInternalServerError , gin . H { "error" : err . Error ( ) } )
return
}
2023-07-04 00:47:00 -04:00
2023-07-14 14:15:53 -07:00
ch := make ( chan any )
go func ( ) {
defer close ( ch )
2023-10-18 16:04:32 -07:00
// an empty request loads the model
if req . Prompt == "" && req . Template == "" && req . System == "" {
ch <- api . GenerateResponse { CreatedAt : time . Now ( ) . UTC ( ) , Model : req . Model , Done : true }
return
}
2023-07-20 12:12:08 -07:00
fn := func ( r api . GenerateResponse ) {
2023-07-31 21:35:18 -04:00
loaded . expireAt = time . Now ( ) . Add ( sessionDuration )
loaded . expireTimer . Reset ( sessionDuration )
2023-07-19 15:00:28 -07:00
2023-07-14 14:15:53 -07:00
r . Model = req . Model
r . CreatedAt = time . Now ( ) . UTC ( )
if r . Done {
2023-07-18 12:02:02 -07:00
r . TotalDuration = time . Since ( checkpointStart )
r . LoadDuration = checkpointLoaded . Sub ( checkpointStart )
2023-07-14 14:15:53 -07:00
}
ch <- r
2023-07-20 12:12:08 -07:00
}
2023-10-18 16:04:32 -07:00
if err := loaded . runner . Predict ( c . Request . Context ( ) , req . Context , prompt , fn ) ; err != nil {
ch <- gin . H { "error" : err . Error ( ) }
2023-07-20 12:12:08 -07:00
}
2023-07-14 14:15:53 -07:00
} ( )
2023-07-11 14:57:17 -07:00
2023-10-11 12:54:27 -04:00
if req . Stream != nil && ! * req . Stream {
var response api . GenerateResponse
generated := ""
for resp := range ch {
if r , ok := resp . ( api . GenerateResponse ) ; ok {
generated += r . Response
response = r
} else {
c . JSON ( http . StatusInternalServerError , gin . H { "error" : err . Error ( ) } )
return
}
}
response . Response = generated
c . JSON ( http . StatusOK , response )
return
}
2023-07-14 14:15:53 -07:00
streamResponse ( c , ch )
2023-07-11 11:54:22 -07:00
}
2023-07-06 10:40:11 -07:00
2023-08-08 15:13:22 -04:00
func EmbeddingHandler ( c * gin . Context ) {
loaded . mu . Lock ( )
defer loaded . mu . Unlock ( )
var req api . EmbeddingRequest
2023-10-18 16:08:42 -07:00
err := c . ShouldBindJSON ( & req )
switch {
case errors . Is ( err , io . EOF ) :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "missing request body" } )
return
case err != nil :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : err . Error ( ) } )
2023-08-08 15:13:22 -04:00
return
}
2023-10-18 15:56:34 -07:00
if req . Model == "" {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "model is required" } )
return
}
2023-08-08 15:13:22 -04:00
model , err := GetModel ( req . Model )
if err != nil {
c . JSON ( http . StatusBadRequest , gin . H { "error" : err . Error ( ) } )
return
}
2023-09-21 20:38:49 +01:00
workDir := c . GetString ( "workDir" )
if err := load ( c . Request . Context ( ) , workDir , model , req . Options , 5 * time . Minute ) ; err != nil {
2023-08-08 15:13:22 -04:00
c . JSON ( http . StatusBadRequest , gin . H { "error" : err . Error ( ) } )
return
}
2023-10-19 10:39:58 -04:00
if ! loaded . Options . EmbeddingOnly {
2023-08-08 15:13:22 -04:00
c . JSON ( http . StatusBadRequest , gin . H { "error" : "embedding option must be set to true" } )
return
}
2023-10-19 10:39:58 -04:00
embedding , err := loaded . runner . Embedding ( c . Request . Context ( ) , req . Prompt )
2023-08-08 15:13:22 -04:00
if err != nil {
log . Printf ( "embedding generation failed: %v" , err )
c . JSON ( http . StatusInternalServerError , gin . H { "error" : "failed to generate embedding" } )
return
}
resp := api . EmbeddingResponse {
Embedding : embedding ,
}
c . JSON ( http . StatusOK , resp )
}
2023-07-20 16:09:23 -07:00
func PullModelHandler ( c * gin . Context ) {
2023-07-11 11:54:22 -07:00
var req api . PullRequest
2023-10-18 16:08:42 -07:00
err := c . ShouldBindJSON ( & req )
switch {
case errors . Is ( err , io . EOF ) :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "missing request body" } )
return
case err != nil :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : err . Error ( ) } )
2023-07-11 11:54:22 -07:00
return
}
2023-10-18 15:56:34 -07:00
if req . Name == "" {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "name is required" } )
return
}
2023-07-16 17:02:22 -07:00
ch := make ( chan any )
go func ( ) {
defer close ( ch )
2023-07-18 18:51:30 -07:00
fn := func ( r api . ProgressResponse ) {
ch <- r
2023-07-16 17:02:22 -07:00
}
2023-07-18 18:51:30 -07:00
2023-07-21 15:42:19 -07:00
regOpts := & RegistryOptions {
Insecure : req . Insecure ,
}
2023-07-25 17:08:51 -04:00
ctx , cancel := context . WithCancel ( c . Request . Context ( ) )
defer cancel ( )
if err := PullModel ( ctx , req . Name , regOpts , fn ) ; err != nil {
2023-07-20 12:12:08 -07:00
ch <- gin . H { "error" : err . Error ( ) }
2023-07-16 17:02:22 -07:00
}
} ( )
2023-10-11 12:54:27 -04:00
if req . Stream != nil && ! * req . Stream {
waitForStream ( c , ch )
return
}
2023-07-16 17:02:22 -07:00
streamResponse ( c , ch )
}
2023-07-20 16:09:23 -07:00
func PushModelHandler ( c * gin . Context ) {
2023-07-16 17:02:22 -07:00
var req api . PushRequest
2023-10-18 16:08:42 -07:00
err := c . ShouldBindJSON ( & req )
switch {
case errors . Is ( err , io . EOF ) :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "missing request body" } )
return
case err != nil :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : err . Error ( ) } )
2023-07-11 11:54:22 -07:00
return
}
2023-07-06 10:40:11 -07:00
2023-10-18 15:56:34 -07:00
if req . Name == "" {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "name is required" } )
return
}
2023-07-16 17:02:22 -07:00
ch := make ( chan any )
go func ( ) {
defer close ( ch )
2023-07-18 18:51:30 -07:00
fn := func ( r api . ProgressResponse ) {
ch <- r
2023-07-16 17:02:22 -07:00
}
2023-07-18 18:51:30 -07:00
2023-07-21 15:42:19 -07:00
regOpts := & RegistryOptions {
Insecure : req . Insecure ,
}
2023-10-09 10:24:27 -07:00
ctx , cancel := context . WithCancel ( c . Request . Context ( ) )
defer cancel ( )
2023-08-11 15:41:55 -07:00
if err := PushModel ( ctx , req . Name , regOpts , fn ) ; err != nil {
2023-07-20 12:12:08 -07:00
ch <- gin . H { "error" : err . Error ( ) }
2023-07-16 17:02:22 -07:00
}
} ( )
2023-10-11 12:54:27 -04:00
if req . Stream != nil && ! * req . Stream {
waitForStream ( c , ch )
return
}
2023-07-16 17:02:22 -07:00
streamResponse ( c , ch )
}
2023-07-20 16:09:23 -07:00
func CreateModelHandler ( c * gin . Context ) {
2023-07-16 17:02:22 -07:00
var req api . CreateRequest
2023-10-18 16:08:42 -07:00
err := c . ShouldBindJSON ( & req )
switch {
case errors . Is ( err , io . EOF ) :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "missing request body" } )
return
case err != nil :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : err . Error ( ) } )
2023-07-12 19:07:15 -07:00
return
2023-07-16 17:02:22 -07:00
}
2023-10-18 15:56:34 -07:00
if req . Name == "" || req . Path == "" {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "name and path are required" } )
return
}
2023-07-11 11:54:22 -07:00
ch := make ( chan any )
2023-07-14 14:15:53 -07:00
go func ( ) {
defer close ( ch )
2023-07-25 14:25:13 -04:00
fn := func ( resp api . ProgressResponse ) {
ch <- resp
2023-07-16 17:02:22 -07:00
}
2023-07-25 17:08:51 -04:00
ctx , cancel := context . WithCancel ( c . Request . Context ( ) )
defer cancel ( )
2023-10-18 15:55:50 -07:00
if err := CreateModel ( ctx , req . Name , req . Path , fn ) ; err != nil {
2023-07-20 12:12:08 -07:00
ch <- gin . H { "error" : err . Error ( ) }
2023-07-16 17:02:22 -07:00
}
2023-07-14 14:15:53 -07:00
} ( )
2023-07-07 15:29:17 -07:00
2023-10-11 12:54:27 -04:00
if req . Stream != nil && ! * req . Stream {
waitForStream ( c , ch )
return
}
2023-07-14 14:15:53 -07:00
streamResponse ( c , ch )
2023-07-05 15:37:33 -04:00
}
2023-07-20 16:09:23 -07:00
func DeleteModelHandler ( c * gin . Context ) {
var req api . DeleteRequest
2023-10-18 16:08:42 -07:00
err := c . ShouldBindJSON ( & req )
switch {
case errors . Is ( err , io . EOF ) :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "missing request body" } )
return
case err != nil :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : err . Error ( ) } )
2023-07-20 16:09:23 -07:00
return
}
2023-10-18 15:56:34 -07:00
if req . Name == "" {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "name is required" } )
return
}
2023-07-21 23:02:12 -07:00
if err := DeleteModel ( req . Name ) ; err != nil {
if os . IsNotExist ( err ) {
c . JSON ( http . StatusNotFound , gin . H { "error" : fmt . Sprintf ( "model '%s' not found" , req . Name ) } )
} else {
2023-07-20 16:09:23 -07:00
c . JSON ( http . StatusInternalServerError , gin . H { "error" : err . Error ( ) } )
}
2023-07-21 23:02:12 -07:00
return
}
2023-09-26 17:28:14 -07:00
manifestsPath , err := GetManifestPath ( )
if err != nil {
c . JSON ( http . StatusInternalServerError , gin . H { "error" : err . Error ( ) } )
return
}
if err := PruneDirectory ( manifestsPath ) ; err != nil {
c . JSON ( http . StatusInternalServerError , gin . H { "error" : err . Error ( ) } )
return
}
2023-09-11 11:46:35 -07:00
c . JSON ( http . StatusOK , nil )
2023-07-20 16:09:23 -07:00
}
2023-09-06 11:04:17 -07:00
func ShowModelHandler ( c * gin . Context ) {
var req api . ShowRequest
2023-10-18 16:08:42 -07:00
err := c . ShouldBindJSON ( & req )
switch {
case errors . Is ( err , io . EOF ) :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "missing request body" } )
return
case err != nil :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : err . Error ( ) } )
2023-09-06 11:04:17 -07:00
return
}
2023-10-18 15:56:34 -07:00
if req . Name == "" {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "name is required" } )
return
}
2023-09-06 11:04:17 -07:00
resp , err := GetModelInfo ( req . Name )
if err != nil {
if os . IsNotExist ( err ) {
c . JSON ( http . StatusNotFound , gin . H { "error" : fmt . Sprintf ( "model '%s' not found" , req . Name ) } )
} else {
c . JSON ( http . StatusInternalServerError , gin . H { "error" : err . Error ( ) } )
}
return
}
c . JSON ( http . StatusOK , resp )
}
func GetModelInfo ( name string ) ( * api . ShowResponse , error ) {
model , err := GetModel ( name )
if err != nil {
return nil , err
}
resp := & api . ShowResponse {
License : strings . Join ( model . License , "\n" ) ,
System : model . System ,
Template : model . Template ,
}
mf , err := ShowModelfile ( model )
if err != nil {
return nil , err
}
resp . Modelfile = mf
var params [ ] string
cs := 30
for k , v := range model . Options {
switch val := v . ( type ) {
case string :
params = append ( params , fmt . Sprintf ( "%-*s %s" , cs , k , val ) )
case int :
params = append ( params , fmt . Sprintf ( "%-*s %s" , cs , k , strconv . Itoa ( val ) ) )
case float64 :
params = append ( params , fmt . Sprintf ( "%-*s %s" , cs , k , strconv . FormatFloat ( val , 'f' , 0 , 64 ) ) )
case bool :
params = append ( params , fmt . Sprintf ( "%-*s %s" , cs , k , strconv . FormatBool ( val ) ) )
case [ ] interface { } :
for _ , nv := range val {
switch nval := nv . ( type ) {
case string :
params = append ( params , fmt . Sprintf ( "%-*s %s" , cs , k , nval ) )
case int :
params = append ( params , fmt . Sprintf ( "%-*s %s" , cs , k , strconv . Itoa ( nval ) ) )
case float64 :
params = append ( params , fmt . Sprintf ( "%-*s %s" , cs , k , strconv . FormatFloat ( nval , 'f' , 0 , 64 ) ) )
case bool :
params = append ( params , fmt . Sprintf ( "%-*s %s" , cs , k , strconv . FormatBool ( nval ) ) )
}
}
}
}
resp . Parameters = strings . Join ( params , "\n" )
return resp , nil
}
2023-07-20 16:09:23 -07:00
func ListModelsHandler ( c * gin . Context ) {
2023-10-17 19:02:43 +02:00
models := make ( [ ] api . ModelResponse , 0 )
2023-07-18 09:09:45 -07:00
fp , err := GetManifestPath ( )
if err != nil {
c . JSON ( http . StatusInternalServerError , gin . H { "error" : err . Error ( ) } )
return
}
2023-08-30 14:14:12 -04:00
walkFunc := func ( path string , info os . FileInfo , _ error ) error {
2023-07-18 09:09:45 -07:00
if ! info . IsDir ( ) {
2023-08-30 14:14:12 -04:00
dir , file := filepath . Split ( path )
dir = strings . Trim ( strings . TrimPrefix ( dir , fp ) , string ( os . PathSeparator ) )
tag := strings . Join ( [ ] string { dir , file } , ":" )
2023-08-21 21:56:56 -07:00
2023-08-22 09:39:42 -07:00
mp := ParseModelPath ( tag )
2023-08-28 20:50:24 -07:00
manifest , digest , err := GetManifest ( mp )
2023-07-18 09:09:45 -07:00
if err != nil {
2023-07-18 12:39:08 -07:00
log . Printf ( "skipping file: %s" , fp )
return nil
2023-07-18 09:09:45 -07:00
}
2023-08-30 14:14:12 -04:00
models = append ( models , api . ModelResponse {
2023-07-18 09:09:45 -07:00
Name : mp . GetShortTagname ( ) ,
Size : manifest . GetTotalSize ( ) ,
2023-08-28 20:50:24 -07:00
Digest : digest ,
2023-08-30 14:14:12 -04:00
ModifiedAt : info . ModTime ( ) ,
} )
2023-07-18 09:09:45 -07:00
}
2023-08-30 14:14:12 -04:00
2023-07-18 09:09:45 -07:00
return nil
2023-08-30 14:14:12 -04:00
}
if err := filepath . Walk ( fp , walkFunc ) ; err != nil {
2023-07-18 09:09:45 -07:00
c . JSON ( http . StatusInternalServerError , gin . H { "error" : err . Error ( ) } )
return
}
2023-07-19 15:00:28 -07:00
c . JSON ( http . StatusOK , api . ListResponse { Models : models } )
2023-07-18 09:09:45 -07:00
}
2023-07-24 11:27:28 -04:00
func CopyModelHandler ( c * gin . Context ) {
var req api . CopyRequest
2023-10-18 16:08:42 -07:00
err := c . ShouldBindJSON ( & req )
switch {
case errors . Is ( err , io . EOF ) :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "missing request body" } )
return
case err != nil :
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : err . Error ( ) } )
2023-07-24 11:27:28 -04:00
return
}
2023-10-18 15:56:34 -07:00
if req . Source == "" || req . Destination == "" {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "error" : "source add destination are required" } )
return
}
2023-07-24 11:27:28 -04:00
if err := CopyModel ( req . Source , req . Destination ) ; err != nil {
if os . IsNotExist ( err ) {
c . JSON ( http . StatusNotFound , gin . H { "error" : fmt . Sprintf ( "model '%s' not found" , req . Source ) } )
} else {
c . JSON ( http . StatusInternalServerError , gin . H { "error" : err . Error ( ) } )
}
return
}
}
2023-09-21 09:42:16 -07:00
var defaultAllowOrigins = [ ] string {
"localhost" ,
"127.0.0.1" ,
"0.0.0.0" ,
}
func Serve ( ln net . Listener , allowOrigins [ ] string ) error {
2023-10-30 11:10:18 -04:00
if noprune := os . Getenv ( "OLLAMA_NOPRUNE" ) ; noprune == "" {
// clean up unused layers and manifests
if err := PruneLayers ( ) ; err != nil {
return err
}
manifestsPath , err := GetManifestPath ( )
if err != nil {
return err
}
if err := PruneDirectory ( manifestsPath ) ; err != nil {
return err
}
}
2023-07-21 18:01:24 -07:00
config := cors . DefaultConfig ( )
config . AllowWildcard = true
2023-09-21 09:42:16 -07:00
config . AllowOrigins = allowOrigins
for _ , allowOrigin := range defaultAllowOrigins {
config . AllowOrigins = append ( config . AllowOrigins ,
fmt . Sprintf ( "http://%s" , allowOrigin ) ,
fmt . Sprintf ( "https://%s" , allowOrigin ) ,
fmt . Sprintf ( "http://%s:*" , allowOrigin ) ,
fmt . Sprintf ( "https://%s:*" , allowOrigin ) ,
)
}
2023-07-21 18:01:24 -07:00
2023-09-21 20:38:49 +01:00
workDir , err := os . MkdirTemp ( "" , "ollama" )
if err != nil {
return err
}
defer os . RemoveAll ( workDir )
2023-07-05 15:37:33 -04:00
r := gin . Default ( )
2023-09-21 20:38:49 +01:00
r . Use (
cors . New ( config ) ,
func ( c * gin . Context ) {
c . Set ( "workDir" , workDir )
c . Next ( )
} ,
)
2023-07-05 15:37:33 -04:00
2023-07-20 16:09:23 -07:00
r . POST ( "/api/pull" , PullModelHandler )
r . POST ( "/api/generate" , GenerateHandler )
2023-08-08 15:13:22 -04:00
r . POST ( "/api/embeddings" , EmbeddingHandler )
2023-07-20 16:09:23 -07:00
r . POST ( "/api/create" , CreateModelHandler )
r . POST ( "/api/push" , PushModelHandler )
2023-07-24 11:27:28 -04:00
r . POST ( "/api/copy" , CopyModelHandler )
2023-07-20 16:09:23 -07:00
r . DELETE ( "/api/delete" , DeleteModelHandler )
2023-09-06 11:04:17 -07:00
r . POST ( "/api/show" , ShowModelHandler )
2023-07-03 15:22:44 -04:00
2023-09-21 16:38:03 -07:00
for _ , method := range [ ] string { http . MethodGet , http . MethodHead } {
r . Handle ( method , "/" , func ( c * gin . Context ) {
c . String ( http . StatusOK , "Ollama is running" )
} )
r . Handle ( method , "/api/tags" , ListModelsHandler )
}
2023-10-13 16:08:35 -07:00
log . Printf ( "Listening on %s (version %s)" , ln . Addr ( ) , version . Version )
2023-07-03 15:22:44 -04:00
s := & http . Server {
Handler : r ,
}
2023-08-30 16:35:03 -04:00
// listen for a ctrl+c and stop any loaded llm
signals := make ( chan os . Signal , 1 )
2023-09-21 20:38:49 +01:00
signal . Notify ( signals , syscall . SIGINT , syscall . SIGTERM )
2023-08-30 16:35:03 -04:00
go func ( ) {
<- signals
2023-10-19 10:39:58 -04:00
if loaded . runner != nil {
loaded . runner . Close ( )
2023-09-22 19:41:52 +01:00
}
2023-09-21 20:38:49 +01:00
os . RemoveAll ( workDir )
2023-08-30 16:35:03 -04:00
os . Exit ( 0 )
} ( )
2023-09-12 11:04:35 -04:00
if runtime . GOOS == "linux" {
// check compatibility to log warnings
if _ , err := llm . CheckVRAM ( ) ; err != nil {
2023-09-20 20:00:41 +01:00
log . Printf ( "Warning: GPU support may not enabled, check you have installed install GPU drivers: %v" , err )
2023-09-12 11:04:35 -04:00
}
}
2023-07-03 15:22:44 -04:00
return s . Serve ( ln )
}
2023-07-06 10:40:11 -07:00
2023-10-11 12:54:27 -04:00
func waitForStream ( c * gin . Context , ch chan interface { } ) {
c . Header ( "Content-Type" , "application/json" )
for resp := range ch {
switch r := resp . ( type ) {
case api . ProgressResponse :
if r . Status == "success" {
c . JSON ( http . StatusOK , r )
return
}
case gin . H :
if errorMsg , ok := r [ "error" ] . ( string ) ; ok {
c . JSON ( http . StatusInternalServerError , gin . H { "error" : errorMsg } )
return
} else {
c . JSON ( http . StatusInternalServerError , gin . H { "error" : "unexpected error format in progress response" } )
return
}
default :
c . JSON ( http . StatusInternalServerError , gin . H { "error" : "unexpected progress response" } )
return
}
}
c . JSON ( http . StatusInternalServerError , gin . H { "error" : "unexpected end of progress response" } )
}
2023-07-14 14:15:53 -07:00
func streamResponse ( c * gin . Context , ch chan any ) {
2023-08-08 21:38:10 -07:00
c . Header ( "Content-Type" , "application/x-ndjson" )
2023-07-11 11:54:22 -07:00
c . Stream ( func ( w io . Writer ) bool {
val , ok := <- ch
if ! ok {
return false
}
bts , err := json . Marshal ( val )
if err != nil {
2023-07-31 16:46:37 -04:00
log . Printf ( "streamResponse: json.Marshal failed with %s" , err )
2023-07-11 11:54:22 -07:00
return false
}
2023-09-30 00:45:52 -04:00
// Delineate chunks with new-line delimiter
2023-07-11 11:54:22 -07:00
bts = append ( bts , '\n' )
if _ , err := w . Write ( bts ) ; err != nil {
2023-07-31 16:46:37 -04:00
log . Printf ( "streamResponse: w.Write failed with %s" , err )
2023-07-11 11:54:22 -07:00
return false
}
return true
} )
}