Compare commits
10 Commits
7fcc97f525
...
8468724a4c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8468724a4c | ||
|
|
6ef71b7e5c | ||
|
|
b2ee8b9031 | ||
|
|
c1a5f8aff5 | ||
|
|
8ee997cb56 | ||
|
|
cd67562a67 | ||
|
|
1f85c03624 | ||
|
|
74a2045def | ||
|
|
9b2b7767b5 | ||
|
|
1718805978 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -42,4 +42,7 @@ Cargo.lock
|
|||||||
*.log
|
*.log
|
||||||
|
|
||||||
# Wanha versio
|
# Wanha versio
|
||||||
temp/
|
temp/
|
||||||
|
|
||||||
|
# Muut
|
||||||
|
zipit/**
|
||||||
@@ -230,6 +230,144 @@ mitä luokkia importata.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Rakennuspalaset vs. vapaa generointi
|
||||||
|
|
||||||
|
Kielimalli voi generoida koodia kahdella perustavanlaatuisesti eri tavalla.
|
||||||
|
Ymmärtäminen milloin kumpikin toimii on avain luotettavaan koodigenerointi-pipelineen.
|
||||||
|
|
||||||
|
### Tapa 1: Vapaa generointi (naivi)
|
||||||
|
|
||||||
|
LLM generoi jokaisen tiedoston tyhjästä. Prompti kuvaa mitä halutaan,
|
||||||
|
malli tuottaa koko tiedoston — importeista lähtien.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
P["Prompti"] --> LLM1["LLM: models.py"]
|
||||||
|
LLM1 --> V1{"Validointi"}
|
||||||
|
V1 -->|virhe| LLM1
|
||||||
|
V1 -->|ok| LLM2["LLM: schemas.py"]
|
||||||
|
LLM2 --> V2{"Validointi"}
|
||||||
|
V2 -->|virhe| LLM2
|
||||||
|
V2 -->|ok| LLM3["LLM: main.py"]
|
||||||
|
LLM3 --> V3{"..."}
|
||||||
|
|
||||||
|
style V1 fill:#1a1e2e,stroke:#f85149,color:#c9d1d9
|
||||||
|
style V2 fill:#1a1e2e,stroke:#f85149,color:#c9d1d9
|
||||||
|
style V3 fill:#1a1e2e,stroke:#f85149,color:#c9d1d9
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ongelma:** Pieni malli (0.5B–7B) tekee toistuvia rakenteellisia virheitä:
|
||||||
|
|
||||||
|
| Virhe | Esiintymistiheys | Selitys |
|
||||||
|
|-------|:---:|------|
|
||||||
|
| Puuttuva import | ~60% | `from datetime import date` unohtuu |
|
||||||
|
| SQLite `connect_args` | ~80% | Malli ei muista SQLite-erityisyyttä |
|
||||||
|
| Väärä Enum-käyttö | ~50% | Sekoittaa `sqlalchemy.Enum` ja `enum.Enum` |
|
||||||
|
| Poetry pyproject.toml:ssa | ~40% | Malli suosii Poetryä vaikka ohje sanoo uv |
|
||||||
|
| Testit kopioivat koko appin | ~70% | Malli ei osaa importata, luo uudet reitit |
|
||||||
|
|
||||||
|
Retry-loopilla (virhe → uusi yritys virheviestin kanssa) osa korjautuu,
|
||||||
|
mutta **sama malli toistaa samoja virheitä** koska ne johtuvat harjoitusdatasta.
|
||||||
|
7 tiedoston projekti vaatii 7–14 LLM-kutsua ja 80–120 sekuntia.
|
||||||
|
|
||||||
|
### Tapa 2: Rakennuspalaset (template pipeline)
|
||||||
|
|
||||||
|
LLM:ltä pyydetään **vain JSON-speksi** — entiteetit, kentät ja tyypit.
|
||||||
|
Koodi kootaan mekaanisesti valmiista pohjista joiden rakenne on todistettavasti
|
||||||
|
oikein.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
P["Projektin kuvaus"] --> LLM["LLM: JSON-speksi"]
|
||||||
|
LLM --> S["{ entities: [...] }"]
|
||||||
|
S --> T1["Template: models.py"]
|
||||||
|
S --> T2["Template: schemas.py"]
|
||||||
|
S --> T3["Template: main.py"]
|
||||||
|
S --> T4["Template: test_main.py"]
|
||||||
|
S --> T5["Template: Dockerfile"]
|
||||||
|
T1 & T2 & T3 & T4 & T5 --> D["Docker build + pytest"]
|
||||||
|
|
||||||
|
style LLM fill:#1a1e2e,stroke:#d29922,color:#c9d1d9
|
||||||
|
style S fill:#1a1e2e,stroke:#3fb950,color:#c9d1d9
|
||||||
|
style D fill:#1a1e2e,stroke:#58a6ff,color:#c9d1d9
|
||||||
|
```
|
||||||
|
|
||||||
|
**Idea:** Malli on hyvä päättämään *mitä* (entiteetit, kentät), mutta huono
|
||||||
|
muistamaan *miten* (importit, engine setup, testikonfiguraatio). Annetaan
|
||||||
|
mallin tehdä se missä se on hyvä, ja hoidetaan loput mekaanisesti.
|
||||||
|
|
||||||
|
### LLM:n ainoa tehtävä
|
||||||
|
|
||||||
|
Malli tuottaa JSON-rakenteen kuten:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"project_name": "todo-app",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"name": "Todo",
|
||||||
|
"table_name": "todos",
|
||||||
|
"fields": [
|
||||||
|
{"name": "title", "sa_type": "String(255)", "py_type": "str", "nullable": false},
|
||||||
|
{"name": "due_date", "sa_type": "Date", "py_type": "date | None", "nullable": true},
|
||||||
|
{"name": "status", "sa_type": "String(20)", "py_type": "str", "default": "pending"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"extra_imports": ["from datetime import date"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Tämä on yksinkertainen tehtävä jossa pienikin malli onnistuu luotettavasti:
|
||||||
|
entiteettien tunnistus projektin kuvauksesta ja kenttätyyppien valinta.
|
||||||
|
|
||||||
|
### Template täyttää loput
|
||||||
|
|
||||||
|
Jokainen template on kuin madlib — aukot täytetään speksin datalla:
|
||||||
|
|
||||||
|
**models.py template (yksinkertaistettu):**
|
||||||
|
```python
|
||||||
|
from sqlalchemy import create_engine, Column, Integer, {sa_types}
|
||||||
|
# ... aina samat importit, engine setup, SessionLocal ...
|
||||||
|
|
||||||
|
class {entity.name}(Base):
|
||||||
|
__tablename__ = "{entity.table_name}"
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
{field.name} = Column({field.sa_type}, nullable={field.nullable})
|
||||||
|
# ... jokainen kenttä speksistä ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Tulos: importit ovat aina oikein, `connect_args` on aina mukana,
|
||||||
|
testit importoivat aina `main.py`:stä eivätkä kopioi sitä.
|
||||||
|
|
||||||
|
### Vertailu: mittaustulokset
|
||||||
|
|
||||||
|
| | Vapaa generointi | Rakennuspalaset |
|
||||||
|
|---|:---:|:---:|
|
||||||
|
| LLM-kutsuja | 7–14 | **1** |
|
||||||
|
| Aika | 80–120s | **~20s** |
|
||||||
|
| Syntaksi OK | ~70% | **100%** |
|
||||||
|
| Docker build | vaihteleva | **100%** |
|
||||||
|
| Pytest läpi | 0% | **100%** |
|
||||||
|
| API toimii | ~30% | **100%** |
|
||||||
|
|
||||||
|
### Milloin kumpikin toimii
|
||||||
|
|
||||||
|
**Rakennuspalaset** kun:
|
||||||
|
- Projektin rakenne on tunnettu (FastAPI + SQLAlchemy CRUD)
|
||||||
|
- Laatu ja luotettavuus ovat tärkeitä
|
||||||
|
- Malli on pieni (0.5B–7B)
|
||||||
|
|
||||||
|
**Vapaa generointi** kun:
|
||||||
|
- Projektin rakenne on epätavallinen
|
||||||
|
- Tarvitaan custom-logiikkaa jota template ei kata
|
||||||
|
- Malli on riittävän iso (>70B tai pilvi-API)
|
||||||
|
|
||||||
|
Paras lopputulos syntyy yhdistelmällä: **rakennuspalaset perusrakenteelle,
|
||||||
|
vapaa generointi business-logiikalle**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Laadun parantaminen
|
## Laadun parantaminen
|
||||||
|
|
||||||
### 1. Isompi malli (suurin vaikutus)
|
### 1. Isompi malli (suurin vaikutus)
|
||||||
|
|||||||
BIN
network-poc/frontend/public/gecko_hero.webp
Normal file
BIN
network-poc/frontend/public/gecko_hero.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
@@ -1,37 +1,37 @@
|
|||||||
<!-- Agenttigalleria + konfigurointipaneeli -->
|
<!-- Agent gallery + configuration panel -->
|
||||||
<div style="display:flex;gap:16px;padding:10px 0;align-items:flex-start">
|
<div style="display:flex;gap:16px;padding:10px 0;align-items:flex-start">
|
||||||
<!-- Agenttilista (drag & drop) -->
|
<!-- Agenttilista (drag & drop) -->
|
||||||
<div id="agent-bar" style="display:flex;gap:6px;align-items:flex-end;flex-wrap:wrap">
|
<div id="agent-bar" style="display:flex;gap:6px;align-items:flex-end;flex-wrap:wrap">
|
||||||
<!-- Renderöidään JS:stä -->
|
<!-- Renderöidään JS:stä -->
|
||||||
</div>
|
</div>
|
||||||
<!-- + Lisää agentti -->
|
<!-- + Add agent -->
|
||||||
<div id="add-agent-btn" class="agent-avatar" onclick="addCustomAgent()" title="Lisää oma agentti" style="opacity:0.4">
|
<div id="add-agent-btn" class="agent-avatar" onclick="addCustomAgent()" title="Add custom agent" style="opacity:0.4">
|
||||||
<div style="width:48px;height:48px;border-radius:50%;border:2px dashed var(--border);display:flex;align-items:center;justify-content:center;font-size:24px;color:var(--border)">+</div>
|
<div style="width:48px;height:48px;border-radius:50%;border:2px dashed var(--border);display:flex;align-items:center;justify-content:center;font-size:24px;color:var(--border)">+</div>
|
||||||
<span style="font-size:10px;color:#8b949e;text-align:center;display:block">Lisää</span>
|
<span style="font-size:10px;color:#8b949e;text-align:center;display:block">Add</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Agentin konfigurointipaneeli (avautuu klikkaamalla avataria) -->
|
<!-- Agent configuration panel (opens clicking avatar) -->
|
||||||
<div id="agent-config" style="display:none;background:var(--panel);border:1px solid var(--border);border-radius:6px;padding:16px;margin-bottom:10px">
|
<div id="agent-config" style="display:none;background:var(--panel);border:1px solid var(--border);border-radius:6px;padding:16px;margin-bottom:10px">
|
||||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">
|
||||||
<div style="display:flex;align-items:center;gap:10px">
|
<div style="display:flex;align-items:center;gap:10px">
|
||||||
<img id="config-avatar" src="" style="width:40px;height:40px;border-radius:50%">
|
<img id="config-avatar" src="" style="width:40px;height:40px;border-radius:50%">
|
||||||
<div>
|
<div>
|
||||||
<input id="config-name" style="background:transparent;border:none;color:var(--text);font-size:16px;font-weight:600;outline:none;width:200px" placeholder="Agentin nimi">
|
<input id="config-name" style="background:transparent;border:none;color:var(--text);font-size:16px;font-weight:600;outline:none;width:200px" placeholder="Agent Name">
|
||||||
<div id="config-role" style="font-size:11px;color:#8b949e"></div>
|
<div id="config-role" style="font-size:11px;color:#8b949e"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;gap:6px">
|
<div style="display:flex;gap:6px">
|
||||||
<button class="btn btn-red" onclick="deleteAgent()" title="Poista agentti">Poista</button>
|
<button class="btn btn-red" onclick="deleteAgent()" title="Delete agent">Delete</button>
|
||||||
<button class="btn btn-muted" onclick="closeAgentConfig()">Sulje</button>
|
<button class="btn btn-muted" onclick="closeAgentConfig()">Close</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Malli -->
|
<!-- Model -->
|
||||||
<div style="margin-bottom:10px">
|
<div style="margin-bottom:10px">
|
||||||
<label style="font-size:12px;color:#8b949e;display:block;margin-bottom:4px">Kielimalli</label>
|
<label style="font-size:12px;color:#8b949e;display:block;margin-bottom:4px">Model</label>
|
||||||
<select id="config-model" style="background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:4px;padding:6px 10px;font-size:13px;width:100%">
|
<select id="config-model" style="background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:4px;padding:6px 10px;font-size:13px;width:100%">
|
||||||
<option value="qwen-coder">Qwen2.5-Coder:0.5B (selain)</option>
|
<option value="qwen-coder">Qwen2.5-Coder:0.5B (browser)</option>
|
||||||
<option value="qwen-coder-3b">Qwen2.5-Coder:3B (Ollama)</option>
|
<option value="qwen-coder-3b">Qwen2.5-Coder:3B (Ollama)</option>
|
||||||
<option value="qwen2.5-coder:7b">Qwen2.5-Coder:7B (Ollama)</option>
|
<option value="qwen2.5-coder:7b">Qwen2.5-Coder:7B (Ollama)</option>
|
||||||
<option value="qwen2.5-coder:1.5b">Qwen2.5-Coder:1.5B (Ollama)</option>
|
<option value="qwen2.5-coder:1.5b">Qwen2.5-Coder:1.5B (Ollama)</option>
|
||||||
@@ -39,41 +39,41 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- System prompt -->
|
<!-- 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.">
|
<div style="margin-bottom:10px" title="System prompt sent to the LLM on every request. Good prompt structure: 1. Role: 'You are an expert...' 2. Rules: RULES/CRITICAL RULES as list 3. Examples: EXAMPLE OUTPUT 4. Restrictions: NEVER-list ">
|
||||||
<label style="font-size:12px;color:#8b949e;display:block;margin-bottom:4px;cursor:help">System prompt 💡</label>
|
<label style="font-size:12px;color:#8b949e;display:block;margin-bottom:4px;cursor:help">System prompt 💡</label>
|
||||||
<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>
|
<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="Describe the agent's role and behavior..."></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Sampling-parametrit -->
|
<!-- Sampling Parameters -->
|
||||||
<div style="margin-bottom:10px">
|
<div style="margin-bottom:10px">
|
||||||
<label style="font-size:12px;color:#8b949e;display:block;margin-bottom:8px">Sampling-parametrit</label>
|
<label style="font-size:12px;color:#8b949e;display:block;margin-bottom:8px">Sampling Parameters</label>
|
||||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">
|
||||||
<div title="Kontrolloi 'luovuutta'. Matala arvo (0.2-0.4) tuottaa ennustettavaa, toistettavaa koodia — hyvä testaajille ja reviewereille. Keskiarvo (0.6-0.8) on paras koodin generointiin. Korkea arvo (1.0+) lisää vaihtelua mutta myös virheitä. Suositus: • Manageri: 0.5 (tarkat tiedostolistat) • Koodari: 0.7 (toimiva koodi + vaihtelu) • Testaaja: 0.3 (deterministinen arviointi)">
|
<div title="Controls 'creativity'. Low value (0.2-0.4) produces predictable, repeatable code — good for testers and reviewers. Medium value (0.6-0.8) is best for generating code. High value (1.0+) adds variation but also errors. Recommendation: • Manager: 0.5 (precise file lists) • Coder: 0.7 (working code + variation) • Tester: 0.3 (deterministic evaluation)">
|
||||||
<label style="font-size:11px;color:#8b949e;cursor:help">Temperature 💡 <span id="config-temp-val" style="color:var(--accent);float:right">0.7</span></label>
|
<label style="font-size:11px;color:#8b949e;cursor:help">Temperature 💡 <span id="config-temp-val" style="color:var(--accent);float:right">0.7</span></label>
|
||||||
<input type="range" id="config-temperature" min="0" max="1.5" step="0.1" value="0.7" style="width:100%;accent-color:var(--accent)">
|
<input type="range" id="config-temperature" min="0" max="1.5" step="0.1" value="0.7" style="width:100%;accent-color:var(--accent)">
|
||||||
<div style="font-size:10px;color:#30363d">0=tarkka · 0.7=oletus · 1.5=luova</div>
|
<div style="font-size:10px;color:#30363d">0=strict · 0.7=default · 1.5=creative</div>
|
||||||
</div>
|
</div>
|
||||||
<div title="Vastauksen maksimipituus tokeneina (~1 token ≈ 4 merkkiä). Suositus: • Manageri: 256-512 (lyhyet tiedostolistat) • Koodari: 1024-2048 (täydet tiedostot, CRUD-endpointit) • Testaaja: 256-512 (lyhyet arvioinnit) Jos koodi katkeaa kesken, nosta tätä. Jos malli tuottaa turhaa toistoa, laske.">
|
<div title="Maximum response length in tokens (~1 token ≈ 4 chars). Recommendation: • Manager: 256-512 (short lists) • Coder: 1024-2048 (full files, CRUD endpoints) • Tester: 256-512 (short evaluations) If code cuts off early, increase this.">
|
||||||
<label style="font-size:11px;color:#8b949e;cursor:help">Max tokens 💡 <span id="config-maxtok-val" style="color:var(--accent);float:right">1024</span></label>
|
<label style="font-size:11px;color:#8b949e;cursor:help">Max tokens 💡 <span id="config-maxtok-val" style="color:var(--accent);float:right">1024</span></label>
|
||||||
<input type="range" id="config-maxtokens" min="64" max="4096" step="64" value="1024" style="width:100%;accent-color:var(--accent)">
|
<input type="range" id="config-maxtokens" min="64" max="4096" step="64" value="1024" style="width:100%;accent-color:var(--accent)">
|
||||||
<div style="font-size:10px;color:#30363d">Vastauksen maksimipituus</div>
|
<div style="font-size:10px;color:#30363d">Maximum response length</div>
|
||||||
</div>
|
</div>
|
||||||
<div title="Montako todennäköisintä tokenia huomioidaan valinnassa. Pieni arvo (1-10) tekee vastauksesta deterministisen. Suuri arvo (50-100) sallii harvinaisempia sanoja. Suositus: • Boilerplate-koodi: 20-30 (tutut patternit) • Yleiskoodi: 40 (hyvä oletus) • Luova teksti: 60-80 Yleensä ei tarvitse muuttaa oletuksesta.">
|
<div title="How many most probable tokens are considered. Low value (1-10) makes response deterministic. High value (50-100) allows rarer words. Recommendation: • Boilerplate code: 20-30 (familiar patterns) • General code: 40 (good default) • Creative text: 60-80">
|
||||||
<label style="font-size:11px;color:#8b949e;cursor:help">Top-K 💡 <span id="config-topk-val" style="color:var(--accent);float:right">40</span></label>
|
<label style="font-size:11px;color:#8b949e;cursor:help">Top-K 💡 <span id="config-topk-val" style="color:var(--accent);float:right">40</span></label>
|
||||||
<input type="range" id="config-topk" min="1" max="100" step="1" value="40" style="width:100%;accent-color:var(--accent)">
|
<input type="range" id="config-topk" min="1" max="100" step="1" value="40" style="width:100%;accent-color:var(--accent)">
|
||||||
<div style="font-size:10px;color:#30363d">1=greedy · 40=oletus · 100=laaja</div>
|
<div style="font-size:10px;color:#30363d">1=greedy · 40=default · 100=wide</div>
|
||||||
</div>
|
</div>
|
||||||
<div title="Vähentää jo tuotettujen sanojen todennäköisyyttä. Estää mallia toistamasta samaa lausetta. Liian korkea arvo (>1.5) voi rikkoa koodin koska samat avainsanat (return, if, def) ovat tarpeellisia. Suositus: • Koodi: 1.1-1.2 (lievä, sallii toiston) • Teksti: 1.15-1.3 (vahvempi) • Review: 1.0-1.1 (ei rangaistusta, lyhyet vastaukset)">
|
<div title="Reduces the probability of already generated words. Prevents model from repeating same sentences. Too high value (>1.5) can break code because common keywords (return, if, def) are necessary. Recommendation: • Code: 1.1-1.2 (mild, allows repetition) • Text: 1.15-1.3 (stronger penalty) • Review: 1.0-1.1 (no penalty, short answers)">
|
||||||
<label style="font-size:11px;color:#8b949e;cursor:help">Repetition penalty 💡 <span id="config-rep-val" style="color:var(--accent);float:right">1.15</span></label>
|
<label style="font-size:11px;color:#8b949e;cursor:help">Repetition penalty 💡 <span id="config-rep-val" style="color:var(--accent);float:right">1.15</span></label>
|
||||||
<input type="range" id="config-repeat" min="1.0" max="2.0" step="0.05" value="1.15" style="width:100%;accent-color:var(--accent)">
|
<input type="range" id="config-repeat" min="1.0" max="2.0" step="0.05" value="1.15" style="width:100%;accent-color:var(--accent)">
|
||||||
<div style="font-size:10px;color:#30363d">1.0=ei · 1.15=oletus · 2.0=vahva</div>
|
<div style="font-size:10px;color:#30363d">1.0=none · 1.15=default · 2.0=strong</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Pipeline-järjestys -->
|
<!-- Pipeline order -->
|
||||||
<div>
|
<div>
|
||||||
<label style="font-size:12px;color:#8b949e;display:block;margin-bottom:4px">Pipeline-järjestys <span style="color:var(--border)">(vedä järjestääksesi)</span></label>
|
<label style="font-size:12px;color:#8b949e;display:block;margin-bottom:4px">Pipeline Order <span style="color:var(--border)">(drag to sort)</span></label>
|
||||||
<div id="config-pipeline" style="display:flex;gap:4px;flex-wrap:wrap"></div>
|
<div id="config-pipeline" style="display:flex;gap:4px;flex-wrap:wrap"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<!-- Monaco Editor paneeli -->
|
<!-- Monaco Editor paneeli -->
|
||||||
<div id="panel-editor" class="panel">
|
<div id="panel-editor" class="panel">
|
||||||
<div style="display:flex;height:calc(100vh - 200px);gap:0;border:1px solid var(--border);border-radius:6px;overflow:hidden">
|
<div style="display:flex;flex:1;min-height:0;gap:0;border:1px solid var(--border);border-radius:6px;overflow:hidden">
|
||||||
<div id="editor-filetree" style="width:200px;min-width:150px;background:var(--bg);border-right:1px solid var(--border);overflow-y:auto;font-family:'Courier New',monospace;font-size:13px">
|
<div id="editor-filetree" style="width:200px;min-width:150px;background:var(--bg);border-right:1px solid var(--border);overflow:auto;resize:horizontal;font-family:'Courier New',monospace;font-size:13px">
|
||||||
<div style="padding:10px 12px;color:#8b949e;font-size:11px;text-transform:uppercase;letter-spacing:0.5px;border-bottom:1px solid var(--border)">Tiedostot</div>
|
<div style="padding:10px 12px;color:#8b949e;font-size:11px;text-transform:uppercase;letter-spacing:0.5px;border-bottom:1px solid var(--border)">Tiedostot</div>
|
||||||
<div id="editor-file-list" style="padding:4px 0">
|
<div id="editor-file-list" style="padding:4px 0">
|
||||||
<div style="padding:8px 16px;color:#8b949e;font-size:12px">Generoi projekti:<br><code style="color:var(--accent)">kpn project "..."</code></div>
|
<div style="padding:8px 16px;color:#8b949e;font-size:12px">Generoi projekti:<br><code style="color:var(--accent)">kpn project "..."</code></div>
|
||||||
|
|||||||
@@ -58,6 +58,49 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Pipeline-rajoitteet -->
|
||||||
|
<div class="settings-section">
|
||||||
|
<h3 class="settings-title">Pipeline-rajoitteet</h3>
|
||||||
|
<p class="settings-desc">Projektin generoinnin rajat. Suuremmat arvot = rikkaampi output, hitaampi suoritus.</p>
|
||||||
|
<div class="settings-grid">
|
||||||
|
<div>
|
||||||
|
<label class="settings-label">Client: max sanat <span id="set-plc-words-val" class="settings-val">400</span></label>
|
||||||
|
<input type="range" id="set-plc-words" min="100" max="800" step="50" value="400" class="settings-slider">
|
||||||
|
<div class="settings-hint">Vaatimusmäärittelyn maksimipituus sanoina</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="settings-label">Client: max ominaisuudet <span id="set-plc-feats-val" class="settings-val">8</span></label>
|
||||||
|
<input type="range" id="set-plc-feats" min="3" max="15" step="1" value="8" class="settings-slider">
|
||||||
|
<div class="settings-hint">Montako ominaisuutta vaatimuksiin</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="settings-label">Manager: max tiedostot <span id="set-plc-mfiles-val" class="settings-val">8</span></label>
|
||||||
|
<input type="range" id="set-plc-mfiles" min="3" max="15" step="1" value="8" class="settings-slider">
|
||||||
|
<div class="settings-hint">Managerin suunnittelemien tiedostojen yläraja</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="settings-label">Vapaa tila: max tiedostot <span id="set-plc-ffiles-val" class="settings-val">8</span></label>
|
||||||
|
<input type="range" id="set-plc-ffiles" min="3" max="15" step="1" value="8" class="settings-slider">
|
||||||
|
<div class="settings-hint">Tiedostoraja kun ei mallipohjaa</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="settings-label">Review-kierrokset <span id="set-plc-review-val" class="settings-val">3</span></label>
|
||||||
|
<input type="range" id="set-plc-review" min="1" max="5" step="1" value="3" class="settings-slider">
|
||||||
|
<div class="settings-hint">Katselmointi-korjaus-syklien max määrä</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="settings-label">Terminaali: max rivit <span id="set-plc-term-val" class="settings-val">300</span></label>
|
||||||
|
<input type="range" id="set-plc-term" min="50" max="1000" step="50" value="300" class="settings-slider">
|
||||||
|
<div class="settings-hint">Terminaalin näyttämien rivien yläraja</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="settings-label">CrewAI: prompt-rivit <span id="set-plc-crew-val" class="settings-val">50</span></label>
|
||||||
|
<input type="range" id="set-plc-crew" min="10" max="200" step="10" value="50" class="settings-slider">
|
||||||
|
<div class="settings-hint">tasks.yaml:n promptin max rivimäärä</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Reset -->
|
<!-- Reset -->
|
||||||
<div style="margin-top:24px;padding-top:16px;border-top:1px solid var(--border)">
|
<div style="margin-top:24px;padding-top:16px;border-top:1px solid var(--border)">
|
||||||
<button class="btn btn-red" onclick="resetSettings()" style="padding:6px 16px">Palauta oletukset</button>
|
<button class="btn btn-red" onclick="resetSettings()" style="padding:6px 16px">Palauta oletukset</button>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -20,10 +20,24 @@ body {
|
|||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container { max-width: 1600px; margin: 0 auto; padding: 20px 40px; }
|
.container {
|
||||||
|
max-width: 1600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app.container {
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app:not(.active) { display: none; }
|
||||||
|
#landing.hidden { display: none; }
|
||||||
|
|
||||||
/* Tabs */
|
/* Tabs */
|
||||||
.tabs { display: flex; gap: 4px; margin-bottom: 16px; }
|
.tabs { display: flex; gap: 4px; margin-bottom: 16px; flex-shrink: 0; }
|
||||||
.tab {
|
.tab {
|
||||||
padding: 10px 20px; 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;
|
||||||
@@ -33,7 +47,7 @@ body {
|
|||||||
|
|
||||||
/* Panels */
|
/* Panels */
|
||||||
.panel { display: none; }
|
.panel { display: none; }
|
||||||
.panel.active { display: block; }
|
.panel.active { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow-y: auto; }
|
||||||
|
|
||||||
/* Status bar */
|
/* Status bar */
|
||||||
.status-bar {
|
.status-bar {
|
||||||
@@ -52,7 +66,7 @@ body {
|
|||||||
.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: 16px;
|
font-family: 'Courier New', monospace; font-size: 16px;
|
||||||
min-height: 400px; max-height: 70vh; overflow-y: auto;
|
flex: 1; min-height: 0; max-height: none; overflow-y: auto;
|
||||||
padding: 12px 16px;
|
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; }
|
||||||
@@ -81,6 +95,12 @@ body {
|
|||||||
}
|
}
|
||||||
.dd-item:hover, .dd-item.active { background: var(--border); color: var(--accent); }
|
.dd-item:hover, .dd-item.active { background: var(--border); color: var(--accent); }
|
||||||
|
|
||||||
|
#editor-file-list .dd-item {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
/* Pipeline progress */
|
/* Pipeline progress */
|
||||||
.pipeline-bar {
|
.pipeline-bar {
|
||||||
display: none; padding: 8px 14px; background: var(--bg);
|
display: none; padding: 8px 14px; background: var(--bg);
|
||||||
@@ -102,6 +122,7 @@ body {
|
|||||||
.project-tab {
|
.project-tab {
|
||||||
padding: 4px 10px; cursor: pointer; border-radius: 4px 4px 0 0;
|
padding: 4px 10px; cursor: pointer; border-radius: 4px 4px 0 0;
|
||||||
font-size: 12px; color: #8b949e;
|
font-size: 12px; color: #8b949e;
|
||||||
|
white-space: nowrap; flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.project-tab.active { background: var(--panel); color: var(--accent); border: 1px solid var(--border); border-bottom: none; }
|
.project-tab.active { background: var(--panel); color: var(--accent); border: 1px solid var(--border); border-bottom: none; }
|
||||||
|
|
||||||
@@ -165,6 +186,13 @@ body {
|
|||||||
.agent-avatar.active img {
|
.agent-avatar.active img {
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
box-shadow: 0 0 25px rgba(88,166,255,0.8);
|
box-shadow: 0 0 25px rgba(88,166,255,0.8);
|
||||||
|
animation: agentBlink 1.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes agentBlink {
|
||||||
|
0% { opacity: 0.8; box-shadow: 0 0 15px rgba(88,166,255,0.5); }
|
||||||
|
50% { opacity: 1.0; box-shadow: 0 0 35px rgba(88,166,255,1.0); }
|
||||||
|
100% { opacity: 0.8; box-shadow: 0 0 15px rgba(88,166,255,0.5); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Settings */
|
/* Settings */
|
||||||
@@ -195,6 +223,207 @@ body {
|
|||||||
display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
|
display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== LANDING PAGE ===== */
|
||||||
|
|
||||||
|
#landing {
|
||||||
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
|
min-height: 100vh;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-mesh {
|
||||||
|
position: fixed; inset: 0; z-index: -1;
|
||||||
|
background:
|
||||||
|
radial-gradient(ellipse 80% 60% at 20% 40%, rgba(255,107,0,0.08) 0%, transparent 70%),
|
||||||
|
radial-gradient(ellipse 60% 50% at 80% 20%, rgba(88,166,255,0.06) 0%, transparent 70%),
|
||||||
|
var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-nav {
|
||||||
|
padding: 20px 40px;
|
||||||
|
}
|
||||||
|
.landing-logo { text-decoration: none; font-size: 18px; font-weight: 700; }
|
||||||
|
.logo-accent { color: #ff6b00; }
|
||||||
|
.logo-sub { color: #8b949e; font-weight: 400; }
|
||||||
|
|
||||||
|
/* Hero */
|
||||||
|
.hero {
|
||||||
|
padding: 60px 40px 40px;
|
||||||
|
}
|
||||||
|
.hero-container {
|
||||||
|
max-width: 1200px; margin: 0 auto;
|
||||||
|
display: grid; grid-template-columns: 1fr 400px; gap: 60px; align-items: center;
|
||||||
|
}
|
||||||
|
.hero-title {
|
||||||
|
font-size: clamp(2rem, 4vw, 3rem); font-weight: 800;
|
||||||
|
line-height: 1.15; color: #e6edf3; margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.hero-divider {
|
||||||
|
width: 60px; height: 3px; background: #ff6b00;
|
||||||
|
border-radius: 2px; margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.hero-desc {
|
||||||
|
font-size: 1.05rem; color: #8b949e; line-height: 1.7; margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.hero-notice {
|
||||||
|
font-size: 0.9rem; color: #6e7681; line-height: 1.6;
|
||||||
|
border-left: 2px solid var(--border); padding-left: 12px; margin-bottom: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hero input */
|
||||||
|
.hero-input-group {
|
||||||
|
display: flex; gap: 8px; margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.hero-input {
|
||||||
|
flex: 1; padding: 14px 18px; font-size: 16px;
|
||||||
|
font-family: 'JetBrains Mono', 'Courier New', monospace;
|
||||||
|
background: var(--panel); color: var(--text);
|
||||||
|
border: 1px solid var(--border); border-radius: 8px;
|
||||||
|
outline: none; transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
.hero-input:focus {
|
||||||
|
border-color: #ff6b00; box-shadow: 0 0 0 3px rgba(255,107,0,0.15);
|
||||||
|
}
|
||||||
|
.hero-input::placeholder { color: #484f58; }
|
||||||
|
.hero-input.shake {
|
||||||
|
animation: shake 0.4s ease;
|
||||||
|
border-color: #f85149;
|
||||||
|
box-shadow: 0 0 0 3px rgba(248,81,73,0.2);
|
||||||
|
}
|
||||||
|
@keyframes shake {
|
||||||
|
0%, 100% { transform: translateX(0); }
|
||||||
|
20%, 60% { transform: translateX(-6px); }
|
||||||
|
40%, 80% { transform: translateX(6px); }
|
||||||
|
}
|
||||||
|
.hero-btn {
|
||||||
|
padding: 14px 28px; font-size: 16px; font-weight: 600;
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
background: #ff6b00; color: #fff; border: none; border-radius: 8px;
|
||||||
|
cursor: pointer; transition: background 0.2s, transform 0.1s;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.hero-btn:hover { background: #e05e00; transform: translateY(-1px); }
|
||||||
|
.hero-btn:active { transform: translateY(0); }
|
||||||
|
|
||||||
|
/* Example buttons */
|
||||||
|
.hero-examples { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
||||||
|
.hero-examples-label { color: #6e7681; font-size: 14px; margin-right: 4px; }
|
||||||
|
.example-btn {
|
||||||
|
padding: 8px 16px; font-size: 13px; font-family: 'Inter', sans-serif;
|
||||||
|
background: transparent; color: var(--accent);
|
||||||
|
border: 1px solid var(--border); border-radius: 6px;
|
||||||
|
cursor: pointer; transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.example-btn:hover {
|
||||||
|
border-color: var(--accent); background: rgba(88,166,255,0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hero orb */
|
||||||
|
.hero-orb-wrapper {
|
||||||
|
display: flex; justify-content: center; align-items: center;
|
||||||
|
}
|
||||||
|
.hero-orb {
|
||||||
|
width: 340px; height: 340px; border-radius: 50%;
|
||||||
|
background: radial-gradient(circle at 30% 30%, rgba(255,107,0,0.15) 0%, transparent 70%);
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
animation: orb-float 6s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
.hero-orb-img {
|
||||||
|
width: 100%; height: 100%; object-fit: contain;
|
||||||
|
filter: drop-shadow(0 0 40px rgba(255,107,0,0.25));
|
||||||
|
}
|
||||||
|
@keyframes orb-float {
|
||||||
|
0%, 100% { transform: translateY(0); }
|
||||||
|
50% { transform: translateY(-12px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* How section */
|
||||||
|
.how-section {
|
||||||
|
padding: 60px 40px;
|
||||||
|
background: rgba(22,27,34,0.6);
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
.how-container { max-width: 900px; margin: 0 auto; }
|
||||||
|
.how-title {
|
||||||
|
text-align: center; font-size: 1.5rem; font-weight: 700;
|
||||||
|
color: #e6edf3; margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
.how-steps {
|
||||||
|
display: grid; grid-template-columns: repeat(3, 1fr); gap: 32px;
|
||||||
|
}
|
||||||
|
.how-step {
|
||||||
|
text-align: center; padding: 24px;
|
||||||
|
background: var(--panel); border: 1px solid var(--border);
|
||||||
|
border-radius: 12px; transition: border-color 0.3s;
|
||||||
|
}
|
||||||
|
.how-step:hover { border-color: rgba(255,107,0,0.4); }
|
||||||
|
.how-step-num {
|
||||||
|
width: 40px; height: 40px; line-height: 40px;
|
||||||
|
border-radius: 50%; background: rgba(255,107,0,0.12);
|
||||||
|
color: #ff6b00; font-weight: 700; font-size: 18px;
|
||||||
|
margin: 0 auto 14px;
|
||||||
|
}
|
||||||
|
.how-step h3 { color: #e6edf3; font-size: 1rem; margin-bottom: 8px; }
|
||||||
|
.how-step p { color: #8b949e; font-size: 0.9rem; line-height: 1.5; }
|
||||||
|
|
||||||
|
/* Landing footer */
|
||||||
|
.landing-footer {
|
||||||
|
text-align: center; padding: 32px 40px;
|
||||||
|
color: #484f58; font-size: 13px;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
.landing-footer a { color: #8b949e; }
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media (max-width: 860px) {
|
||||||
|
.hero-container { grid-template-columns: 1fr; gap: 32px; }
|
||||||
|
.hero-orb-wrapper { order: -1; }
|
||||||
|
.hero-orb { width: 220px; height: 220px; }
|
||||||
|
.how-steps { grid-template-columns: 1fr; }
|
||||||
|
.hero-input-group { flex-direction: column; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== OPPIMISPOLKU ===== */
|
||||||
|
.learn-step {
|
||||||
|
margin: 12px 0; border: 1px solid var(--border);
|
||||||
|
border-radius: 8px; background: var(--panel); overflow: hidden;
|
||||||
|
}
|
||||||
|
.learn-step-header {
|
||||||
|
display: flex; align-items: center; gap: 12px;
|
||||||
|
padding: 12px 16px; cursor: pointer;
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
.learn-step-header:hover { background: rgba(88,166,255,0.04); }
|
||||||
|
.learn-step-num {
|
||||||
|
width: 28px; height: 28px; line-height: 28px; text-align: center;
|
||||||
|
border-radius: 50%; background: rgba(255,107,0,0.12);
|
||||||
|
color: #ff6b00; font-weight: 700; font-size: 13px; flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.learn-step-agent {
|
||||||
|
font-weight: 600; color: #e6edf3; font-size: 14px;
|
||||||
|
}
|
||||||
|
.learn-step-label {
|
||||||
|
color: #8b949e; font-size: 13px; margin-left: auto;
|
||||||
|
}
|
||||||
|
.learn-step-body {
|
||||||
|
display: none; padding: 0 16px 16px;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
.learn-step-body.open { display: block; }
|
||||||
|
.learn-section-title {
|
||||||
|
color: var(--accent); font-size: 12px; font-weight: 600;
|
||||||
|
text-transform: uppercase; letter-spacing: 0.5px;
|
||||||
|
margin: 14px 0 6px;
|
||||||
|
}
|
||||||
|
.learn-code {
|
||||||
|
font-family: 'JetBrains Mono', 'Courier New', monospace;
|
||||||
|
font-size: 12px; line-height: 1.6;
|
||||||
|
background: #010409; border: 1px solid var(--border);
|
||||||
|
border-radius: 6px; padding: 12px; overflow-x: auto;
|
||||||
|
max-height: 300px; overflow-y: auto; white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
/* Animations */
|
/* Animations */
|
||||||
@keyframes blink { 0%,100% { opacity:1 } 50% { opacity:0 } }
|
@keyframes blink { 0%,100% { opacity:1 } 50% { opacity:0 } }
|
||||||
@keyframes spin { to { transform: rotate(360deg) } }
|
@keyframes spin { to { transform: rotate(360deg) } }
|
||||||
|
|||||||
@@ -1011,15 +1011,15 @@ async fn handle_socket(socket: WebSocket, state: Arc<AppState>, ip: IpAddr) {
|
|||||||
if let Some(obj) = json.as_object_mut() {
|
if let Some(obj) = json.as_object_mut() {
|
||||||
let model = obj.get("model").and_then(|v| v.as_str()).unwrap_or("?");
|
let model = obj.get("model").and_then(|v| v.as_str()).unwrap_or("?");
|
||||||
let prompt = obj.get("prompt").and_then(|v| v.as_str()).unwrap_or("");
|
let prompt = obj.get("prompt").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
let response = obj.get("response").and_then(|v| v.as_str()).unwrap_or("");
|
let _response = obj.get("response").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
let tok_gen = obj.get("tokens_generated").and_then(|v| v.as_u64()).unwrap_or(0);
|
let tok_gen = obj.get("tokens_generated").and_then(|v| v.as_u64()).unwrap_or(0);
|
||||||
let duration = obj.get("duration_ms").and_then(|v| v.as_f64()).unwrap_or(0.0);
|
let duration = obj.get("duration_ms").and_then(|v| v.as_f64()).unwrap_or(0.0);
|
||||||
let tok_s = obj.get("tokens_per_sec").and_then(|v| v.as_f64()).unwrap_or(0.0);
|
let tok_s = obj.get("tokens_per_sec").and_then(|v| v.as_f64()).unwrap_or(0.0);
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
println!("\x1b[35m━━━ Solmu {} ━━━ {} ━━━\x1b[0m", node_id, model);
|
println!("\x1b[35m━━━ Solmu {} ━━━ {} ━━━\x1b[0m", node_id, model);
|
||||||
println!(" Prompt: \x1b[33m\"{}\"\x1b[0m", prompt);
|
let prompt_preview: String = prompt.chars().take(80).collect();
|
||||||
println!(" Vastaus: \x1b[32m{}\x1b[0m", response);
|
println!(" Prompt: \x1b[33m\"{}...\"\x1b[0m", prompt_preview);
|
||||||
println!(" {} tokenia | {:.0}ms | \x1b[36m{:.1} tok/s\x1b[0m", tok_gen, duration, tok_s);
|
println!(" {} tokenia | {:.0}ms | \x1b[36m{:.1} tok/s\x1b[0m", tok_gen, duration, tok_s);
|
||||||
|
|
||||||
state.db.increment_tasks(node_id);
|
state.db.increment_tasks(node_id);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{self, Event, EventStream, KeyCode},
|
event::{Event, EventStream, KeyCode},
|
||||||
execute,
|
execute,
|
||||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
};
|
};
|
||||||
|
|||||||
Binary file not shown.
Submodule projektit/projektiopinnot-1-datan-hallinta-ttm23sai added at c20e918b34
Reference in New Issue
Block a user