8 Commits

Author SHA1 Message Date
38367eac97 Terminaaliin latauksen tilaindikaattori (spinner + vaihe)
Mallin latauksen aikana terminaalissa näkyy animoitu spinner
ja nykyinen vaihe: WASM → tokenizer → malli (%) → rakennus → valmis.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:29:33 +03:00
20716186bc Hub: qwen-coder reititys tunnistaa kaikki coder-solmut (05b, 3b, 1.5b)
API etsi vain 'qwen-coder-05b' tai 'qwen-coder', ei 'qwen-coder-3b'.
Nyt task.starts_with('qwen-coder') matchaa kaikki variantit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:27:49 +03:00
4e810ed4a2 Kaikki agentit käyttävät qwen-coder -mallia + valmis-viesti deduplikoitu
QA ja DevOps käyttivät smollm-135m:ää jota ei ole selaimessa ladattuna.
Nyt kaikki agentit käyttävät ladattua qwen-coder-mallia.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:23:59 +03:00
91ff9e00f9 kvantisointia 2026-04-06 16:15:56 +03:00
e652bf7ab6 1.5B Q4_K_M: vaihdettu 3B→1.5B koska 3B ei mahdu WASM:iin (~1 GB vs ~2 GB)
3B GGUF vaati ~5 GB muistia parsinnassa → SIGILL WASM:n 4 GB rajalla.
1.5B Q4_K_M on ~1 GB ja mahtuu turvallisesti selaimeen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:14:41 +03:00
eb69893124 WASM release-build: GGUF dequantize vaatii optimointeja
Debug-moodi aiheutti SIGILL (Illegal Instruction) GGUF-tensorien
dequantisoinnissa. Release-build ratkaisee ongelman.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:07:02 +03:00
d18314bfc8 GGUF Q4_K_M -tuki 3B-mallille: kvantisoidtu versio (~1.9 GB) mahtuu selaimeen
Safetensors-muotoinen 3B (~6.2 GB) aiheutti WASM capacity overflow.
Nyt käytetään candle quantized_qwen2 -moduulia GGUF-tiedoston lataamiseen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 13:54:23 +03:00
99b011e399 Isomman qwen-mallin lataus 2026-04-06 13:40:19 +03:00
6 changed files with 591 additions and 42 deletions

475
docker-errors.log Normal file
View File

