Show local AI activity feedback
Build and publish static assets / publish (push) Successful in 8m21s
Build and publish static assets / publish (push) Successful in 8m21s
This commit is contained in:
@@ -40,28 +40,28 @@ jobs:
|
||||
test -s public/static/piper-wasm-v1/voices/sv/sv_SE/lisa/medium/sv_SE-lisa-medium.onnx
|
||||
test -s public/static/piper-wasm-v1/voices/sv/sv_SE/nst/medium/sv_SE-nst-medium.onnx
|
||||
test -s public/static/piper-wasm-v1/voices/ATTRIBUTION.txt
|
||||
test -s public/static/wllama-gemma3-v1/manifest.json
|
||||
test -s public/static/wllama-gemma3-v1/licenses/GEMMA_USER_TERMS.html
|
||||
test -s public/static/wllama-gemma3-v1/licenses/GEMMA_TERMS.html
|
||||
test -s public/static/wllama-gemma3-v1/licenses/GEMMA_PROHIBITED_USE_POLICY.html
|
||||
test -s public/static/wllama-gemma3-v1/licenses/GEMMA_NOTICE.txt
|
||||
test -s public/static/wllama-gemma3-v1/licenses/GEMMA_SOURCE.txt
|
||||
test -s public/static/wllama-gemma3-v1/licenses/WLLAMA_LICENSE.txt
|
||||
test -s public/static/wllama-gemma3-v1/licenses/LLAMA_CPP_LICENSE.txt
|
||||
test -s public/static/wllama-gemma3-v2/manifest.json
|
||||
test -s public/static/wllama-gemma3-v2/licenses/GEMMA_USER_TERMS.html
|
||||
test -s public/static/wllama-gemma3-v2/licenses/GEMMA_TERMS.html
|
||||
test -s public/static/wllama-gemma3-v2/licenses/GEMMA_PROHIBITED_USE_POLICY.html
|
||||
test -s public/static/wllama-gemma3-v2/licenses/GEMMA_NOTICE.txt
|
||||
test -s public/static/wllama-gemma3-v2/licenses/GEMMA_SOURCE.txt
|
||||
test -s public/static/wllama-gemma3-v2/licenses/WLLAMA_LICENSE.txt
|
||||
test -s public/static/wllama-gemma3-v2/licenses/LLAMA_CPP_LICENSE.txt
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
m = json.load(open("public/static/wllama-gemma3-v1/manifest.json", encoding="utf-8"))
|
||||
m = json.load(open("public/static/wllama-gemma3-v2/manifest.json", encoding="utf-8"))
|
||||
assert m["model"]["bytes"] == 806058240
|
||||
assert m["model"]["sha256"] == "8ccc5cd1f1b3602548715ae25a66ed73fd5dc68a210412eea643eb20eb75a135"
|
||||
assert len(m["license"]["acceptance_id"]) == 64
|
||||
required = ["user_terms", "terms", "prohibited_use_policy", "notice", "source", "runtime", "llama_cpp_runtime"]
|
||||
assert all(m["license"][key].startswith("/static/") for key in required)
|
||||
PY
|
||||
test -s public/static/wllama-gemma3-v1/browser-assistance.yaml
|
||||
test -s public/static/wllama-gemma3-v1/local-ai.js
|
||||
test -s public/static/wllama-gemma3-v1/wllama/esm/index.js
|
||||
test -s public/static/wllama-gemma3-v1/wllama/esm/wasm/wllama.wasm
|
||||
test "$(stat -c %s public/static/wllama-gemma3-v1/models/gemma-3-1b-it-Q4_K_M.gguf)" = 806058240
|
||||
test -s public/static/wllama-gemma3-v2/browser-assistance.yaml
|
||||
test -s public/static/wllama-gemma3-v2/local-ai.js
|
||||
test -s public/static/wllama-gemma3-v2/wllama/esm/index.js
|
||||
test -s public/static/wllama-gemma3-v2/wllama/esm/wasm/wllama.wasm
|
||||
test "$(stat -c %s public/static/wllama-gemma3-v2/models/gemma-3-1b-it-Q4_K_M.gguf)" = 806058240
|
||||
grep -q 'version.json' Dockerfile
|
||||
|
||||
- name: Ensure Helm is available
|
||||
@@ -174,21 +174,21 @@ jobs:
|
||||
/static/piper-wasm-v1/onnx/ort-wasm-simd-threaded.wasm \
|
||||
/static/piper-wasm-v1/piper/piper_phonemize.data \
|
||||
/static/piper-wasm-v1/voices/sv/sv_SE/nst/medium/sv_SE-nst-medium.onnx \
|
||||
/static/wllama-gemma3-v1/manifest.json \
|
||||
/static/wllama-gemma3-v1/browser-assistance.yaml \
|
||||
/static/wllama-gemma3-v1/local-ai.js \
|
||||
/static/wllama-gemma3-v1/wllama/esm/index.js \
|
||||
/static/wllama-gemma3-v1/wllama/esm/wasm/wllama.wasm \
|
||||
/static/wllama-gemma3-v1/models/gemma-3-1b-it-Q4_K_M.gguf; do
|
||||
/static/wllama-gemma3-v2/manifest.json \
|
||||
/static/wllama-gemma3-v2/browser-assistance.yaml \
|
||||
/static/wllama-gemma3-v2/local-ai.js \
|
||||
/static/wllama-gemma3-v2/wllama/esm/index.js \
|
||||
/static/wllama-gemma3-v2/wllama/esm/wasm/wllama.wasm \
|
||||
/static/wllama-gemma3-v2/models/gemma-3-1b-it-Q4_K_M.gguf; do
|
||||
curl -fsSI "https://ai-spel.k3s.k0.nu$path" >/dev/null
|
||||
done
|
||||
for path in \
|
||||
/static/wllama-gemma3-v1/licenses/GEMMA_TERMS.html \
|
||||
/static/wllama-gemma3-v1/licenses/GEMMA_USER_TERMS.html \
|
||||
/static/wllama-gemma3-v1/licenses/GEMMA_PROHIBITED_USE_POLICY.html \
|
||||
/static/wllama-gemma3-v1/licenses/GEMMA_NOTICE.txt \
|
||||
/static/wllama-gemma3-v1/licenses/GEMMA_SOURCE.txt \
|
||||
/static/wllama-gemma3-v1/licenses/WLLAMA_LICENSE.txt \
|
||||
/static/wllama-gemma3-v1/licenses/LLAMA_CPP_LICENSE.txt; do
|
||||
/static/wllama-gemma3-v2/licenses/GEMMA_TERMS.html \
|
||||
/static/wllama-gemma3-v2/licenses/GEMMA_USER_TERMS.html \
|
||||
/static/wllama-gemma3-v2/licenses/GEMMA_PROHIBITED_USE_POLICY.html \
|
||||
/static/wllama-gemma3-v2/licenses/GEMMA_NOTICE.txt \
|
||||
/static/wllama-gemma3-v2/licenses/GEMMA_SOURCE.txt \
|
||||
/static/wllama-gemma3-v2/licenses/WLLAMA_LICENSE.txt \
|
||||
/static/wllama-gemma3-v2/licenses/LLAMA_CPP_LICENSE.txt; do
|
||||
curl -fsSI "https://ai-spel.k3s.k0.nu$path" >/dev/null
|
||||
done
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
- Git release commit/tag, image, Helm chart, and manifest use the same version.
|
||||
- Use Harbor project `ai-spel`, Argo CD project `ai-spel`, and Kubernetes namespace `ai-spel`.
|
||||
- `/static/piper-wasm-v1/` is immutable and cached for one year. Incompatible asset updates require a new path and coordinated frontend change.
|
||||
- `/static/wllama-gemma3-v1/` serves the client adapter, pinned Wllama WebAssembly/runtime, the Gemma 3 1B Q4_K_M model, its manifest, and its YAML prompt. Browser runtime requests must remain same-origin under `/static/`; upstream URLs are allowed only in the static CI asset fetcher, never in client runtime code. Any changed adapter/model/runtime/prompt gets a new immutable path and coordinated frontend update.
|
||||
- `/static/wllama-gemma3-v2/` serves the client adapter, pinned Wllama WebAssembly/runtime, the Gemma 3 1B Q4_K_M model, its manifest, and its YAML prompt. Browser runtime requests must remain same-origin under `/static/`; upstream URLs are allowed only in the static CI asset fetcher, never in client runtime code. Any changed adapter/model/runtime/prompt gets a new immutable path and coordinated frontend update.
|
||||
- Gemma publication is blocked unless the static release includes the applicable Gemma Terms of Use copy, prohibited-use policy reference, required Gemma notice, model provenance, and third-party runtime/license notices. The frontend must obtain explicit user acceptance before model download/use; do not publish or enable Gemma until both the legal resources and the acceptance gate are implemented and verified in CI.
|
||||
- Publish `/static/version.json` with `version` generated from `VERSION` in the same image as the Helm appVersion; keep it uncached and verify its value after rollout. The frontend footer displays this release independently from frontend and API versions.
|
||||
- No secrets or runtime network access.
|
||||
|
||||
@@ -11,7 +11,7 @@ Assets under `/static/piper-wasm-v1/` are served with `Cache-Control: public, ma
|
||||
- `/static/piper-wasm-v1/voices/` — Alma, Lisa, and NST Swedish voices, plus attribution.
|
||||
- `/static/piper-wasm-v1/onnx/` — ONNX Runtime WASM and ES module loader.
|
||||
- `/static/piper-wasm-v1/piper/` — Piper phonemizer WASM and data.
|
||||
- `/static/wllama-gemma3-v1/` — local AI client adapter, integrity-pinned Wllama WebAssembly runtime, Gemma 3 1B Q4_K_M (806 MB), static manifest, and YAML assistance prompt. The client downloads these only from this server and caches the model on-device.
|
||||
- `/static/wllama-gemma3-v2/` — local AI client adapter, integrity-pinned Wllama WebAssembly runtime, Gemma 3 1B Q4_K_M (806 MB), static manifest, and YAML assistance prompt. The client downloads these only from this server and caches the model on-device.
|
||||
- Gemma distribution must also include the applicable Gemma Terms of Use copy, required notice, prohibited-use policy reference, source/conversion provenance, and all runtime/model license notices. The frontend must obtain explicit acceptance before download or use. Do not release the Gemma files until those resources and the client acceptance gate are implemented and checked by CI.
|
||||
- `/static/version.txt` — active deployment version, without long-lived caching.
|
||||
- `/static/version.json` — active deployment version as `{"version":"<semver>"}`, generated from the same `VERSION` value used by the image and Helm chart and checked after deployment.
|
||||
|
||||
+17
-2
@@ -1,6 +1,6 @@
|
||||
/* Browser-only, best-effort assistance. All executable/model/prompt assets come from our static service. */
|
||||
(function (global) {
|
||||
const base = '/static/wllama-gemma3-v1/';
|
||||
const base = '/static/wllama-gemma3-v2/';
|
||||
const dbName = 'ai-spel-local-assistance-v1';
|
||||
let enginePromise;
|
||||
let loadedEngine;
|
||||
@@ -161,12 +161,27 @@
|
||||
}
|
||||
async function assist(message, transcript, worldSnapshot) {
|
||||
if (!enabled || !loadedEngine || !await isAccepted()) return {};
|
||||
return boundedInfer('prepare_turn', {player_message: message.slice(0, 1200), recent_dialogue: transcript.slice(-12).map(line => line.slice(0, 240)), world_snapshot: JSON.stringify(worldSnapshot).slice(0, 1200)}, 5000);
|
||||
announce({type: 'activity', text: 'Lokal Gemma 3 körs på den här enheten för att förbereda handlingen.'});
|
||||
try {
|
||||
const result = await boundedInfer('prepare_turn', {player_message: message.slice(0, 1200), recent_dialogue: transcript.slice(-12).map(line => line.slice(0, 240)), world_snapshot: JSON.stringify(worldSnapshot).slice(0, 1200)}, 5000);
|
||||
announce({type: 'activity', text: 'Lokal Gemma 3 är klar. Originalhandlingen skickas nu vidare till berättartjänsten.'});
|
||||
return result;
|
||||
} catch (error) {
|
||||
announce({type: 'activity', text: 'Lokal Gemma 3 kunde inte förbereda handlingen. Originaltexten skickas ändå vidare till berättartjänsten.'});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async function offline(message, transcript, worldSnapshot) {
|
||||
if (!enabled || !loadedEngine || !await isAccepted()) return '';
|
||||
announce({type: 'activity', text: 'Lokal Gemma 3 arbetar offline med ett osparat hjälpförslag.'});
|
||||
try {
|
||||
const result = await boundedInfer('offline_help', {player_message: message.slice(0, 1200), recent_dialogue: transcript.slice(-12).map(line => line.slice(0, 240)), world_snapshot: JSON.stringify(worldSnapshot).slice(0, 1200)}, 10000);
|
||||
announce({type: 'activity', text: 'Lokal Gemma 3 har skapat ett osparat hjälpförslag.'});
|
||||
return String(result.offline_reply || 'Jag kan inte skapa ett lokalt förslag med den information som finns sparad.');
|
||||
} catch (error) {
|
||||
announce({type: 'activity', text: 'Lokal Gemma 3 kunde inte skapa ett offlineförslag.'});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async function settings(command) {
|
||||
const normalized = command.trim().toLocaleLowerCase('sv');
|
||||
|
||||
+3
-3
@@ -21,21 +21,21 @@ server {
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location = /static/wllama-gemma3-v1/manifest.json {
|
||||
location = /static/wllama-gemma3-v2/manifest.json {
|
||||
default_type application/json;
|
||||
add_header Cache-Control "no-cache" always;
|
||||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location = /static/wllama-gemma3-v1/local-ai.js {
|
||||
location = /static/wllama-gemma3-v2/local-ai.js {
|
||||
default_type application/javascript;
|
||||
add_header Cache-Control "no-cache" always;
|
||||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location ~ ^/static/wllama-gemma3-v1/licenses/ {
|
||||
location ~ ^/static/wllama-gemma3-v2/licenses/ {
|
||||
types {
|
||||
text/html html;
|
||||
text/plain txt;
|
||||
|
||||
@@ -10,7 +10,7 @@ const root = process.cwd();
|
||||
const output = join(root, 'public', 'static', assetPrefix);
|
||||
const onnxSource = join(root, 'node_modules', 'onnxruntime-web', 'dist');
|
||||
const piperSource = join(root, 'node_modules', '@diffusionstudio', 'piper-wasm', 'build');
|
||||
const localAiPrefix = 'wllama-gemma3-v1';
|
||||
const localAiPrefix = 'wllama-gemma3-v2';
|
||||
const wllamaUrl = 'https://registry.npmjs.org/@wllama/wllama/-/wllama-3.8.1.tgz';
|
||||
const wllamaSha512 = 'Pl7BEJhPU8OHnV/yhn3DFZYhLLOdSHh+4TND79WCftHnlUAdQPAsyc/CfIOY+4mVxvf5VW3Xw3Uf7BBfotS1gw==';
|
||||
const gemmaUrl = 'https://huggingface.co/ggml-org/gemma-3-1b-it-GGUF/resolve/f9c28bcd85737ffc5aef028638d3341d49869c27/gemma-3-1b-it-Q4_K_M.gguf';
|
||||
|
||||
@@ -12,14 +12,14 @@ grep -q 'application/wasm wasm;' nginx/default.conf
|
||||
grep -Fq 'try_files $uri =404;' nginx/default.conf
|
||||
grep -q 'version.json' Dockerfile
|
||||
grep -q 'location = /static/version.json' nginx/default.conf
|
||||
grep -q 'location = /static/wllama-gemma3-v1/manifest.json' nginx/default.conf
|
||||
grep -q 'location = /static/wllama-gemma3-v1/local-ai.js' nginx/default.conf
|
||||
grep -q 'location = /static/wllama-gemma3-v2/manifest.json' nginx/default.conf
|
||||
grep -q 'location = /static/wllama-gemma3-v2/local-ai.js' nginx/default.conf
|
||||
grep -q 'Cache-Control "no-cache"' nginx/default.conf
|
||||
grep -q 'text/html html;' nginx/default.conf
|
||||
grep -q 'application/octet-stream onnx data gguf;' nginx/default.conf
|
||||
grep -q "client', 'local-ai.js" scripts/build-assets.mjs
|
||||
grep -q 'wllama-gemma3-v1' scripts/build-assets.mjs
|
||||
grep -q 'test -s public/static/wllama-gemma3-v1/local-ai.js' .gitea/workflows/publish.yaml
|
||||
grep -q 'wllama-gemma3-v2' scripts/build-assets.mjs
|
||||
grep -q 'test -s public/static/wllama-gemma3-v2/local-ai.js' .gitea/workflows/publish.yaml
|
||||
grep -q 'lokal ai godkänn villkor' client/local-ai.js
|
||||
grep -q 'acceptance_id' scripts/build-assets.mjs
|
||||
grep -q 'GEMMA_TERMS.html' .gitea/workflows/publish.yaml
|
||||
|
||||
@@ -21,7 +21,7 @@ test('browser local AI prompt declares non-authoritative, bounded assistance tas
|
||||
|
||||
test('local AI runtime and model are published only under the owned static origin', async () => {
|
||||
const source = fs.readFileSync(path.join(__dirname, '..', 'scripts', 'build-assets.mjs'), 'utf8');
|
||||
assert.ok(source.includes('wllama-gemma3-v1'));
|
||||
assert.ok(source.includes('wllama-gemma3-v2'));
|
||||
assert.ok(source.includes('wllama-3.8.1.tgz'));
|
||||
assert.ok(source.includes('gemma-3-1b-it-Q4_K_M.gguf'));
|
||||
assert.ok(source.includes('manifest.json'));
|
||||
@@ -45,6 +45,6 @@ test('Gemma terms, prohibited-use policy, notice, provenance, and runtime licens
|
||||
assert.match(terms, /must include the use restrictions/);
|
||||
assert.doesNotMatch(terms, /<script|google-signin|google-analytics/);
|
||||
const nginx = fs.readFileSync(path.join(__dirname, '..', 'nginx', 'default.conf'), 'utf8');
|
||||
assert.match(nginx, /location = \/static\/wllama-gemma3-v1\/manifest\.json[\s\S]*?Cache-Control "no-cache"/);
|
||||
assert.match(nginx, /location = \/static\/wllama-gemma3-v1\/local-ai\.js[\s\S]*?Cache-Control "no-cache"/);
|
||||
assert.match(nginx, /location = \/static\/wllama-gemma3-v2\/manifest\.json[\s\S]*?Cache-Control "no-cache"/);
|
||||
assert.match(nginx, /location = \/static\/wllama-gemma3-v2\/local-ai\.js[\s\S]*?Cache-Control "no-cache"/);
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ const source = fs.readFileSync(path.join(__dirname, '..', 'client', 'local-ai.js
|
||||
test('local AI runtime rejects assets outside the same-origin static service', () => {
|
||||
assert.match(source, /parsed\.origin !== location\.origin/);
|
||||
assert.match(source, /parsed\.pathname\.startsWith\('\/static\/'\)/);
|
||||
assert.match(source, /\/static\/wllama-gemma3-v1\//);
|
||||
assert.match(source, /\/static\/wllama-gemma3-v2\//);
|
||||
assert.doesNotMatch(source, /huggingface\.co|registry\.npmjs\.org/);
|
||||
});
|
||||
|
||||
@@ -19,6 +19,13 @@ test('local assistance includes preflight, bounded continuity, and unsaved offli
|
||||
assert.match(source, /Jag kan inte skapa ett lokalt förslag/);
|
||||
});
|
||||
|
||||
test('local AI reports when on-device inference starts, finishes, or fails', () => {
|
||||
assert.match(source, /type: 'activity', text: 'Lokal Gemma 3 körs på den här enheten/);
|
||||
assert.match(source, /Lokal Gemma 3 är klar\. Originalhandlingen skickas nu vidare/);
|
||||
assert.match(source, /Lokal Gemma 3 kunde inte förbereda handlingen/);
|
||||
assert.match(source, /Lokal Gemma 3 arbetar offline med ett osparat hjälpförslag/);
|
||||
});
|
||||
|
||||
test('local diagnostics are opt-in, stored in IndexedDB, exportable, and deletable', () => {
|
||||
assert.match(source, /ai-spel-local-ai-logs/);
|
||||
assert.match(source, /indexedDB\.open/);
|
||||
|
||||
Reference in New Issue
Block a user