15 Commits

Author SHA1 Message Date
Jaakko Vanhala
4e83569194 Konsoliloki näyttää mallin nimen: ✓ qwen2.5-coder:3b | 438 tok | 4952ms | 93.4 tok/s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 10:01:32 +03:00
Jaakko Vanhala
f42b692eeb Lyhennetty konsolilogi: yksi rivi per pyyntö + yksi rivi per tulos
Ennen: koko prompti + vastaus logitettiin (satoja rivejä)
Jälkeen:
  → task_id:abc | 42r prompti | "Write ONLY models.py..."
  ✓ 128 tok | 3200ms | 40.0 tok/s | "from sqlalchemy import..."

llm_done-viestissä prompt lyhennetty viimeiseen riviin (ei koko kontekstia).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 10:00:39 +03:00
Jaakko Vanhala
f79bb16f3d kipina-node binäärijakelu: download-skripti + macOS ARM64 binääri
kipina.studio/kipina-node — shell-skripti joka:
1. Tunnistaa OS/arch (macOS ARM, Linux x86/ARM)
2. Tarkistaa Ollaman (asennettu? käynnissä?)
3. Lataa kielimallin automaattisesti
4. Lataa oikean binäärin kipina.studio/download/
5. Käynnistää noden → yhdistää hubiin

Käyttö: curl -sSL https://kipina.studio/kipina-node | bash
Tai:    curl -sSL https://kipina.studio/kipina-node -o kipina-node && chmod +x kipina-node && ./kipina-node

build-binaries.sh — kääntää binäärit kaikille alustoille (Docker).
macOS ARM64 binääri (4.9MB) valmis, Linux x86_64 build käynnissä.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 09:51:31 +03:00
Jaakko Vanhala
e81fc33faf Join-dialogi: kaksi selkeää vaihetta (Ollama + kipina-node binääri)
Vaihe 1: Asenna Ollama
  curl -fsSL https://ollama.ai/install.sh | sh
  (+ brew/Windows-vaihtoehdot)

Vaihe 2: Lataa ja käynnistä kipina-node
  curl -sSL https://kipina.studio/kipina-node -o kipina-node && chmod +x kipina-node && ./kipina-node

Ei vaadi Rustia — valmis binääri ladataan suoraan.
Molemmat komennot kopioitavissa yhdellä klikkauksella.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 08:56:00 +03:00
Jaakko Vanhala
433726c553 Palautettu docker-compose.prod.yml: vain Caddy + Hub (ei Ollamaa palvelimella)
Ollama ajetaan käyttäjien omilla koneilla join.sh:n kautta,
ei palvelimella. Selain-Wasm toimii fallbackina.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 08:53:06 +03:00
Jaakko Vanhala
dec2e24e2f "Liitä koneesi" -nappi + join.sh + Docker native-node
UI: status-palkissa vihreä "+ Liitä koneesi" -nappi joka avaa dialogin:
  curl -sSL https://kipina.studio/join.sh | bash

join.sh:
- Tarkistaa Ollaman → tarjoaa asennusta jos puuttuu
- Käynnistää Ollaman jos ei pyöri
- Lataa kielimallin (qwen2.5-coder:3b)
- Käynnistää native-noden → yhdistää wss://kipina.studio/ws

Docker: Dockerfile.native + docker-compose.prod.yml päivitetty
ollama + native-node -konteilla palvelinpuolelle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 08:46:22 +03:00
Jaakko Vanhala
9058033669 Poistettu fonttiskaalaus (A-/A+) — ei vaikuttanut terminaaliin
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 08:33:20 +03:00
Jaakko Vanhala
8bd86e6325 Fonttikoon A-/A+ säädin: ±20% viidessä askeleessa
Oikeassa yläkulmassa A- ja A+ napit. Skaalaa 80-120%, tallennetaan localStorageen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 08:31:33 +03:00
Jaakko Vanhala
c1133bb075 Terminaalin fontti 15→16px
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 08:27:31 +03:00
Jaakko Vanhala
6502d75efc Terminaalin syöttökenttä korostettu: sininen reunus, varjo, isompi fontti 16px
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 08:25:37 +03:00
Jaakko Vanhala
9f8b7fe920 UI-fonttikoot kasvatettu: body 16px, terminaali 15px, tabit 15px, status 14px
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 08:23:46 +03:00
Jaakko Vanhala
746bc20fcb Agenttikuvakkeet kasvatettu: 50→64px kuva, 72→90px kortti, isompi fontti
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 08:22:42 +03:00
Jaakko Vanhala
93f6baa0ea UI kasvatettu: container 1200→1600px, terminaali korkeampi, padding leveämpi
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 08:18:34 +03:00
Jaakko Vanhala
cc8e871735 deploy-fast.sh: luo hakemisto palvelimelle ennen rsync:iä
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 08:15:19 +03:00
Jaakko Vanhala
e90f3460c3 deploy-fast.sh: päivitä vain frontend ilman kontin uudelleenkäynnistystä
docker-compose.prod.yml: frontend/dist mountataan volumena (read-only).
Hub servaa tiedostot suoraan — rsync päivittää ne lennossa.

