summarylogtreecommitdiffstats
path: root/PKGBUILD
blob: d9763479339e660d1e709bc4f86cb2526dc50215 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# Maintainer: Mark Austin <ganthore@gmail.com>

pkgname=(gaia-amd gaia-amd-webui)
pkgver=0.22.0
pkgrel=1
pkgdesc="AI-powered inference engine for AMD hardware"
arch=(x86_64)
url="https://github.com/amd/gaia"
license=(MIT)

# AUR-only deps (must be installed via an AUR helper or manually):
#   lemonade-server, python-faiss, python-sentence-transformers,
#   python-transformers, python-accelerate

# Runtime deps for gaia-amd; gaia-amd-webui overrides with its own depends=()
depends=(
    python
    python-requests
    python-aiohttp
    python-pydantic
    python-dotenv
    python-rich
    python-fastapi
    uvicorn
    python-psutil
    python-pymupdf
    python-pypdf
    python-torchvision
    python-joblib
    python-cloudpickle
    python-prompt_toolkit
    python-typer
    python-async-lru
    python-fsspec
    python-pyarrow
    python-cryptography
    python-lz4
    python-orjson
    python-cachetools
    python-lxml
    python-greenlet
    python-msgpack
    python-toml
    python-fastjsonschema
    python-python-multipart
    python-httpx
    python-keyring
    python-numpy
    python-beautifulsoup4
    python-faiss
    python-sentence-transformers
    python-transformers
    'python-tokenizers<0.23'  # python-transformers fails to build against tokenizers 0.23+
    python-accelerate
    python-aiodns
    python-openai
    lemonade-server
)
makedepends=(
    git
    cmake
    ninja
    python-setuptools
    python-wheel
    python-build
    python-installer
    nodejs
    npm
    electron40
    libappindicator
)

sha256sums=(284031c728e9c189d08a7831dfd41f691a1f79304bdaba0d19facf25821e2fd5
            96ab23bd2b0d3d402a6c3160f0f5016f582994533723b503098deb042ebbcb03
            13598d2e9294b09ac7cf26739a0ae42acc4993fc2e63ac94a7867ec3d22c99df
            3d185692ac7bd9834643052cb570a6a214878bf74f9e6e14b8c5115493bc7c7e)

source=("gaia-amd-$pkgver.tar.gz::https://github.com/amd/gaia/archive/refs/tags/v$pkgver.tar.gz"
        "gaia.service"
        "gaia-user.service"
        "gaia.sysusers")

