3 Commits

Author SHA1 Message Date
66d1e8c4b1 Ollama-kutsut hubin kautta: ei mixed content HTTPS-sivulla
Lisätty GET /api/v1/ollama/tags proxy-endpoint hubiin.
Poistettu suorat http://hostname:11434 -kutsut frontendistä.
Hub välittää Ollama-kutsut sisäisessä Docker-verkossa.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 08:57:48 +03:00
2eeac255f6 Piilotetut paneelit tavoitettavissa hashilla: #network, #laskentaverkko, #codelab
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 08:52:57 +03:00
6097cfc263 Ylimääräiset rönsyt karsittu. Playground suht ok. 2026-04-07 08:51:47 +03:00
4 changed files with 25 additions and 16 deletions

View File

@@ -16,3 +16,4 @@ futures = "0.3"
rusqlite = { version = "0.31", features = ["bundled"] }
chrono = "0.4"
base64 = "0.22"
reqwest = { version = "0.12", features = ["json"] }

Binary file not shown.

View File

@@ -386,6 +386,7 @@ async fn main() {
.route("/api/v1/chat/completions", axum::routing::post(api_chat_completions))
.route("/api/v1/model", axum::routing::post(api_change_model))
.route("/api/v1/hardware", get(api_hardware))
.route("/api/v1/ollama/tags", get(api_ollama_tags))
.route("/admin", get(admin_page))
.nest_service("/", {
let static_dir = std::env::var("STATIC_DIR").unwrap_or_else(|_| "../static".to_string());
@@ -960,6 +961,20 @@ struct ChatCompletionResponse {
tokens_generated: u64,
}
async fn api_ollama_tags() -> axum::response::Response {
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) => {
if let Ok(body) = resp.json::<serde_json::Value>().await {
axum::Json(body).into_response()
} else {
axum::Json(serde_json::json!({ "models": [] })).into_response()
}
}
Err(_) => axum::Json(serde_json::json!({ "models": [] })).into_response(),
}
}
async fn api_hardware(
axum::extract::State(state): axum::extract::State<Arc<AppState>>,
) -> axum::response::Response {

View File

@@ -1708,8 +1708,9 @@ IMPORTANT: Include get_db() dependency for FastAPI` },
// URL-hash navigointi
const initHash = window.location.hash.replace('#', '');
if (['codelab', 'agents', 'guide'].includes(initHash)) {
switchMainTab(initHash);
const hashMap = { 'laskentaverkko': 'network', 'network': 'network', 'codelab': 'codelab', 'agents': 'agents', 'guide': 'guide' };
if (hashMap[initHash]) {
switchMainTab(hashMap[initHash]);
}
// Synkronoi coder-status kun WS on jo auki (suora #codelab navigointi)
@@ -2829,19 +2830,11 @@ ${generatedFiles['Dockerfile'] || '(puuttuu)'}`;
if (content) content.textContent = spinFrames[spinIdx] + ' Ladataan ' + selected.name + '...';
}, 100);
// Vaihdetaan malli hubille + Ollama pull
Promise.all([
fetch('/api/v1/model', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model: selected.name }),
}).then(r => r.json()),
// Suora pull Ollamasta — odotetaan kunnes malli on ladattu
fetch('http://' + window.location.hostname + ':11434/api/pull', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: selected.name, stream: false }),
}).then(r => r.json()).catch(() => ({ status: 'ok' })),
]).then(([hubData, _]) => {
fetch('/api/v1/model', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model: selected.name }),
}).then(r => r.json()).then(hubData => {
clearInterval(spinTimer);
pullLine.remove();
if (hubData.status === 'ok') {
@@ -2877,7 +2870,7 @@ ${generatedFiles['Dockerfile'] || '(puuttuu)'}`;
// Haetaan ladatut mallit Ollamasta
Promise.all([
fetch('/api/v1/hardware').then(r => r.json()).catch(() => ({})),
fetch('http://' + window.location.hostname + ':11434/api/tags').then(r => r.json()).catch(() => ({ models: [] })),
fetch('/api/v1/ollama/tags').then(r => r.json()).catch(() => ({ models: [] })),
]).then(([hw, ollama]) => {
const loadedNames = (ollama.models || []).map(m => m.name.replace(':latest', ''));
const btn = document.getElementById('agent-compute-btn');