Kolme deploy-tasoa:
1. deploy-fast.sh — vain frontend (sekunteja, ei downtime)
2. deploy-light.sh — rsync + remote Docker build (minuutteja)
3. deploy.sh — lokaali build + image siirto (hidas mutta varma)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 08:12:10 +03:00
12 changed files with 297 additions and 22 deletions

View File

@@ -0,0 +1,21 @@
# Native-node: Rust + Ollama-client (ei GPU-tunnistusta)
FROM rust:slim AS builder
RUN apt-get update && apt-get install -y pkg-config libssl-dev && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY Cargo.toml Cargo.lock* ./
COPY native-node/Cargo.toml native-node/Cargo.toml
COPY native-node/src native-node/src
# Dummy-cratet workspace-yhteensopivuuteen
COPY hub/Cargo.toml hub/Cargo.toml
COPY node/Cargo.toml node/Cargo.toml
COPY cli/Cargo.toml cli/Cargo.toml
RUN mkdir -p hub/src node/src cli/src && touch hub/src/main.rs node/src/lib.rs cli/src/main.rs
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/app/target \
cargo build --release -p native-node --no-default-features \
&& cp /app/target/release/native-node /usr/local/bin/native-node
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local/bin/native-node /usr/local/bin/native-node
CMD ["native-node"]

38
network-poc/build-binaries.sh Executable file
View File

@@ -0,0 +1,38 @@
#!/bin/bash
# Käännä kipina-node binäärit kaikille alustoille
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
OUT="$SCRIPT_DIR/frontend/public/download"
mkdir -p "$OUT"
echo "=== Kipinä Node — Binary Build ==="
# macOS ARM (natiivi)
echo "[1/3] 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..."
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 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-x86_64"
echo " $(ls -lh "$OUT/kipina-node-linux-x86_64" | awk '{print $5}')"
# Linux ARM64 (Docker)
echo "[3/3] Linux ARM64..."
docker run --rm \
-v "$SCRIPT_DIR":/app -w /app \
--platform linux/arm64 \
rust:slim \
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}')"
echo ""
echo "=== Binäärit valmiina ==="
ls -lh "$OUT"/kipina-node-*

28
network-poc/deploy-fast.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/bin/bash
# Nopea deploy: päivittää vain frontendin (ei kontin uudelleenkäynnistystä)
# Hub-binäärin päivitys: käytä deploy.sh tai deploy-light.sh
set -e
SERVER="ubuntu@86.50.252.98"
REMOTE_DIR="~/code/agentic-studio/network-poc"
SSH_OPTS="-o StrictHostKeyChecking=no"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
echo "=== Kipinä Studio — Frontend Deploy ==="
# 1. Buildaa frontend paikallisesti
echo "[1/2] Rakennetaan frontend..."
cd "$SCRIPT_DIR/frontend"
[ -d node_modules ] || npm install --silent
npm run build --silent 2>&1 | tail -1
# 2. Synkataan dist/ palvelimelle (vain muuttuneet tiedostot)
echo "[2/2] Synkataan dist/ → palvelin..."
ssh $SSH_OPTS $SERVER "mkdir -p $REMOTE_DIR/frontend/dist"
rsync -az --delete -e "ssh $SSH_OPTS" "$SCRIPT_DIR/frontend/dist/" "$SERVER:$REMOTE_DIR/frontend/dist/"
echo ""
echo "=== Valmis! Frontend päivitetty — ei uudelleenkäynnistystä ==="
echo " https://kipina.studio"
echo ""
echo "Huom: Jos Rust-koodi (hub/) muuttui, aja: ./deploy.sh"

View File