prepare() {
    rm -rf "$srcdir/build"
    mkdir -p "$srcdir/build"
    cd "$srcdir/gaia-$pkgver/cpp"
    cmake -B "$srcdir/build" -S . \
        -G Ninja \
        -DCMAKE_BUILD_TYPE=Release \
        -DCMAKE_INSTALL_PREFIX=/usr

    # Upstream bug: tray-manager passes the raw 4096x4096 app icon to the Tray
    # constructor. The pixel data (~67MB uncompressed RGBA) exceeds the D-Bus
    # session bus message limit, so KDE's StatusNotifierWatcher silently drops
    # the registration and no tray icon appears. Resize to 22px at load time
    # (standard KDE Plasma tray icon size) before handing it to Electron.
    sed -i \
        's#return nativeImage\.createFromPath(iconPath);#return nativeImage.createFromPath(iconPath).resize({ width: 22, height: 22 });#' \
        "$srcdir/gaia-$pkgver/src/gaia/apps/webui/services/tray-manager.cjs"

    local _webui="$srcdir/gaia-$pkgver/src/gaia/apps/webui"

    # With system Electron the binary is at /usr/bin/electron40, so
    # process.resourcesPath resolves to /usr/lib/electron40/resources/ rather
    # than our install dir. The wrapper script exports GAIA_RESOURCES_PATH so
    # these two services find agents/ and vendor/uv/ in the right place.
    # Use # as delimiter to avoid conflicts with | in the replacement strings.
    sed -i \
        's#path\.join(process\.resourcesPath, "agents")#path.join(process.env.GAIA_RESOURCES_PATH || process.resourcesPath, "agents")#' \
        "$_webui/services/agent-seeder.cjs"
    sed -i \
        's#const resourcesPath = process\.resourcesPath;#const resourcesPath = process.env.GAIA_RESOURCES_PATH || process.resourcesPath;#' \
        "$_webui/services/backend-installer.cjs"

    # lemond v10.6.0 passes bare --flash-attn (no value) to llama-server for
    # Gemma-4 models; llama-server requires --flash-attn on. Explicitly supply
    # the arg so the default model loads on first run.
    sed -i \
        's/prompt=False$/prompt=False, llamacpp_args="--flash-attn on"/' \
        "$srcdir/gaia-$pkgver/src/gaia/ui/server.py"

    # The lifespan calls keyring (via tripwire_check) which blocks until
    # kwalletd responds — up to 7+ minutes if KWallet isn't running yet.
    # Port 4200 never opens until the lifespan yields. Bypass the system
    # keyring for the Electron-spawned backend; OAuth connectors fall back
    # to prompting for credentials at first use.
    # Also set GAIA_SKIP_DEVICE_CHECK: the device support check is Windows-only;
    # on Linux it always returns false and shows a warning banner.
    sed -i \
        's/env: { \.\.\.process\.env }/env: { ...process.env, PYTHON_KEYRING_BACKEND: "keyring.backends.null.Keyring", GAIA_SKIP_DEVICE_CHECK: "1" }/' \
        "$srcdir/gaia-$pkgver/src/gaia/apps/webui/main.cjs"

    # The onboarding wizard installs lemonade via Ubuntu PPA — useless on Arch.
    # Mark the install as initialized on any non-Windows platform so the UI
    # opens directly to the chat interface rather than the setup wizard.
    sed -i \
        's/status\.initialized = init_marker\.exists()/status.initialized = init_marker.exists() or sys.platform != "win32"/' \
        "$srcdir/gaia-$pkgver/src/gaia/ui/routers/system.py"

    # lemond v10+ returns ctx_size=0 in its health response when it does not
    # track context size. The status endpoint guards the sufficiency check with
    # `is not None`, so 0 passes through and triggers a false "context window
    # too small" banner. Treat 0 the same as None (not reported).
    sed -i \
        's/if status\.model_context_size is not None:/if status.model_context_size:/' \
        "$srcdir/gaia-$pkgver/src/gaia/ui/routers/system.py"

    # The ConnectionBanner shows "Cannot connect" on the very first health-check
    # because the React frontend fires immediately on mount while gaia's Python
    # server is still starting (port 4200 not yet open). Apply the same 3-
    # consecutive-failure threshold already used for the lemonade check so
    # startup noise is suppressed without hiding genuine outages.
    local _app="$srcdir/gaia-$pkgver/src/gaia/apps/webui/src/App.tsx"
    sed -i \
        '/const LEMONADE_FAIL_THRESHOLD = 3;/a\    const backendFailCountRef = useRef(0);\n    const BACKEND_FAIL_THRESHOLD = 3; // require 3 consecutive failures before showing banner' \
        "$_app"
    sed -i \
        's/const status = await api\.getSystemStatus();/const status = await api.getSystemStatus();\n            backendFailCountRef.current = 0;/' \
        "$_app"
    python3 -c '
import sys
path = sys.argv[1]
with open(path) as f: s = f.read()
s = s.replace(
    "            setBackendConnected(false);\n            setSystemStatus(null);",
    "            backendFailCountRef.current += 1;\n            if (backendFailCountRef.current >= BACKEND_FAIL_THRESHOLD) {\n                setBackendConnected(false);\n                setSystemStatus(null);\n            }"
)
with open(path, "w") as f: f.write(s)
' "$_app"
}

