Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Ollama-webui package and service for Mixtral #275448

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions maintainers/maintainer-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11273,6 +11273,11 @@
githubId = 1780588;
name = "Malte Poll";
};
malteneuss = {
github = "malteneuss";
githubId = 5301202;
name = "Malte Neuss";
};
malte-v = {
email = "nixpkgs@mal.tc";
github = "malte-v";
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1320,6 +1320,7 @@
./services/web-apps/nexus.nix
./services/web-apps/nifi.nix
./services/web-apps/node-red.nix
./services/web-apps/ollama-webui.nix
./services/web-apps/onlyoffice.nix
./services/web-apps/openvscode-server.nix
./services/web-apps/mobilizon.nix
Expand Down
120 changes: 92 additions & 28 deletions nixos/modules/services/misc/ollama.nix
Original file line number Diff line number Diff line change
@@ -1,42 +1,106 @@
{ config, lib, pkgs, ... }: let
{ config, pkgs, lib, ... }:

cfg = config.services.ollama;
with lib;

in {
let cfg = config.services.ollama;

in {
options = {
services.ollama = {
enable = lib.mkEnableOption (
lib.mdDoc "Server for local large language models"
);
package = lib.mkPackageOption pkgs "ollama" { };
enable = mkEnableOption (lib.mdDoc ''
Ollama backend service.
Run state-of-the-art AI large language models (LLM) similar to ChatGPT locally with privacy
on your personal computer.

This module provides the `ollama serve` backend runner service so that you can run
`ollam run <model-name>` locally in a terminal; this will automatically download the
LLM model and open a chat. See <https://github.com/jmorganca/ollama#quickstart>.
The model names can be looked up on <https://ollama.ai/library> and are available in
varying sizes to fit your CPU, GPU, RAM and disk storage.
See <https://github.com/jmorganca/ollama#model-library>.

Optional: This module is intended to be run locally, but can be served from a (home) server,
ideally behind a secured reverse-proxy.
Look at <https://nixos.wiki/wiki/Nginx> or <https://nixos.wiki/wiki/Caddy>
on how to set up a reverse proxy.

Optional: This service doesn't persist any chats and is only available in the terminal.
For a convenient, graphical web app on top of it, take look at
<https://github.com/ollama-webui/ollama-webui>, also as `ollama-webui` in Nixpkgs.
'');

ollama-package = mkPackageOption pkgs "ollama" { };

host = mkOption {
type = types.str;
default = "127.0.0.1";
description = lib.mdDoc ''
The host/domain name under which the Ollama backend service is reachable.
'';
};

port = mkOption {
type = types.port;
default = 11434;
description = lib.mdDoc "The port for the Ollama backend service.";
};

cors_origins = mkOption {
type = types.nullOr types.str;
default = null;
example = "https://myserver:8080,http://10.0.0.10:*";
description = lib.mdDoc ''
Allow access from web apps that are served under some (different) URL.
If a web app like Ollama-WebUI is available/served on `https://myserver:8080`,
then add this URL here. Otherwise the Ollama backend server will reject the
UIs request and return 403 forbidden due to CORS.
See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS>.
'';
};

openFirewall = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc "Open ports in the firewall for the Ollama backend service.";
};
};
};

config = lib.mkIf cfg.enable {

systemd = {
services.ollama = {
wantedBy = [ "multi-user.target" ];
description = "Server for local large language models";
after = [ "network.target" ];
environment = {
HOME = "%S/ollama";
OLLAMA_MODELS = "%S/ollama/models";
};
serviceConfig = {
ExecStart = "${lib.getExe cfg.package} serve";
WorkingDirectory = "/var/lib/ollama";
StateDirectory = [ "ollama" ];
DynamicUser = true;
};
config = mkIf cfg.enable {
systemd.services.ollama = {
description = "Ollama: A backend service for local large language models (LLM).";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];

environment = {
OLLAMA_HOST = "${cfg.host}:${toString cfg.port}";
OLLAMA_ORIGINS = cfg.cors_origins;
# Where to store LLM model files.
# Directory is managed by systemd DynamicUser feature, see below.
HOME = "%S/ollama";
OLLAMA_MODELS = "%S/ollama/models";
Comment on lines +80 to +81
Copy link
Contributor

@Kreyren Kreyren May 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it better to use /var/lib/private/ollama instead of the %s so that it's more functional to work with the variables?

image

^ Having to hard-code the path as the expansion doesn't seem to work like other services

};

serviceConfig = {
ExecStart = "${lib.getExe cfg.ollama-package} serve";
# Systemd takes care of username, user id, security & permissions
# See https://0pointer.net/blog/dynamic-users-with-systemd.html
# Almost nothing on the disk is readable for this dynamic user; only a few places writable:
# See https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#RuntimeDirectory=
DynamicUser = "true";
Type = "simple";
Restart = "on-failure";
RestartSec = 3;
WorkingDirectory = "/var/lib/ollama";
# Persistent storage for model files, i.e. /var/lib/<StateDirectory>
StateDirectory = [ "ollama" ];
};
};

environment.systemPackages = [ cfg.package ];
networking.firewall = mkIf cfg.openFirewall {
allowedTCPPorts = [ cfg.port ];
};

};

meta.maintainers = with lib.maintainers; [ onny ];

meta.maintainers = with lib.maintainers; [ onny malteneuss ];
}
94 changes: 94 additions & 0 deletions nixos/modules/services/web-apps/ollama-webui.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
{ config, pkgs, lib, ... }:

with lib;

let cfg = config.services.ollama-webui;

in {
options = {
services.ollama-webui = {
enable = mkEnableOption (lib.mdDoc ''
Ollama-WebUI frontend service for the Ollama backend service.
It's a single page web application that integrates with Ollama to
run state-of-the-art AI large language models (LLM) locally with privacy
on your personal computer.
It's look and feel is similar to ChatGPT, supports different LLM models,
it stores chats (in the browser storage), supports image uploads,
Markdown and Latex rendering etc.See <https://github.com/ollama-webui/ollama-webui>.

The model names can be looked up on <https://ollama.ai/library> and are available in
varying sizes to fit your CPU, GPU, RAM and disk storage.

Required: This module requires the Ollama backend runner service that actually runs the
LLM models. The Ollama backend can be running locally or on a server; it's available
as a Nix package or a NixOS module.
The URL to the Ollama backend service can be set in this module, but overriden
later in the running Ollama-WebUI as well.

Optional: This module is configured to run locally, but can be served from a (home) server,
ideally behind a secured reverse-proxy.
Look at <https://nixos.wiki/wiki/Nginx> or <https://nixos.wiki/wiki/Caddy>
on how to set up a reverse proxy.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you think referencing the caddy file of the original repo would be a good idea ?
https://github.com/ollama-webui/ollama-webui/blob/main/Caddyfile.localhost

'');

ollama-webui-package = mkPackageOption pkgs "ollama-webui" { };

host = mkOption {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this host isn't used anywhere, is that intentional ?

type = types.str;
default = "127.0.0.1";
description = lib.mdDoc ''
The host/domain name under which the Ollama-WebUI service is reachable.
'';
};

port = mkOption {
type = types.port;
default = 8080;
description = lib.mdDoc "The port for the Ollama-WebUI service.";
};

cors_origins = mkOption {
type = types.nullOr types.str;
default = null;
example = "*,https://myserver:8080,http://10.0.0.10:*";
description = lib.mdDoc ''
Allow access from web apps that are served under some (different) URL.
If a web app like Ollama-WebUI is available/served on `https://myserver:8080`,
then add this URL here. Otherwise the Ollama frontend server will reject the
UIs request and return 403 forbidden due to CORS.
See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS>.
'';
};

openFirewall = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc "Open ports in the firewall for the Ollama-WebUI service.";
};
};
};

config = mkIf cfg.enable {

systemd.services.ollama-webui = {
description = "Ollama WebUI Service";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];

serviceConfig = let
cors-arg = if cfg.cors_origins == null then "" else "--cors='" + cfg.cors_origin +"'";
in {
ExecStart = "${cfg.ollama-webui-package}/bin/ollama-webui --port ${toString cfg.port} ${cors-arg}";
DynamicUser = "true";
Type = "simple";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just for information the default type is simple for services is simple. I'm not sure how I feel about having it written explicitely, if you prefer it that way, feel free to keep it.

Restart = "on-failure";
};
};

networking.firewall = mkIf cfg.openFirewall {
allowedTCPPorts = [ cfg.port ];
};

};
meta.maintainers = with lib.maintainers; [ malteneuss ];
}
62 changes: 62 additions & 0 deletions pkgs/tools/misc/ollama-webui/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{ lib
, buildNpmPackage
, nodePackages
, fetchFromGitHub
, runtimeShell
}:

# We just package the JS frontend part, not the Python reverse-proxy backend.
# NixOS can provide any another reverse proxy such as nginx.
buildNpmPackage rec {
pname = "ollama-webui";
# ollama-webui doesn't tag versions yet.
version = "0.0.0-unstable-2023-12-22";

src = fetchFromGitHub {
owner = "ollama-webui";
repo = "ollama-webui";
rev = "77c1a77fccb04337ff95440030cd051fd16c2cd8";
hash = "sha256-u7h2tpHgtQwYXornslY3CZjKjigqBK2mHmaiK1EoEgk=";
};
# dependencies are downloaded into a separate node_modules Nix package
npmDepsHash = "sha256-SI2dPn1SwbGwl8093VBtcDsA2eHSxr3UUC+ta68w2t8=";

# We have to bake in the default URL it will use for ollama webserver here,
# but it can be overriden in the UI later.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when you say it can be overridden later, does that mean inside the module ?
just wondering if you still need to add the override.

PUBLIC_API_BASE_URL = "http://localhost:11434/api";

# The path '/ollama/api' will be redirected to the specified backend URL
OLLAMA_API_BASE_URL = PUBLIC_API_BASE_URL;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the env var only seems to be set after the build has succeeded
https://github.com/ollama-webui/ollama-webui/blob/main/Dockerfile#L22
does the build fail without ?
if not, this is probably something that should be set in the service, not in the package.

# "npm run build" creates a static page in the "build" folder.
installPhase = ''
mkdir -p $out/lib
cp -R ./build/. $out/lib

mkdir -p $out/bin
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't need those last instructions.
I could be wrong, but if this is a standard node project, you just need to package the build directory
then in the service you can use node directly to just run the build directory
here are some reference of how we did this with lemmy

name = "lemmy-ui";

cfg = config.services.lemmy;

second point and this is entirely optional.
rather than making a complety separate service for ollama-webui, how about including it in the original ollama service.
I don't think it makes sense to have ollama-webui as a standalone for now.
(you can take inspiration in the lemmy module for what we did if you find stuff that you like).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a bit more context on not providing an executable for a node package.
the main reason is that without several environment variable set, the binary is just not runnable. So most of the time you package the build directory and in the service you provide all the necessary to run it.

if you prefer to provide a binary, I respect your decision, in that case, you should probably use makeWrapper, you can look at jellyseerr for an example (you'll find many more if you don't like this particular one).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

second point and this is entirely optional.
rather than making a complety separate service for ollama-webui, how about including it in the original ollama service.
I don't think it makes sense to have ollama-webui as a standalone for now.
(you can take inspiration in the lemmy module for what we did if you find stuff that you like).

Since ollama-webui is a community project and not officially associated with ollama (see the top of their README.md), I tend to think it does not make sense to have the ollama-webui service live under ollama as ollama.ui or ollama.web-ui.

There are a bunch of other frontends that people might want to use with the ollama service, such as oterm.

cat <<EOF >>$out/bin/${pname}
#!${runtimeShell}
${nodePackages.http-server}/bin/http-server $out/lib
EOF
chmod +x $out/bin/${pname}
'';

meta = with lib; {
description = "ChatGPT-Style Web Interface for Ollama";
longDescription = ''
Tools like Ollama make open-source large langue models (LLM) accessible and almost
trivial to download and run them locally on a consumer computer.
However, Ollama only runs in a terminal and doesn't store any chat history.
Ollama-WebUI is a web frontend on top of Ollama that looks and behaves similar to ChatGPT's web frontend.
You can have separate chats with different LLMs that are saved in your browser,
automatic Markdown and Latex rendering, upload files etc.
This package contains two parts:
- `<nix-store-package-path>/lib` The WebUI as a compiled, static html folder to bundle in your web server
- `<nix-store-package-path>/bin/${pname}` A runnable webserver the serves the WebUI for convenience.
'';
homepage = "https://github.com/ollama-webui/ollama-webui";
license = licenses.mit;
mainProgram = pname;
maintainers = with maintainers; [ malteneuss ];
platforms = platforms.all;
};
}
2 changes: 2 additions & 0 deletions pkgs/top-level/all-packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,8 @@ with pkgs;

ollama = callPackage ../tools/misc/ollama { };

ollama-webui = callPackage ../tools/misc/ollama-webui { };

ots = callPackage ../tools/security/ots { };

credential-detector = callPackage ../tools/security/credential-detector { };
Expand Down