Compare commits
4 Commits
b1de0d37f7
...
591cfcb04b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
591cfcb04b | ||
|
|
3cda57f0bc | ||
|
|
23e7b92d03 | ||
|
|
9f58febe21 |
@@ -9,14 +9,14 @@ mkdir -p "$OUT"
|
||||
echo "=== Kipinä Node — Binary Build ==="
|
||||
|
||||
# macOS ARM (natiivi)
|
||||
echo "[1/3] macOS ARM64..."
|
||||
echo "[1/4] macOS ARM64..."
|
||||
cd "$SCRIPT_DIR"
|
||||
cargo build --release -p native-node --no-default-features 2>&1 | tail -1
|
||||
cp target/release/native-node "$OUT/kipina-node-macos-arm64"
|
||||
echo " $(ls -lh "$OUT/kipina-node-macos-arm64" | awk '{print $5}')"
|
||||
|
||||
# Linux x86_64 (Docker)
|
||||
echo "[2/3] Linux x86_64..."
|
||||
echo "[2/4] Linux x86_64..."
|
||||
docker run --rm \
|
||||
-v "$SCRIPT_DIR":/app -w /app \
|
||||
--platform linux/amd64 \
|
||||
@@ -25,7 +25,7 @@ docker run --rm \
|
||||
echo " $(ls -lh "$OUT/kipina-node-linux-x86_64" | awk '{print $5}')"
|
||||
|
||||
# Linux ARM64 (Docker)
|
||||
echo "[3/3] Linux ARM64..."
|
||||
echo "[3/4] Linux ARM64..."
|
||||
docker run --rm \
|
||||
-v "$SCRIPT_DIR":/app -w /app \
|
||||
--platform linux/arm64 \
|
||||
@@ -33,6 +33,15 @@ docker run --rm \
|
||||
bash -c "apt-get update -qq && apt-get install -y -qq pkg-config libssl-dev >/dev/null 2>&1 && cargo build --release -p native-node --no-default-features 2>&1 | tail -1 && cp target/release/native-node /app/frontend/public/download/kipina-node-linux-arm64"
|
||||
echo " $(ls -lh "$OUT/kipina-node-linux-arm64" | awk '{print $5}')"
|
||||
|
||||
# Windows x86_64 (Docker + mingw-w64)
|
||||
echo "[4/4] Windows x86_64..."
|
||||
docker run --rm \
|
||||
-v "$SCRIPT_DIR":/app -w /app \
|
||||
--platform linux/amd64 \
|
||||
rust:slim \
|
||||
bash -c "apt-get update -qq && apt-get install -y -qq gcc-mingw-w64-x86-64 pkg-config libssl-dev >/dev/null 2>&1 && rustup target add x86_64-pc-windows-gnu && cargo build --release -p native-node --no-default-features --target x86_64-pc-windows-gnu 2>&1 | tail -1 && cp target/x86_64-pc-windows-gnu/release/native-node.exe /app/frontend/public/download/kipina-node-windows-x86_64.exe"
|
||||
echo " $(ls -lh "$OUT/kipina-node-windows-x86_64.exe" | awk '{print $5}')"
|
||||
|
||||
echo ""
|
||||
echo "=== Binäärit valmiina ==="
|
||||
ls -lh "$OUT"/kipina-node-*
|
||||
|
||||
@@ -35,25 +35,29 @@ if ! git -C "$SCRIPT_DIR" diff --quiet HEAD 2>/dev/null || \
|
||||
echo " Commitoitu: $DEPLOY_MSG"
|
||||
fi
|
||||
|
||||
# 1. Rakennetaan Docker-image lokaalisti
|
||||
echo "[1/4] Rakennetaan image lokaalisti..."
|
||||
# 1. Käännetään native-node-binäärit kaikille alustoille
|
||||
echo "[1/6] Käännetään native-node-binäärit..."
|
||||
./build-binaries.sh
|
||||
|
||||
# 2. Rakennetaan Docker-image lokaalisti
|
||||
echo "[2/6] Rakennetaan image lokaalisti..."
|
||||
docker build --platform linux/amd64 -f Dockerfile.prod -t kipina-agentic:latest .
|
||||
|
||||
# 2. Tallennetaan tiedostoon
|
||||
echo "[2/5] Pakataan image..."
|
||||
# 3. Tallennetaan tiedostoon
|
||||
echo "[3/6] Pakataan image..."
|
||||
docker save kipina-agentic:latest | gzip > /tmp/kipina-agentic.tar.gz
|
||||
echo " Koko: $(du -h /tmp/kipina-agentic.tar.gz | cut -f1)"
|
||||
|
||||
# 3. Siirretään palvelimelle
|
||||
echo "[3/5] Siirretään palvelimelle..."
|
||||
# 4. Siirretään palvelimelle
|
||||
echo "[4/6] Siirretään palvelimelle..."
|
||||
scp $SSH_OPTS /tmp/kipina-agentic.tar.gz $SERVER:/tmp/
|
||||
scp $SSH_OPTS docker-compose.prod.yml Caddyfile.prod $SERVER:$REMOTE_DIR/
|
||||
|
||||
# 4. Ladataan image ja käynnistetään
|
||||
echo "[4/5] Ladataan image palvelimella..."
|
||||
# 5. Ladataan image ja käynnistetään
|
||||
echo "[5/6] Ladataan image palvelimella..."
|
||||
ssh $SSH_OPTS $SERVER "gunzip -c /tmp/kipina-agentic.tar.gz | docker load && rm /tmp/kipina-agentic.tar.gz"
|
||||
|
||||
echo "[5/5] Käynnistetään palvelut uudelleen..."
|
||||
echo "[6/6] Käynnistetään palvelut uudelleen..."
|
||||
ssh $SSH_OPTS $SERVER "cd $REMOTE_DIR && docker compose -f docker-compose.prod.yml down && docker compose -f docker-compose.prod.yml up -d"
|
||||
|
||||
echo "=== Valmis! https://kipina.studio ==="
|
||||
|
||||
BIN
network-poc/frontend/public/download/kipina-node-linux-arm64
Executable file
BIN
network-poc/frontend/public/download/kipina-node-linux-arm64
Executable file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
network-poc/frontend/public/download/kipina-node-windows-x86_64.exe
Executable file
BIN
network-poc/frontend/public/download/kipina-node-windows-x86_64.exe
Executable file
Binary file not shown.
@@ -44,6 +44,7 @@ struct AppState {
|
||||
pending_task_ids: Mutex<std::collections::HashSet<String>>, // Hubin jakamat task_id:t (gamification-validointi)
|
||||
pending_responses: Mutex<HashMap<String, tokio::sync::oneshot::Sender<serde_json::Value>>>, // task_id → oneshot API-vastaukselle
|
||||
api_rate_limits: Mutex<HashMap<IpAddr, (std::time::Instant, u32)>>, // IP → (ikkuna-alku, pyyntömäärä)
|
||||
node_models: tokio::sync::RwLock<HashMap<u64, serde_json::Value>>, // node_id → ollama tags JSON
|
||||
db: db::NodeDb,
|
||||
}
|
||||
|
||||
@@ -303,6 +304,7 @@ async fn main() {
|
||||
pending_task_ids: Mutex::new(std::collections::HashSet::new()),
|
||||
pending_responses: Mutex::new(HashMap::new()),
|
||||
api_rate_limits: Mutex::new(HashMap::new()),
|
||||
node_models: tokio::sync::RwLock::new(HashMap::new()),
|
||||
db: db::NodeDb::new(&std::env::var("DATABASE_PATH").unwrap_or_else(|_| "nodes.db".to_string())),
|
||||
});
|
||||
|
||||
@@ -781,6 +783,12 @@ async fn handle_socket(socket: WebSocket, state: Arc<AppState>, ip: IpAddr) {
|
||||
node_id, ip, hostname, os, cores, ram, allocated
|
||||
);
|
||||
|
||||
// Tallennetaan välitetyt mallit muistiin
|
||||
if let Some(models) = json.get("models") {
|
||||
let mut nm = state.node_models.write().await;
|
||||
nm.insert(node_id, models.clone());
|
||||
}
|
||||
|
||||
if let Some(gpus) = json.get("gpus").and_then(|v| v.as_array()) {
|
||||
for gpu in gpus {
|
||||
tracing::info!(
|
||||
@@ -1045,6 +1053,7 @@ async fn handle_socket(socket: WebSocket, state: Arc<AppState>, ip: IpAddr) {
|
||||
vram.remove(&node_id);
|
||||
}
|
||||
state.node_types.lock().unwrap().remove(&node_id);
|
||||
state.node_models.write().await.remove(&node_id);
|
||||
tracing::info!("Solmu {} ({}) poistui verkosta.", node_id, ip);
|
||||
broadcast_stats(&state).await;
|
||||
sender_task.abort();
|
||||
@@ -1065,7 +1074,16 @@ struct ChatCompletionResponse {
|
||||
tokens_generated: u64,
|
||||
}
|
||||
|
||||
async fn api_ollama_tags() -> axum::response::Response {
|
||||
async fn api_ollama_tags(
|
||||
axum::extract::State(state): axum::extract::State<Arc<AppState>>,
|
||||
) -> axum::response::Response {
|
||||
// Haetaan natiivisolmun tila muistista — priorisoidaan aito verkko-solmu
|
||||
let node_models = state.node_models.read().await;
|
||||
if let Some((_, models_json)) = node_models.iter().next() {
|
||||
return axum::Json(models_json.clone()).into_response();
|
||||
}
|
||||
|
||||
// Fallback: Haetaan lokaalista infra-Ollamasta ohjaimesta käsin (esim dev ympäristö)
|
||||
let ollama_url = std::env::var("OLLAMA_URL").unwrap_or_else(|_| "http://ollama:11434".to_string());
|
||||
match reqwest::get(format!("{}/api/tags", ollama_url)).await {
|
||||
Ok(resp) => {
|
||||
@@ -1089,11 +1107,10 @@ async fn api_hardware(
|
||||
});
|
||||
|
||||
let (mut vram_mb, mut gpu_name, ram_mb) = if let Some(s) = native {
|
||||
let gpus = s.get("gpus").and_then(|v| v.as_array());
|
||||
let gpu = gpus.and_then(|g| g.first());
|
||||
let vram = gpu.and_then(|g| g.get("vram_total_mb")).and_then(|v| v.as_u64()).unwrap_or(0);
|
||||
let name = gpu.and_then(|g| g.get("name")).and_then(|v| v.as_str()).unwrap_or("").to_string();
|
||||
let ram = s.get("system").and_then(|v| v.get("ram_total_mb")).and_then(|v| v.as_u64()).unwrap_or(0);
|
||||
// Tieto on tietokannassa litteänä
|
||||
let vram = s.get("vram_total_mb").and_then(|v| v.as_u64()).unwrap_or(0);
|
||||
let name = s.get("gpu_name").and_then(|v| v.as_str()).unwrap_or("").to_string();
|
||||
let ram = s.get("ram_mb").and_then(|v| v.as_u64()).unwrap_or(0);
|
||||
(vram, name, ram)
|
||||
} else {
|
||||
(0, String::new(), 0)
|
||||
|
||||
@@ -78,6 +78,20 @@ impl LlmEngine {
|
||||
}
|
||||
}
|
||||
|
||||
/// Hakee kaikki Ollamaan asennetut mallit
|
||||
pub async fn fetch_models(&self) -> Result<serde_json::Value, String> {
|
||||
let resp = self.client.get(format!("{}/api/tags", self.ollama_url))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("Ollama tags fetch: {}", e))?;
|
||||
|
||||
if resp.status().is_success() {
|
||||
resp.json().await.map_err(|e| format!("Ollama tags json: {}", e))
|
||||
} else {
|
||||
Err(format!("Ollama tags epäonnistui: {}", resp.status()))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn generate(&self, prompt: &str, max_tokens: usize) -> Result<GenerateResult, String> {
|
||||
// System prompt tulee agentin konfiguraatiosta (frontend lähettää sen osana promptia).
|
||||
// Tässä ei yliajeta sitä — Ollama saa vain prompt-kentän.
|
||||
|
||||
@@ -222,7 +222,7 @@ fn collect_system_info() -> serde_json::Value {
|
||||
}
|
||||
|
||||
/// Koko auth-viesti hubille
|
||||
fn build_auth_message(allocated_gb: u32, model_name: &str) -> String {
|
||||
fn build_auth_message(allocated_gb: u32, model_name: &str, models_data: Option<serde_json::Value>) -> String {
|
||||
let sys = collect_system_info();
|
||||
let gpus = collect_all_gpus();
|
||||
|
||||
@@ -251,6 +251,10 @@ fn build_auth_message(allocated_gb: u32, model_name: &str) -> String {
|
||||
msg.as_object_mut().unwrap().insert("gpus".to_string(), json!(gpu_json));
|
||||
}
|
||||
|
||||
if let Some(models) = models_data {
|
||||
msg.as_object_mut().unwrap().insert("models".to_string(), models);
|
||||
}
|
||||
|
||||
msg.to_string()
|
||||
}
|
||||
|
||||
@@ -324,6 +328,19 @@ async fn main() {
|
||||
let active_model = llm.as_ref().map(|e| e.model_name()).unwrap_or_else(|| "unknown".to_string());
|
||||
tracing::info!("Käytettävä kielimalli konfiguroitu (selected_task): {}", active_model);
|
||||
|
||||
// Haetaan paikalliset mallit hubille lähetettäväksi
|
||||
let mut available_models = None;
|
||||
if let Some(ref engine) = llm {
|
||||
match engine.fetch_models().await {
|
||||
Ok(models) => {
|
||||
available_models = Some(models);
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!("Mallilistauksen haku epäonnistui: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Yhdistetään hubiin
|
||||
loop {
|
||||
match connect_async(&hub_url).await {
|
||||
@@ -331,7 +348,7 @@ async fn main() {
|
||||
tracing::info!("Yhdistetty hubiin!");
|
||||
let (mut write, mut read) = ws_stream.split();
|
||||
|
||||
let auth = build_auth_message(allocated_gb, &active_model);
|
||||
let auth = build_auth_message(allocated_gb, &active_model, available_models.clone());
|
||||
if write.send(Message::Text(auth)).await.is_err() {
|
||||
tracing::error!("Auth-viestin lähetys epäonnistui");
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user