@@ -19,8 +19,12 @@ services:
restart: unless-stopped restart: unless-stopped
environment: environment:
- DATABASE_PATH=/data/nodes.db - DATABASE_PATH=/data/nodes.db
- STATIC_DIR=/app/frontend/dist
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-}
- NODE_API_KEY=${NODE_API_KEY:-}
volumes: volumes:
- hub_data:/data - hub_data:/data
- ./frontend/dist:/app/frontend/dist:ro
volumes: volumes:
caddy_data: caddy_data:

Binary file not shown.

View File

@@ -0,0 +1,73 @@
#!/bin/bash
# Kipinä — liitä koneesi laskentaverkkoon
set -e
HUB_URL="${KIPINA_HUB:-wss://kipina.studio/ws}"
MODEL="${KIPINA_MODEL:-qwen2.5-coder:3b}"
echo ""
echo " ╔══════════════════════════════════════╗"
echo " ║ Kipinä Agentic Network — Node Join ║"
echo " ╚══════════════════════════════════════╝"
echo ""
# 1. Ollama
if command -v ollama &>/dev/null; then
echo " ✓ Ollama löytyi: $(ollama --version 2>/dev/null || echo 'asennettu')"
else
echo " Ollama ei ole asennettu."
echo ""
read -p " Asennetaanko Ollama? (k/e) " -n 1 -r; echo
if [[ $REPLY =~ ^[Kk]$ ]]; then
echo " Asennetaan Ollama..."
curl -fsSL https://ollama.ai/install.sh | sh
else
echo " Ollama vaaditaan laskentaan. Asenna: https://ollama.ai"
exit 1
fi
fi
# 2. Varmistetaan että Ollama on käynnissä
if ! curl -s http://localhost:11434/api/tags &>/dev/null; then
echo " Käynnistetään Ollama..."
ollama serve &>/dev/null &
sleep 3
if ! curl -s http://localhost:11434/api/tags &>/dev/null; then
echo " ✗ Ollama ei käynnistynyt. Aja: ollama serve"
exit 1
fi
fi
echo " ✓ Ollama käynnissä"
# 3. Malli
if ollama list 2>/dev/null | grep -q "$MODEL"; then
echo " ✓ Malli $MODEL ladattu"
else
echo " Ladataan malli $MODEL..."
ollama pull "$MODEL"
fi
# 4. Native-node
echo ""
echo " Yhdistetään hubiin: $HUB_URL"
echo " Malli: $MODEL"
echo " Ctrl+C pysäyttää"
echo ""
# Tarkistetaan onko native-node käännetty
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
NATIVE_BIN="$SCRIPT_DIR/target/release/native-node"
if [ -f "$NATIVE_BIN" ]; then
HUB_URL="$HUB_URL" OLLAMA_MODEL="$MODEL" "$NATIVE_BIN"
elif command -v cargo &>/dev/null && [ -f "$SCRIPT_DIR/native-node/Cargo.toml" ]; then
echo " Käännetään native-node..."
cd "$SCRIPT_DIR"
cargo build --release -p native-node --no-default-features 2>&1 | tail -1
HUB_URL="$HUB_URL" OLLAMA_MODEL="$MODEL" "$NATIVE_BIN"
else
echo " ✗ native-node binääriä ei löydy eikä Rust ole asennettu."
echo " Asenna Rust: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
echo " Tai lataa valmis binääri: https://kipina.studio/download"
exit 1
fi

View File

@@ -0,0 +1,67 @@
#!/bin/bash
# Kipinä Node — lataa oikea binääri ja käynnistä
set -e
BASE_URL="https://kipina.studio/download"
HUB_URL="${KIPINA_HUB:-wss://kipina.studio/ws}"
MODEL="${KIPINA_MODEL:-qwen2.5-coder:3b}"
# Tunnista OS ja arkkitehtuuri
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
case "$OS-$ARCH" in
darwin-arm64) BINARY="kipina-node-macos-arm64" ;;
darwin-x86_64) BINARY="kipina-node-macos-arm64" ;; # Rosetta
linux-x86_64) BINARY="kipina-node-linux-x86_64" ;;
linux-aarch64) BINARY="kipina-node-linux-arm64" ;;
*) echo "Ei tuettu: $OS-$ARCH"; exit 1 ;;
esac
echo ""
echo " ╔══════════════════════════════════════╗"
echo " ║ Kipinä Agentic Node ║"
echo " ╚══════════════════════════════════════╝"
echo ""
echo " OS: $OS ($ARCH)"
echo " Hub: $HUB_URL"
echo " Malli: $MODEL"
echo ""
# Tarkista Ollama
if ! command -v ollama &>/dev/null; then
echo " ✗ Ollama ei ole asennettu."
echo " Asenna: curl -fsSL https://ollama.ai/install.sh | sh"
echo " macOS: brew install ollama"
exit 1
fi
# Varmista Ollama käynnissä
if ! curl -s http://localhost:11434/api/tags &>/dev/null; then
echo " Käynnistetään Ollama..."
ollama serve &>/dev/null &
sleep 3
fi
echo " ✓ Ollama käynnissä"
# Lataa malli
if ! ollama list 2>/dev/null | grep -q "$MODEL"; then
echo " Ladataan $MODEL..."
ollama pull "$MODEL"
fi
echo " ✓ Malli $MODEL valmis"
# Lataa binääri
BIN_PATH="./kipina-node-bin"
if [ ! -f "$BIN_PATH" ]; then
echo " Ladataan $BINARY..."
curl -sSL "$BASE_URL/$BINARY" -o "$BIN_PATH"
chmod +x "$BIN_PATH"
fi
echo ""
echo " ✓ Yhdistetään laskentaverkkoon..."
echo " Ctrl+C pysäyttää"
echo ""
HUB_URL="$HUB_URL" OLLAMA_MODEL="$MODEL" exec "$BIN_PATH"

View File

@@ -10,6 +10,39 @@
<span id="compute-dot" class="status-dot" style="background:#30363d"></span> <span id="compute-dot" class="status-dot" style="background:#30363d"></span>
<span style="color:#8b949e">Laskenta:</span> <span style="color:#8b949e">Laskenta:</span>
<span id="compute-label" style="color:#8b949e">—</span> <span id="compute-label" style="color:#8b949e">—</span>
<button id="compute-btn" class="btn btn-accent" title="Käynnistä kielimalli">Alusta</button> <button id="compute-btn" class="btn btn-accent" title="Käynnistä kielimalli selaimessa">Alusta</button>
</span>
<span class="status-separator">│</span>
<span class="status-group">
<button id="join-btn" class="btn btn-green" onclick="showJoinDialog()" title="Liitä oma koneesi laskentaverkkoon (natiivi, nopea)">+ Liitä koneesi</button>
</span> </span>
</div> </div>
<!-- Join-dialogi -->
<div id="join-dialog" style="display:none;margin-top:8px;padding:16px;background:var(--panel);border:1px solid var(--border);border-radius:6px;font-size:14px">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">
<span style="color:#e6edf3;font-weight:600;font-size:16px">Liitä koneesi laskentaverkkoon</span>
<button onclick="document.getElementById('join-dialog').style.display='none'" style="background:none;border:none;color:#8b949e;cursor:pointer;font-size:18px">✕</button>
</div>
<p style="color:#8b949e;margin-bottom:16px">Koneesi suorittaa tehtäviä ~10-50x nopeammin kuin selainlaskenta. Kaksi vaihetta:</p>
<!-- Vaihe 1: Ollama -->
<div style="margin-bottom:14px;padding:12px;background:var(--bg);border-radius:4px;border-left:3px solid var(--accent)">
<div style="color:#e6edf3;font-weight:600;margin-bottom:6px">1. Asenna Ollama <span style="color:#8b949e;font-weight:normal">(kielimallimoottori)</span></div>
<div style="display:flex;gap:6px;align-items:center;margin-bottom:6px">
<code style="flex:1;background:#010409;padding:8px 12px;border-radius:4px;color:var(--green);font-family:'Courier New',monospace;font-size:13px;user-select:all">curl -fsSL https://ollama.ai/install.sh | sh</code>
<button onclick="navigator.clipboard.writeText('curl -fsSL https://ollama.ai/install.sh | sh');this.textContent='✓';setTimeout(()=>this.textContent='Kopioi',1500)" class="btn btn-accent" style="padding:6px 10px">Kopioi</button>
</div>
<div style="color:#8b949e;font-size:12px">macOS: <code style="color:var(--accent)">brew install ollama</code> · Windows: <a href="https://ollama.ai/download" target="_blank" style="color:var(--accent)">ollama.ai/download</a> · Jos jo asennettu → siirry vaiheeseen 2.</div>
</div>
<!-- Vaihe 2: Kipinä-node -->
<div style="padding:12px;background:var(--bg);border-radius:4px;border-left:3px solid var(--green)">
<div style="color:#e6edf3;font-weight:600;margin-bottom:6px">2. Käynnistä Kipinä-node</div>
<div style="display:flex;gap:6px;align-items:center;margin-bottom:6px">
<code style="flex:1;background:#010409;padding:8px 12px;border-radius:4px;color:var(--green);font-family:'Courier New',monospace;font-size:13px;user-select:all">curl -sSL https://kipina.studio/kipina-node -o kipina-node && chmod +x kipina-node && ./kipina-node</code>
<button onclick="navigator.clipboard.writeText('curl -sSL https://kipina.studio/kipina-node -o kipina-node && chmod +x kipina-node && ./kipina-node');this.textContent='✓';setTimeout(()=>this.textContent='Kopioi',1500)" class="btn btn-green" style="padding:6px 10px">Kopioi</button>
</div>
<div style="color:#8b949e;font-size:12px">Lataa kielimallin (~2GB) automaattisesti ensimmäisellä kerralla. Ctrl+C pysäyttää.</div>
</div>
</div>

View File

@@ -53,6 +53,11 @@ import Settings from "../components/Settings.astro";
<script is:inline> <script is:inline>
// === Helpers === // === Helpers ===
window.showJoinDialog = function() {
const d = document.getElementById('join-dialog');
d.style.display = d.style.display === 'none' ? 'block' : 'none';
};
function esc(str) { function esc(str) {
if (!str) return ''; if (!str) return '';
return String(str).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); return String(str).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');

View File

@@ -14,19 +14,20 @@
body { body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 16px;
background: var(--bg); background: var(--bg);
color: var(--text); color: var(--text);
min-height: 100vh; min-height: 100vh;
} }
.container { max-width: 1200px; margin: 0 auto; padding: 20px; } .container { max-width: 1600px; margin: 0 auto; padding: 20px 40px; }
/* Tabs */ /* Tabs */
.tabs { display: flex; gap: 4px; margin-bottom: 16px; } .tabs { display: flex; gap: 4px; margin-bottom: 16px; }
.tab { .tab {
padding: 8px 16px; border-radius: 6px 6px 0 0; cursor: pointer; padding: 10px 20px; border-radius: 6px 6px 0 0; cursor: pointer;
border: 1px solid var(--border); border-bottom: none; border: 1px solid var(--border); border-bottom: none;
background: var(--bg); color: #8b949e; font-size: 14px; background: var(--bg); color: #8b949e; font-size: 15px;
} }
.tab.active { background: var(--panel); color: var(--accent); border-color: var(--border); } .tab.active { background: var(--panel); color: var(--accent); border-color: var(--border); }
@@ -37,9 +38,9 @@ body {
/* Status bar */ /* Status bar */
.status-bar { .status-bar {
display: flex; align-items: center; gap: 12px; display: flex; align-items: center; gap: 12px;
padding: 8px 14px; background: var(--bg); padding: 10px 16px; background: var(--bg);
border: 1px solid var(--border); border-radius: 6px 6px 0 0; border: 1px solid var(--border); border-radius: 6px 6px 0 0;
font-family: 'Courier New', monospace; font-size: 13px; font-family: 'Courier New', monospace; font-size: 14px;
} }
.status-dot { .status-dot {
width: 8px; height: 8px; border-radius: 50%; display: inline-block; width: 8px; height: 8px; border-radius: 50%; display: inline-block;
@@ -50,21 +51,22 @@ body {
/* Terminal */ /* Terminal */
.terminal { .terminal {
background: #010409; border: 1px solid var(--border); border-top: none; background: #010409; border: 1px solid var(--border); border-top: none;
font-family: 'Courier New', monospace; font-size: 14px; font-family: 'Courier New', monospace; font-size: 16px;
min-height: 300px; max-height: 60vh; overflow-y: auto; min-height: 400px; max-height: 70vh; overflow-y: auto;
padding: 8px 12px; padding: 12px 16px;
} }
.terminal-line { padding: 1px 0; white-space: pre-wrap; word-break: break-word; } .terminal-line { padding: 1px 0; white-space: pre-wrap; word-break: break-word; }
.terminal-prompt { color: var(--yellow); margin-right: 8px; } .terminal-prompt { color: var(--yellow); margin-right: 8px; }
.terminal-input-row { .terminal-input-row {
display: flex; align-items: center; position: relative; display: flex; align-items: center; position: relative;
background: #010409; border: 1px solid var(--border); border-top: none; background: #0d1117; border: 1px solid var(--accent); border-top: none;
border-radius: 0 0 6px 6px; padding: 8px 12px; border-radius: 0 0 6px 6px; padding: 10px 14px;
font-family: 'Courier New', monospace; font-size: 14px; font-family: 'Courier New', monospace; font-size: 15px;
box-shadow: 0 2px 8px rgba(88,166,255,0.1);
} }
.terminal-input { .terminal-input {
flex: 1; background: transparent; border: none; outline: none; flex: 1; background: transparent; border: none; outline: none;
color: var(--green); font-family: inherit; font-size: inherit; color: var(--green); font-family: inherit; font-size: 16px;
} }
.terminal-dropdown { .terminal-dropdown {
display: none; position: absolute; bottom: 100%; left: 30px; display: none; position: absolute; bottom: 100%; left: 30px;
@@ -128,10 +130,10 @@ body {
background: linear-gradient(145deg, rgba(33,38,45,0.4) 0%, rgba(13,17,23,0.8) 100%); background: linear-gradient(145deg, rgba(33,38,45,0.4) 0%, rgba(13,17,23,0.8) 100%);
backdrop-filter: blur(12px); backdrop-filter: blur(12px);
border: 1px solid rgba(240,246,252,0.1); border: 1px solid rgba(240,246,252,0.1);
border-radius: 12px; border-radius: 14px;
padding: 6px 6px 4px; padding: 8px 8px 6px;
text-align: center; text-align: center;
width: 72px; width: 90px;
opacity: 0.8; opacity: 0.8;
cursor: pointer; cursor: pointer;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
@@ -144,12 +146,12 @@ body {
box-shadow: 0 8px 14px rgba(0,0,0,0.4); box-shadow: 0 8px 14px rgba(0,0,0,0.4);
} }
.agent-avatar img { .agent-avatar img {
width: 50px; height: 50px; border-radius: 12px; width: 64px; height: 64px; border-radius: 14px;
margin-bottom: 4px; border: 2px solid rgba(240,246,252,0.1); margin-bottom: 4px; border: 2px solid rgba(240,246,252,0.1);
transition: all 0.4s ease; object-fit: cover; transition: all 0.4s ease; object-fit: cover;
} }
.agent-avatar .avatar-name { .agent-avatar .avatar-name {
font-size: 10px; color: #8b949e; white-space: nowrap; font-size: 11px; color: #8b949e; white-space: nowrap;
overflow: hidden; text-overflow: ellipsis; overflow: hidden; text-overflow: ellipsis;
} }
.agent-avatar.active { .agent-avatar.active {

View File

@@ -347,22 +347,26 @@ async fn main() {
if let Some(ref engine) = llm { if let Some(ref engine) = llm {
let max_tokens = task.get("max_tokens").and_then(|v| v.as_u64()).unwrap_or(1024) as usize; let max_tokens = task.get("max_tokens").and_then(|v| v.as_u64()).unwrap_or(1024) as usize;
tracing::info!("Generoidaan (task_id: {}, max_tokens: {}): \"{}\"", task_id, max_tokens, &prompt[..prompt.len().min(100)]); let prompt_lines = prompt.lines().count();
let prompt_last: String = prompt.lines().last().unwrap_or("").chars().take(60).collect();
tracing::info!("→ task_id:{} | {}r prompti | \"{}...\"", task_id, prompt_lines, prompt_last);
let model_name = engine.model_name(); let model_name = engine.model_name();
match engine.generate(prompt, max_tokens).await { match engine.generate(prompt, max_tokens).await {
Ok(result) => { Ok(result) => {
tracing::info!( tracing::info!(
"Tulos: {} tokenia | {:.0}ms | {:.1} tok/s | \"{}\"", "✓ {} | {} tok | {:.0}ms | {:.1} tok/s",
model_name,
result.tokens_generated, result.tokens_generated,
result.duration_ms, result.duration_ms,
result.tokens_per_sec, result.tokens_per_sec,
&result.text[..result.text.len().min(80)]
); );
// Lähetetään vain lyhyt prompti-esikatselu (ei koko kontekstia)
let prompt_short: String = prompt.lines().last().unwrap_or("").chars().take(100).collect();
let done = json!({ let done = json!({
"type": "llm_done", "type": "llm_done",
"prompt": prompt, "prompt": prompt_short,
"model": format!("{} (Ollama)", model_name), "model": format!("{} (Ollama)", model_name),
"response": result.text, "response": result.text,
"tokens_generated": result.tokens_generated, "tokens_generated": result.tokens_generated,