@@ -0,0 +1,475 @@
[INFO]: Checking for the Wasm target...
info: downloading component rust-std
[INFO]: Compiling to Wasm...
Compiling node v0.1.0 (/app/node)
warning: unused imports: `DType`, `Device`, and `Tensor`
--> node/src/smollm.rs:1:19
|
1 | use candle_core::{Device, Tensor, DType};
| ^^^^^^ ^^^^^^ ^^^^^
|
= note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default
warning: unused import: `candle_nn::VarBuilder`
--> node/src/smollm.rs:2:5
|
2 | use candle_nn::VarBuilder;
| ^^^^^^^^^^^^^^^^^^^^^
warning: unused imports: `Cache`, `LlamaConfig`, `LlamaEosToks`, and `Llama`
--> node/src/smollm.rs:3:42
|
3 | use candle_transformers::models::llama::{Llama, LlamaConfig, LlamaEosToks, Cache};
| ^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^
warning: unused imports: `DType`, `Device`, and `Tensor`
--> node/src/phi3.rs:1:19
|
1 | use candle_core::{Device, Tensor, DType};
| ^^^^^^ ^^^^^^ ^^^^^
warning: unused import: `candle_nn::VarBuilder`
--> node/src/phi3.rs:2:5
|
2 | use candle_nn::VarBuilder;
| ^^^^^^^^^^^^^^^^^^^^^
warning: unused imports: `Config as Phi3Config` and `Model as Phi3Model`
--> node/src/phi3.rs:3:41
|
3 | use candle_transformers::models::phi3::{Config as Phi3Config, Model as Phi3Model};
| ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
warning: unused import: `wasm_bindgen::JsCast`
--> node/src/phi3.rs:4:5
|
4 | use wasm_bindgen::JsCast;
| ^^^^^^^^^^^^^^^^^^^^
warning: unused import: `crate::storage`
--> node/src/phi3.rs:9:5
|
9 | use crate::storage;
| ^^^^^^^^^^^^^^
warning: unused import: `Int`
--> node/src/burn_smollm/attention.rs:2:46
|
2 | use burn::tensor::{backend::Backend, Tensor, Int};
| ^^^
warning: unused imports: `Mlp` and `RmsNorm`
--> node/src/burn_smollm/attention.rs:4:22
|
4 | use super::modules::{RmsNorm, Mlp};
| ^^^^^^^ ^^^
warning: use of deprecated struct `burn::tensor::Data`: the internal data format has changed, please use `TensorData` instead
--> node/src/smollm.rs:174:23
|
174 | burn::tensor::Data::new(input_ids.iter().map(|&x| x as i32).collect::<Vec<_>>(), [input_len].into()),
| ^^^^
|
= note: `#[warn(deprecated)]` on by default
warning: use of deprecated struct `burn::tensor::Data`: the internal data format has changed, please use `TensorData` instead
--> node/src/smollm.rs:200:27
|
200 | burn::tensor::Data::new(vec![next_token as i32], [1].into()),
| ^^^^
warning: use of deprecated struct `burn::tensor::Data`: the internal data format has changed, please use `TensorData` instead
--> node/src/burn_smollm/loader.rs:1:46
|
1 | use burn::tensor::{backend::Backend, Tensor, Data};
| ^^^^
warning: use of deprecated struct `burn::tensor::Data`: the internal data format has changed, please use `TensorData` instead
--> node/src/burn_smollm/loader.rs:17:16
|
17 | let data = Data::new(vec, shape_out_in.into());
| ^^^^
warning: use of deprecated struct `burn::tensor::Data`: the internal data format has changed, please use `TensorData` instead
--> node/src/burn_smollm/loader.rs:32:16
|
32 | let data = Data::new(vec, shape.into());
| ^^^^
warning: use of deprecated struct `burn::tensor::Data`: the internal data format has changed, please use `TensorData` instead
--> node/src/burn_smollm/loader.rs:45:16
|
45 | let data = Data::new(vec, shape.into());
| ^^^^
error[E0061]: this function takes 2 arguments but 1 argument was supplied
--> node/src/smollm.rs:124:9
|
124 | burn_wgpu::init_async::<burn_wgpu::AutoGraphicsApi>(&Default::default()).await;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--------------------- argument #2 of type `RuntimeOptions` is missing
|
note: function defined here
--> /usr/local/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cubecl-wgpu-0.2.0/src/runtime.rs:116:14
|
116 | pub async fn init_async<G: GraphicsApi>(device: &WgpuDevice, options: RuntimeOptions) {
| ^^^^^^^^^^
help: provide the argument
|
124 | burn_wgpu::init_async::<burn_wgpu::AutoGraphicsApi>(&Default::default(), /* RuntimeOptions */).await;
| ++++++++++++++++++++++
error[E0277]: the trait bound `TensorData: From<burn::tensor::Data<i32, 1>>` is not satisfied
--> node/src/smollm.rs:174:9
|
173 | let mut input_tensor = burn::tensor::Tensor::<B, 1, burn::tensor::Int>::from_data(
| ---------------------------------------------------------- required by a bound introduced by this call
174 | burn::tensor::Data::new(input_ids.iter().map(|&x| x as i32).collect::<Vec<_>>(), [input_len].into()),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<burn::tensor::Data<i32, 1>>` is not implemented for `TensorData`
|
= help: the following other types implement trait `From<T>`:
`TensorData` implements `From<&[E]>`
`TensorData` implements `From<&[usize]>`
`TensorData` implements `From<[E; A]>`
`TensorData` implements `From<[[E; B]; A]>`
`TensorData` implements `From<[[[E; C]; B]; A]>`
`TensorData` implements `From<[[[[E; D]; C]; B]; A]>`
`TensorData` implements `From<[[[[[Elem; E]; D]; C]; B]; A]>`
`TensorData` implements `From<[usize; A]>`
= note: required for `burn::tensor::Data<i32, 1>` to implement `Into<TensorData>`
note: required by a bound in `burn::tensor::Tensor::<B, D, K>::from_data`
--> /usr/local/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.14.0/src/tensor/api/base.rs:719:12
|
717 | pub fn from_data<T>(data: T, device: &B::Device) -> Self
| --------- required by a bound in this associated function
718 | where
719 | T: Into<TensorData>,
| ^^^^^^^^^^^^^^^^ required by this bound in `Tensor::<B, D, K>::from_data`
error[E0061]: this method takes 2 arguments but 0 arguments were supplied
--> node/src/smollm.rs:183:51
|
183 | let next_token_tensor = last_logits.argmax(2).flatten::<1>();
| ^^^^^^^^^^^^-- two arguments of type `usize` and `usize` are missing
|
note: method defined here
--> /usr/local/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.14.0/src/tensor/api/base.rs:292:12
|
292 | pub fn flatten<const D2: usize>(self, start_dim: usize, end_dim: usize) -> Tensor<B, D2, K> {
| ^^^^^^^
help: provide the arguments
|
183 | let next_token_tensor = last_logits.argmax(2).flatten::<1>(/* usize */, /* usize */);
| ++++++++++++++++++++++++
error[E0277]: the trait bound `TensorData: From<burn::tensor::Data<i32, 1>>` is not satisfied
--> node/src/smollm.rs:200:13
|
199 | let mut input_tensor = burn::tensor::Tensor::<B, 1, burn::tensor::Int>::from_data(
| ---------------------------------------------------------- required by a bound introduced by this call
200 | burn::tensor::Data::new(vec![next_token as i32], [1].into()),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<burn::tensor::Data<i32, 1>>` is not implemented for `TensorData`
|
= help: the following other types implement trait `From<T>`:
`TensorData` implements `From<&[E]>`
`TensorData` implements `From<&[usize]>`
`TensorData` implements `From<[E; A]>`
`TensorData` implements `From<[[E; B]; A]>`
`TensorData` implements `From<[[[E; C]; B]; A]>`
`TensorData` implements `From<[[[[E; D]; C]; B]; A]>`
`TensorData` implements `From<[[[[[Elem; E]; D]; C]; B]; A]>`
`TensorData` implements `From<[usize; A]>`
= note: required for `burn::tensor::Data<i32, 1>` to implement `Into<TensorData>`
note: required by a bound in `burn::tensor::Tensor::<B, D, K>::from_data`
--> /usr/local/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.14.0/src/tensor/api/base.rs:719:12
|
717 | pub fn from_data<T>(data: T, device: &B::Device) -> Self
| --------- required by a bound in this associated function
718 | where
719 | T: Into<TensorData>,
| ^^^^^^^^^^^^^^^^ required by this bound in `Tensor::<B, D, K>::from_data`
error[E0061]: this method takes 2 arguments but 0 arguments were supplied
--> node/src/smollm.rs:207:50
|
207 | let next_token_tensor = logits.argmax(2).flatten::<1>();
| ^^^^^^^^^^^^-- two arguments of type `usize` and `usize` are missing
|
note: method defined here
--> /usr/local/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.14.0/src/tensor/api/base.rs:292:12
|
292 | pub fn flatten<const D2: usize>(self, start_dim: usize, end_dim: usize) -> Tensor<B, D2, K> {
| ^^^^^^^
help: provide the arguments
|
207 | let next_token_tensor = logits.argmax(2).flatten::<1>(/* usize */, /* usize */);
| ++++++++++++++++++++++++
error[E0308]: mismatched types
--> node/src/burn_smollm/attention.rs:58:13
|
58 | q = q.reshape([batch, seq_len, self.num_heads, self.head_dim]).swap_dims(1, 2);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `3`, found `4`
|
= note: expected struct `burn::tensor::Tensor<_, 3>`
found struct `burn::tensor::Tensor<_, 4>`
error[E0308]: mismatched types
--> node/src/burn_smollm/attention.rs:59:13
|
59 | k = k.reshape([batch, seq_len, self.num_kv_heads, self.head_dim]).swap_dims(1, 2);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `3`, found `4`
|
= note: expected struct `burn::tensor::Tensor<_, 3>`
found struct `burn::tensor::Tensor<_, 4>`
error[E0308]: mismatched types
--> node/src/burn_smollm/attention.rs:60:13
|
60 | v = v.reshape([batch, seq_len, self.num_kv_heads, self.head_dim]).swap_dims(1, 2);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `3`, found `4`
|
= note: expected struct `burn::tensor::Tensor<_, 3>`
found struct `burn::tensor::Tensor<_, 4>`
error[E0308]: mismatched types
--> node/src/burn_smollm/attention.rs:63:31
|
63 | q = self.rope.forward(q, offset);
| ------- ^ expected `4`, found `3`
| |
| arguments to this method are incorrect
|
= note: expected struct `burn::tensor::Tensor<_, 4>`
found struct `burn::tensor::Tensor<_, 3>`
note: method defined here
--> node/src/burn_smollm/rope.rs:35:12
|
35 | pub fn forward(&self, x: Tensor<B, 4>, offset: usize) -> Tensor<B, 4> {
| ^^^^^^^ ---------------
error[E0308]: mismatched types
--> node/src/burn_smollm/attention.rs:63:13
|
63 | q = self.rope.forward(q, offset);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `3`, found `4`
|
= note: expected struct `burn::tensor::Tensor<_, 3>`
found struct `burn::tensor::Tensor<_, 4>`
error[E0308]: mismatched types
--> node/src/burn_smollm/attention.rs:64:31
|
64 | k = self.rope.forward(k, offset);
| ------- ^ expected `4`, found `3`
| |
| arguments to this method are incorrect
|
= note: expected struct `burn::tensor::Tensor<_, 4>`
found struct `burn::tensor::Tensor<_, 3>`
note: method defined here
--> node/src/burn_smollm/rope.rs:35:12
|
35 | pub fn forward(&self, x: Tensor<B, 4>, offset: usize) -> Tensor<B, 4> {
| ^^^^^^^ ---------------
error[E0308]: mismatched types
--> node/src/burn_smollm/attention.rs:64:13
|
64 | k = self.rope.forward(k, offset);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `3`, found `4`
|
= note: expected struct `burn::tensor::Tensor<_, 3>`
found struct `burn::tensor::Tensor<_, 4>`
error[E0308]: mismatched types
--> node/src/burn_smollm/attention.rs:68:41
|
68 | c.k = Tensor::cat(vec![c.k, k], 2);
| ^ expected `4`, found `3`
|
= note: expected struct `burn::tensor::Tensor<_, 4>`
found struct `burn::tensor::Tensor<_, 3>`
error[E0308]: mismatched types
--> node/src/burn_smollm/attention.rs:69:41
|
69 | c.v = Tensor::cat(vec![c.v, v], 2);
| ^ expected `4`, found `3`
|
= note: expected struct `burn::tensor::Tensor<_, 4>`
found struct `burn::tensor::Tensor<_, 3>`
error[E0308]: `if` and `else` have incompatible types
--> node/src/burn_smollm/attention.rs:72:13
|
67 | let (k, v) = if let Some(mut c) = cache {
| ______________________-
68 | | c.k = Tensor::cat(vec![c.k, k], 2);
69 | | c.v = Tensor::cat(vec![c.v, v], 2);
70 | | (c.k.clone(), c.v.clone())
| | -------------------------- expected because of this
71 | | } else {
72 | | (k.clone(), v.clone())
| | ^^^^^^^^^^^^^^^^^^^^^^ expected `4`, found `3`
73 | | };
| |_________- `if` and `else` have incompatible types
|
= note: expected tuple `(burn::tensor::Tensor<_, 4>, burn::tensor::Tensor<_, 4>)`
found tuple `(burn::tensor::Tensor<_, 3>, burn::tensor::Tensor<_, 3>)`
error[E0282]: type annotations needed
--> node/src/burn_smollm/attention.rs:75:38
|
75 | let new_cache = KVCache { k: k.clone(), v: v.clone() };
| ^ cannot infer type
error[E0282]: type annotations needed
--> node/src/burn_smollm/attention.rs:75:52
|
75 | let new_cache = KVCache { k: k.clone(), v: v.clone() };
| ^ cannot infer type
error[E0277]: the trait bound `TensorData: From<burn::tensor::Data<f32, 2>>` is not satisfied
--> node/src/burn_smollm/loader.rs:18:44
|
18 | let t_burn = Tensor::<B, 2>::from_data(data, device);
| ------------------------- ^^^^ the trait `From<burn::tensor::Data<f32, 2>>` is not implemented for `TensorData`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `From<T>`:
`TensorData` implements `From<&[E]>`
`TensorData` implements `From<&[usize]>`
`TensorData` implements `From<[E; A]>`
`TensorData` implements `From<[[E; B]; A]>`
`TensorData` implements `From<[[[E; C]; B]; A]>`
`TensorData` implements `From<[[[[E; D]; C]; B]; A]>`
`TensorData` implements `From<[[[[[Elem; E]; D]; C]; B]; A]>`
`TensorData` implements `From<[usize; A]>`
= note: required for `burn::tensor::Data<f32, 2>` to implement `Into<TensorData>`
note: required by a bound in `burn::tensor::Tensor::<B, D, K>::from_data`
--> /usr/local/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.14.0/src/tensor/api/base.rs:719:12
|
717 | pub fn from_data<T>(data: T, device: &B::Device) -> Self
| --------- required by a bound in this associated function
718 | where
719 | T: Into<TensorData>,
| ^^^^^^^^^^^^^^^^ required by this bound in `Tensor::<B, D, K>::from_data`
error[E0277]: the trait bound `TensorData: From<burn::tensor::Data<f32, 1>>` is not satisfied
--> node/src/burn_smollm/loader.rs:33:53
|
33 | Ok(Param::from_tensor(Tensor::<B, 1>::from_data(data, device)))
| ------------------------- ^^^^ the trait `From<burn::tensor::Data<f32, 1>>` is not implemented for `TensorData`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `From<T>`:
`TensorData` implements `From<&[E]>`
`TensorData` implements `From<&[usize]>`
`TensorData` implements `From<[E; A]>`
`TensorData` implements `From<[[E; B]; A]>`
`TensorData` implements `From<[[[E; C]; B]; A]>`
`TensorData` implements `From<[[[[E; D]; C]; B]; A]>`
`TensorData` implements `From<[[[[[Elem; E]; D]; C]; B]; A]>`
`TensorData` implements `From<[usize; A]>`
= note: required for `burn::tensor::Data<f32, 1>` to implement `Into<TensorData>`
note: required by a bound in `burn::tensor::Tensor::<B, D, K>::from_data`
--> /usr/local/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.14.0/src/tensor/api/base.rs:719:12
|
717 | pub fn from_data<T>(data: T, device: &B::Device) -> Self
| --------- required by a bound in this associated function
718 | where
719 | T: Into<TensorData>,
| ^^^^^^^^^^^^^^^^ required by this bound in `Tensor::<B, D, K>::from_data`
error[E0277]: the trait bound `TensorData: From<burn::tensor::Data<f32, 2>>` is not satisfied
--> node/src/burn_smollm/loader.rs:47:53
|
47 | Ok(Param::from_tensor(Tensor::<B, 2>::from_data(data, device)))
| ------------------------- ^^^^ the trait `From<burn::tensor::Data<f32, 2>>` is not implemented for `TensorData`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `From<T>`:
`TensorData` implements `From<&[E]>`
`TensorData` implements `From<&[usize]>`
`TensorData` implements `From<[E; A]>`
`TensorData` implements `From<[[E; B]; A]>`
`TensorData` implements `From<[[[E; C]; B]; A]>`
`TensorData` implements `From<[[[[E; D]; C]; B]; A]>`
`TensorData` implements `From<[[[[[Elem; E]; D]; C]; B]; A]>`
`TensorData` implements `From<[usize; A]>`
= note: required for `burn::tensor::Data<f32, 2>` to implement `Into<TensorData>`
note: required by a bound in `burn::tensor::Tensor::<B, D, K>::from_data`
--> /usr/local/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.14.0/src/tensor/api/base.rs:719:12
|
717 | pub fn from_data<T>(data: T, device: &B::Device) -> Self
| --------- required by a bound in this associated function
718 | where
719 | T: Into<TensorData>,
| ^^^^^^^^^^^^^^^^ required by this bound in `Tensor::<B, D, K>::from_data`
error[E0599]: no function or associated item named `arange` found for struct `burn::tensor::Tensor<B, 1>` in the current scope
--> node/src/burn_smollm/rope.rs:19:33
|
19 | let t = Tensor::<B, 1>::arange(0..max_seq_len as i64, device).float().unsqueeze::<2>().transpose();
| ^^^^^^ function or associated item not found in `burn::tensor::Tensor<B, 1>`
|
note: if you're trying to build a new `burn::tensor::Tensor<B, 1>` consider using one of the following associated functions:
burn::tensor::Tensor::<B, D, K>::new
burn::tensor::Tensor::<B, D, K>::from_primitive
burn::tensor::Tensor::<B, D, K>::empty
burn::tensor::Tensor::<B, D, K>::from_data
and 9 others
--> /usr/local/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.14.0/src/tensor/api/base.rs:24:10
|
24 | #[derive(new, Clone, Debug)]
| ^^^
...
55 | pub fn from_primitive(tensor: K::Primitive<D>) -> Self {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
60 | pub fn empty<S: Into<Shape<D>>>(shape: S, device: &B::Device) -> Self {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
717 | / pub fn from_data<T>(data: T, device: &B::Device) -> Self
718 | | where
719 | | T: Into<TensorData>,
| |____________________________^
= note: the function or associated item was found for
- `burn::tensor::Tensor<B, 1, burn::tensor::Int>`
= note: this error originates in the derive macro `new` (in Nightly builds, run with -Z macro-backtrace for more info)
warning: variable does not need to be mutable
--> node/src/burn_smollm/loader.rs:70:13
|
70 | let mut layer = &mut model.layers[i];
| ----^^^^^
| |
| help: remove this `mut`
|
= note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default
warning: unused variable: `batch`
--> node/src/burn_smollm/model.rs:79:14
|
79 | let [batch, seq_len] = input_ids.dims();
| ^^^^^ help: if this is intentional, prefix it with an underscore: `_batch`
|
= note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
warning: unused variable: `seq_len`
--> node/src/burn_smollm/model.rs:79:21
|
79 | let [batch, seq_len] = input_ids.dims();
| ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_seq_len`
Some errors have detailed explanations: E0061, E0277, E0282, E0308, E0599.
For more information about an error, try `rustc --explain E0061`.
warning: `node` (lib) generated 19 warnings
error: could not compile `node` (lib) due to 21 previous errors; 19 warnings emitted
Error: Compiling your crate to WebAssembly failed
Caused by: Compiling your crate to WebAssembly failed
Caused by: failed to execute `cargo build`: exited with exit status: 101
full command: cd "/app/node" && "cargo" "build" "--lib" "--release" "--target" "wasm32-unknown-unknown"

View File

@@ -9,7 +9,7 @@ services:
volumes: volumes:
- .:/app - .:/app
# Käännetään aina käynnistyksen yhteydessä varmuuden vuoksi Wasm uusimmista koodeista, ja päälle pyöräytetään Hub! # Käännetään aina käynnistyksen yhteydessä varmuuden vuoksi Wasm uusimmista koodeista, ja päälle pyöräytetään Hub!
command: bash -c "cd node && wasm-pack build --target web --out-dir ../static/pkg && cd ../hub && cargo run" command: bash -c "cd node && wasm-pack build --release --target web --out-dir ../static/pkg && cd ../hub && cargo run"
# Valinnainen natiivi-solmu — kerää oikeat laitteistotiedot (nvidia-smi-taso) # Valinnainen natiivi-solmu — kerää oikeat laitteistotiedot (nvidia-smi-taso)
native-node: native-node:

Binary file not shown.

View File

@@ -978,7 +978,7 @@ async fn api_chat_completions(
let busy = state.node_busy.lock().unwrap(); let busy = state.node_busy.lock().unwrap();
let matching: Vec<u64> = tasks.iter().filter(|(_, task)| { let matching: Vec<u64> = tasks.iter().filter(|(_, task)| {
if payload.model == "qwen-coder" { if payload.model == "qwen-coder" {
*task == "qwen-coder-05b" || *task == "qwen-coder" task.starts_with("qwen-coder")
} else { } else {
**task == payload.model **task == payload.model
} }

View File

@@ -1,6 +1,8 @@
use candle_core::{Device, Tensor, DType}; use candle_core::{Device, Tensor, DType};
use candle_core::quantized::gguf_file;
use candle_nn::VarBuilder; use candle_nn::VarBuilder;
use candle_transformers::models::qwen2::{Config as QwenConfig, ModelForCausalLM as QwenModel}; use candle_transformers::models::qwen2::{Config as QwenConfig, ModelForCausalLM as QwenModel};
use candle_transformers::models::quantized_qwen2::ModelWeights as QwenQuantizedModel;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
@@ -16,13 +18,36 @@ macro_rules! console_log {
const MODEL_05B_URL: &str = "https://huggingface.co/Qwen/Qwen2.5-Coder-0.5B-Instruct/resolve/main/model.safetensors"; const MODEL_05B_URL: &str = "https://huggingface.co/Qwen/Qwen2.5-Coder-0.5B-Instruct/resolve/main/model.safetensors";
const TOKENIZER_05B_URL: &str = "https://huggingface.co/Qwen/Qwen2.5-Coder-0.5B-Instruct/resolve/main/tokenizer.json"; const TOKENIZER_05B_URL: &str = "https://huggingface.co/Qwen/Qwen2.5-Coder-0.5B-Instruct/resolve/main/tokenizer.json";
// 3B — parempi laatu, vaatii enemmän muistia (~6 GB lataus, ~12 GB RAM) // 1.5B GGUF Q4_K_M — kvantisoidtu, mahtuu selaimeen (~1 GB)
const MODEL_3B_PART1_URL: &str = "https://huggingface.co/Qwen/Qwen2.5-Coder-3B-Instruct/resolve/main/model-00001-of-00002.safetensors"; const MODEL_GGUF_URL: &str = "https://huggingface.co/Qwen/Qwen2.5-Coder-1.5B-Instruct-GGUF/resolve/main/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf";
const MODEL_3B_PART2_URL: &str = "https://huggingface.co/Qwen/Qwen2.5-Coder-3B-Instruct/resolve/main/model-00002-of-00002.safetensors"; const TOKENIZER_GGUF_URL: &str = "https://huggingface.co/Qwen/Qwen2.5-Coder-1.5B-Instruct/resolve/main/tokenizer.json";
const TOKENIZER_3B_URL: &str = "https://huggingface.co/Qwen/Qwen2.5-Coder-3B-Instruct/resolve/main/tokenizer.json";
enum CoderModel {
Full(QwenModel),
Quantized(QwenQuantizedModel),
}
impl CoderModel {
fn forward(&mut self, x: &Tensor, pos: usize) -> candle_core::Result<Tensor> {
match self {
CoderModel::Full(m) => m.forward(x, pos),
CoderModel::Quantized(m) => m.forward(x, pos),
}
}
fn clear_kv_cache(&mut self) {
match self {
CoderModel::Full(m) => m.clear_kv_cache(),
CoderModel::Quantized(_) => {
// Quantized model nollaa KV-cachen automaattisesti kun forward kutsutaan pos=0:lla
// (ks. quantized_qwen2.rs rivi 118: if index_pos == 0)
}
}
}
}
struct CachedModel { struct CachedModel {
model: QwenModel, model: CoderModel,
tokenizer: tokenizers::Tokenizer, tokenizer: tokenizers::Tokenizer,
is_3b: bool, is_3b: bool,
} }
@@ -182,50 +207,39 @@ async fn get_or_build_model(use_3b: bool, ws: &Rc<RefCell<WebSocket>>) -> Result
let dtype = DType::F32; let dtype = DType::F32;
// Tokenizer // Tokenizer
let tok_url = if use_3b { TOKENIZER_3B_URL } else { TOKENIZER_05B_URL }; let tok_url = if use_3b { TOKENIZER_GGUF_URL } else { TOKENIZER_05B_URL };
let tok_key = if use_3b { "coder3b-tokenizer.json" } else { "coder05b-tokenizer.json" }; let tok_key = if use_3b { "coder15b-tokenizer.json" } else { "coder05b-tokenizer.json" };
let tok_bytes = ensure_cached(tok_key, tok_url, ws).await?; let tok_bytes = ensure_cached(tok_key, tok_url, ws).await?;
let tokenizer = tokenizers::Tokenizer::from_bytes(&tok_bytes[..]) let tokenizer = tokenizers::Tokenizer::from_bytes(&tok_bytes[..])
.map_err(|e| format!("Tokenizer: {}", e))?; .map_err(|e| format!("Tokenizer: {}", e))?;
// Painot // Painot
let tensors = if use_3b { let model = if use_3b {
let part1 = ensure_cached("coder3b-model-part1.safetensors", MODEL_3B_PART1_URL, ws).await?; // GGUF Q4_K_M — kvantisoidtu 3B-malli (~1.9 GB)
let part2 = ensure_cached("coder3b-model-part2.safetensors", MODEL_3B_PART2_URL, ws).await?; let gguf_bytes = ensure_cached("coder15b-q4km.gguf", MODEL_GGUF_URL, ws).await?;
console_log!("[Coder] Rakennetaan 3B-mallia..."); console_log!("[Coder] Rakennetaan kvantisoidun 1.5B-mallia (Q4_K_M)...");
let mut all_tensors = candle_core::safetensors::load_buffer(&part1[..], &device) let mut cursor = std::io::Cursor::new(&gguf_bytes[..]);
.map_err(|e| format!("Part1: {}", e))?; let content = gguf_file::Content::read(&mut cursor)
let tensors2 = candle_core::safetensors::load_buffer(&part2[..], &device) .map_err(|e| format!("GGUF parse: {}", e))?;
.map_err(|e| format!("Part2: {}", e))?; let qmodel = QwenQuantizedModel::from_gguf(content, &mut cursor, &device)
all_tensors.extend(tensors2); .map_err(|e| format!("GGUF model: {}", e))?;
all_tensors CoderModel::Quantized(qmodel)
} else { } else {
let model_bytes = ensure_cached("coder05b-model.safetensors", MODEL_05B_URL, ws).await?; let model_bytes = ensure_cached("coder05b-model.safetensors", MODEL_05B_URL, ws).await?;
console_log!("[Coder] Rakennetaan 0.5B-mallia..."); console_log!("[Coder] Rakennetaan 0.5B-mallia...");
candle_core::safetensors::load_buffer(&model_bytes[..], &device) let tensors = candle_core::safetensors::load_buffer(&model_bytes[..], &device)
.map_err(|e| format!("Safetensors: {}", e))? .map_err(|e| format!("Safetensors: {}", e))?;
}; let config = QwenConfig {
let vb = VarBuilder::from_tensors(tensors, dtype, &device);
let config = if use_3b {
QwenConfig {
vocab_size: 151936, hidden_size: 2048, intermediate_size: 11008,
num_hidden_layers: 36, num_attention_heads: 16, num_key_value_heads: 2,
max_position_embeddings: 32768, sliding_window: 32768, max_window_layers: 36,
tie_word_embeddings: true, rope_theta: 1000000.0, rms_norm_eps: 1e-6,
use_sliding_window: false, hidden_act: candle_nn::Activation::Silu,
}
} else {
QwenConfig {
vocab_size: 151936, hidden_size: 896, intermediate_size: 4864, vocab_size: 151936, hidden_size: 896, intermediate_size: 4864,
num_hidden_layers: 24, num_attention_heads: 14, num_key_value_heads: 2, num_hidden_layers: 24, num_attention_heads: 14, num_key_value_heads: 2,
max_position_embeddings: 32768, sliding_window: 32768, max_window_layers: 21, max_position_embeddings: 32768, sliding_window: 32768, max_window_layers: 21,
tie_word_embeddings: true, rope_theta: 1000000.0, rms_norm_eps: 1e-6, tie_word_embeddings: true, rope_theta: 1000000.0, rms_norm_eps: 1e-6,
use_sliding_window: false, hidden_act: candle_nn::Activation::Silu, use_sliding_window: false, hidden_act: candle_nn::Activation::Silu,
} };
let vb = VarBuilder::from_tensors(tensors, dtype, &device);
let qwen = QwenModel::new(&config, vb).map_err(|e| format!("Malli: {}", e))?;
CoderModel::Full(qwen)
}; };
let model = QwenModel::new(&config, vb).map_err(|e| format!("Malli: {}", e))?;
console_log!("[Coder] Malli ladattu ja välimuistitettu"); console_log!("[Coder] Malli ladattu ja välimuistitettu");
MODEL_CACHE.with(|c| { MODEL_CACHE.with(|c| {

View File

@@ -1156,8 +1156,8 @@
manager: { name: 'Manageri — System Prompt', model: 'qwen-coder', default: 'Olet projektipäällikkö. Jaa tehtävät osiin, priorisoi ja koordinoi tiimin työtä.' }, manager: { name: 'Manageri — System Prompt', model: 'qwen-coder', default: 'Olet projektipäällikkö. Jaa tehtävät osiin, priorisoi ja koordinoi tiimin työtä.' },
coder: { name: 'Koodari — System Prompt', model: 'qwen-coder', default: 'Olet kokenut ohjelmistokehittäjä. Kirjoita selkeää, testattavaa koodia ja vastaa aina koodilla.' }, coder: { name: 'Koodari — System Prompt', model: 'qwen-coder', default: 'Olet kokenut ohjelmistokehittäjä. Kirjoita selkeää, testattavaa koodia ja vastaa aina koodilla.' },
data: { name: 'Data-Agentti — System Prompt', model: 'qwen-coder', default: 'Olet tietokanta-asiantuntija. Vastaat skeemojen suunnittelusta, SQL-kyselyiden optimoinnista ja datamalleista.' }, data: { name: 'Data-Agentti — System Prompt', model: 'qwen-coder', default: 'Olet tietokanta-asiantuntija. Vastaat skeemojen suunnittelusta, SQL-kyselyiden optimoinnista ja datamalleista.' },
qa: { name: 'QA — System Prompt', model: 'smollm-135m', default: 'Olet laadunvarmistaja (QA). Kirjoitat testejä, etsit virheitä ja varmistat, että kaikki reunatapaukset on huomioitu.' }, qa: { name: 'QA — System Prompt', model: 'qwen-coder', default: 'Olet laadunvarmistaja (QA). Kirjoitat testejä, etsit virheitä ja varmistat, että kaikki reunatapaukset on huomioitu.' },
tester: { name: 'DevOps — System Prompt', model: 'smollm-135m', default: 'Olet DevOps-insinööri. Vastaat koodin julkaisuputkista, serveri-infrastruktuurista ja ympäristön suorituskyvystä.' }, tester: { name: 'DevOps — System Prompt', model: 'qwen-coder', default: 'Olet DevOps-insinööri. Vastaat koodin julkaisuputkista, serveri-infrastruktuurista ja ympäristön suorituskyvystä.' },
}; };
const selectedAgents = new Set(); const selectedAgents = new Set();
let sharedPrompt = localStorage.getItem('kpn-shared-prompt') || ''; let sharedPrompt = localStorage.getItem('kpn-shared-prompt') || '';
@@ -2229,7 +2229,7 @@ Write the corrected code.`;
// Mallikatalogista valinta numerolla tai nimellä // Mallikatalogista valinta numerolla tai nimellä
const loadModels = [ const loadModels = [
{ id: '1', key: '05b', name: 'Qwen2.5-Coder:0.5B', size: '~990 MB', coderSize: '05b' }, { id: '1', key: '05b', name: 'Qwen2.5-Coder:0.5B', size: '~990 MB', coderSize: '05b' },
{ id: '2', key: '3b', name: 'Qwen2.5-Coder:3B', size: '~6.2 GB', coderSize: '3b' }, { id: '2', key: '3b', name: 'Qwen2.5-Coder:1.5B Q4', size: '~1 GB', coderSize: '3b' },
]; ];
if (!arg) { if (!arg) {
// Näytetään lista // Näytetään lista
@@ -2268,7 +2268,7 @@ Write the corrected code.`;
if (sub === 'models') { if (sub === 'models') {
termLog(' Käytettävissä olevat mallit:', '#c9d1d9'); termLog(' Käytettävissä olevat mallit:', '#c9d1d9');
termLog(' <span style="color:#58a6ff">1</span> qwen-coder Qwen2.5-Coder:0.5B <span style="color:#8b949e">~990 MB | koodin generointi</span>'); termLog(' <span style="color:#58a6ff">1</span> qwen-coder Qwen2.5-Coder:0.5B <span style="color:#8b949e">~990 MB | koodin generointi</span>');
termLog(' <span style="color:#58a6ff">2</span> qwen-coder-3b Qwen2.5-Coder:3B <span style="color:#8b949e">~6.2 GB | parempi koodinlaatu</span>'); termLog(' <span style="color:#58a6ff">2</span> qwen-coder-3b Qwen2.5-Coder:1.5B Q4 <span style="color:#8b949e">~1 GB | kvantisoidtu, parempi laatu</span>');
termLog(' <span style="color:#58a6ff">3</span> smollm-135m SmolLM 135M <span style="color:#8b949e">~270 MB | kevyt, nopea</span>'); termLog(' <span style="color:#58a6ff">3</span> smollm-135m SmolLM 135M <span style="color:#8b949e">~270 MB | kevyt, nopea</span>');
termLog(' <span style="color:#58a6ff">4</span> qwen-05b Qwen2.5:0.5B <span style="color:#8b949e">~990 MB | yleismalli</span>'); termLog(' <span style="color:#58a6ff">4</span> qwen-05b Qwen2.5:0.5B <span style="color:#8b949e">~990 MB | yleismalli</span>');
termLog(' <span style="color:#58a6ff">5</span> phi3-mini Phi-3 Mini <span style="color:#8b949e">~2.2 GB | Microsoftin malli</span>'); termLog(' <span style="color:#58a6ff">5</span> phi3-mini Phi-3 Mini <span style="color:#8b949e">~2.2 GB | Microsoftin malli</span>');
@@ -2552,7 +2552,7 @@ Write the corrected code.`;
uiSocket.onmessage = (event) => { uiSocket.onmessage = (event) => {
try { try {
const raw = event.data; const raw = event.data;
if (raw.includes('"single_tokenize"') || raw.includes('"download_progress"')) return; if (raw.includes('"single_tokenize"')) return;
const data = JSON.parse(raw); const data = JSON.parse(raw);
if (data.type === "stats") { if (data.type === "stats") {
@@ -2577,6 +2577,24 @@ Write the corrected code.`;
} else { } else {
dlBar.style.display = 'none'; dlBar.style.display = 'none';
} }
// Terminaaliin latauksen edistyminen
const term = document.getElementById('agent-terminal');
if (term) {
let dlLine = term.querySelector('.term-download');
if (data.pct >= 100) {
if (dlLine) dlLine.remove();
termLog(` <span style="color:#3fb950">✓</span> ${data.file} ladattu`, '#a5d6ff');
} else {
if (!dlLine) {
dlLine = document.createElement('div');
dlLine.className = 'terminal-line term-download';
term.appendChild(dlLine);
}
const bar = '█'.repeat(Math.floor(data.pct / 5)) + '░'.repeat(20 - Math.floor(data.pct / 5));
dlLine.innerHTML = ` <span style="color:#d29922">${data.file}</span> <span style="color:#8b949e">${bar}</span> <span style="color:#58a6ff">${data.pct}%</span> <span style="color:#8b949e">${data.loaded_mb}/${data.total_mb} MB</span>`;
term.scrollTop = term.scrollHeight;
}
}
} else if (data.type === "single_tokenize_done") { } else if (data.type === "single_tokenize_done") {
chatBox.classList.remove('hidden'); chatBox.classList.remove('hidden');
const r = data.result || {}; const r = data.result || {};
@@ -3104,10 +3122,43 @@ Write the corrected code.`;
} }
// Kuuntele console.log-viestejä pipeline-vaiheiden seuraamiseksi // Kuuntele console.log-viestejä pipeline-vaiheiden seuraamiseksi
// Terminaalin lataustilarivi — päivittyy dynaamisesti
function termLoadStatus(phase, detail) {
const term = document.getElementById('agent-terminal');
if (!term) return;
let statusLine = term.querySelector('.term-load-status');
if (!statusLine) {
statusLine = document.createElement('div');
statusLine.className = 'terminal-line term-load-status';
term.appendChild(statusLine);
}
const spinner = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
const frame = spinner[Math.floor(Date.now() / 100) % spinner.length];
statusLine.innerHTML = ` <span style="color:#d29922">${frame}</span> <span style="color:#8b949e">${phase}</span>${detail ? ` <span style="color:#58a6ff">${detail}</span>` : ''}`;
term.scrollTop = term.scrollHeight;
}
function termLoadDone() {
const term = document.getElementById('agent-terminal');
if (!term) return;
const statusLine = term.querySelector('.term-load-status');
if (statusLine) statusLine.remove();
}
const origCodeLog = console.log; const origCodeLog = console.log;
const codeLogListener = (...args) => { const codeLogListener = (...args) => {
const msg = args.join(' '); const msg = args.join(' ');
if (msg.includes('[Coder]') || msg.includes('[Storage]') || msg.includes('Burn Wasm') || msg.includes('Kipinä Agent Node')) { if (msg.includes('[Coder]') || msg.includes('[Storage]') || msg.includes('Burn Wasm') || msg.includes('Kipinä Agent Node')) {
// Terminaalin lataustilapäivitys
if (msg.includes('Agent Node käynnistyy')) termLoadStatus('WASM alustettu');
if (msg.includes('Ladataan') && msg.includes('tokenizer')) termLoadStatus('Ladataan tokenizer...');
if (msg.includes('tokenizer') && (msg.includes('löytyi') || msg.includes('tallennettu'))) termLoadStatus('Tokenizer ✓');
if (msg.includes('Ladataan') && msg.includes('gguf')) termLoadStatus('Ladataan mallia...');
const dlMatch = msg.match(/lataus: (\d+)%/);
if (dlMatch) termLoadStatus('Ladataan mallia...', dlMatch[1] + '%');
if (msg.includes('tallennettu') && msg.includes('gguf')) termLoadStatus('Malli tallennettu');
if (msg.includes('Rakennetaan')) termLoadStatus('Rakennetaan mallia...');
if (msg.includes('Malli ladattu')) termLoadDone();
if (msg.includes('Burn Wasm')) setStep('step-wasm', 'active'); if (msg.includes('Burn Wasm')) setStep('step-wasm', 'active');
if (msg.includes('Agent Node käynnistyy')) { setStep('step-wasm', 'done'); } if (msg.includes('Agent Node käynnistyy')) { setStep('step-wasm', 'done'); }
// Tokenizer: [Coder] tai [Storage] -prefiksi // Tokenizer: [Coder] tai [Storage] -prefiksi
@@ -3160,6 +3211,15 @@ Write the corrected code.`;
if (cl) { cl.textContent = 'Qwen2.5-Coder:' + (coderSize === '3b' ? '3B' : '0.5B'); cl.style.color = '#3fb950'; cl.title = sizeLabel + ' · Candle Wasm · CPU · max 512 tok'; } if (cl) { cl.textContent = 'Qwen2.5-Coder:' + (coderSize === '3b' ? '3B' : '0.5B'); cl.style.color = '#3fb950'; cl.title = sizeLabel + ' · Candle Wasm · CPU · max 512 tok'; }
if (btn) { btn.dataset.state = 'ready'; btn.textContent = '✓ Valmis'; btn.style.borderColor = '#3fb950'; btn.style.color = '#3fb950'; btn.style.cursor = 'default'; btn.title = 'Kielimalli ladattu — oma kone on valmis laskentaan'; } if (btn) { btn.dataset.state = 'ready'; btn.textContent = '✓ Valmis'; btn.style.borderColor = '#3fb950'; btn.style.color = '#3fb950'; btn.style.cursor = 'default'; btn.title = 'Kielimalli ladattu — oma kone on valmis laskentaan'; }
localStorage.setItem('kpn-coder-loaded', 'true'); localStorage.setItem('kpn-coder-loaded', 'true');
// Terminaaliin valmis-viesti (vain kerran)
if (!window._coderReadyLogged) {
window._coderReadyLogged = true;
const term = document.getElementById('agent-terminal');
if (term) {
const sLabel = coderSize === '3b' ? 'Qwen2.5-Coder:1.5B Q4' : 'Qwen2.5-Coder:0.5B';
termLog(` <span style="color:#3fb950">✓</span> ${sLabel} valmis — kpn run coder "prompti"`, '#3fb950');
}
}
} }
if (msg.includes('[Coder]') && msg.includes('Syöte:')) { if (msg.includes('[Coder]') && msg.includes('Syöte:')) {
// Pipeline piiloon kun generointi alkaa // Pipeline piiloon kun generointi alkaa