Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
wangkx1
ollama_dcu
Commits
22cb4ffc
Commit
22cb4ffc
authored
Aug 13, 2024
by
wangkx1
Browse files
remove v0.3.5
parent
cb75098c
Pipeline
#1532
canceled with stages
Changes
335
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
0 additions
and
1186 deletions
+0
-1186
ollama/api/types_test.go
ollama/api/types_test.go
+0
-233
ollama/app/.gitignore
ollama/app/.gitignore
+0
-1
ollama/app/README.md
ollama/app/README.md
+0
-22
ollama/app/assets/app.ico
ollama/app/assets/app.ico
+0
-0
ollama/app/assets/assets.go
ollama/app/assets/assets.go
+0
-17
ollama/app/assets/setup.bmp
ollama/app/assets/setup.bmp
+0
-0
ollama/app/assets/tray.ico
ollama/app/assets/tray.ico
+0
-0
ollama/app/assets/tray_upgrade.ico
ollama/app/assets/tray_upgrade.ico
+0
-0
ollama/app/lifecycle/getstarted_nonwindows.go
ollama/app/lifecycle/getstarted_nonwindows.go
+0
-9
ollama/app/lifecycle/getstarted_windows.go
ollama/app/lifecycle/getstarted_windows.go
+0
-43
ollama/app/lifecycle/lifecycle.go
ollama/app/lifecycle/lifecycle.go
+0
-92
ollama/app/lifecycle/logging.go
ollama/app/lifecycle/logging.go
+0
-80
ollama/app/lifecycle/logging_nonwindows.go
ollama/app/lifecycle/logging_nonwindows.go
+0
-9
ollama/app/lifecycle/logging_test.go
ollama/app/lifecycle/logging_test.go
+0
-44
ollama/app/lifecycle/logging_windows.go
ollama/app/lifecycle/logging_windows.go
+0
-19
ollama/app/lifecycle/paths.go
ollama/app/lifecycle/paths.go
+0
-79
ollama/app/lifecycle/server.go
ollama/app/lifecycle/server.go
+0
-180
ollama/app/lifecycle/server_unix.go
ollama/app/lifecycle/server_unix.go
+0
-38
ollama/app/lifecycle/server_windows.go
ollama/app/lifecycle/server_windows.go
+0
-91
ollama/app/lifecycle/updater.go
ollama/app/lifecycle/updater.go
+0
-229
No files found.
Too many changes to show.
To preserve performance only
335 of 335+
files are displayed.
Plain diff
Email patch
ollama/api/types_test.go
deleted
100755 → 0
View file @
cb75098c
package
api
import
(
"encoding/json"
"errors"
"math"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func
TestKeepAliveParsingFromJSON
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
req
string
exp
*
Duration
}{
{
name
:
"Positive Integer"
,
req
:
`{ "keep_alive": 42 }`
,
exp
:
&
Duration
{
42
*
time
.
Second
},
},
{
name
:
"Positive Float"
,
req
:
`{ "keep_alive": 42.5 }`
,
exp
:
&
Duration
{
42
*
time
.
Second
},
},
{
name
:
"Positive Integer String"
,
req
:
`{ "keep_alive": "42m" }`
,
exp
:
&
Duration
{
42
*
time
.
Minute
},
},
{
name
:
"Negative Integer"
,
req
:
`{ "keep_alive": -1 }`
,
exp
:
&
Duration
{
math
.
MaxInt64
},
},
{
name
:
"Negative Float"
,
req
:
`{ "keep_alive": -3.14 }`
,
exp
:
&
Duration
{
math
.
MaxInt64
},
},
{
name
:
"Negative Integer String"
,
req
:
`{ "keep_alive": "-1m" }`
,
exp
:
&
Duration
{
math
.
MaxInt64
},
},
}
for
_
,
test
:=
range
tests
{
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
var
dec
ChatRequest
err
:=
json
.
Unmarshal
([]
byte
(
test
.
req
),
&
dec
)
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
test
.
exp
,
dec
.
KeepAlive
)
})
}
}
func
TestDurationMarshalUnmarshal
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
time
.
Duration
expected
time
.
Duration
}{
{
"negative duration"
,
time
.
Duration
(
-
1
),
time
.
Duration
(
math
.
MaxInt64
),
},
{
"positive duration"
,
42
*
time
.
Second
,
42
*
time
.
Second
,
},
{
"another positive duration"
,
42
*
time
.
Minute
,
42
*
time
.
Minute
,
},
{
"zero duration"
,
time
.
Duration
(
0
),
time
.
Duration
(
0
),
},
{
"max duration"
,
time
.
Duration
(
math
.
MaxInt64
),
time
.
Duration
(
math
.
MaxInt64
),
},
}
for
_
,
test
:=
range
tests
{
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
b
,
err
:=
json
.
Marshal
(
Duration
{
test
.
input
})
require
.
NoError
(
t
,
err
)
var
d
Duration
err
=
json
.
Unmarshal
(
b
,
&
d
)
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
test
.
expected
,
d
.
Duration
,
"input %v, marshalled %v, got %v"
,
test
.
input
,
string
(
b
),
d
.
Duration
)
})
}
}
func
TestUseMmapParsingFromJSON
(
t
*
testing
.
T
)
{
tr
:=
true
fa
:=
false
tests
:=
[]
struct
{
name
string
req
string
exp
*
bool
}{
{
name
:
"Undefined"
,
req
:
`{ }`
,
exp
:
nil
,
},
{
name
:
"True"
,
req
:
`{ "use_mmap": true }`
,
exp
:
&
tr
,
},
{
name
:
"False"
,
req
:
`{ "use_mmap": false }`
,
exp
:
&
fa
,
},
}
for
_
,
test
:=
range
tests
{
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
var
oMap
map
[
string
]
interface
{}
err
:=
json
.
Unmarshal
([]
byte
(
test
.
req
),
&
oMap
)
require
.
NoError
(
t
,
err
)
opts
:=
DefaultOptions
()
err
=
opts
.
FromMap
(
oMap
)
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
test
.
exp
,
opts
.
UseMMap
)
})
}
}
func
TestUseMmapFormatParams
(
t
*
testing
.
T
)
{
tr
:=
true
fa
:=
false
tests
:=
[]
struct
{
name
string
req
map
[
string
][]
string
exp
*
bool
err
error
}{
{
name
:
"True"
,
req
:
map
[
string
][]
string
{
"use_mmap"
:
{
"true"
},
},
exp
:
&
tr
,
err
:
nil
,
},
{
name
:
"False"
,
req
:
map
[
string
][]
string
{
"use_mmap"
:
{
"false"
},
},
exp
:
&
fa
,
err
:
nil
,
},
{
name
:
"Numeric True"
,
req
:
map
[
string
][]
string
{
"use_mmap"
:
{
"1"
},
},
exp
:
&
tr
,
err
:
nil
,
},
{
name
:
"Numeric False"
,
req
:
map
[
string
][]
string
{
"use_mmap"
:
{
"0"
},
},
exp
:
&
fa
,
err
:
nil
,
},
{
name
:
"invalid string"
,
req
:
map
[
string
][]
string
{
"use_mmap"
:
{
"foo"
},
},
exp
:
nil
,
err
:
errors
.
New
(
"invalid bool value [foo]"
),
},
}
for
_
,
test
:=
range
tests
{
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
resp
,
err
:=
FormatParams
(
test
.
req
)
require
.
Equal
(
t
,
test
.
err
,
err
)
respVal
,
ok
:=
resp
[
"use_mmap"
]
if
test
.
exp
!=
nil
{
assert
.
True
(
t
,
ok
,
"resp: %v"
,
resp
)
assert
.
Equal
(
t
,
*
test
.
exp
,
*
respVal
.
(
*
bool
))
}
})
}
}
func
TestMessage_UnmarshalJSON
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
input
string
expected
string
}{
{
`{"role": "USER", "content": "Hello!"}`
,
"user"
},
{
`{"role": "System", "content": "Initialization complete."}`
,
"system"
},
{
`{"role": "assistant", "content": "How can I help you?"}`
,
"assistant"
},
{
`{"role": "TOOl", "content": "Access granted."}`
,
"tool"
},
}
for
_
,
test
:=
range
tests
{
var
msg
Message
if
err
:=
json
.
Unmarshal
([]
byte
(
test
.
input
),
&
msg
);
err
!=
nil
{
t
.
Errorf
(
"Unexpected error: %v"
,
err
)
}
if
msg
.
Role
!=
test
.
expected
{
t
.
Errorf
(
"role not lowercased: got %v, expected %v"
,
msg
.
Role
,
test
.
expected
)
}
}
}
ollama/app/.gitignore
deleted
100755 → 0
View file @
cb75098c
ollama.syso
ollama/app/README.md
deleted
100755 → 0
View file @
cb75098c
# Ollama App
## Linux
TODO
## MacOS
TODO
## Windows
If you want to build the installer, youll need to install
-
https://jrsoftware.org/isinfo.php
In the top directory of this repo, run the following powershell script
to build the ollama CLI, ollama app, and ollama installer.
```
powershell -ExecutionPolicy Bypass -File .\scripts\build_windows.ps1
```
ollama/app/assets/app.ico
deleted
100755 → 0
View file @
cb75098c
7.33 KB
ollama/app/assets/assets.go
deleted
100755 → 0
View file @
cb75098c
package
assets
import
(
"embed"
"io/fs"
)
//go:embed *.ico
var
icons
embed
.
FS
func
ListIcons
()
([]
string
,
error
)
{
return
fs
.
Glob
(
icons
,
"*"
)
}
func
GetIcon
(
filename
string
)
([]
byte
,
error
)
{
return
icons
.
ReadFile
(
filename
)
}
ollama/app/assets/setup.bmp
deleted
100755 → 0
View file @
cb75098c
75.6 KB
ollama/app/assets/tray.ico
deleted
100755 → 0
View file @
cb75098c
88.9 KB
ollama/app/assets/tray_upgrade.ico
deleted
100755 → 0
View file @
cb75098c
90.7 KB
ollama/app/lifecycle/getstarted_nonwindows.go
deleted
100755 → 0
View file @
cb75098c
//go:build !windows
package
lifecycle
import
"errors"
func
GetStarted
()
error
{
return
errors
.
New
(
"not implemented"
)
}
ollama/app/lifecycle/getstarted_windows.go
deleted
100755 → 0
View file @
cb75098c
package
lifecycle
import
(
"fmt"
"log/slog"
"os"
"os/exec"
"path/filepath"
"syscall"
)
func
GetStarted
()
error
{
const
CREATE_NEW_CONSOLE
=
0x00000010
var
err
error
bannerScript
:=
filepath
.
Join
(
AppDir
,
"ollama_welcome.ps1"
)
args
:=
[]
string
{
// TODO once we're signed, the execution policy bypass should be removed
"powershell"
,
"-noexit"
,
"-ExecutionPolicy"
,
"Bypass"
,
"-nologo"
,
"-file"
,
bannerScript
,
}
args
[
0
],
err
=
exec
.
LookPath
(
args
[
0
])
if
err
!=
nil
{
return
err
}
// Make sure the script actually exists
_
,
err
=
os
.
Stat
(
bannerScript
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"getting started banner script error %s"
,
err
)
}
slog
.
Info
(
fmt
.
Sprintf
(
"opening getting started terminal with %v"
,
args
))
attrs
:=
&
os
.
ProcAttr
{
Files
:
[]
*
os
.
File
{
os
.
Stdin
,
os
.
Stdout
,
os
.
Stderr
},
Sys
:
&
syscall
.
SysProcAttr
{
CreationFlags
:
CREATE_NEW_CONSOLE
,
HideWindow
:
false
},
}
proc
,
err
:=
os
.
StartProcess
(
args
[
0
],
args
,
attrs
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"unable to start getting started shell %w"
,
err
)
}
slog
.
Debug
(
fmt
.
Sprintf
(
"getting started terminal PID: %d"
,
proc
.
Pid
))
return
proc
.
Release
()
}
ollama/app/lifecycle/lifecycle.go
deleted
100755 → 0
View file @
cb75098c
package
lifecycle
import
(
"context"
"fmt"
"log"
"log/slog"
"os"
"os/signal"
"syscall"
"github.com/ollama/ollama/app/store"
"github.com/ollama/ollama/app/tray"
)
func
Run
()
{
InitLogging
()
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
var
done
chan
int
t
,
err
:=
tray
.
NewTray
()
if
err
!=
nil
{
log
.
Fatalf
(
"Failed to start: %s"
,
err
)
}
callbacks
:=
t
.
GetCallbacks
()
signals
:=
make
(
chan
os
.
Signal
,
1
)
signal
.
Notify
(
signals
,
syscall
.
SIGINT
,
syscall
.
SIGTERM
)
go
func
()
{
slog
.
Debug
(
"starting callback loop"
)
for
{
select
{
case
<-
callbacks
.
Quit
:
slog
.
Debug
(
"quit called"
)
t
.
Quit
()
case
<-
signals
:
slog
.
Debug
(
"shutting down due to signal"
)
t
.
Quit
()
case
<-
callbacks
.
Update
:
err
:=
DoUpgrade
(
cancel
,
done
)
if
err
!=
nil
{
slog
.
Warn
(
fmt
.
Sprintf
(
"upgrade attempt failed: %s"
,
err
))
}
case
<-
callbacks
.
ShowLogs
:
ShowLogs
()
case
<-
callbacks
.
DoFirstUse
:
err
:=
GetStarted
()
if
err
!=
nil
{
slog
.
Warn
(
fmt
.
Sprintf
(
"Failed to launch getting started shell: %s"
,
err
))
}
}
}
}()
// Are we first use?
if
!
store
.
GetFirstTimeRun
()
{
slog
.
Debug
(
"First time run"
)
err
=
t
.
DisplayFirstUseNotification
()
if
err
!=
nil
{
slog
.
Debug
(
fmt
.
Sprintf
(
"XXX failed to display first use notification %v"
,
err
))
}
store
.
SetFirstTimeRun
(
true
)
}
else
{
slog
.
Debug
(
"Not first time, skipping first run notification"
)
}
if
IsServerRunning
(
ctx
)
{
slog
.
Info
(
"Detected another instance of ollama running, exiting"
)
os
.
Exit
(
1
)
}
else
{
done
,
err
=
SpawnServer
(
ctx
,
CLIName
)
if
err
!=
nil
{
// TODO - should we retry in a backoff loop?
// TODO - should we pop up a warning and maybe add a menu item to view application logs?
slog
.
Error
(
fmt
.
Sprintf
(
"Failed to spawn ollama server %s"
,
err
))
done
=
make
(
chan
int
,
1
)
done
<-
1
}
}
StartBackgroundUpdaterChecker
(
ctx
,
t
.
UpdateAvailable
)
t
.
Run
()
cancel
()
slog
.
Info
(
"Waiting for ollama server to shutdown..."
)
if
done
!=
nil
{
<-
done
}
slog
.
Info
(
"Ollama app exiting"
)
}
ollama/app/lifecycle/logging.go
deleted
100755 → 0
View file @
cb75098c
package
lifecycle
import
(
"fmt"
"log/slog"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/ollama/ollama/envconfig"
)
func
InitLogging
()
{
level
:=
slog
.
LevelInfo
if
envconfig
.
Debug
()
{
level
=
slog
.
LevelDebug
}
var
logFile
*
os
.
File
var
err
error
// Detect if we're a GUI app on windows, and if not, send logs to console
if
os
.
Stderr
.
Fd
()
!=
0
{
// Console app detected
logFile
=
os
.
Stderr
// TODO - write one-line to the app.log file saying we're running in console mode to help avoid confusion
}
else
{
rotateLogs
(
AppLogFile
)
logFile
,
err
=
os
.
OpenFile
(
AppLogFile
,
os
.
O_APPEND
|
os
.
O_WRONLY
|
os
.
O_CREATE
,
0
o755
)
if
err
!=
nil
{
slog
.
Error
(
fmt
.
Sprintf
(
"failed to create server log %v"
,
err
))
return
}
}
handler
:=
slog
.
NewTextHandler
(
logFile
,
&
slog
.
HandlerOptions
{
Level
:
level
,
AddSource
:
true
,
ReplaceAttr
:
func
(
_
[]
string
,
attr
slog
.
Attr
)
slog
.
Attr
{
if
attr
.
Key
==
slog
.
SourceKey
{
source
:=
attr
.
Value
.
Any
()
.
(
*
slog
.
Source
)
source
.
File
=
filepath
.
Base
(
source
.
File
)
}
return
attr
},
})
slog
.
SetDefault
(
slog
.
New
(
handler
))
slog
.
Info
(
"ollama app started"
)
}
func
rotateLogs
(
logFile
string
)
{
if
_
,
err
:=
os
.
Stat
(
logFile
);
os
.
IsNotExist
(
err
)
{
return
}
index
:=
strings
.
LastIndex
(
logFile
,
"."
)
pre
:=
logFile
[
:
index
]
post
:=
"."
+
logFile
[
index
+
1
:
]
for
i
:=
LogRotationCount
;
i
>
0
;
i
--
{
older
:=
pre
+
"-"
+
strconv
.
Itoa
(
i
)
+
post
newer
:=
pre
+
"-"
+
strconv
.
Itoa
(
i
-
1
)
+
post
if
i
==
1
{
newer
=
pre
+
post
}
if
_
,
err
:=
os
.
Stat
(
newer
);
err
==
nil
{
if
_
,
err
:=
os
.
Stat
(
older
);
err
==
nil
{
err
:=
os
.
Remove
(
older
)
if
err
!=
nil
{
slog
.
Warn
(
"Failed to remove older log"
,
"older"
,
older
,
"error"
,
err
)
continue
}
}
err
:=
os
.
Rename
(
newer
,
older
)
if
err
!=
nil
{
slog
.
Warn
(
"Failed to rotate log"
,
"older"
,
older
,
"newer"
,
newer
,
"error"
,
err
)
}
}
}
}
ollama/app/lifecycle/logging_nonwindows.go
deleted
100755 → 0
View file @
cb75098c
//go:build !windows
package
lifecycle
import
"log/slog"
func
ShowLogs
()
{
slog
.
Warn
(
"not implemented"
)
}
ollama/app/lifecycle/logging_test.go
deleted
100755 → 0
View file @
cb75098c
package
lifecycle
import
(
"os"
"path/filepath"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func
TestRotateLogs
(
t
*
testing
.
T
)
{
logDir
:=
t
.
TempDir
()
logFile
:=
filepath
.
Join
(
logDir
,
"testlog.log"
)
// No log exists
rotateLogs
(
logFile
)
require
.
NoError
(
t
,
os
.
WriteFile
(
logFile
,
[]
byte
(
"1"
),
0
o644
))
assert
.
FileExists
(
t
,
logFile
)
// First rotation
rotateLogs
(
logFile
)
assert
.
FileExists
(
t
,
filepath
.
Join
(
logDir
,
"testlog-1.log"
))
assert
.
NoFileExists
(
t
,
filepath
.
Join
(
logDir
,
"testlog-2.log"
))
assert
.
NoFileExists
(
t
,
logFile
)
// Should be a no-op without a new log
rotateLogs
(
logFile
)
assert
.
FileExists
(
t
,
filepath
.
Join
(
logDir
,
"testlog-1.log"
))
assert
.
NoFileExists
(
t
,
filepath
.
Join
(
logDir
,
"testlog-2.log"
))
assert
.
NoFileExists
(
t
,
logFile
)
for
i
:=
2
;
i
<=
LogRotationCount
+
1
;
i
++
{
require
.
NoError
(
t
,
os
.
WriteFile
(
logFile
,
[]
byte
(
strconv
.
Itoa
(
i
)),
0
o644
))
assert
.
FileExists
(
t
,
logFile
)
rotateLogs
(
logFile
)
assert
.
NoFileExists
(
t
,
logFile
)
for
j
:=
1
;
j
<
i
;
j
++
{
assert
.
FileExists
(
t
,
filepath
.
Join
(
logDir
,
"testlog-"
+
strconv
.
Itoa
(
j
)
+
".log"
))
}
assert
.
NoFileExists
(
t
,
filepath
.
Join
(
logDir
,
"testlog-"
+
strconv
.
Itoa
(
i
+
1
)
+
".log"
))
}
}
ollama/app/lifecycle/logging_windows.go
deleted
100755 → 0
View file @
cb75098c
package
lifecycle
import
(
"fmt"
"log/slog"
"os/exec"
"syscall"
)
func
ShowLogs
()
{
cmd_path
:=
"c:
\\
Windows
\\
system32
\\
cmd.exe"
slog
.
Debug
(
fmt
.
Sprintf
(
"viewing logs with start %s"
,
AppDataDir
))
cmd
:=
exec
.
Command
(
cmd_path
,
"/c"
,
"start"
,
AppDataDir
)
cmd
.
SysProcAttr
=
&
syscall
.
SysProcAttr
{
HideWindow
:
false
,
CreationFlags
:
0x08000000
}
err
:=
cmd
.
Start
()
if
err
!=
nil
{
slog
.
Error
(
fmt
.
Sprintf
(
"Failed to open log dir: %s"
,
err
))
}
}
ollama/app/lifecycle/paths.go
deleted
100755 → 0
View file @
cb75098c
package
lifecycle
import
(
"errors"
"fmt"
"log/slog"
"os"
"path/filepath"
"runtime"
"strings"
)
var
(
AppName
=
"ollama app"
CLIName
=
"ollama"
AppDir
=
"/opt/Ollama"
AppDataDir
=
"/opt/Ollama"
// TODO - should there be a distinct log dir?
UpdateStageDir
=
"/tmp"
AppLogFile
=
"/tmp/ollama_app.log"
ServerLogFile
=
"/tmp/ollama.log"
UpgradeLogFile
=
"/tmp/ollama_update.log"
Installer
=
"OllamaSetup.exe"
LogRotationCount
=
5
)
func
init
()
{
if
runtime
.
GOOS
==
"windows"
{
AppName
+=
".exe"
CLIName
+=
".exe"
// Logs, configs, downloads go to LOCALAPPDATA
localAppData
:=
os
.
Getenv
(
"LOCALAPPDATA"
)
AppDataDir
=
filepath
.
Join
(
localAppData
,
"Ollama"
)
UpdateStageDir
=
filepath
.
Join
(
AppDataDir
,
"updates"
)
AppLogFile
=
filepath
.
Join
(
AppDataDir
,
"app.log"
)
ServerLogFile
=
filepath
.
Join
(
AppDataDir
,
"server.log"
)
UpgradeLogFile
=
filepath
.
Join
(
AppDataDir
,
"upgrade.log"
)
// Executables are stored in APPDATA
AppDir
=
filepath
.
Join
(
localAppData
,
"Programs"
,
"Ollama"
)
// Make sure we have PATH set correctly for any spawned children
paths
:=
strings
.
Split
(
os
.
Getenv
(
"PATH"
),
";"
)
// Start with whatever we find in the PATH/LD_LIBRARY_PATH
found
:=
false
for
_
,
path
:=
range
paths
{
d
,
err
:=
filepath
.
Abs
(
path
)
if
err
!=
nil
{
continue
}
if
strings
.
EqualFold
(
AppDir
,
d
)
{
found
=
true
}
}
if
!
found
{
paths
=
append
(
paths
,
AppDir
)
pathVal
:=
strings
.
Join
(
paths
,
";"
)
slog
.
Debug
(
"setting PATH="
+
pathVal
)
err
:=
os
.
Setenv
(
"PATH"
,
pathVal
)
if
err
!=
nil
{
slog
.
Error
(
fmt
.
Sprintf
(
"failed to update PATH: %s"
,
err
))
}
}
// Make sure our logging dir exists
_
,
err
:=
os
.
Stat
(
AppDataDir
)
if
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
if
err
:=
os
.
MkdirAll
(
AppDataDir
,
0
o755
);
err
!=
nil
{
slog
.
Error
(
fmt
.
Sprintf
(
"create ollama dir %s: %v"
,
AppDataDir
,
err
))
}
}
}
else
if
runtime
.
GOOS
==
"darwin"
{
// TODO
AppName
+=
".app"
// } else if runtime.GOOS == "linux" {
// TODO
}
}
ollama/app/lifecycle/server.go
deleted
100755 → 0
View file @
cb75098c
package
lifecycle
import
(
"context"
"errors"
"fmt"
"io"
"log/slog"
"os"
"os/exec"
"path/filepath"
"time"
"github.com/ollama/ollama/api"
)
func
getCLIFullPath
(
command
string
)
string
{
var
cmdPath
string
appExe
,
err
:=
os
.
Executable
()
if
err
==
nil
{
cmdPath
=
filepath
.
Join
(
filepath
.
Dir
(
appExe
),
command
)
_
,
err
:=
os
.
Stat
(
cmdPath
)
if
err
==
nil
{
return
cmdPath
}
}
cmdPath
,
err
=
exec
.
LookPath
(
command
)
if
err
==
nil
{
_
,
err
:=
os
.
Stat
(
cmdPath
)
if
err
==
nil
{
return
cmdPath
}
}
pwd
,
err
:=
os
.
Getwd
()
if
err
==
nil
{
cmdPath
=
filepath
.
Join
(
pwd
,
command
)
_
,
err
=
os
.
Stat
(
cmdPath
)
if
err
==
nil
{
return
cmdPath
}
}
return
command
}
func
start
(
ctx
context
.
Context
,
command
string
)
(
*
exec
.
Cmd
,
error
)
{
cmd
:=
getCmd
(
ctx
,
getCLIFullPath
(
command
))
stdout
,
err
:=
cmd
.
StdoutPipe
()
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to spawn server stdout pipe: %w"
,
err
)
}
stderr
,
err
:=
cmd
.
StderrPipe
()
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to spawn server stderr pipe: %w"
,
err
)
}
rotateLogs
(
ServerLogFile
)
logFile
,
err
:=
os
.
OpenFile
(
ServerLogFile
,
os
.
O_APPEND
|
os
.
O_WRONLY
|
os
.
O_CREATE
,
0
o755
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to create server log: %w"
,
err
)
}
logDir
:=
filepath
.
Dir
(
ServerLogFile
)
_
,
err
=
os
.
Stat
(
logDir
)
if
err
!=
nil
{
if
!
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
return
nil
,
fmt
.
Errorf
(
"stat ollama server log dir %s: %v"
,
logDir
,
err
)
}
if
err
:=
os
.
MkdirAll
(
logDir
,
0
o755
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"create ollama server log dir %s: %v"
,
logDir
,
err
)
}
}
go
func
()
{
defer
logFile
.
Close
()
io
.
Copy
(
logFile
,
stdout
)
//nolint:errcheck
}()
go
func
()
{
defer
logFile
.
Close
()
io
.
Copy
(
logFile
,
stderr
)
//nolint:errcheck
}()
// Re-wire context done behavior to attempt a graceful shutdown of the server
cmd
.
Cancel
=
func
()
error
{
if
cmd
.
Process
!=
nil
{
err
:=
terminate
(
cmd
)
if
err
!=
nil
{
slog
.
Warn
(
"error trying to gracefully terminate server"
,
"err"
,
err
)
return
cmd
.
Process
.
Kill
()
}
tick
:=
time
.
NewTicker
(
10
*
time
.
Millisecond
)
defer
tick
.
Stop
()
for
{
select
{
case
<-
tick
.
C
:
exited
,
err
:=
isProcessExited
(
cmd
.
Process
.
Pid
)
if
err
!=
nil
{
return
err
}
if
exited
{
return
nil
}
case
<-
time
.
After
(
5
*
time
.
Second
)
:
slog
.
Warn
(
"graceful server shutdown timeout, killing"
,
"pid"
,
cmd
.
Process
.
Pid
)
return
cmd
.
Process
.
Kill
()
}
}
}
return
nil
}
// run the command and wait for it to finish
if
err
:=
cmd
.
Start
();
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to start server %w"
,
err
)
}
if
cmd
.
Process
!=
nil
{
slog
.
Info
(
fmt
.
Sprintf
(
"started ollama server with pid %d"
,
cmd
.
Process
.
Pid
))
}
slog
.
Info
(
fmt
.
Sprintf
(
"ollama server logs %s"
,
ServerLogFile
))
return
cmd
,
nil
}
func
SpawnServer
(
ctx
context
.
Context
,
command
string
)
(
chan
int
,
error
)
{
done
:=
make
(
chan
int
)
go
func
()
{
// Keep the server running unless we're shuttind down the app
crashCount
:=
0
for
{
slog
.
Info
(
"starting server..."
)
cmd
,
err
:=
start
(
ctx
,
command
)
if
err
!=
nil
{
crashCount
++
slog
.
Error
(
fmt
.
Sprintf
(
"failed to start server %s"
,
err
))
time
.
Sleep
(
500
*
time
.
Millisecond
*
time
.
Duration
(
crashCount
))
continue
}
cmd
.
Wait
()
//nolint:errcheck
var
code
int
if
cmd
.
ProcessState
!=
nil
{
code
=
cmd
.
ProcessState
.
ExitCode
()
}
select
{
case
<-
ctx
.
Done
()
:
slog
.
Info
(
fmt
.
Sprintf
(
"server shutdown with exit code %d"
,
code
))
done
<-
code
return
default
:
crashCount
++
slog
.
Warn
(
fmt
.
Sprintf
(
"server crash %d - exit code %d - respawning"
,
crashCount
,
code
))
time
.
Sleep
(
500
*
time
.
Millisecond
*
time
.
Duration
(
crashCount
))
break
}
}
}()
return
done
,
nil
}
func
IsServerRunning
(
ctx
context
.
Context
)
bool
{
client
,
err
:=
api
.
ClientFromEnvironment
()
if
err
!=
nil
{
slog
.
Info
(
"unable to connect to server"
)
return
false
}
err
=
client
.
Heartbeat
(
ctx
)
if
err
!=
nil
{
slog
.
Debug
(
fmt
.
Sprintf
(
"heartbeat from server: %s"
,
err
))
slog
.
Info
(
"unable to connect to server"
)
return
false
}
return
true
}
ollama/app/lifecycle/server_unix.go
deleted
100755 → 0
View file @
cb75098c
//go:build !windows
package
lifecycle
import
(
"context"
"errors"
"fmt"
"os"
"os/exec"
"syscall"
)
func
getCmd
(
ctx
context
.
Context
,
cmd
string
)
*
exec
.
Cmd
{
return
exec
.
CommandContext
(
ctx
,
cmd
,
"serve"
)
}
func
terminate
(
cmd
*
exec
.
Cmd
)
error
{
return
cmd
.
Process
.
Signal
(
os
.
Interrupt
)
}
func
isProcessExited
(
pid
int
)
(
bool
,
error
)
{
proc
,
err
:=
os
.
FindProcess
(
pid
)
if
err
!=
nil
{
return
false
,
fmt
.
Errorf
(
"failed to find process: %v"
,
err
)
}
err
=
proc
.
Signal
(
syscall
.
Signal
(
0
))
if
err
!=
nil
{
if
errors
.
Is
(
err
,
os
.
ErrProcessDone
)
||
errors
.
Is
(
err
,
syscall
.
ESRCH
)
{
return
true
,
nil
}
return
false
,
fmt
.
Errorf
(
"error signaling process: %v"
,
err
)
}
return
false
,
nil
}
ollama/app/lifecycle/server_windows.go
deleted
100755 → 0
View file @
cb75098c
package
lifecycle
import
(
"context"
"fmt"
"os/exec"
"syscall"
"golang.org/x/sys/windows"
)
func
getCmd
(
ctx
context
.
Context
,
exePath
string
)
*
exec
.
Cmd
{
cmd
:=
exec
.
CommandContext
(
ctx
,
exePath
,
"serve"
)
cmd
.
SysProcAttr
=
&
syscall
.
SysProcAttr
{
HideWindow
:
true
,
CreationFlags
:
windows
.
CREATE_NEW_PROCESS_GROUP
,
}
return
cmd
}
func
terminate
(
cmd
*
exec
.
Cmd
)
error
{
dll
,
err
:=
windows
.
LoadDLL
(
"kernel32.dll"
)
if
err
!=
nil
{
return
err
}
//nolint:errcheck
defer
dll
.
Release
()
pid
:=
cmd
.
Process
.
Pid
f
,
err
:=
dll
.
FindProc
(
"AttachConsole"
)
if
err
!=
nil
{
return
err
}
r1
,
_
,
err
:=
f
.
Call
(
uintptr
(
pid
))
if
r1
==
0
&&
err
!=
syscall
.
ERROR_ACCESS_DENIED
{
return
err
}
f
,
err
=
dll
.
FindProc
(
"SetConsoleCtrlHandler"
)
if
err
!=
nil
{
return
err
}
r1
,
_
,
err
=
f
.
Call
(
0
,
1
)
if
r1
==
0
{
return
err
}
f
,
err
=
dll
.
FindProc
(
"GenerateConsoleCtrlEvent"
)
if
err
!=
nil
{
return
err
}
r1
,
_
,
err
=
f
.
Call
(
windows
.
CTRL_BREAK_EVENT
,
uintptr
(
pid
))
if
r1
==
0
{
return
err
}
r1
,
_
,
err
=
f
.
Call
(
windows
.
CTRL_C_EVENT
,
uintptr
(
pid
))
if
r1
==
0
{
return
err
}
return
nil
}
const
STILL_ACTIVE
=
259
func
isProcessExited
(
pid
int
)
(
bool
,
error
)
{
hProcess
,
err
:=
windows
.
OpenProcess
(
windows
.
PROCESS_QUERY_INFORMATION
,
false
,
uint32
(
pid
))
if
err
!=
nil
{
return
false
,
fmt
.
Errorf
(
"failed to open process: %v"
,
err
)
}
//nolint:errcheck
defer
windows
.
CloseHandle
(
hProcess
)
var
exitCode
uint32
err
=
windows
.
GetExitCodeProcess
(
hProcess
,
&
exitCode
)
if
err
!=
nil
{
return
false
,
fmt
.
Errorf
(
"failed to get exit code: %v"
,
err
)
}
if
exitCode
==
STILL_ACTIVE
{
return
false
,
nil
}
return
true
,
nil
}
ollama/app/lifecycle/updater.go
deleted
100755 → 0
View file @
cb75098c
package
lifecycle
import
(
"context"
"crypto/rand"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
"mime"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"github.com/ollama/ollama/auth"
"github.com/ollama/ollama/version"
)
var
(
UpdateCheckURLBase
=
"https://ollama.com/api/update"
UpdateDownloaded
=
false
UpdateCheckInterval
=
60
*
60
*
time
.
Second
)
// TODO - maybe move up to the API package?
type
UpdateResponse
struct
{
UpdateURL
string
`json:"url"`
UpdateVersion
string
`json:"version"`
}
func
IsNewReleaseAvailable
(
ctx
context
.
Context
)
(
bool
,
UpdateResponse
)
{
var
updateResp
UpdateResponse
requestURL
,
err
:=
url
.
Parse
(
UpdateCheckURLBase
)
if
err
!=
nil
{
return
false
,
updateResp
}
query
:=
requestURL
.
Query
()
query
.
Add
(
"os"
,
runtime
.
GOOS
)
query
.
Add
(
"arch"
,
runtime
.
GOARCH
)
query
.
Add
(
"version"
,
version
.
Version
)
query
.
Add
(
"ts"
,
strconv
.
FormatInt
(
time
.
Now
()
.
Unix
(),
10
))
nonce
,
err
:=
auth
.
NewNonce
(
rand
.
Reader
,
16
)
if
err
!=
nil
{
return
false
,
updateResp
}
query
.
Add
(
"nonce"
,
nonce
)
requestURL
.
RawQuery
=
query
.
Encode
()
data
:=
[]
byte
(
fmt
.
Sprintf
(
"%s,%s"
,
http
.
MethodGet
,
requestURL
.
RequestURI
()))
signature
,
err
:=
auth
.
Sign
(
ctx
,
data
)
if
err
!=
nil
{
return
false
,
updateResp
}
req
,
err
:=
http
.
NewRequestWithContext
(
ctx
,
http
.
MethodGet
,
requestURL
.
String
(),
nil
)
if
err
!=
nil
{
slog
.
Warn
(
fmt
.
Sprintf
(
"failed to check for update: %s"
,
err
))
return
false
,
updateResp
}
req
.
Header
.
Set
(
"Authorization"
,
signature
)
req
.
Header
.
Set
(
"User-Agent"
,
fmt
.
Sprintf
(
"ollama/%s (%s %s) Go/%s"
,
version
.
Version
,
runtime
.
GOARCH
,
runtime
.
GOOS
,
runtime
.
Version
()))
slog
.
Debug
(
"checking for available update"
,
"requestURL"
,
requestURL
)
resp
,
err
:=
http
.
DefaultClient
.
Do
(
req
)
if
err
!=
nil
{
slog
.
Warn
(
fmt
.
Sprintf
(
"failed to check for update: %s"
,
err
))
return
false
,
updateResp
}
defer
resp
.
Body
.
Close
()
if
resp
.
StatusCode
==
http
.
StatusNoContent
{
slog
.
Debug
(
"check update response 204 (current version is up to date)"
)
return
false
,
updateResp
}
body
,
err
:=
io
.
ReadAll
(
resp
.
Body
)
if
err
!=
nil
{
slog
.
Warn
(
fmt
.
Sprintf
(
"failed to read body response: %s"
,
err
))
}
if
resp
.
StatusCode
!=
http
.
StatusOK
{
slog
.
Info
(
fmt
.
Sprintf
(
"check update error %d - %.96s"
,
resp
.
StatusCode
,
string
(
body
)))
return
false
,
updateResp
}
err
=
json
.
Unmarshal
(
body
,
&
updateResp
)
if
err
!=
nil
{
slog
.
Warn
(
fmt
.
Sprintf
(
"malformed response checking for update: %s"
,
err
))
return
false
,
updateResp
}
// Extract the version string from the URL in the github release artifact path
updateResp
.
UpdateVersion
=
path
.
Base
(
path
.
Dir
(
updateResp
.
UpdateURL
))
slog
.
Info
(
"New update available at "
+
updateResp
.
UpdateURL
)
return
true
,
updateResp
}
func
DownloadNewRelease
(
ctx
context
.
Context
,
updateResp
UpdateResponse
)
error
{
// Do a head first to check etag info
req
,
err
:=
http
.
NewRequestWithContext
(
ctx
,
http
.
MethodHead
,
updateResp
.
UpdateURL
,
nil
)
if
err
!=
nil
{
return
err
}
resp
,
err
:=
http
.
DefaultClient
.
Do
(
req
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"error checking update: %w"
,
err
)
}
if
resp
.
StatusCode
!=
http
.
StatusOK
{
return
fmt
.
Errorf
(
"unexpected status attempting to download update %d"
,
resp
.
StatusCode
)
}
resp
.
Body
.
Close
()
etag
:=
strings
.
Trim
(
resp
.
Header
.
Get
(
"etag"
),
"
\"
"
)
if
etag
==
""
{
slog
.
Debug
(
"no etag detected, falling back to filename based dedup"
)
etag
=
"_"
}
filename
:=
Installer
_
,
params
,
err
:=
mime
.
ParseMediaType
(
resp
.
Header
.
Get
(
"content-disposition"
))
if
err
==
nil
{
filename
=
params
[
"filename"
]
}
stageFilename
:=
filepath
.
Join
(
UpdateStageDir
,
etag
,
filename
)
// Check to see if we already have it downloaded
_
,
err
=
os
.
Stat
(
stageFilename
)
if
err
==
nil
{
slog
.
Info
(
"update already downloaded"
)
return
nil
}
cleanupOldDownloads
()
req
.
Method
=
http
.
MethodGet
resp
,
err
=
http
.
DefaultClient
.
Do
(
req
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"error checking update: %w"
,
err
)
}
defer
resp
.
Body
.
Close
()
etag
=
strings
.
Trim
(
resp
.
Header
.
Get
(
"etag"
),
"
\"
"
)
if
etag
==
""
{
slog
.
Debug
(
"no etag detected, falling back to filename based dedup"
)
// TODO probably can get rid of this redundant log
etag
=
"_"
}
stageFilename
=
filepath
.
Join
(
UpdateStageDir
,
etag
,
filename
)
_
,
err
=
os
.
Stat
(
filepath
.
Dir
(
stageFilename
))
if
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
if
err
:=
os
.
MkdirAll
(
filepath
.
Dir
(
stageFilename
),
0
o755
);
err
!=
nil
{
return
fmt
.
Errorf
(
"create ollama dir %s: %v"
,
filepath
.
Dir
(
stageFilename
),
err
)
}
}
payload
,
err
:=
io
.
ReadAll
(
resp
.
Body
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to read body response: %w"
,
err
)
}
fp
,
err
:=
os
.
OpenFile
(
stageFilename
,
os
.
O_WRONLY
|
os
.
O_CREATE
|
os
.
O_TRUNC
,
0
o755
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"write payload %s: %w"
,
stageFilename
,
err
)
}
defer
fp
.
Close
()
if
n
,
err
:=
fp
.
Write
(
payload
);
err
!=
nil
||
n
!=
len
(
payload
)
{
return
fmt
.
Errorf
(
"write payload %s: %d vs %d -- %w"
,
stageFilename
,
n
,
len
(
payload
),
err
)
}
slog
.
Info
(
"new update downloaded "
+
stageFilename
)
UpdateDownloaded
=
true
return
nil
}
func
cleanupOldDownloads
()
{
files
,
err
:=
os
.
ReadDir
(
UpdateStageDir
)
if
err
!=
nil
&&
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
// Expected behavior on first run
return
}
else
if
err
!=
nil
{
slog
.
Warn
(
fmt
.
Sprintf
(
"failed to list stage dir: %s"
,
err
))
return
}
for
_
,
file
:=
range
files
{
fullname
:=
filepath
.
Join
(
UpdateStageDir
,
file
.
Name
())
slog
.
Debug
(
"cleaning up old download: "
+
fullname
)
err
=
os
.
RemoveAll
(
fullname
)
if
err
!=
nil
{
slog
.
Warn
(
fmt
.
Sprintf
(
"failed to cleanup stale update download %s"
,
err
))
}
}
}
func
StartBackgroundUpdaterChecker
(
ctx
context
.
Context
,
cb
func
(
string
)
error
)
{
go
func
()
{
// Don't blast an update message immediately after startup
// time.Sleep(30 * time.Second)
time
.
Sleep
(
3
*
time
.
Second
)
for
{
available
,
resp
:=
IsNewReleaseAvailable
(
ctx
)
if
available
{
err
:=
DownloadNewRelease
(
ctx
,
resp
)
if
err
!=
nil
{
slog
.
Error
(
fmt
.
Sprintf
(
"failed to download new release: %s"
,
err
))
}
err
=
cb
(
resp
.
UpdateVersion
)
if
err
!=
nil
{
slog
.
Warn
(
fmt
.
Sprintf
(
"failed to register update available with tray: %s"
,
err
))
}
}
select
{
case
<-
ctx
.
Done
()
:
slog
.
Debug
(
"stopping background update checker"
)
return
default
:
time
.
Sleep
(
UpdateCheckInterval
)
}
}
}()
}
Prev
1
2
3
4
5
6
…
17
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment