Compare commits
10 Commits
529a30a6e1
...
b8e8a83e49
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8e8a83e49 | ||
|
|
3d6914974d | ||
|
|
9aff2ec154 | ||
|
|
ecd4525a7f | ||
|
|
7a3e5278b9 | ||
|
|
8dcf269b42 | ||
|
|
cb16f35265 | ||
|
|
b9d340b4b4 | ||
|
|
dd07e536f0 | ||
|
|
9af481a022 |
@@ -1,47 +1,57 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM rust:slim AS builder
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl pkg-config libssl-dev g++ \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
# --- Vaihe 1: Frontend (Astro) ---
|
||||
FROM node:22-slim AS frontend
|
||||
WORKDIR /app/frontend
|
||||
COPY frontend/package.json frontend/package-lock.json* ./
|
||||
RUN npm install --silent
|
||||
COPY frontend/ .
|
||||
COPY frontend/public/pkg public/pkg
|
||||
RUN npm run build
|
||||
|
||||
# --- Vaihe 2: Wasm (wasm-pack) ---
|
||||
FROM rust:slim AS wasm-builder
|
||||
RUN apt-get update && apt-get install -y curl pkg-config libssl-dev g++ && rm -rf /var/lib/apt/lists/*
|
||||
RUN curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
WORKDIR /app
|
||||
COPY Cargo.toml Cargo.lock* ./
|
||||
COPY node/Cargo.toml node/Cargo.toml
|
||||
COPY node/src node/src
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
--mount=type=cache,target=/app/target \
|
||||
cd node && wasm-pack build --target web --out-dir /app/wasm-pkg
|
||||
|
||||
# Kopioi kaikki Cargo-tiedostot
|
||||
COPY Cargo.toml ./
|
||||
COPY Cargo.lock* ./
|
||||
# --- Vaihe 3: Hub (Rust) ---
|
||||
FROM rust:slim AS hub-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 hub/Cargo.toml hub/Cargo.toml
|
||||
COPY hub/src hub/src
|
||||
# Tarvitaan dummy-cratet jotta workspace kompiloi
|
||||
COPY node/Cargo.toml node/Cargo.toml
|
||||
COPY native-node/Cargo.toml native-node/Cargo.toml
|
||||
COPY cli/Cargo.toml cli/Cargo.toml
|
||||
|
||||
# Kopioi lähdekoodi
|
||||
COPY hub/src hub/src
|
||||
COPY node/src node/src
|
||||
COPY native-node/src native-node/src
|
||||
COPY cli/src cli/src
|
||||
COPY static static
|
||||
|
||||
# Rakenna Wasm — cache mount pitää Cargo-rekisterin ja target-kansion buildien välillä
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
--mount=type=cache,target=/app/target \
|
||||
cd node && wasm-pack build --target web --out-dir ../static/pkg
|
||||
|
||||
# Rakenna Hub
|
||||
RUN mkdir -p node/src native-node/src cli/src && touch node/src/lib.rs native-node/src/main.rs cli/src/main.rs
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
--mount=type=cache,target=/app/target \
|
||||
cargo build --release -p hub \
|
||||
&& cp /app/target/release/hub /usr/local/bin/hub
|
||||
|
||||
# --- Vaihe 4: Tuotantoimage ---
|
||||
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/hub /usr/local/bin/hub
|
||||
COPY --from=builder /app/static /app/static
|
||||
COPY --from=hub-builder /usr/local/bin/hub /usr/local/bin/hub
|
||||
COPY --from=frontend /app/frontend/dist /app/frontend/dist
|
||||
COPY --from=wasm-builder /app/wasm-pkg /app/frontend/dist/pkg
|
||||
|
||||
# Kopioidaan GUIDE.md ja templates
|
||||
COPY frontend/public/GUIDE.md /app/frontend/dist/GUIDE.md
|
||||
COPY frontend/public/templates /app/frontend/dist/templates
|
||||
COPY frontend/public/avatars /app/frontend/dist/avatars
|
||||
|
||||
WORKDIR /app
|
||||
ENV STATIC_DIR=/app/static
|
||||
ENV STATIC_DIR=/app/frontend/dist
|
||||
EXPOSE 3000
|
||||
CMD ["hub"]
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"pyproject.toml": {
|
||||
"description": "Project dependencies",
|
||||
"example": "[project]\nname = \"myapp\"\nversion = \"0.1.0\"\nrequires-python = \">=3.11\"\ndependencies = [\n \"fastapi\",\n \"uvicorn[standard]\",\n \"sqlalchemy\",\n]\n\n[project.scripts]\ndev = \"uvicorn main:app --reload\"",
|
||||
"instructions": "List the exact dependencies needed. Use [project.scripts] for run commands."
|
||||
"instructions": "Use [project] format (PEP 621, compatible with uv). List dependencies under [project.dependencies]. Add [project.scripts] with dev command. Never use requirements.txt or Poetry format. Run with: uv run uvicorn main:app --reload"
|
||||
}
|
||||
},
|
||||
"order": ["models.py", "schemas.py", "main.py", "pyproject.toml"]
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<!-- System prompt -->
|
||||
<div style="margin-bottom:10px" title="Agentin perusohje joka lähetetään kielimallille jokaisessa pyynnössä. Hyvän promptin rakenne: 1. Rooli: 'You are an expert...' 2. Säännöt: RULES/CRITICAL RULES listana 3. Esimerkit: EXAMPLE OUTPUT 4. Kiellot: NEVER-lista Vinkki: käytä englantia — malli ymmärtää sen paremmin ja se kuluttaa vähemmän tokeneita.">
|
||||
<label style="font-size:12px;color:#8b949e;display:block;margin-bottom:4px;cursor:help">System prompt 💡</label>
|
||||
<textarea id="config-prompt" rows="8" style="width:100%;background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:4px;padding:8px;font-size:13px;font-family:'Courier New',monospace;resize:vertical" placeholder="Kuvaa agentin rooli ja käyttäytyminen..."></textarea>
|
||||
<textarea id="config-prompt" style="width:100%;background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:4px;padding:8px;font-size:13px;font-family:'Courier New',monospace;resize:vertical;overflow:hidden;min-height:60px" placeholder="Kuvaa agentin rooli ja käyttäytyminen..."></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Sampling-parametrit -->
|
||||
|
||||
@@ -101,7 +101,9 @@ NEVER:
|
||||
- Add explanations or comments like "# Add routes here"
|
||||
- Leave placeholder code or TODO comments
|
||||
- Use Flask syntax (app.run) in FastAPI projects
|
||||
- Forget to import from other project files` },
|
||||
- Forget to import from other project files
|
||||
- Use requirements.txt or Poetry — always use pyproject.toml with [project] format (PEP 621)
|
||||
- Use pip install — use uv (e.g. uv run uvicorn main:app --reload)` },
|
||||
data: { name: 'Data', avatar: '/avatars/pesukarhu_notext.png', model: 'qwen-coder', order: 2,
|
||||
temperature: 0.5, topK: 40, repeatPenalty: 1.15, maxTokens: 1024,
|
||||
prompt: `You are a database architect specializing in SQLAlchemy and relational databases.
|
||||
@@ -138,16 +140,18 @@ TEST STRUCTURE:
|
||||
ALWAYS: from fastapi.testclient import TestClient` },
|
||||
tester: { name: 'DevOps', avatar: '/avatars/laiskiainen_notext.png', model: 'qwen-coder', order: 4,
|
||||
temperature: 0.3, topK: 40, repeatPenalty: 1.1, maxTokens: 512,
|
||||
prompt: `You are a strict code reviewer. Review the provided code and check for these issues:
|
||||
prompt: `You are a strict code reviewer and static analysis expert. Analyze the code line by line.
|
||||
|
||||
CHECKLIST:
|
||||
1. ✓ All imports exist (no missing "from X import Y")
|
||||
2. ✓ Import names match: if models.py exports "User", main.py imports "User" (not "UserModel")
|
||||
3. ✓ Pydantic schema names don't conflict with SQLAlchemy model names
|
||||
4. ✓ All CRUD endpoints have error handling (404 for not found)
|
||||
5. ✓ Database session is properly closed (get_db with yield + finally)
|
||||
6. ✓ Response models are specified for type safety
|
||||
7. ✓ No placeholder comments like "# Add routes here"
|
||||
STATIC ANALYSIS CHECKLIST:
|
||||
1. IMPORTS: Every "from X import Y" must match an actual export in file X
|
||||
2. NAMES: Pydantic schemas (UserCreate) must not shadow SQLAlchemy models (User)
|
||||
3. TYPES: All function parameters have type hints, return types specified
|
||||
4. ERRORS: Every db query that can return None has a 404 check
|
||||
5. RESOURCES: Database session uses yield+finally pattern (no leaks)
|
||||
6. SECURITY: No raw SQL, no hardcoded secrets, inputs validated via Pydantic
|
||||
7. ENDPOINTS: All CRUD operations exist (POST/GET/GET-by-id/PUT/DELETE)
|
||||
8. MODELS: Pydantic Config has from_attributes=True, uses model_dump() not dict()
|
||||
9. COMPLETENESS: No placeholder comments, no "TODO", no "pass" in handlers
|
||||
|
||||
RESPOND:
|
||||
- If all checks pass: "LGTM"
|
||||
@@ -254,7 +258,11 @@ OUTPUT FORMAT:
|
||||
document.getElementById('config-name').value = a.name;
|
||||
document.getElementById('config-role').textContent = key;
|
||||
document.getElementById('config-model').value = a.model;
|
||||
document.getElementById('config-prompt').value = a.prompt || '';
|
||||
const promptEl = document.getElementById('config-prompt');
|
||||
promptEl.value = a.prompt || '';
|
||||
// Auto-resize: textarea kasvaa sisällön mukaan
|
||||
promptEl.style.height = 'auto';
|
||||
promptEl.style.height = promptEl.scrollHeight + 'px';
|
||||
|
||||
// Sampling-parametrit
|
||||
const tempEl = document.getElementById('config-temperature');
|
||||
@@ -281,7 +289,7 @@ OUTPUT FORMAT:
|
||||
// Muutosten tallennus
|
||||
document.getElementById('config-name').oninput = () => { agents[key].name = document.getElementById('config-name').value; saveAgents(); renderAgentBar(); };
|
||||
document.getElementById('config-model').onchange = () => { agents[key].model = document.getElementById('config-model').value; saveAgents(); };
|
||||
document.getElementById('config-prompt').oninput = () => { agents[key].prompt = document.getElementById('config-prompt').value; saveAgents(); };
|
||||
promptEl.oninput = () => { agents[key].prompt = promptEl.value; saveAgents(); promptEl.style.height = 'auto'; promptEl.style.height = promptEl.scrollHeight + 'px'; };
|
||||
tempEl.oninput = () => { agents[key].temperature = +tempEl.value; tempValEl.textContent = tempEl.value; saveAgents(); };
|
||||
maxtokEl.oninput = () => { agents[key].maxTokens = +maxtokEl.value; maxtokValEl.textContent = maxtokEl.value; saveAgents(); };
|
||||
topkEl.oninput = () => { agents[key].topK = +topkEl.value; topkValEl.textContent = topkEl.value; saveAgents(); };
|
||||
@@ -724,7 +732,6 @@ OUTPUT FORMAT:
|
||||
|
||||
async function kpnProject(task) {
|
||||
const cdr = agents.coder || Object.values(agents)[1];
|
||||
const tst = agents.tester || Object.values(agents)[2];
|
||||
|
||||
// Etsitään sopivin mallipohja
|
||||
const template = Object.values(templates)[0]; // Toistaiseksi vain FastAPI CRUD
|
||||
@@ -791,43 +798,74 @@ OUTPUT FORMAT:
|
||||
const allCode = Object.entries(files).map(([n,c]) => `--- ${n} ---\n${c}`).join('\n\n');
|
||||
let stepN = template.order.length + 1;
|
||||
|
||||
// DevOps/Testaaja: koodikatselmointi
|
||||
// Review-korjausluuppi: max 2 kierrosta
|
||||
const tst = agents.tester || Object.values(agents)[4];
|
||||
termLog(`\n<span style="color:var(--accent);font-weight:bold">[${stepN}] ${esc(tst.name)}</span> — koodikatselmointi`);
|
||||
const MAX_REVIEW_ROUNDS = 3;
|
||||
|
||||
for (let round = 0; round < MAX_REVIEW_ROUNDS; round++) {
|
||||
const currentCode = Object.entries(files).map(([n,c]) => `--- ${n} ---\n${c}`).join('\n\n');
|
||||
|
||||
// DevOps review
|
||||
termLog(`\n<span style="color:var(--accent);font-weight:bold">[${stepN}] ${esc(tst.name)}</span> — koodikatselmointi${round > 0 ? ' (kierros '+(round+1)+')' : ''}`);
|
||||
highlightAgent('tester');
|
||||
explainStep('Koodikatselmointi', `${tst.name} tarkistaa importit, nimeämiset, virheenkäsittelyn ja tiedostojen yhteensopivuuden.`);
|
||||
const tstPrompt = (tst.prompt ? tst.prompt+'\n\n' : '') + `Review this project:\n\n${allCode}`;
|
||||
const review = await kpnRun(tst.model, tstPrompt);
|
||||
if (round === 0) explainStep('Koodikatselmointi', `${tst.name} analysoi koodin rivi riviltä: importit, nimeämiset, virheenkäsittely, tietoturva.`);
|
||||
else explainStep('Uudelleentarkistus', `${tst.name} tarkistaa korjaukset.`);
|
||||
|
||||
const reviewPrompt = (tst.prompt ? tst.prompt+'\n\n' : '') + `Review this project:\n\n${currentCode}`;
|
||||
const review = await kpnRun(tst.model, reviewPrompt);
|
||||
stepN++;
|
||||
|
||||
// Korjausluuppi (jos tarpeen)
|
||||
if (review && !review.toLowerCase().includes('lgtm')) {
|
||||
termLog(`\n<span style="color:#d29922;font-weight:bold">[${stepN}] ${esc(cdr.name)}</span> — korjaukset`);
|
||||
highlightAgent('coder');
|
||||
explainStep('Korjausluuppi', `${tst.name} löysi ongelmia. ${cdr.name} saa palautteen ja korjaa koodin.`);
|
||||
await kpnRun(cdr.model, `${cdr.prompt ? cdr.prompt+'\n\n' : ''}Fix these issues:\n${review}\n\nCurrent code:\n${allCode}\n\nWrite the corrected files.`);
|
||||
stepN++;
|
||||
// LGTM → ei korjauksia tarvita
|
||||
if (!review || review.toLowerCase().includes('lgtm')) {
|
||||
termLog(` <span style="color:#3fb950">✓ ${esc(tst.name)}: LGTM</span>`);
|
||||
break;
|
||||
}
|
||||
|
||||
// QA: testit
|
||||
// Korjaukset
|
||||
termLog(`\n<span style="color:#d29922;font-weight:bold">[${stepN}] ${esc(cdr.name)}</span> — korjaukset${round > 0 ? ' (kierros '+(round+1)+')' : ''}`);
|
||||
highlightAgent('coder');
|
||||
explainStep('Korjaus', `${tst.name} löysi ongelmia. ${cdr.name} saa palautteen ja korjaa.`);
|
||||
|
||||
const fixPrompt = `${cdr.prompt ? cdr.prompt+'\n\n' : ''}Fix these issues:\n${review}\n\nCurrent code:\n${currentCode}\n\nWrite ALL corrected files. Start each file with: --- filename.py ---`;
|
||||
const fixedCode = await kpnRun(cdr.model, fixPrompt);
|
||||
|
||||
// Parsitaan korjatut tiedostot takaisin files-objektiin
|
||||
if (fixedCode) {
|
||||
const fixedParts = fixedCode.split(/^---\s*(\S+)\s*---$/m);
|
||||
for (let j = 1; j < fixedParts.length; j += 2) {
|
||||
const fname = fixedParts[j].trim();
|
||||
const fcode = (fixedParts[j+1] || '').trim();
|
||||
if (fname && fcode && files[fname] !== undefined) {
|
||||
files[fname] = fcode;
|
||||
}
|
||||
}
|
||||
}
|
||||
stepN++;
|
||||
} // for review round
|
||||
|
||||
// Päivitetään allCode korjausten jälkeen
|
||||
const updatedCode = Object.entries(files).map(([n,c]) => `--- ${n} ---\n${c}`).join('\n\n');
|
||||
|
||||
// QA: testit (saa korjatut tiedostot)
|
||||
const qaAgent = agents.qa || Object.values(agents)[3];
|
||||
if (qaAgent) {
|
||||
termLog(`\n<span style="color:#d2a8ff;font-weight:bold">[${stepN}] ${esc(qaAgent.name)}</span> — testit`);
|
||||
highlightAgent('qa');
|
||||
explainStep('Testit', `${qaAgent.name} kirjoittaa pytest-testit kaikille endpointeille.`);
|
||||
const qaPrompt = (qaAgent.prompt ? qaAgent.prompt+'\n\n' : '') + `Write pytest tests for this project:\n\n${allCode}\n\nWrite a complete test_main.py file with TestClient.`;
|
||||
explainStep('Testit', `${qaAgent.name} kirjoittaa pytest-testit korjatulle koodille.`);
|
||||
const qaPrompt = (qaAgent.prompt ? qaAgent.prompt+'\n\n' : '') + `Write pytest tests for this project:\n\n${updatedCode}\n\nWrite a complete test_main.py file with TestClient.`;
|
||||
const tests = await kpnRun(qaAgent.model, qaPrompt);
|
||||
if (tests) files['test_main.py'] = tests;
|
||||
stepN++;
|
||||
}
|
||||
|
||||
// DevOps: Dockerfile
|
||||
// DevOps: Dockerfile (saa kaikki tiedostot mukaan lukien testit)
|
||||
const allFilesNow = Object.keys(files).join(', ');
|
||||
termLog(`\n<span style="color:var(--accent);font-weight:bold">[${stepN}] ${esc(tst.name)}</span> — Dockerfile`);
|
||||
highlightAgent('tester');
|
||||
explainStep('Dockerfile', `${tst.name} generoi Docker-kontin joka pakkaa projektin ajettavaksi.`);
|
||||
explainStep('Dockerfile', `${tst.name} generoi Docker-kontin kaikista ${Object.keys(files).length} tiedostosta: ${allFilesNow}`);
|
||||
const dockerPrompt = (tst.prompt ? tst.prompt+'\n\n' : '') +
|
||||
`Write a Dockerfile for this Python FastAPI project.\n\n` +
|
||||
`Project files: ${Object.keys(files).join(', ')}\n\n` +
|
||||
`Project files: ${allFilesNow}\n\n` +
|
||||
`Requirements:\n` +
|
||||
`- Use python:3.12-slim as base\n` +
|
||||
`- Install uv: COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv\n` +
|
||||
@@ -846,15 +884,29 @@ OUTPUT FORMAT:
|
||||
highlightAgent('observer');
|
||||
explainStep('Raportti', `${obs.name} kokoaa yhteenvedon ja antaa arvosanan.`);
|
||||
const finalCode = Object.entries(files).map(([n,c]) => `--- ${n} ---\n${c}`).join('\n\n');
|
||||
const fileList = Object.keys(files).join(', ');
|
||||
const obsPrompt = (obs.prompt ? obs.prompt+'\n\n' : '') +
|
||||
`Write a project README.md report in markdown for: ${task}\n\n` +
|
||||
`IMPORTANT: Start the FIRST LINE with exactly one of these verdicts:\n` +
|
||||
`VERDICT: GREEN — project is production-ready, no issues\n` +
|
||||
`VERDICT: ORANGE — project works but has warnings or improvements needed\n` +
|
||||
`VERDICT: RED — project has critical issues that must be fixed\n\n` +
|
||||
`Then include:\n` +
|
||||
`# Project: ${task}\n` +
|
||||
`## Files\n## How to run\n## API Endpoints\n## Architecture\n## Risk assessment\n\n` +
|
||||
`Write a project report in clean markdown for: ${task}\n\n` +
|
||||
`FIRST LINE must be exactly one of:\n` +
|
||||
`VERDICT: GREEN\nVERDICT: ORANGE\nVERDICT: RED\n\n` +
|
||||
`Then write this report:\n\n` +
|
||||
`# ${task}\n\n` +
|
||||
`## Overview\nOne paragraph describing what this project does.\n\n` +
|
||||
`## Files\n| File | Purpose |\n|------|---------|` +
|
||||
Object.entries(files).map(([n]) => `\n| ${n} | ... |`).join('') + `\n\n` +
|
||||
`## Quick Start\n` +
|
||||
'```bash\n' +
|
||||
`git clone <repo>\ncd project\nuv sync\nuv run uvicorn main:app --reload\n` +
|
||||
'```\n\n' +
|
||||
`## Docker\n` +
|
||||
'```bash\n' +
|
||||
`docker build -t ${task.toLowerCase().replace(/[^a-z0-9]/g, '-')} .\ndocker run -p 8000:8000 ${task.toLowerCase().replace(/[^a-z0-9]/g, '-')}\n` +
|
||||
'```\n\n' +
|
||||
`## API Endpoints\n| Method | Path | Description |\n|--------|------|-------------|` +
|
||||
`\n| POST | /items/ | Create |\n| GET | /items/ | List all |\n| GET | /items/{id} | Get by ID |\n| PUT | /items/{id} | Update |\n| DELETE | /items/{id} | Delete |\n` +
|
||||
`(Adapt paths and descriptions to match the actual code)\n\n` +
|
||||
`## Architecture\nDescribe the project structure and design decisions.\n\n` +
|
||||
`## Risk Assessment\n| Severity | Issue |\n|----------|-------|\n| ... | ... |\n\n` +
|
||||
`Project code:\n${finalCode}`;
|
||||
const readme = await kpnRun(obs.model, obsPrompt);
|
||||
if (readme) {
|
||||
|
||||
59
network-poc/install.sh
Executable file
59
network-poc/install.sh
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
# Kipinä Agentic Studio — asennusskripti (Debian/Ubuntu)
|
||||
set -e
|
||||
|
||||
echo "=== Kipinä Agentic Studio — Asennus ==="
|
||||
echo ""
|
||||
|
||||
# Tarkistetaan käyttöjärjestelmä
|
||||
if [ ! -f /etc/debian_version ]; then
|
||||
echo "⚠ Tämä skripti on suunniteltu Debian/Ubuntu-järjestelmille."
|
||||
echo " Muilla jakeluilla voit asentaa riippuvuudet manuaalisesti."
|
||||
read -p " Jatketaanko? (k/e) " -n 1 -r; echo
|
||||
[[ $REPLY =~ ^[Kk]$ ]] || exit 1
|
||||
fi
|
||||
|
||||
echo "[1/6] Päivitetään pakettilistaus..."
|
||||
sudo apt-get update -qq
|
||||
|
||||
echo "[2/6] Asennetaan peruspaketteja..."
|
||||
sudo apt-get install -y -qq curl git build-essential pkg-config libssl-dev
|
||||
|
||||
# Rust
|
||||
if command -v rustc &>/dev/null; then
|
||||
echo "[3/6] Rust löytyi: $(rustc --version)"
|
||||
else
|
||||
echo "[3/6] Asennetaan Rust..."
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
source "$HOME/.cargo/env"
|
||||
fi
|
||||
|
||||
# Node.js (Astro-frontend vaatii)
|
||||
if command -v node &>/dev/null; then
|
||||
echo "[4/6] Node.js löytyi: $(node --version)"
|
||||
else
|
||||
echo "[4/6] Asennetaan Node.js 22..."
|
||||
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
|
||||
sudo apt-get install -y -qq nodejs
|
||||
fi
|
||||
|
||||
# Ollama
|
||||
if command -v ollama &>/dev/null; then
|
||||
echo "[5/6] Ollama löytyi"
|
||||
else
|
||||
echo "[5/6] Asennetaan Ollama..."
|
||||
curl -fsSL https://ollama.ai/install.sh | sh
|
||||
fi
|
||||
|
||||
# Malli
|
||||
echo "[6/6] Ladataan kielimalli (qwen2.5-coder:3b)..."
|
||||
ollama pull qwen2.5-coder:3b
|
||||
|
||||
echo ""
|
||||
echo "=== Asennus valmis! ==="
|
||||
echo ""
|
||||
echo "Käynnistä:"
|
||||
echo " cd $(pwd)"
|
||||
echo " ./network-poc/local.sh"
|
||||
echo ""
|
||||
echo "Avaa selaimessa: http://localhost:3000"
|
||||
@@ -109,7 +109,7 @@ impl LlmEngine {
|
||||
.map_err(|e| format!("Ollama JSON: {}", e))?;
|
||||
|
||||
let text = body["response"].as_str().unwrap_or("").to_string();
|
||||
let total_duration_ns = body["total_duration"].as_u64().unwrap_or(0);
|
||||
let _total_duration_ns = body["total_duration"].as_u64().unwrap_or(0);
|
||||
let eval_count = body["eval_count"].as_u64().unwrap_or(0) as usize;
|
||||
let eval_duration_ns = body["eval_duration"].as_u64().unwrap_or(1);
|
||||
|
||||
@@ -129,23 +129,17 @@ impl LlmEngine {
|
||||
|
||||
/// Siivoa markdown-koodiblokki-merkit ja selitystekstit
|
||||
fn strip_code_fences(text: &str) -> String {
|
||||
let mut result = text.trim().to_string();
|
||||
|
||||
// Poista aloittava ```lang
|
||||
if result.starts_with("```") {
|
||||
if let Some(nl) = result.find('\n') {
|
||||
result = result[nl + 1..].to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// Poista sulkeva ```
|
||||
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();
|
||||
}
|
||||
// Poistetaan kaikki ```-rivit ja kielitunnisteet (```python, ```rust jne.)
|
||||
let lines: Vec<&str> = text.lines().collect();
|
||||
let filtered: Vec<&str> = lines.into_iter().filter(|line| {
|
||||
let trimmed = line.trim();
|
||||
// Poista rivit jotka ovat pelkkiä ``` tai ```kielitunniste
|
||||
if trimmed.starts_with("```") {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}).collect();
|
||||
let mut result = filtered.join("\n").trim().to_string();
|
||||
|
||||
// Poista selitysteksti lopusta (kaikki rivin "\nPlease note" jälkeen jne.)
|
||||
let lower = result.to_lowercase();
|
||||
|
||||
Reference in New Issue
Block a user