Compare commits
43 Commits
ff3a720b8d
...
pre-worker
| Author | SHA1 | Date | |
|---|---|---|---|
| fc95cf8c1b | |||
| 1ae1bf98e2 | |||
| f567fd3f8a | |||
| 38367eac97 | |||
| 20716186bc | |||
| 4e810ed4a2 | |||
| 91ff9e00f9 | |||
| e652bf7ab6 | |||
| eb69893124 | |||
| d18314bfc8 | |||
| 99b011e399 | |||
|
|
3976bb6251 | ||
|
|
0c32fecdc4 | ||
|
|
801cc0371d | ||
|
|
176f2d6915 | ||
|
|
dd1945ab28 | ||
|
|
262fee3b49 | ||
|
|
aa7540a6bf | ||
|
|
762066102a | ||
|
|
bef5b6fc3c | ||
|
|
095b72d2d6 | ||
|
|
4cb6128a27 | ||
|
|
4dff534fbf | ||
|
|
d5ab6272d3 | ||
|
|
2e7b86deeb | ||
|
|
a6e49870d6 | ||
|
|
d68882249e | ||
|
|
6a587cd080 | ||
|
|
f17fcf0f9d | ||
|
|
ac15336c9f | ||
|
|
7a15cacebf | ||
|
|
27135a8f14 | ||
|
|
e28a715f32 | ||
|
|
24d29d9ba9 | ||
|
|
7eca426e77 | ||
|
|
7a1352ead7 | ||
|
|
b9017448d8 | ||
|
|
3d1b406e8d | ||
|
|
aa6c4739dd | ||
|
|
cbbf427a93 | ||
|
|
0a216f19e2 | ||
|
|
a2e7ed53ff | ||
|
|
950cae9d96 |
475
docker-errors.log
Normal file
475
docker-errors.log
Normal 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"
|
||||
525
network-poc/BUILDING_BLOCKS.md
Normal file
525
network-poc/BUILDING_BLOCKS.md
Normal file
@@ -0,0 +1,525 @@
|
||||
# Kipinä Agentic Studio — Rakennuspalaset
|
||||
|
||||
Tämä dokumentti kuvaa projektin UI-komponentit, arkkitehtuuripatternit ja työnkulut niin, että vastaavan hajautetun AI-laskentaverkon ja agenttipohjaisen käyttöliittymän voi rakentaa alusta asti.
|
||||
|
||||
## Yleiskuva
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Selain (käyttäjä) │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌───────────────────┐ │
|
||||
│ │ Verkko- │ │ Koodi- │ │ Agents-näkymä │ │
|
||||
│ │ näkymä │ │ labra │ │ ┌───────────────┐ │ │
|
||||
│ │ │ │ │ │ │ Terminaali │ │ │
|
||||
│ │ Stats │ │ Editor │ │ │ Tab-complete │ │ │
|
||||
│ │ Chat │ │ Pipeline │ │ │ Dropdown │ │ │
|
||||
│ │ Tokenit │ │ Tulokset │ │ │ Historia │ │ │
|
||||
│ └────┬─────┘ └────┬─────┘ │ └───────────────┘ │ │
|
||||
│ │ │ └────────┬──────────┘ │
|
||||
│ └──────────┬───┘ │ │
|
||||
│ UI WebSocket HTTP API │
|
||||
│ │ /api/v1/chat │
|
||||
│ ┌───────────────┴──────────────┐ │ │
|
||||
│ │ Wasm Compute Node │ │ │
|
||||
│ │ (Candle + Burn) │ │ │
|
||||
│ │ ┌─────────┐ ┌────────────┐ │ │ │
|
||||
│ │ │ RAM │ │ IndexedDB │ │ │ │
|
||||
│ │ │ Cache │ │ Cache │ │ │ │
|
||||
│ │ └─────────┘ └────────────┘ │ │ │
|
||||
│ │ ┌─────────────────────────┐ │ │ │
|
||||
│ │ │ Model Cache (QwenModel) │ │ │ │
|
||||
│ │ └─────────────────────────┘ │ │ │
|
||||
│ └──────────────┬───────────────┘ │ │
|
||||
│ │ WS │ │
|
||||
└─────────────────┼──────────────────────┼─────────────┘
|
||||
│ │
|
||||
┌────────┴──────────────────────┴──┐
|
||||
│ Hub (Axum + Tokio) │
|
||||
│ ┌────────────┐ ┌─────────────┐ │
|
||||
│ │ Broadcast │ │ Node │ │
|
||||
│ │ Channel │ │ Registry │ │
|
||||
│ └────────────┘ └─────────────┘ │
|
||||
│ ┌────────────┐ ┌─────────────┐ │
|
||||
│ │ Busy-State │ │ Rate Limit │ │
|
||||
│ │ Tracker │ │ + Auth │ │
|
||||
│ └────────────┘ └─────────────┘ │
|
||||
│ ┌─────────────────────────────┐ │
|
||||
│ │ SQLite (sessiot, tulokset) │ │
|
||||
│ └─────────────────────────────┘ │
|
||||
└──────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. WebSocket-reaaliaikakommunikaatio
|
||||
|
||||
### 1.1 Hub ↔ Node broadcast-kanava
|
||||
|
||||
**Tarkoitus:** Jakaa tehtäviä ja vastaanottaa tuloksia kaikilta laskentasolmuilta.
|
||||
|
||||
**Työnkulku:**
|
||||
1. Hub luo `tokio::sync::broadcast::channel(100)`
|
||||
2. Jokainen solmu saa oman `rx = stats_tx.subscribe()`
|
||||
3. Hub broadcastaa tehtävät: `stats_tx.send(json)`
|
||||
4. Solmut suodattavat viestin tyypin ja `selected_task`:n perusteella
|
||||
|
||||
**Viestityupit:**
|
||||
|
||||
| Tyyppi | Suunta | Sisältö |
|
||||
|--------|--------|---------|
|
||||
| `stats` | Hub → kaikki | nodes, vram_gb, tasks |
|
||||
| `pair_task` | Hub → tokenize-solmut | en, fi tekstiparit |
|
||||
| `llm_prompt` | Hub → valittu solmu | prompt, model, task_id |
|
||||
| `llm_chunk` | Solmu → Hub → UI | token (1 kerrallaan) |
|
||||
| `llm_done` | Solmu → Hub → UI | response, tokens_generated, duration_ms |
|
||||
| `llm_error` | Solmu → Hub → UI | error, task_id |
|
||||
| `task_routed` | Hub → UI | status (routed/queued), node_id, message |
|
||||
|
||||
**Lagged-viestien käsittely:**
|
||||
```rust
|
||||
match rx.recv().await {
|
||||
Ok(msg) => { /* käsittele */ }
|
||||
Err(broadcast::error::RecvError::Lagged(n)) => {
|
||||
// Ohitetaan vanhat viestit, ei katkaista yhteyttä
|
||||
continue;
|
||||
}
|
||||
Err(_) => break, // Kanava suljettu
|
||||
}
|
||||
```
|
||||
|
||||
### 1.2 Kohdennettu reititys (Direct Channel)
|
||||
|
||||
**Tarkoitus:** Lähetä tehtävä yhdelle tietylle solmulle broadcastin sijaan.
|
||||
|
||||
**Työnkulku:**
|
||||
1. Jokainen solmu saa `mpsc::unbounded_channel` yhdistyessään
|
||||
2. Hub tallentaa `node_channels: HashMap<u64, UnboundedSender>`
|
||||
3. API-pyyntö → valitaan vapaa solmu → lähetetään suoraan kanavaan
|
||||
4. Broadcast-kanavaa käytetään vain tuloksen välittämiseen UI:lle
|
||||
|
||||
```rust
|
||||
let channels = state.node_channels.read().await;
|
||||
if let Some(tx) = channels.get(&target_node_id) {
|
||||
tx.send(msg.to_string());
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 Busy-state ja työjono
|
||||
|
||||
**Tarkoitus:** Estä tehtävien reititys varatuille solmuille.
|
||||
|
||||
**Rakenne:**
|
||||
- `node_busy: HashSet<u64>` — solmut joilla on aktiivinen tehtävä
|
||||
- Asetetaan kun tehtävä reititetään, vapautetaan `llm_done`/`llm_error`:ssa
|
||||
- Jos kaikki solmut varattuja → pollaa 500ms välein, max 30s
|
||||
|
||||
**UI-palaute:**
|
||||
```json
|
||||
{"type": "task_routed", "status": "queued", "message": "Kaikki 2 solmua varattuja — odotetaan..."}
|
||||
{"type": "task_routed", "status": "routed", "node_id": 3, "message": "Solmu #3 vapautui (2.5s jonossa)"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Wasm-laskentasolmu
|
||||
|
||||
### 2.1 Elinkaari
|
||||
|
||||
```
|
||||
init() → start_agent_node(ws_url, has_webgpu, device_info, task_id)
|
||||
│
|
||||
├─ Avaa WebSocket hubiin
|
||||
├─ Lähettää auth-viestin (laitetiedot, selected_task)
|
||||
├─ Rekisteröityy onmessage-käsittelijä
|
||||
│ ├─ pair_task → tokenize
|
||||
│ ├─ llm_prompt → inference
|
||||
│ └─ ai_task → tensor matmul
|
||||
└─ Odottaa tehtäviä loopissa
|
||||
```
|
||||
|
||||
**Globaali tila (atominen, lukitsematon):**
|
||||
```rust
|
||||
static GPU_LOAD_PERCENT: AtomicU32 = AtomicU32::new(50);
|
||||
static LLM_BUSY: AtomicBool = AtomicBool::new(false);
|
||||
static SELECTED_TASK: AtomicU32 = AtomicU32::new(0);
|
||||
```
|
||||
|
||||
### 2.2 Kolmitasoinen cache
|
||||
|
||||
```
|
||||
Pyyntö → [1] RAM-cache (thread_local HashMap)
|
||||
│ miss
|
||||
▼
|
||||
[2] IndexedDB (selaimen pysyvä tallennus)
|
||||
│ miss
|
||||
▼
|
||||
[3] Verkko (HuggingFace CDN, streaming + 5% progressi)
|
||||
│
|
||||
▼
|
||||
Tallenna → IndexedDB → RAM-cache
|
||||
```
|
||||
|
||||
| Taso | Nopeus | Koko | Pysyvyys |
|
||||
|------|--------|------|----------|
|
||||
| RAM | ~0ms | Rajaton | Sivulataus |
|
||||
| IndexedDB | ~50ms | ~50GB | Pysyvä |
|
||||
| Verkko | ~10s/100MB | ∞ | — |
|
||||
|
||||
**Malliinstanssin cache (neljäs taso):**
|
||||
```rust
|
||||
thread_local! {
|
||||
static MODEL_CACHE: RefCell<Option<CachedModel>> = RefCell::new(None);
|
||||
}
|
||||
// clear_kv_cache() promptien välillä — ei tarvitse rakentaa mallia uusiksi
|
||||
```
|
||||
|
||||
### 2.3 Warmup-esilataus
|
||||
|
||||
**Tarkoitus:** Lataa malli valmiiksi ennen ensimmäistä oikeaa promptia.
|
||||
|
||||
```javascript
|
||||
// Lähetetään 1 tokenin warmup heti kun WS on auki
|
||||
uiSocket.send(JSON.stringify({
|
||||
type: 'user_text',
|
||||
text: '{"prompt":"warmup","max_tokens":1}',
|
||||
task_type: 'qwen-coder'
|
||||
}));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. LLM-inferenssipipeline
|
||||
|
||||
### 3.1 Prompt-formaatti (ChatML + prefill)
|
||||
|
||||
```
|
||||
<|im_start|>system
|
||||
You are a coding assistant. Respond with ONLY code.<|im_end|>
|
||||
<|im_start|>user
|
||||
hello world in python<|im_end|>
|
||||
<|im_start|>assistant
|
||||
``` ← PREFILL: pakottaa mallin aloittamaan koodilla
|
||||
```
|
||||
|
||||
**Prefill-tekniikka:** Lisäämällä ` ``` ` assistantin vastauksen alkuun malli jatkaa suoraan koodilla eikä tuota "Sure! Here is..." -johdantoa. Säästää 10-20 tokenia per vastaus.
|
||||
|
||||
### 3.2 Sampling-parametrit
|
||||
|
||||
| Parametri | Arvo | Tarkoitus |
|
||||
|-----------|------|-----------|
|
||||
| `temperature` | 0.7 | Pehmentää jakaumaa, vähentää toistoa |
|
||||
| `top_k` | 40 | Rajaa valinnan 40 todennäköisimpään tokeniin |
|
||||
| `repetition_penalty` | 1.15 | Rankaisee jo generoitujen tokenien uudelleenvalintaa |
|
||||
| `max_tokens` | 128 | Oletusraja, JSON-promptilla konfiguroitavissa |
|
||||
|
||||
**Sampling-funktio (top-k + temperature + repetition penalty):**
|
||||
```rust
|
||||
fn sample_top_k_with_penalty(logits, k, temperature, generated_tokens, penalty) -> u32 {
|
||||
// 1. Repetition penalty: vähennä aiempien tokenien logitteja
|
||||
// 2. Temperature scaling: jaa logitit temperaturella
|
||||
// 3. Top-k: ota k suurinta
|
||||
// 4. Softmax top-k:lle
|
||||
// 5. Satunnaisvalinta kumulatiivisella todennäköisyydellä (XorShift RNG)
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 Stop-sekvenssit
|
||||
|
||||
Generointi katkaistaan ja teksti trimmataan kun malli alkaa selittää:
|
||||
|
||||
```rust
|
||||
let stop_patterns = ["\n###", "\nExplanation", "\nNote:", "\nOutput:", "\n```\n\n"];
|
||||
```
|
||||
|
||||
### 3.4 Vastauksen siivous
|
||||
|
||||
```
|
||||
Raakavastaus: "Sure! Here is...\n```python\n# This is a simple program\nprint('hi')\n```"
|
||||
│
|
||||
strip_markdown: "# This is a simple program\nprint('hi')"
|
||||
│
|
||||
strip_preamble: "print('hi')"
|
||||
```
|
||||
|
||||
**Tunnistettavat selityskommentit:** `# This is`, `# simple`, `# program that`, `# here is`, `# the following`, `# below`
|
||||
|
||||
### 3.5 Streaming
|
||||
|
||||
Jokainen generoitu token lähetetään heti `llm_chunk`-viestinä:
|
||||
```json
|
||||
{"type": "llm_chunk", "token": "print", "prompt": "...", "model": "Qwen2.5-Coder", "task_id": "uuid"}
|
||||
```
|
||||
|
||||
UI päivittää streaming-korttia reaaliaikaisesti appendaamalla tokeneita.
|
||||
|
||||
---
|
||||
|
||||
## 4. Terminaaliemulaattori
|
||||
|
||||
### 4.1 Rakenne
|
||||
|
||||
```html
|
||||
<div id="agent-hub-status"> <!-- Status-palkki (Hub + Laskenta) -->
|
||||
<div id="agent-terminal"> <!-- Scrollaava tulosalue, max 100 riviä -->
|
||||
<div> <!-- Input-rivi -->
|
||||
<span>$</span>
|
||||
<input id="term-input">
|
||||
<div id="term-dropdown"> <!-- Autocompletion-valikko -->
|
||||
</div>
|
||||
```
|
||||
|
||||
### 4.2 Komentojen käsittely
|
||||
|
||||
```javascript
|
||||
function termExec(cmd) {
|
||||
// Parsitaan: "kpn" + alikomento + argumentit
|
||||
// Tuetut: help, run, pipeline, load, status, models, hello, clear
|
||||
// Agenttinimi → malli-mapping: "coder" → "qwen-coder"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 Tab-completion (kolmitasoinen)
|
||||
|
||||
```javascript
|
||||
const kpnCommands = {
|
||||
'kpn': ['help', 'run', 'pipeline', 'load', ...],
|
||||
'kpn run': ['coder', 'manager', 'qwen-coder', ...],
|
||||
};
|
||||
const kpnExamples = {
|
||||
'kpn run coder': ['"hello world in python"', ...],
|
||||
};
|
||||
```
|
||||
|
||||
**Käyttö:**
|
||||
|
||||
| Näppäin | Toiminto |
|
||||
|---------|----------|
|
||||
| TAB | Täydennä seuraava sana tai avaa dropdown |
|
||||
| Shift-TAB | Poista viimeinen sana (lainausmerkit kokonaisuutena) |
|
||||
| ↑ / ↓ | Navigoi dropdownissa (tai komentohistoriassa) |
|
||||
| Enter | Valitse dropdownista tai suorita komento |
|
||||
| Esc | Sulje dropdown |
|
||||
|
||||
### 4.4 Dropdown-valikko
|
||||
|
||||
```javascript
|
||||
function showDropdown(items, prefix) {
|
||||
// Luo div.term-dd-item per vaihtoehto
|
||||
// Positio: absolute, bottom: 100% (inputin yläpuolella)
|
||||
// Mouseenter → highlight, click → valinta
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 Komentohistoria
|
||||
|
||||
```javascript
|
||||
const termHistory = []; // Kaikki ajetut komennot (viimeisin ensin)
|
||||
let termHistIdx = -1; // Nykyinen positio historiassa
|
||||
// ArrowUp: termHistIdx++, ArrowDown: termHistIdx--
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Status-palkit ja tilaindikaattorit
|
||||
|
||||
### 5.1 Hub-yhteyden tila
|
||||
|
||||
| Tila | Väri | Teksti | Tooltip |
|
||||
|------|------|--------|---------|
|
||||
| Yhdistetään | 🟡 | "Yhdistetään..." | WebSocket-yhteys Kipinä Hubiin |
|
||||
| Yhdistetty | 🟢 | "Yhdistetty" | Tehtävien jakelu aktiivinen |
|
||||
| Katkennut | 🔴 | "Yhteys katkennut" | Tarkista verkko, lataa uudelleen |
|
||||
|
||||
### 5.2 Laskentasolmun tila
|
||||
|
||||
| Tila | Väri | Teksti | Nappi |
|
||||
|------|------|--------|-------|
|
||||
| Ei käynnissä | ⚫ | "—" | `[Alusta laskentasolmu]` sininen |
|
||||
| Lataa | 🟡 | "Ladataan..." | `[Peruuta]` punainen |
|
||||
| Valmis | 🟢 | "Qwen2.5-Coder" | `[✓ Valmis]` vihreä |
|
||||
|
||||
### 5.3 Pipeline-tilakone (Codelab)
|
||||
|
||||
```
|
||||
Step 1: WebAssembly-ytimen lataus [◯ → ◷ → ✓]
|
||||
Step 2: Tokenizer (7 MB) [◯ → ◷ → ✓]
|
||||
Step 3: Mallipainot (990 MB) [◯ → ◷ 45% → ✓ cache]
|
||||
Step 4: Mallin rakentaminen [◯ → ◷ → ✓]
|
||||
Step 5: Valmis generoimaan [◯ → ✓]
|
||||
```
|
||||
|
||||
**Seuranta console.log-viesteistä:**
|
||||
```javascript
|
||||
if (msg.includes('[Coder]') && msg.includes('Malli ladattu')) {
|
||||
// Merkkaa kaikki vaiheet valmiiksi (myös cache-hitillä)
|
||||
setStep('step-wasm', 'done');
|
||||
setStep('step-tokenizer', 'done');
|
||||
setStep('step-model', 'done', 'cache');
|
||||
setStep('step-build', 'done');
|
||||
setStep('step-ready', 'done');
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Tietoturva
|
||||
|
||||
### 6.1 XSS-suojaus
|
||||
|
||||
```javascript
|
||||
function esc(str) {
|
||||
return String(str).replace(/&/g,'&').replace(/</g,'<')
|
||||
.replace(/>/g,'>').replace(/"/g,'"');
|
||||
}
|
||||
```
|
||||
|
||||
**Käyttöpaikat:** Kaikki `innerHTML`-insertoinnit joissa on käyttäjä- tai backend-dataa.
|
||||
|
||||
### 6.2 System prompt -piilotus
|
||||
|
||||
```javascript
|
||||
function stripSystemPrompt(prompt) {
|
||||
const parts = prompt.split('\n\n');
|
||||
return parts[parts.length - 1] || prompt;
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 Viestityyppivalidointi (backend)
|
||||
|
||||
```rust
|
||||
const ALLOWED_MSG_TYPES: &[&str] = &[
|
||||
"auth", "result", "pair_done", "llm_chunk", "llm_done",
|
||||
"llm_error", "download_progress", "user_text", "single_tokenize_done"
|
||||
];
|
||||
|
||||
fn validate_message(text: &str) -> Result<Value, &'static str> {
|
||||
// 1. JSON-parsinta
|
||||
// 2. "type"-kenttä pakollinen
|
||||
// 3. Tyyppi sallittujen listalla
|
||||
// 4. Tyyppikohtainen validointi (esim. pair_done: token_count <= 10000)
|
||||
}
|
||||
```
|
||||
|
||||
### 6.4 Rate limiting
|
||||
|
||||
```rust
|
||||
// Per-IP liukuva ikkuna: max 10 pyyntöä per 60s
|
||||
let entry = limits.entry(addr.ip()).or_insert((now, 0));
|
||||
if now.duration_since(entry.0).as_secs() >= 60 {
|
||||
*entry = (now, 1);
|
||||
} else {
|
||||
entry.1 += 1;
|
||||
if entry.1 > 10 { return 429 Too Many Requests; }
|
||||
}
|
||||
```
|
||||
|
||||
### 6.5 Gamification-huijauksen esto
|
||||
|
||||
```rust
|
||||
// Hub jakaa task_id:n → tallentaa pending_task_ids:hen
|
||||
// Merkkejä jaetaan VAIN jos llm_done sisältää validin task_id:n
|
||||
let valid_task = state.pending_task_ids.lock().unwrap().remove(tid);
|
||||
if active_incentives && valid_task {
|
||||
*balance += 20;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Syntaksikorostus
|
||||
|
||||
### 7.1 Highlight.js-integraatio
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark.min.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
|
||||
```
|
||||
|
||||
```javascript
|
||||
function highlightCode(code) {
|
||||
if (typeof hljs !== 'undefined') {
|
||||
return hljs.highlightAuto(code).value; // Automaattinen kielentunnistus
|
||||
}
|
||||
return esc(code); // Fallback
|
||||
}
|
||||
```
|
||||
|
||||
**Käyttöpaikat:** Codelab-tulokset, agents-terminaalin vastaukset, network-chat.
|
||||
|
||||
---
|
||||
|
||||
## 8. Agenttien orkestrointi
|
||||
|
||||
### 8.1 Multi-agent pipeline
|
||||
|
||||
```
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ Manageri │ ──→ │ Koodari │ ──→ │ Testaaja │
|
||||
│ Analysoi │ │ Koodaa │ │ Arvioi │
|
||||
│ tehtävä │ │ ratkaisu │ │ koodi │
|
||||
└──────────┘ └──────────┘ └──────────┘
|
||||
```
|
||||
|
||||
```javascript
|
||||
async function kpnPipeline(task) {
|
||||
const plan = await kpnRun('qwen-coder', `Analysoi: ${task}`);
|
||||
if (!plan) return;
|
||||
const code = await kpnRun('qwen-coder', `Koodaa: ${plan}`);
|
||||
if (!code) return;
|
||||
await kpnRun('smollm-135m', `Arvioi: ${code}`);
|
||||
}
|
||||
```
|
||||
|
||||
### 8.2 Agenttien promptien hallinta
|
||||
|
||||
```javascript
|
||||
const agentPrompts = {
|
||||
manager: { model: 'qwen-coder', prompt: 'Olet projektipäällikkö...' },
|
||||
coder: { model: 'qwen-coder', prompt: 'Olet ohjelmistokehittäjä...' },
|
||||
// ...
|
||||
};
|
||||
// Tallennetaan localStorage:en per agentti
|
||||
localStorage.setItem('kpn-agent-prompt-coder', customPrompt);
|
||||
```
|
||||
|
||||
### 8.3 Yhteinen promptikonteksti
|
||||
|
||||
```javascript
|
||||
async function kpnRun(model, prompt) {
|
||||
const parts = [];
|
||||
if (sharedPrompt) parts.push(sharedPrompt); // Kaikille yhteinen
|
||||
if (agent.prompt) parts.push(agent.prompt); // Agenttikohtainen
|
||||
parts.push(prompt); // Käyttäjän pyyntö
|
||||
const fullPrompt = parts.join('\n\n');
|
||||
// → HTTP POST /api/v1/chat/completions
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Teknologiapino
|
||||
|
||||
| Kerros | Teknologia | Tarkoitus |
|
||||
|--------|------------|-----------|
|
||||
| Frontend | Vanilla JS + HTML + CSS | Ei build-steppiä, toimii suoraan |
|
||||
| Wasm | Rust + wasm-bindgen | Inferenssi selaimessa |
|
||||
| LLM | Candle (Rust) | Transformer-inferenssi CPU:lla |
|
||||
| Tensorit | Burn (Rust) | GPU-tensorilaskenta (WebGPU/NdArray) |
|
||||
| Backend | Axum + Tokio (Rust) | Async WebSocket + HTTP -palvelin |
|
||||
| Tietokanta | SQLite (rusqlite) | Sessiot ja tulokset |
|
||||
| Cache | IndexedDB | Mallipainot selaimen pysyvässä muistissa |
|
||||
| Korostus | Highlight.js (CDN) | Syntaksikorostus, automaattinen kielentunnistus |
|
||||
| Tokenizer | HuggingFace tokenizers | BPE-tokenisaatio Wasmissa |
|
||||
|
||||
---
|
||||
|
||||
## 10. Jatkokehitysideoita
|
||||
|
||||
Näiden rakennuspalasten pohjalta voi rakentaa:
|
||||
|
||||
- **Oma chat-UI:** WebSocket + streaming + syntaksikorostus
|
||||
- **Hajautettu laskentaverkko:** Hub + node-rekisteri + busy-state + työjono
|
||||
- **Selain-LLM:** Wasm + Candle + IndexedDB-cache + warmup
|
||||
- **Agenttipohjainen työnkulku:** Pipeline + prompt-orkestrointi + reititys
|
||||
- **Terminaaliemulasttori:** Input + historia + tab-completion + dropdown
|
||||
- **Reaaliaikadashboard:** WebSocket broadcast + tilaindikaattorit + metriikat
|
||||
348
network-poc/PROMPTS.md
Normal file
348
network-poc/PROMPTS.md
Normal file
@@ -0,0 +1,348 @@
|
||||
# Kipinä Agentic Studio — Promptit
|
||||
|
||||
Kaikki järjestelmässä käytetyt promptit. Jokainen on dokumentoitu eksaktisti
|
||||
niin kuin se lähetetään mallille, muuttujat merkitty `${...}`-syntaksilla.
|
||||
|
||||
---
|
||||
|
||||
## 1. Inferenssin system prompt (Wasm + natiivi)
|
||||
|
||||
**Sijainti:** `node/src/qwen_coder.rs` rivi 256, `native-node/src/inference.rs` rivi 127
|
||||
**Malli:** Qwen2.5-Coder-0.5B/3B
|
||||
**ChatML-rooli:** `<|im_start|>system`
|
||||
|
||||
```
|
||||
You are a coding assistant. Respond with ONLY code. No explanations, no markdown, no comments unless asked.
|
||||
```
|
||||
|
||||
**Tarkoitus:** Pakottaa malli tuottamaan pelkkää koodia ilman selityksiä.
|
||||
**Prefill:** Assistantin vastaus alkaa ` ``` ` joka ohjaa mallin koodiblokkiin.
|
||||
|
||||
---
|
||||
|
||||
## 2. Agenttikohtaiset system promptit (frontend)
|
||||
|
||||
**Sijainti:** `static/index.html` rivit 1136-1144
|
||||
**Tallennus:** localStorage (`kpn-agent-prompt-{key}`)
|
||||
**ChatML-rooli:** Liitetään `<|im_start|>user` -blokkiin osaksi promptia
|
||||
|
||||
### 2.1 Manageri (manager)
|
||||
```
|
||||
Olet projektipäällikkö. Jaa tehtävät osiin, priorisoi ja koordinoi tiimin työtä.
|
||||
```
|
||||
**Malli:** qwen-coder
|
||||
|
||||
### 2.2 Koodari (coder)
|
||||
```
|
||||
Olet kokenut ohjelmistokehittäjä. Kirjoita selkeää, testattavaa koodia ja vastaa aina koodilla.
|
||||
```
|
||||
**Malli:** qwen-coder
|
||||
|
||||
### 2.3 Data-agentti (data)
|
||||
```
|
||||
Olet tietokanta-asiantuntija. Vastaat skeemojen suunnittelusta, SQL-kyselyiden optimoinnista ja datamalleista.
|
||||
```
|
||||
**Malli:** qwen-coder
|
||||
|
||||
### 2.4 QA (qa)
|
||||
```
|
||||
Olet laadunvarmistaja (QA). Kirjoitat testejä, etsit virheitä ja varmistat, että kaikki reunatapaukset on huomioitu.
|
||||
```
|
||||
**Malli:** smollm-135m
|
||||
|
||||
### 2.5 DevOps / Testaaja (tester)
|
||||
```
|
||||
Olet DevOps-insinööri. Vastaat koodin julkaisuputkista, serveri-infrastruktuurista ja ympäristön suorituskyvystä.
|
||||
```
|
||||
**Malli:** smollm-135m
|
||||
|
||||
### 2.6 Tarkkailija (observer)
|
||||
```
|
||||
Olet ohjelmistoprojektin riippumaton valvoja. Sinulla on täysi pääsy kaikkiin projektin tietoihin ja muiden agenttien keskusteluihin. Valvo tiimin (Manageri, Koodari, Data, QA, DevOps) toimintaa asiantuntijana kokonaisuutena ja huomauta välittömästi visio- tai turvallisuusriskeistä.
|
||||
```
|
||||
**Malli:** deepseek-r1
|
||||
|
||||
### 2.7 Asiakas (client)
|
||||
```
|
||||
Kirjoita tähän asiakkaan toiveet ja projektin vaatimukset. Orkestraattori (Manageri) purkaa ja delegoi nämä työt asiantuntijoille.
|
||||
```
|
||||
**Malli:** user-input (ei LLM:ää, käyttäjän teksti)
|
||||
|
||||
---
|
||||
|
||||
## 3. Projekti-pipeline (`kpn project`)
|
||||
|
||||
### 3.1 Vaihe 1: Managerin tiedostojako
|
||||
|
||||
**Konteksti:** Käyttäjä on antanut projektin kuvauksen.
|
||||
**Tavoite:** Pilkotaan projekti yksittäisiksi tiedostoiksi oikeassa riippuvuusjärjestyksessä.
|
||||
|
||||
```
|
||||
List the source files needed for this project. One file per line, format:
|
||||
filename.py: what this file contains
|
||||
|
||||
Rules:
|
||||
- Max 4 files
|
||||
- Only .py, .toml, .json, .html files
|
||||
- No directories, no paths, just filenames
|
||||
- List dependencies first, then main app (e.g. models.py before main.py)
|
||||
- Use pyproject.toml for dependencies (not requirements.txt)
|
||||
|
||||
Project: ${task}
|
||||
```
|
||||
|
||||
**Odotettu vastausformaatti:**
|
||||
```
|
||||
models.py: SQLAlchemy User model and database setup
|
||||
main.py: FastAPI app with CRUD endpoints
|
||||
pyproject.toml: project dependencies
|
||||
```
|
||||
|
||||
**Parsintasäännöt:**
|
||||
- Rivi voi olla `filename.ext: kuvaus` tai pelkkä `filename.ext`
|
||||
- Tiedostonimessä ei saa olla `/`, välilyöntejä tai polkuja
|
||||
- Päättyy tiedostopäätteeseen (`/\.\w{1,5}$/`)
|
||||
- Numerot, `-`, `*` ja `` ` `` strippataan rivin alusta
|
||||
- Max 40 merkin tiedostonimi
|
||||
|
||||
### 3.2 Vaihe 2: Koodarin tiedostogenerointi (per tiedosto)
|
||||
|
||||
**Konteksti:** Managerin tiedostolista on parsittu. Jokaiselle tiedostolle generoidaan koodi erikseen. Aiemmin generoidut tiedostot annetaan kontekstina.
|
||||
|
||||
**Perusmuoto:**
|
||||
```
|
||||
${context}Project: ${task}
|
||||
Write ONLY the file "${filename}"${description ? ': ' + description : ''}.
|
||||
Use the exact libraries mentioned in the project description. Write correct, working code.
|
||||
```
|
||||
|
||||
**`${context}` (kun aiempia tiedostoja on generoitu):**
|
||||
```
|
||||
Already written files:
|
||||
--- models.py ---
|
||||
from sqlalchemy import ...
|
||||
...
|
||||
|
||||
--- main.py ---
|
||||
from fastapi import ...
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
**Erikoistapaus: pyproject.toml**
|
||||
|
||||
Koska 0.5B-malli ei tunne uv/pyproject.toml-formaattia, annetaan eksplisiittinen esimerkki:
|
||||
```
|
||||
${context}Project: ${task}
|
||||
Write ONLY the file "pyproject.toml": ${description}.
|
||||
Use this exact format:
|
||||
[project]
|
||||
name = "projectname"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = ["fastapi", "uvicorn"]
|
||||
|
||||
[project.scripts]
|
||||
start = "uvicorn main:app --reload"
|
||||
Use the exact libraries mentioned in the project description. Write correct, working code.
|
||||
```
|
||||
|
||||
**Erikoistapaus: requirements.txt (fallback)**
|
||||
```
|
||||
...
|
||||
List one dependency per line. No version pins unless necessary.
|
||||
...
|
||||
```
|
||||
|
||||
### 3.3 Vaihe 2 (fallback): Yhtenä kokonaisuutena
|
||||
|
||||
Jos managerin vastaus ei tuota parsittavaa tiedostolistaa:
|
||||
```
|
||||
Project: ${task}
|
||||
Files: ${managerin_vastaus}
|
||||
|
||||
Write all the code for this project. Use the exact libraries mentioned in the project description. Use pyproject.toml for dependencies (not requirements.txt).
|
||||
```
|
||||
|
||||
### 3.4 Vaihe 3: Testerin arviointi
|
||||
|
||||
**Konteksti:** Kaikki generoidut tiedostot yhdistettynä.
|
||||
|
||||
```
|
||||
Review this project. List bugs or issues. Be brief.
|
||||
If the code is correct, say "LGTM".
|
||||
|
||||
--- models.py ---
|
||||
from sqlalchemy import ...
|
||||
|
||||
--- main.py ---
|
||||
from fastapi import ...
|
||||
```
|
||||
|
||||
**Odotettu vastaus:** Bugilista tai `LGTM`.
|
||||
**Trigger korjausluuppiin:** Jos vastaus EI sisällä "lgtm" tai "looks good" (case-insensitive).
|
||||
|
||||
### 3.5 Vaihe 4: Koodarin korjaukset (ehdollinen)
|
||||
|
||||
Ajetaan vain jos testeri löysi ongelmia.
|
||||
|
||||
```
|
||||
Fix the issues found in the review.
|
||||
Review feedback: ${review}
|
||||
|
||||
Current code:
|
||||
--- models.py ---
|
||||
...
|
||||
|
||||
--- main.py ---
|
||||
...
|
||||
|
||||
Write the corrected code.
|
||||
```
|
||||
|
||||
### 3.6 Vaihe 5: Testerin uudelleenarviointi (ehdollinen)
|
||||
|
||||
```
|
||||
Review the corrected code briefly:
|
||||
${fixedCode}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Yksinkertainen pipeline (`kpn pipeline`)
|
||||
|
||||
### 4.1 Manageri
|
||||
```
|
||||
Analyse this task briefly and write a technical spec for a coder:
|
||||
${task}
|
||||
```
|
||||
|
||||
### 4.2 Koodari
|
||||
```
|
||||
${managerin_vastaus}
|
||||
|
||||
Write the code.
|
||||
```
|
||||
|
||||
### 4.3 Testaaja
|
||||
```
|
||||
Review briefly:
|
||||
${koodarin_vastaus}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Yksittäiset komennot
|
||||
|
||||
### 5.1 `kpn run <malli> "<prompti>"`
|
||||
|
||||
Promptin koostaminen `kpnRun`-funktiossa:
|
||||
```
|
||||
${sharedPrompt} ← Kaikille agenteille yhteinen (jos asetettu)
|
||||
|
||||
${agentPrompt} ← Valitun agentin system prompt (jos löytyy)
|
||||
|
||||
${käyttäjän_prompti} ← Käyttäjän kirjoittama teksti
|
||||
```
|
||||
|
||||
Osat yhdistetään `\n\n`-erottimella ja lähetetään `<|im_start|>user`-blokkiin.
|
||||
|
||||
### 5.2 `kpn hello`
|
||||
|
||||
Kiinteä prompti SmolLM-135M -mallille:
|
||||
```
|
||||
Tervehdi käyttäjää iloisesti ja lyhyesti suomeksi. Ole innostunut ja energinen! Vastaa yhdellä lauseella.
|
||||
```
|
||||
|
||||
### 5.3 Warmup (automaattinen)
|
||||
|
||||
Lähetetään automaattisesti kun laskentasolmu käynnistyy. Triggeröi mallin latauksen ilman näkyvää tulosta.
|
||||
```json
|
||||
{"prompt": "warmup", "max_tokens": 1}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Stop-sekvenssit (inferenssi)
|
||||
|
||||
**Sijainti:** `node/src/qwen_coder.rs` rivi 345, `native-node/src/inference.rs` rivi 210
|
||||
|
||||
Generointi katkaistaan ja teksti trimmataan kun malli tuottaa minkä tahansa näistä:
|
||||
|
||||
| Sekvenssi | Tarkoitus |
|
||||
|-----------|-----------|
|
||||
| `\n###` | Markdown-otsikko (selitysosio alkaa) |
|
||||
| `\nExplanation` | Selitysosio |
|
||||
| `\nNote:` | Huomautus |
|
||||
| `\nOutput:` | Esimerkkitulostus |
|
||||
| `` \n```\n\n `` | Koodiblokin loppu + tyhjä rivi |
|
||||
| `\n// Example` | Esimerkkikoodi (C/Rust/JS) |
|
||||
| `\n// example` | Sama pienellä |
|
||||
| `\n# Example` | Esimerkkikoodi (Python/Ruby) |
|
||||
| `\n# example` | Sama pienellä |
|
||||
|
||||
---
|
||||
|
||||
## 7. Vastauksen siivous (post-processing)
|
||||
|
||||
**Sijainti:** `strip_markdown_wrapper()` molemmissa inferenssimoduuleissa
|
||||
|
||||
### 7.1 Kielitunnisteen poisto
|
||||
|
||||
Jos ensimmäinen rivi on tunnettu kielitunniste, se poistetaan.
|
||||
Tunnistetut: `python`, `py`, `rust`, `rs`, `javascript`, `js`, `typescript`, `ts`,
|
||||
`java`, `kotlin`, `scala`, `go`, `ruby`, `rb`, `php`, `swift`,
|
||||
`c`, `cpp`, `c++`, `c#`, `csharp`, `r`, `sql`, `bash`, `sh`, `zsh`,
|
||||
`html`, `css`, `json`, `yaml`, `yml`, `toml`, `xml`, `markdown`, `md`,
|
||||
`lua`, `perl`, `dart`, `elixir`, `haskell`, `hs`, `ocaml`, `zig`,
|
||||
`plaintext`, `text`, `txt`
|
||||
|
||||
### 7.2 Sulkevan ` ``` ` poisto
|
||||
|
||||
Poistetaan VAIN jos ` ``` ` on omalla rivillään tiedoston lopussa
|
||||
(edeltävä merkki on rivinvaihto tai alku).
|
||||
|
||||
### 7.3 Johdantolauseiden poisto
|
||||
|
||||
Ensimmäinen rivi poistetaan jos se alkaa (case-insensitive):
|
||||
`Sure!`, `Here is`, `Here's`, `Certainly!`, `Below is`
|
||||
|
||||
### 7.4 Selityskommenttien poisto
|
||||
|
||||
Alun `# `-alkuiset rivit poistetaan jos ne sisältävät (case-insensitive):
|
||||
`this is`, `simple`, `program that`, `here is`, `the following`, `below`
|
||||
|
||||
Shebang (`#!`) säilytetään.
|
||||
|
||||
---
|
||||
|
||||
## 8. Promptin kulku mallille (ChatML)
|
||||
|
||||
Lopullinen viesti mallille koostetaan näin:
|
||||
|
||||
```
|
||||
<|im_start|>system
|
||||
You are a coding assistant. Respond with ONLY code. No explanations, no markdown, no comments unless asked.<|im_end|>
|
||||
<|im_start|>user
|
||||
${sharedPrompt}
|
||||
|
||||
${agentPrompt}
|
||||
|
||||
${käyttäjän/pipelinen prompti}<|im_end|>
|
||||
<|im_start|>assistant
|
||||
```
|
||||
```
|
||||
|
||||
**Huomio:** ` ``` ` assistantin alussa on prefill — se on osa syötettä eikä mallin tuottamaa. Malli jatkaa suoraan koodilla.
|
||||
|
||||
---
|
||||
|
||||
## 9. Sampling-parametrit
|
||||
|
||||
| Parametri | Arvo | Kuvaus |
|
||||
|-----------|------|--------|
|
||||
| `temperature` | 0.7 | Jakaumaa pehmentävä kerroin |
|
||||
| `top_k` | 40 | Valinnan rajoitus 40 todennäköisimpään tokeniin |
|
||||
| `repetition_penalty` | 1.15 | Aiemmin generoitujen tokenien rankaisu |
|
||||
| `max_tokens` | 512 (oletus) | Konfiguroitavissa JSON-promptilla |
|
||||
| `eos_token` | 151645 | Qwen2.5:n päätöstokeni |
|
||||
@@ -9,7 +9,7 @@ services:
|
||||
volumes:
|
||||
- .:/app
|
||||
# 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)
|
||||
native-node:
|
||||
|
||||
Binary file not shown.
@@ -486,15 +486,15 @@ async fn ws_handler(
|
||||
.and_then(|s| s.trim().parse::<IpAddr>().ok())
|
||||
.unwrap_or_else(|| addr.ip());
|
||||
|
||||
// Max 2 yhteyttä per IP (dashboard-UI + selainsolmu)
|
||||
// Max yhteyttä per IP: jokainen selain tarvitsee 2 (UI + coder-node)
|
||||
{
|
||||
let conns = state.ip_connections.lock().unwrap();
|
||||
let count = conns.get(&ip).copied().unwrap_or(0);
|
||||
if count >= 4 {
|
||||
tracing::warn!("IP {} ylitti yhteysrajan ({}/4) — estetty", ip, count);
|
||||
if count >= 10 {
|
||||
tracing::warn!("IP {} ylitti yhteysrajan ({}/10) — estetty", ip, count);
|
||||
return (
|
||||
axum::http::StatusCode::TOO_MANY_REQUESTS,
|
||||
"Max 4 yhteyttä per IP",
|
||||
"Max 10 yhteyttä per IP",
|
||||
).into_response();
|
||||
}
|
||||
}
|
||||
@@ -978,7 +978,7 @@ async fn api_chat_completions(
|
||||
let busy = state.node_busy.lock().unwrap();
|
||||
let matching: Vec<u64> = tasks.iter().filter(|(_, task)| {
|
||||
if payload.model == "qwen-coder" {
|
||||
*task == "qwen-coder-05b" || *task == "qwen-coder"
|
||||
task.starts_with("qwen-coder")
|
||||
} else {
|
||||
**task == payload.model
|
||||
}
|
||||
@@ -1080,7 +1080,7 @@ async fn api_chat_completions(
|
||||
}
|
||||
}
|
||||
|
||||
let timeout = tokio::time::timeout(std::time::Duration::from_secs(120), async move {
|
||||
let timeout = tokio::time::timeout(std::time::Duration::from_secs(600), async move {
|
||||
loop {
|
||||
let msg_str = match rx.recv().await {
|
||||
Ok(msg) => msg,
|
||||
|
||||
@@ -207,8 +207,8 @@ impl LlmEngine {
|
||||
|
||||
// Stop-sekvenssit: katkaistaan kun malli alkaa selittää
|
||||
let lower = generated_text.to_lowercase();
|
||||
if lower.contains("\n###") || lower.contains("\nexplanation") || lower.contains("\nnote:") || lower.contains("\noutput:") || lower.contains("\n```\n\n") {
|
||||
for stop in &["\n###", "\nExplanation", "\nNote:", "\nOutput:", "\n```\n\n"] {
|
||||
if lower.contains("\n###") || lower.contains("\nexplanation") || lower.contains("\nnote:") || lower.contains("\noutput:") || lower.contains("\n```\n\n") || lower.contains("\n// example") || lower.contains("\n# example") {
|
||||
for stop in &["\n###", "\nExplanation", "\nNote:", "\nOutput:", "\n```\n\n", "\n// Example", "\n// example", "\n# Example", "\n# example"] {
|
||||
if let Some(pos) = generated_text.find(stop) {
|
||||
generated_text.truncate(pos);
|
||||
}
|
||||
@@ -234,33 +234,50 @@ impl LlmEngine {
|
||||
}
|
||||
}
|
||||
|
||||
/// Poistaa mallin tuottaman markdown-wrapperin ja johdantotekstin.
|
||||
const LANG_TAGS: &[&str] = &[
|
||||
"python", "py", "rust", "rs", "javascript", "js", "typescript", "ts",
|
||||
"java", "kotlin", "scala", "go", "ruby", "rb", "php", "swift",
|
||||
"c", "cpp", "c++", "c#", "csharp", "r", "sql", "bash", "sh", "zsh",
|
||||
"html", "css", "json", "yaml", "yml", "toml", "xml", "markdown", "md",
|
||||
"lua", "perl", "dart", "elixir", "haskell", "hs", "ocaml", "zig",
|
||||
"plaintext", "text", "txt",
|
||||
];
|
||||
|
||||
/// Siivoa mallin tuottama vastaus (prefill-yhteensopiva).
|
||||
fn strip_markdown_wrapper(text: &str) -> String {
|
||||
let text = text.trim();
|
||||
if let Some(start) = text.find("```") {
|
||||
let after = &text[start + 3..];
|
||||
let code_start = after.find('\n').map(|i| i + 1).unwrap_or(0);
|
||||
let code = &after[code_start..];
|
||||
if let Some(end) = code.find("```") {
|
||||
return code[..end].trim().to_string();
|
||||
let mut result = text.trim().to_string();
|
||||
|
||||
// 1. Kielitunniste — VAIN tunnettu kieli
|
||||
if let Some(nl) = result.find('\n') {
|
||||
let first = result[..nl].trim().to_lowercase();
|
||||
if LANG_TAGS.contains(&first.as_str()) {
|
||||
result = result[nl + 1..].to_string();
|
||||
}
|
||||
return code.trim().to_string();
|
||||
}
|
||||
let mut result = text.to_string();
|
||||
let lower = result.to_lowercase();
|
||||
|
||||
// 2. Sulkeva ``` — VAIN omalla rivillään lopussa
|
||||
let trimmed = result.trim_end();
|
||||
if trimmed.ends_with("```") {
|
||||
let before = &trimmed[..trimmed.len() - 3];
|
||||
if before.is_empty() || before.ends_with('\n') {
|
||||
result = before.trim_end().to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Johdantolauseet
|
||||
let lower = result.trim().to_lowercase();
|
||||
for prefix in &["sure!", "here is", "here's", "certainly!", "below is"] {
|
||||
if lower.starts_with(prefix) {
|
||||
if let Some(nl) = result.find('\n') {
|
||||
result = result[nl + 1..].to_string();
|
||||
}
|
||||
if let Some(nl) = result.find('\n') { result = result[nl + 1..].to_string(); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Selityskommentit alusta
|
||||
let mut lines: Vec<&str> = result.trim().lines().collect();
|
||||
while !lines.is_empty() {
|
||||
let first = lines[0].trim();
|
||||
let is_preamble = first.starts_with("# ")
|
||||
&& !first.starts_with("#!")
|
||||
let is_preamble = first.starts_with("# ") && !first.starts_with("#!")
|
||||
&& (first.to_lowercase().contains("this is")
|
||||
|| first.to_lowercase().contains("simple")
|
||||
|| first.to_lowercase().contains("program that")
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use candle_core::{Device, Tensor, DType};
|
||||
use candle_core::quantized::gguf_file;
|
||||
use candle_nn::VarBuilder;
|
||||
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 std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
@@ -16,37 +18,76 @@ macro_rules! console_log {
|
||||
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";
|
||||
|
||||
// 3B — parempi laatu, vaatii enemmän muistia (~6 GB lataus, ~12 GB RAM)
|
||||
const MODEL_3B_PART1_URL: &str = "https://huggingface.co/Qwen/Qwen2.5-Coder-3B-Instruct/resolve/main/model-00001-of-00002.safetensors";
|
||||
const MODEL_3B_PART2_URL: &str = "https://huggingface.co/Qwen/Qwen2.5-Coder-3B-Instruct/resolve/main/model-00002-of-00002.safetensors";
|
||||
const TOKENIZER_3B_URL: &str = "https://huggingface.co/Qwen/Qwen2.5-Coder-3B-Instruct/resolve/main/tokenizer.json";
|
||||
// 1.5B GGUF Q4_K_M — kvantisoidtu, mahtuu selaimeen (~1 GB)
|
||||
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 TOKENIZER_GGUF_URL: &str = "https://huggingface.co/Qwen/Qwen2.5-Coder-1.5B-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 {
|
||||
model: QwenModel,
|
||||
model: CoderModel,
|
||||
tokenizer: tokenizers::Tokenizer,
|
||||
is_3b: bool,
|
||||
}
|
||||
|
||||
/// Poistaa mallin tuottaman markdown-wrapperin ja johdantotekstin.
|
||||
/// "Sure! Here is...\n```python\nprint('hi')\n```" → "print('hi')"
|
||||
/// Tunnetut kielitunnisteet joita malli voi tuottaa prefill-backtickien jälkeen.
|
||||
const LANG_TAGS: &[&str] = &[
|
||||
"python", "py", "rust", "rs", "javascript", "js", "typescript", "ts",
|
||||
"java", "kotlin", "scala", "go", "ruby", "rb", "php", "swift",
|
||||
"c", "cpp", "c++", "c#", "csharp", "r", "sql", "bash", "sh", "zsh",
|
||||
"html", "css", "json", "yaml", "yml", "toml", "xml", "markdown", "md",
|
||||
"lua", "perl", "dart", "elixir", "haskell", "hs", "ocaml", "zig",
|
||||
"plaintext", "text", "txt",
|
||||
];
|
||||
|
||||
/// Siivoa mallin tuottama vastaus.
|
||||
/// Prefill-tekniikan vuoksi malli tuottaa: "rust\nfn main() {...}\n```"
|
||||
/// eli kielitunniste alussa + sulkeva ``` lopussa. Molemmat poistetaan.
|
||||
fn strip_markdown_wrapper(text: &str) -> String {
|
||||
let text = text.trim();
|
||||
// Jos vastaus sisältää ```-koodiblokin, ota vain sen sisältö
|
||||
if let Some(start) = text.find("```") {
|
||||
let after_backticks = &text[start + 3..];
|
||||
// Ohita mahdollinen kielitunniste (```python, ```rust jne.)
|
||||
let code_start = after_backticks.find('\n').map(|i| i + 1).unwrap_or(0);
|
||||
let code = &after_backticks[code_start..];
|
||||
// Etsi sulkeva ```
|
||||
if let Some(end) = code.find("```") {
|
||||
return code[..end].trim().to_string();
|
||||
let mut result = text.trim().to_string();
|
||||
|
||||
// 1. Poistetaan kielitunniste ensimmäiseltä riviltä — VAIN jos se on tunnettu kieli
|
||||
if let Some(first_newline) = result.find('\n') {
|
||||
let first_line = result[..first_newline].trim().to_lowercase();
|
||||
if LANG_TAGS.contains(&first_line.as_str()) {
|
||||
result = result[first_newline + 1..].to_string();
|
||||
}
|
||||
// Ei sulkevaa ``` — ota kaikki loput
|
||||
return code.trim().to_string();
|
||||
}
|
||||
// Ei koodiblokkia — poista yleiset johdantolauseet ja selityskommentit alusta
|
||||
let mut result = text.to_string();
|
||||
let lower = result.to_lowercase();
|
||||
|
||||
// 2. Poistetaan sulkeva ``` VAIN jos se on omalla rivillään lopussa
|
||||
let trimmed = result.trim_end();
|
||||
if trimmed.ends_with("```") {
|
||||
let before = &trimmed[..trimmed.len() - 3];
|
||||
// Varmistetaan: edellinen merkki on rivinvaihto tai alku (eli ``` on oma rivinsä)
|
||||
if before.is_empty() || before.ends_with('\n') {
|
||||
result = before.trim_end().to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Poistetaan johdantolauseet: "Sure! Here is...", "Certainly!" jne.
|
||||
let lower = result.trim().to_lowercase();
|
||||
for prefix in &["sure!", "here is", "here's", "certainly!", "below is"] {
|
||||
if lower.starts_with(prefix) {
|
||||
if let Some(newline) = result.find('\n') {
|
||||
@@ -55,12 +96,12 @@ fn strip_markdown_wrapper(text: &str) -> String {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Poistetaan alun selityskommentit: "# This is a simple..." -tyyppiset rivit
|
||||
// jotka eivät ole osa varsinaista koodia (esim. shebangia #! pidetään)
|
||||
|
||||
// 4. Poistetaan selityskommentit alusta: "# This is a simple program..."
|
||||
let mut lines: Vec<&str> = result.trim().lines().collect();
|
||||
while !lines.is_empty() {
|
||||
let first = lines[0].trim();
|
||||
let is_preamble_comment = first.starts_with("# ")
|
||||
let is_preamble = first.starts_with("# ")
|
||||
&& !first.starts_with("#!")
|
||||
&& (first.to_lowercase().contains("this is")
|
||||
|| first.to_lowercase().contains("simple")
|
||||
@@ -68,12 +109,9 @@ fn strip_markdown_wrapper(text: &str) -> String {
|
||||
|| first.to_lowercase().contains("here is")
|
||||
|| first.to_lowercase().contains("the following")
|
||||
|| first.to_lowercase().contains("below"));
|
||||
if is_preamble_comment {
|
||||
lines.remove(0);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
if is_preamble { lines.remove(0); } else { break; }
|
||||
}
|
||||
|
||||
lines.join("\n").trim().to_string()
|
||||
}
|
||||
|
||||
@@ -169,50 +207,39 @@ async fn get_or_build_model(use_3b: bool, ws: &Rc<RefCell<WebSocket>>) -> Result
|
||||
let dtype = DType::F32;
|
||||
|
||||
// Tokenizer
|
||||
let tok_url = if use_3b { TOKENIZER_3B_URL } else { TOKENIZER_05B_URL };
|
||||
let tok_key = if use_3b { "coder3b-tokenizer.json" } else { "coder05b-tokenizer.json" };
|
||||
let tok_url = if use_3b { TOKENIZER_GGUF_URL } else { TOKENIZER_05B_URL };
|
||||
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 tokenizer = tokenizers::Tokenizer::from_bytes(&tok_bytes[..])
|
||||
.map_err(|e| format!("Tokenizer: {}", e))?;
|
||||
|
||||
// Painot
|
||||
let tensors = if use_3b {
|
||||
let part1 = ensure_cached("coder3b-model-part1.safetensors", MODEL_3B_PART1_URL, ws).await?;
|
||||
let part2 = ensure_cached("coder3b-model-part2.safetensors", MODEL_3B_PART2_URL, ws).await?;
|
||||
console_log!("[Coder] Rakennetaan 3B-mallia...");
|
||||
let mut all_tensors = candle_core::safetensors::load_buffer(&part1[..], &device)
|
||||
.map_err(|e| format!("Part1: {}", e))?;
|
||||
let tensors2 = candle_core::safetensors::load_buffer(&part2[..], &device)
|
||||
.map_err(|e| format!("Part2: {}", e))?;
|
||||
all_tensors.extend(tensors2);
|
||||
all_tensors
|
||||
let model = if use_3b {
|
||||
// GGUF Q4_K_M — kvantisoidtu 3B-malli (~1.9 GB)
|
||||
let gguf_bytes = ensure_cached("coder15b-q4km.gguf", MODEL_GGUF_URL, ws).await?;
|
||||
console_log!("[Coder] Rakennetaan kvantisoidun 1.5B-mallia (Q4_K_M)...");
|
||||
let mut cursor = std::io::Cursor::new(&gguf_bytes[..]);
|
||||
let content = gguf_file::Content::read(&mut cursor)
|
||||
.map_err(|e| format!("GGUF parse: {}", e))?;
|
||||
let qmodel = QwenQuantizedModel::from_gguf(content, &mut cursor, &device)
|
||||
.map_err(|e| format!("GGUF model: {}", e))?;
|
||||
CoderModel::Quantized(qmodel)
|
||||
} else {
|
||||
let model_bytes = ensure_cached("coder05b-model.safetensors", MODEL_05B_URL, ws).await?;
|
||||
console_log!("[Coder] Rakennetaan 0.5B-mallia...");
|
||||
candle_core::safetensors::load_buffer(&model_bytes[..], &device)
|
||||
.map_err(|e| format!("Safetensors: {}", e))?
|
||||
};
|
||||
|
||||
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 {
|
||||
let tensors = candle_core::safetensors::load_buffer(&model_bytes[..], &device)
|
||||
.map_err(|e| format!("Safetensors: {}", e))?;
|
||||
let config = QwenConfig {
|
||||
vocab_size: 151936, hidden_size: 896, intermediate_size: 4864,
|
||||
num_hidden_layers: 24, num_attention_heads: 14, num_key_value_heads: 2,
|
||||
max_position_embeddings: 32768, sliding_window: 32768, max_window_layers: 21,
|
||||
tie_word_embeddings: true, rope_theta: 1000000.0, rms_norm_eps: 1e-6,
|
||||
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");
|
||||
|
||||
MODEL_CACHE.with(|c| {
|
||||
@@ -245,13 +272,13 @@ pub async fn run_coder_inference(prompt: String, ws: Rc<RefCell<WebSocket>>, use
|
||||
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&prompt) {
|
||||
let p = json.get("prompt").and_then(|v| v.as_str()).unwrap_or(&prompt).to_string();
|
||||
let s = json.get("system").and_then(|v| v.as_str()).unwrap_or(default_system).to_string();
|
||||
let m = json.get("max_tokens").and_then(|v| v.as_u64()).unwrap_or(128) as usize;
|
||||
let m = json.get("max_tokens").and_then(|v| v.as_u64()).unwrap_or(512) as usize;
|
||||
(p, s, m)
|
||||
} else {
|
||||
(prompt.clone(), default_system.to_string(), 128)
|
||||
(prompt.clone(), default_system.to_string(), 512)
|
||||
}
|
||||
} else {
|
||||
(prompt.clone(), default_system.to_string(), 128)
|
||||
(prompt.clone(), default_system.to_string(), 512)
|
||||
};
|
||||
|
||||
// Prefill: aloitetaan vastaus ```-koodiblokkilla, jolloin malli jatkaa suoraan koodilla
|
||||
@@ -329,8 +356,8 @@ pub async fn run_coder_inference(prompt: String, ws: Rc<RefCell<WebSocket>>, use
|
||||
|
||||
// Stop-sekvenssit: katkaistaan kun malli alkaa selittää
|
||||
let lower = generated_text.to_lowercase();
|
||||
if lower.contains("\n###") || lower.contains("\nexplanation") || lower.contains("\nnote:") || lower.contains("\noutput:") || lower.contains("\n```\n\n") {
|
||||
for stop in &["\n###", "\nExplanation", "\nNote:", "\nOutput:", "\n```\n\n"] {
|
||||
if lower.contains("\n###") || lower.contains("\nexplanation") || lower.contains("\nnote:") || lower.contains("\noutput:") || lower.contains("\n```\n\n") || lower.contains("\n// example") || lower.contains("\n# example") {
|
||||
for stop in &["\n###", "\nExplanation", "\nNote:", "\nOutput:", "\n```\n\n", "\n// Example", "\n// example", "\n# Example", "\n# example"] {
|
||||
if let Some(pos) = generated_text.find(stop) {
|
||||
generated_text.truncate(pos);
|
||||
}
|
||||
|
||||
429
network-poc/static/GUIDE.md
Normal file
429
network-poc/static/GUIDE.md
Normal file
@@ -0,0 +1,429 @@
|
||||
# Kipinä Agentic Studio — Opas
|
||||
|
||||
Hajautettu AI-laskentaverkko jossa kielimallit ajavat koodia suoraan selaimessa.
|
||||
Tämä opas selittää miten kielimallit toimivat, miten niitä ohjataan, ja miten
|
||||
tuloksia voi parantaa.
|
||||
|
||||
---
|
||||
|
||||
## Kielimallit ja niiden koot
|
||||
|
||||
Kielimalli on neuroverkko joka ennustaa seuraavan sanan (tokenin) edellisten
|
||||
perusteella. Mallin "koko" tarkoittaa parametrien (painojen) määrää:
|
||||
|
||||
| Malli | Parametrit | Koko levyllä | Nopeus selaimessa | Koodinlaatu |
|
||||
|-------|-----------|-------------|-------------------|-------------|
|
||||
| SmolLM 135M | 135 miljoonaa | ~270 MB | ~5 tok/s | Yksinkertainen teksti |
|
||||
| Qwen2.5-Coder:0.5B | 500 miljoonaa | ~990 MB | ~3-6 tok/s | Pienet funktiot |
|
||||
| Qwen2.5-Coder:3B | 3 miljardia | ~6.2 GB | ~0.4 tok/s | Kokonaiset tiedostot |
|
||||
| GPT-4 (vertailu) | ~1800 miljardia | ~3.6 TB | pilvipalvelu | Kokonaiset projektit |
|
||||
|
||||
**Parametrien vaikutus:** Jokainen parametri on yksi liukuluku (float16 = 2 tavua)
|
||||
joka tallentaa opittua tietoa. 0.5B-malli tietää perusrakenteet mutta tekee
|
||||
loogisia virheitä. 3B-malli ymmärtää kontekstin paremmin. Ero on kuin sanakirjan
|
||||
ja oppikirjan välillä.
|
||||
|
||||
**Miksi selaimessa?** Malli ajetaan käyttäjän omalla laitteella WebAssemblyn
|
||||
kautta. Data ei lähde koneelta, eikä tarvita pilvipalvelua. Haittapuoli on
|
||||
hitaus — GPU-palvelimella sama 0.5B-malli tuottaa ~100 tok/s.
|
||||
|
||||
---
|
||||
|
||||
## Tokenit — kielimallin "sanat"
|
||||
|
||||
Malli ei näe tekstiä kirjaimina vaan **tokeneina**. Tokeni on yleensä
|
||||
sanan osa, kokonainen sana tai välilyönti. Tokenisaatio tehdään
|
||||
BPE-algoritmilla (Byte Pair Encoding) joka oppii yleisimmät
|
||||
merkkijonot harjoitusdatasta.
|
||||
|
||||
### Esimerkki: koodi
|
||||
|
||||
```
|
||||
"print('Hello')" → [print] [(' ] [Hello] [')] = 4 tokenia
|
||||
"tulosta('Hei')" → [tul] [osta] [(' ] [He] [i] [')] = 6 tokenia
|
||||
```
|
||||
|
||||
Koodi tokenisoidaan tehokkaasti koska `print`, `def`, `return` yms.
|
||||
ovat kokonaisia tokeneita. Suomenkielinen `tulosta` joudutaan pilkkomaan
|
||||
osiin koska se ei esiinny harjoitusdatassa kokonaisena.
|
||||
|
||||
### Esimerkki: suomi vs. englanti
|
||||
|
||||
Sama lause kahdella kielellä Qwen2.5-Coder -tokenisaattorilla:
|
||||
|
||||
| | Teksti | Tokenit | Määrä | Merkkejä/token |
|
||||
|---|---|---|---|---|
|
||||
| EN | The cat sat on the mat | [The] [ cat] [ sat] [ on] [ the] [ mat] | **6** | 3.7 |
|
||||
| FI | Kissa istui matolla | [K] [issa] [ ist] [ui] [ mat] [olla] | **6** | 3.2 |
|
||||
| EN | Distributed computing in the browser | [Dist] [ributed] [ computing] [ in] [ the] [ browser] | **6** | 6.0 |
|
||||
| FI | Hajautettu laskenta selaimessa | [H] [aj] [au] [tettu] [ las] [kenta] [ sel] [aim] [essa] | **9** | 3.3 |
|
||||
| EN | Write a function that sorts a list | [Write] [ a] [ function] [ that] [ sorts] [ a] [ list] | **7** | 5.0 |
|
||||
| FI | Kirjoita funktio joka lajittelee listan | [K] [irj] [oita] [ funkt] [io] [ joka] [ laj] [ittel] [ee] [ listan] | **10** | 4.0 |
|
||||
|
||||
**Huomaa miten:**
|
||||
- Englannin yleiset sanat (`the`, `in`, `a`, `function`) ovat kokonaisia tokeneita
|
||||
- Suomen sanat pilkotaan pienempiin osiin (`Hajautettu` → 4 tokenia, `Distributed` → 2)
|
||||
- Suomi vaatii **30-50% enemmän tokeneita** saman merkityksen välittämiseen
|
||||
- Koodiavainsanat (`function`, `list`, `sort`) ovat tehokkaita molemmilla kielillä
|
||||
|
||||
### Miksi tämä merkitsee?
|
||||
|
||||
**Jokainen tokeni = yksi laskentakierros.** Jos suomi vaatii 50% enemmän tokeneita:
|
||||
|
||||
1. **Hitaampi vastaus:** 100 tokenin englanninkielinen vastaus ≈ 150 tokenia suomeksi
|
||||
→ 50% pidempi odotusaika
|
||||
2. **Pienempi konteksti:** Sama merkityssisältö vie enemmän tilaa konteksti-ikkunasta
|
||||
3. **Huonompi ymmärrys:** Pitkät sanat pilkotaan osiin jotka malli ei välttämättä
|
||||
tunnista → hallusinaatiot lisääntyvät
|
||||
|
||||
**Siksi tekniset promptit ovat englanniksi** — malli saa enemmän informaatiota
|
||||
samassa token-budjetissa ja ymmärtää ohjeet paremmin.
|
||||
|
||||
**Token-budjetti tässä järjestelmässä:**
|
||||
|
||||
| Osa | Tokeneita | Osuus |
|
||||
|-----|-----------|-------|
|
||||
| System prompt | ~30 | kiinteä |
|
||||
| Agent prompt | ~25 | kiinteä |
|
||||
| Konteksti (aiemmat tiedostot) | 0-300 | kasvaa |
|
||||
| Käyttäjän prompti | ~20-50 | vaihtelee |
|
||||
| **Syöte yhteensä** | **~75-400** | |
|
||||
| Generoitu vastaus (max) | 512 | raja |
|
||||
| **Yhteensä** | **~600-900** | /32 768 |
|
||||
|
||||
Konteksti-ikkuna on reilusti riittävä. Pullonkaula ei ole ikkunan koko
|
||||
vaan **mallin kyky ymmärtää pitkää kontekstia** — 0.5B-malli alkaa
|
||||
"unohtaa" ohjeet kun konteksti kasvaa yli ~200 tokenin.
|
||||
|
||||
---
|
||||
|
||||
## Promptit — miten mallia ohjataan
|
||||
|
||||
### Kolmitasoinen prompttirakenne
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S["System prompt<br/><i>You are a coding assistant. Respond with ONLY code.</i><br/>🔒 Kiinteä, kovakoodattu — malli priorisoi tämän"]
|
||||
A["Agent prompt<br/><i>Olet kokenut ohjelmistokehittäjä...</i><br/>✏️ Käyttäjän muokattavissa UI:ssa"]
|
||||
U["User prompt<br/><i>Write ONLY the file main.py...</i><br/>📋 Vaihtelee joka kutsussa, sisältää kontekstin"]
|
||||
P["Prefill: ``` <br/>🎯 Pakottaa mallin aloittamaan koodilla"]
|
||||
S --> A --> U --> P
|
||||
P -->|malli jatkaa| R["Generoitu koodi"]
|
||||
|
||||
style S fill:#1a1e2e,stroke:#f85149,color:#c9d1d9
|
||||
style A fill:#1a1e2e,stroke:#d29922,color:#c9d1d9
|
||||
style U fill:#1a1e2e,stroke:#3fb950,color:#c9d1d9
|
||||
style P fill:#1a1e2e,stroke:#a371f7,color:#c9d1d9
|
||||
style R fill:#0d1117,stroke:#58a6ff,color:#58a6ff
|
||||
```
|
||||
|
||||
### Miksi promptit ovat englanniksi?
|
||||
|
||||
Qwen2.5-Coder on harjoitettu pääosin englanninkielisellä koodilla ja
|
||||
dokumentaatiolla. Suomenkielinen ohje kuluttaa enemmän tokeneita JA
|
||||
malli ymmärtää sen huonommin. Agenttien nimet ja käyttöliittymä ovat
|
||||
suomeksi, mutta tekniset ohjeet mallille englanniksi.
|
||||
|
||||
Poikkeus: agenttipromptit ovat suomeksi koska ne menevät user-blokkiin
|
||||
(ei system-blokkiin) ja niiden tarkoitus on enemmän "persoonallisuus"
|
||||
kuin tekninen ohje.
|
||||
|
||||
---
|
||||
|
||||
## Prefill-tekniikka
|
||||
|
||||
Normaalisti malli päättää vapaasti miten vastaa:
|
||||
|
||||
```
|
||||
Ilman prefilliä:
|
||||
Malli: "Sure! Here is a Python program that prints Hello World:\n```python\nprint('Hello')\n```"
|
||||
→ 25 tokenia, joista 15 turhia
|
||||
|
||||
Prefillin kanssa:
|
||||
Me syötämme: ```
|
||||
Malli jatkaa: python\nprint('Hello')\n```
|
||||
→ 5 tokenia, kaikki hyödyllisiä
|
||||
```
|
||||
|
||||
Prefill on kuin aloittaisit lauseen toisen puolesta — malli jatkaa
|
||||
siitä mihin jäit sen sijaan, että aloittaisi kohteliaalla johdannolla.
|
||||
|
||||
**Sivuvaikutus:** Malli tuottaa kielitunnisteen (`python`, `rust`) ja
|
||||
sulkevan ` ``` `:n. Nämä siivotaan jälkikäteen `strip_markdown_wrapper`-funktiolla.
|
||||
|
||||
---
|
||||
|
||||
## Sampling — miten malli valitsee seuraavan tokenin
|
||||
|
||||
Malli ei "tiedä" oikeaa vastausta. Se laskee jokaiselle mahdolliselle
|
||||
seuraavalle tokenille todennäköisyyden ja valitsee yhden. Valintaa
|
||||
ohjataan kolmella parametrilla:
|
||||
|
||||
### Temperature (0.7)
|
||||
|
||||
Kontrolloi "luovuutta" vs. "varmuutta":
|
||||
|
||||
```
|
||||
Temperature 0.0 (greedy): Aina todennäköisin tokeni → "def fibonacci(n):"
|
||||
Temperature 0.7 (oletus): Painottaa todennäköisiä mutta sallii vaihtelua
|
||||
Temperature 1.5 (luova): Lähes satunnainen → "async lambda fib = ..."
|
||||
```
|
||||
|
||||
0.7 on kompromissi: tarpeeksi determinististä tuottamaan toimivaa koodia,
|
||||
mutta tarpeeksi vaihtelevaa välttämään toistoa.
|
||||
|
||||
### Top-k (40)
|
||||
|
||||
Rajaa valinnan 40 todennäköisimpään tokeniin. Estää mallia valitsemasta
|
||||
täysin absurdeja vaihtoehtoja:
|
||||
|
||||
```
|
||||
Ilman top-k: 150 936 vaihtoehtoa → voi valita minkä tahansa
|
||||
Top-k 40: 40 vaihtoehtoa → järkevät vaihtoehdot
|
||||
Top-k 1: 1 vaihtoehto → greedy (aina sama vastaus)
|
||||
```
|
||||
|
||||
### Repetition penalty (1.15)
|
||||
|
||||
Vähentää jo tuotettujen tokenien todennäköisyyttä. Estää mallia
|
||||
juuttumasta luuppiin:
|
||||
|
||||
```
|
||||
Ilman rangaistusta: "print print print print print..."
|
||||
Penalty 1.15: "print('Hello')\nprint('World')"
|
||||
```
|
||||
|
||||
1.15 on lievä rangaistus — estää pahimman toiston mutta sallii
|
||||
saman avainsanan (esim. `return`) esiintymisen useasti.
|
||||
|
||||
---
|
||||
|
||||
## Stop-sekvenssit — milloin generointi loppuu
|
||||
|
||||
Malli generoi tokeneita kunnes jokin näistä tapahtuu:
|
||||
|
||||
1. **EOS-tokeni** (151645): Mallin oma "loppu"-merkki
|
||||
2. **Max tokens** (512): Kovakoodattu raja
|
||||
3. **Stop-sekvenssi**: Malli alkaa tuottaa selitystä
|
||||
|
||||
```
|
||||
fn fibonacci(n: usize) -> usize {
|
||||
if n <= 1 { return n; }
|
||||
fibonacci(n-1) + fibonacci(n-2)
|
||||
}
|
||||
← Tähän asti koodia, ok
|
||||
// Example usage: ← Stop! Tämä ei ole enää vastausta
|
||||
let result = fibonacci(10); ← Ei generoida
|
||||
```
|
||||
|
||||
Tunnistetut stop-sekvenssit: `### `, `Explanation`, `Note:`, `Output:`,
|
||||
`// Example`, `# Example`. Generointi katkaistaan ja teksti trimmataan
|
||||
stop-kohtaan.
|
||||
|
||||
---
|
||||
|
||||
## Projekti-pipeline — miten agenttitiimi toimii
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
U["Käyttäjä: FastAPI + SQLite REST API for users"] --> M
|
||||
M["🟡 Manageri: Pilko tiedostoiksi"] -->|tiedostolista| C1
|
||||
C1["🟢 Koodari: models.py"] -->|"konteksti: models.py"| C2
|
||||
C2["🟢 Koodari: main.py"] -->|"konteksti: models + main"| C3
|
||||
C3["🟢 Koodari: pyproject.toml"] -->|kaikki tiedostot| T1
|
||||
T1["🔵 Testaaja: Review"] -->|bugeja löytyi| C4
|
||||
T1 -->|LGTM| Done["✅ Projekti valmis"]
|
||||
C4["🟡 Koodari: Korjaukset"] --> T2
|
||||
T2["🔵 Testaaja: Uudelleenarviointi"] --> Done
|
||||
```
|
||||
|
||||
**Kontekstin ketjutus** on kriittistä: kun koodari kirjoittaa `main.py`:tä,
|
||||
se saa `models.py`:n sisällön promptissa. Ilman tätä se ei tietäisi
|
||||
mitä luokkia importata.
|
||||
|
||||
**Riippuvuusjärjestys:** Manageria pyydetään listaamaan riippuvuudet ensin
|
||||
(models.py ennen main.py) jotta kontekstiketju toimii oikeaan suuntaan.
|
||||
|
||||
---
|
||||
|
||||
## Laadun parantaminen
|
||||
|
||||
### 1. Isompi malli (suurin vaikutus)
|
||||
|
||||
| | 0.5B | 3B | Pilvi-API |
|
||||
|---|---|---|---|
|
||||
| Fibonacci | Joskus virheitä | Yleensä oikein | Aina oikein |
|
||||
| FastAPI CRUD | Voi käyttää Flaskia | Oikea kirjasto | Täydellinen |
|
||||
| Monimutkainen logiikka | Hallusinoi | Osaa perusasiat | Syvä ymmärrys |
|
||||
| Nopeus (selain) | ~5 tok/s | ~0.4 tok/s | — |
|
||||
| Latauksen koko | 990 MB | 6.2 GB | 0 (API) |
|
||||
|
||||
**Käytännössä:** `kpn load 2` lataa 3B-mallin. Hitaampi mutta huomattavasti
|
||||
parempi koodinlaatu. Suositus monimutkaisiin projekteihin.
|
||||
|
||||
### 2. Paremmat promptit (ilmaista)
|
||||
|
||||
**Huono:** `"tee fibonacci"`
|
||||
- Malli ei tiedä kieltä, formaattia tai kontekstia
|
||||
|
||||
**Hyvä:** `"Write a fibonacci function in Rust that returns Vec<u64>"`
|
||||
- Kieli, palautustyyppi ja rakenne määritelty
|
||||
|
||||
**Promptin säännöt:**
|
||||
- Englanniksi (tehokkaampi tokenisointi, parempi ymmärrys)
|
||||
- Konkreettinen (mainitse kieli, kirjastot, palautustyyppi)
|
||||
- Lyhyt (jokainen sana kuluttaa tokenin konteksti-ikkunasta)
|
||||
- Positiivinen ("Write X" ei "Don't write Y")
|
||||
|
||||
### 3. Kontekstin hallinta (pipeline-taso)
|
||||
|
||||
**Ongelma:** 0.5B-malli "unohtaa" promptin alun kun konteksti kasvaa.
|
||||
|
||||
**Ratkaisu:** Pienet, kohdennetut promptit:
|
||||
- Yksi tiedosto kerrallaan (ei "kirjoita koko projekti")
|
||||
- Vain relevantit aiemmat tiedostot kontekstina
|
||||
- Max 4 tiedostoa per projekti
|
||||
|
||||
### 4. Iterointi (review-luuppi)
|
||||
|
||||
Yksi generointikierros tuottaa harvoin virheetöntä koodia.
|
||||
Pipeline-arkkitehtuuri mahdollistaa:
|
||||
|
||||
1. **Generointi** — ensimmäinen versio
|
||||
2. **Review** — testaaja löytää ongelmat
|
||||
3. **Korjaus** — koodari saa palautteen ja korjaa
|
||||
4. **Uusi review** — tarkistetaan korjaukset
|
||||
|
||||
Nykyinen järjestelmä tekee max 1 korjauskierroksen. Useampi
|
||||
iteraatio parantaisi laatua mutta kasvattaisi laskenta-aikaa.
|
||||
|
||||
### 5. Erikoistetut system promptit
|
||||
|
||||
Oletuspromptit ovat yleiskäyttöisiä. Projektikohtaiset promptit
|
||||
parantavat laatua merkittävästi:
|
||||
|
||||
```
|
||||
Oletus: "Olet kokenut ohjelmistokehittäjä."
|
||||
|
||||
Parempi: "You are a Python backend developer specializing in FastAPI.
|
||||
Always use Pydantic models for request/response schemas.
|
||||
Always use dependency injection for database sessions.
|
||||
Follow the repository pattern."
|
||||
```
|
||||
|
||||
Agenttikohtaiset promptit voi muokata suoraan UI:ssa.
|
||||
|
||||
### 6. Few-shot esimerkit
|
||||
|
||||
Malli oppii parhaiten esimerkeistä. Sen sijaan, että sanot "kirjoita
|
||||
FastAPI endpoint", näytä miltä haluat tuloksen näyttävän:
|
||||
|
||||
```
|
||||
Write a GET endpoint like this example:
|
||||
|
||||
@app.get("/items")
|
||||
def list_items():
|
||||
db = SessionLocal()
|
||||
return db.query(Item).all()
|
||||
|
||||
Now write a similar endpoint for /users.
|
||||
```
|
||||
|
||||
0.5B-malli jäljittelee rakennetta tehokkaasti — se on parempi kopioimaan
|
||||
kuin keksimään. Nykyinen pyproject.toml-esimerkki promptissa on tätä tekniikkaa.
|
||||
|
||||
### 7. Temperature-säätö tehtävän mukaan
|
||||
|
||||
Nykyinen temperature 0.7 on kompromissi. Eri tehtävät hyötyisivät eri arvoista:
|
||||
|
||||
| Tehtävä | Paras temperature | Miksi |
|
||||
|---------|-------------------|-------|
|
||||
| Tarkka koodi (CRUD, boilerplate) | 0.2-0.4 | Determinismi tärkeää |
|
||||
| Luova koodi (algoritmit, arkkitehtuuri) | 0.6-0.8 | Vaihtelu löytää ratkaisuja |
|
||||
| Vapaa teksti (kommentit, dokumentaatio) | 0.8-1.0 | Luonnollisempi kieli |
|
||||
|
||||
Järjestelmä voisi valita temperaturen automaattisesti tehtävätyypin perusteella.
|
||||
|
||||
### 8. Ensemble — sama prompti usealle mallille
|
||||
|
||||
Lähetetään sama tehtävä kahdelle solmulle ja valitaan parempi vastaus.
|
||||
Nykyinen Proof of Compute -arkkitehtuuri tukee tätä periaatteessa:
|
||||
hub voisi reitittää saman task_id:n kahdelle solmulle ja verrata tuloksia.
|
||||
|
||||
Käytännössä tämä kaksinkertaistaa laskenta-ajan mutta parantaa laatua
|
||||
merkittävästi — virheellinen vastaus harvoin on sama kahdella ajolla
|
||||
koska sampling on stokastinen.
|
||||
|
||||
### 9. Post-processing (nykyinen)
|
||||
|
||||
Mallin raakavastaus siivotaan:
|
||||
1. Kielitunniste poistetaan (`python`, `rust`, ...)
|
||||
2. Sulkeva ` ``` ` poistetaan
|
||||
3. Johdantolauseet poistetaan ("Sure!", "Here is...")
|
||||
4. Selityskommentit poistetaan ("# This is a simple...")
|
||||
5. Stop-sekvenssit katkaisevat generoinnin
|
||||
|
||||
Tämä ei paranna mallin ajattelua mutta poistaa turhan roskan.
|
||||
|
||||
### 10. Mallin hienosäätö (fine-tuning)
|
||||
|
||||
Qwen2.5-Coder on yleiskäyttöinen koodimalli. Jos sitä hienosäätäisi
|
||||
omalla koodiaineistolla (esim. yrityksen koodikanta, tietty framework),
|
||||
se tuottaisi huomattavasti parempaa koodia juuri siihen kontekstiin.
|
||||
|
||||
LoRA-hienosäätö 0.5B-mallille vaatii ~4 GB GPU-muistia ja muutaman
|
||||
tunnin harjoittelua. Tulos on erikoistunut malli joka osaa tuottaa
|
||||
esimerkiksi juuri FastAPI + SQLAlchemy -koodia luotettavasti.
|
||||
|
||||
---
|
||||
|
||||
## Välimuistiarkkitehtuuri — miksi toinen lataus on nopea
|
||||
|
||||
```
|
||||
Ensimmäinen lataus (hidas):
|
||||
Verkko (HuggingFace CDN) → IndexedDB → RAM → Mallin rakennus
|
||||
~990 MB lataus, ~30-60s
|
||||
|
||||
Toinen lataus samalla sivulatauksella (nopea):
|
||||
RAM-cache → Mallia ei rakenneta uusiksi, vain KV-cache nollataan
|
||||
~0ms
|
||||
|
||||
Refresh jälkeen (keskitaso):
|
||||
IndexedDB → RAM → Mallin rakennus
|
||||
~0 MB lataus, ~2-5s rakennus
|
||||
|
||||
Uusi selain/laite (hidas):
|
||||
Verkko → IndexedDB → RAM → Mallin rakennus
|
||||
Kuten ensimmäinen lataus
|
||||
```
|
||||
|
||||
**KV-cache:** Mallin sisäinen muisti joka tallentaa aiempien tokenien
|
||||
laskenta tulokset. Nollataan (`clear_kv_cache()`) jokaisen promptin
|
||||
välillä jotta edellinen vastaus ei vuoda seuraavaan.
|
||||
|
||||
---
|
||||
|
||||
## Lukuja käytännöstä
|
||||
|
||||
**Yksittäinen funktio** (esim. fibonacci):
|
||||
- Input: ~80 tokenia
|
||||
- Output: ~50-100 tokenia
|
||||
- Aika: ~10-20s (0.5B, selain)
|
||||
- Laatu: Yleensä toimiva, joskus loogisia virheitä
|
||||
|
||||
**3 tiedoston projekti** (esim. FastAPI CRUD):
|
||||
- Manageri: ~30 tok out
|
||||
- Koodari (3x): ~100-150 tok out per tiedosto
|
||||
- Testeri: ~50 tok out
|
||||
- Korjaukset: ~100 tok out (jos tarpeen)
|
||||
- **Yhteensä: ~500-700 tokenia, ~3-5 min**
|
||||
- Laatu: Rakenne oikein, yksittäisiä bugeja
|
||||
|
||||
**Token-kustannus vs. pilvipalvelu:**
|
||||
- Tässä järjestelmässä: 0 euroa (laskenta omalla koneella)
|
||||
- GPT-4 API: ~700 tokenia x $0.03/1K = ~$0.02 per projekti
|
||||
- Claude API: ~700 tokenia x $0.015/1K = ~$0.01 per projekti
|
||||
|
||||
Selaimessa ajettava malli on ilmainen mutta huomattavasti hitaampi
|
||||
ja heikompilaatuinen kuin pilvi-API. Sopii oppimiseen, prototypointiin
|
||||
ja tilanteisiin joissa data ei saa lähteä omalta koneelta.
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user