build() {
    cmake --build "$srcdir/build" -j$(nproc)

    # Build browser frontend first so package_data picks up dist/
    cd "$srcdir/gaia-$pkgver/src/gaia/apps/webui"
    npm install --quiet
    npm run build

    cd "$srcdir/gaia-$pkgver"
    python -m build --wheel --no-isolation

    local _electronver
    _electronver=$(cat /usr/lib/electron40/version)
    cd "$srcdir/gaia-$pkgver/src/gaia/apps/webui"
    ELECTRON_BUILDER_OFFLINE=true \
    ./node_modules/.bin/electron-builder --linux dir --x64 \
        --publish never \
        -c.electronDist=/usr/lib/electron40 \
        -c.electronVersion="$_electronver"
}

package_gaia-amd() {
    options=(!strip)
    install=gaia-amd.install

    DESTDIR="$pkgdir" cmake --install "$srcdir/build"

    cd "$srcdir/gaia-$pkgver"
    python -m installer --destdir="$pkgdir" dist/*.whl

    install -Dm755 "$srcdir/build/vlm_agent" "$pkgdir/usr/bin/gaia-vlm"
    install -Dm755 "$srcdir/build/security_demo" "$pkgdir/usr/bin/gaia-security-demo"

    install -Dm644 "$srcdir/gaia.service"      "$pkgdir/usr/lib/systemd/system/gaia.service"
    install -Dm644 "$srcdir/gaia-user.service" "$pkgdir/usr/lib/systemd/user/gaia.service"
    install -Dm644 "$srcdir/gaia.sysusers"     "$pkgdir/usr/lib/sysusers.d/gaia.conf"

    install -Dm644 "$srcdir/gaia-$pkgver/LICENSE.md" "$pkgdir/usr/share/licenses/$pkgname/LICENSE"

    if [[ -f "$srcdir/gaia-$pkgver/scripts/gaia.bash-completion" ]]; then
        install -Dm644 "$srcdir/gaia-$pkgver/scripts/gaia.bash-completion" \
            "$pkgdir/usr/share/bash-completion/completions/gaia"
    fi
    if [[ -f "$srcdir/gaia-$pkgver/share/man/man1/gaia.1" ]]; then
        install -Dm644 "$srcdir/gaia-$pkgver/share/man/man1/gaia.1" \
            "$pkgdir/usr/share/man/man1/gaia.1"
    fi
}

package_gaia-amd-webui() {
    pkgdesc="Electron desktop UI for the AMD Gaia inference engine"
    depends=(gaia-amd electron40 libappindicator)
    optdepends=('ngrok: mobile access / remote tunnel feature')

    local _resources="$srcdir/gaia-$pkgver/src/gaia/apps/webui/dist-app/linux-unpacked/resources"

    install -dm755 "$pkgdir/usr/lib/gaia-amd-webui"
    cp -r --no-preserve=mode "$_resources/." "$pkgdir/usr/lib/gaia-amd-webui/"

    install -Dm755 /dev/stdin "$pkgdir/usr/bin/gaia-amd-webui" <<'EOF'
#!/bin/sh
export GAIA_RESOURCES_PATH=/usr/lib/gaia-amd-webui
exec /usr/bin/electron40 --no-sandbox /usr/lib/gaia-amd-webui/app.asar "$@"
EOF

    install -dm755 "$pkgdir/usr/share/applications"
    cat > "$pkgdir/usr/share/applications/gaia-amd.desktop" <<EOF
[Desktop Entry]
Name=GAIA Agent UI
Comment=Privacy-first agentic AI interface
Exec=/usr/bin/gaia-amd-webui %U
Icon=gaia-amd
Terminal=false
Type=Application
Categories=Development;Utility;
StartupWMClass=gaia-desktop
EOF

    install -Dm644 "$srcdir/gaia-$pkgver/installer/linux/gaia-agent-ui.png" \
        "$pkgdir/usr/share/icons/hicolor/256x256/apps/gaia-amd.png"

    install -Dm644 "$srcdir/gaia-$pkgver/LICENSE.md" "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
}