feat(ai): refine chat UX and add AI roadmap view
This commit is contained in:
parent
113cade1b2
commit
370007813d
@ -126,7 +126,7 @@ export function fallbackServices() {
|
|||||||
name: "AI Chat",
|
name: "AI Chat",
|
||||||
category: "ai",
|
category: "ai",
|
||||||
summary: "LLM chat (public beta)",
|
summary: "LLM chat (public beta)",
|
||||||
link: "/ai",
|
link: "/ai/chat",
|
||||||
host: "chat.ai.bstein.dev",
|
host: "chat.ai.bstein.dev",
|
||||||
status: "live",
|
status: "live",
|
||||||
},
|
},
|
||||||
@ -134,7 +134,7 @@ export function fallbackServices() {
|
|||||||
name: "AI Image",
|
name: "AI Image",
|
||||||
category: "ai",
|
category: "ai",
|
||||||
summary: "Visualization tool - Planned",
|
summary: "Visualization tool - Planned",
|
||||||
link: "/ai",
|
link: "/ai/roadmap",
|
||||||
host: "draw.ai.bstein.dev",
|
host: "draw.ai.bstein.dev",
|
||||||
status: "planned",
|
status: "planned",
|
||||||
},
|
},
|
||||||
@ -142,7 +142,7 @@ export function fallbackServices() {
|
|||||||
name: "AI Speech",
|
name: "AI Speech",
|
||||||
category: "ai",
|
category: "ai",
|
||||||
summary: "Live Translation - Planned",
|
summary: "Live Translation - Planned",
|
||||||
link: "/ai",
|
link: "/ai/roadmap",
|
||||||
host: "talk.ai.bstein.dev",
|
host: "talk.ai.bstein.dev",
|
||||||
status: "planned",
|
status: "planned",
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { createRouter, createWebHistory } from "vue-router";
|
|||||||
import HomeView from "./views/HomeView.vue";
|
import HomeView from "./views/HomeView.vue";
|
||||||
import AboutView from "./views/AboutView.vue";
|
import AboutView from "./views/AboutView.vue";
|
||||||
import AiView from "./views/AiView.vue";
|
import AiView from "./views/AiView.vue";
|
||||||
|
import AiPlanView from "./views/AiPlanView.vue";
|
||||||
import MoneroView from "./views/MoneroView.vue";
|
import MoneroView from "./views/MoneroView.vue";
|
||||||
|
|
||||||
export default createRouter({
|
export default createRouter({
|
||||||
@ -9,7 +10,9 @@ export default createRouter({
|
|||||||
routes: [
|
routes: [
|
||||||
{ path: "/", name: "home", component: HomeView },
|
{ path: "/", name: "home", component: HomeView },
|
||||||
{ path: "/about", name: "about", component: AboutView },
|
{ path: "/about", name: "about", component: AboutView },
|
||||||
{ path: "/ai", name: "ai", component: AiView },
|
{ path: "/ai", redirect: "/ai/chat" },
|
||||||
|
{ path: "/ai/chat", name: "ai-chat", component: AiView },
|
||||||
|
{ path: "/ai/roadmap", name: "ai-roadmap", component: AiPlanView },
|
||||||
{ path: "/monero", name: "monero", component: MoneroView },
|
{ path: "/monero", name: "monero", component: MoneroView },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
89
frontend/src/views/AiPlanView.vue
Normal file
89
frontend/src/views/AiPlanView.vue
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page">
|
||||||
|
<section class="card hero glass">
|
||||||
|
<div>
|
||||||
|
<p class="eyebrow">Atlas AI</p>
|
||||||
|
<h1>Roadmap</h1>
|
||||||
|
<p class="lede">
|
||||||
|
Chat is live today. Image generation and speech / translation will roll out next. This page tracks what’s planned and
|
||||||
|
what hardware it will land on.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="card grid">
|
||||||
|
<div class="track">
|
||||||
|
<div class="pill mono">AI Image</div>
|
||||||
|
<h3>Visualization</h3>
|
||||||
|
<p class="text">
|
||||||
|
Goal: small, fast image generation for diagrams, thumbnails, and mockups. Targeting Jetson nodes once stable. Output
|
||||||
|
will be gated to members only.
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>Models: open-source SD/FLUX variants distilled for 16GB GPUs.</li>
|
||||||
|
<li>Pipeline: upload prompt → queued job → signed URL in Nextcloud.</li>
|
||||||
|
<li>Status: planned (no UI yet).</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="track">
|
||||||
|
<div class="pill mono">AI Speech</div>
|
||||||
|
<h3>Voice + Translation</h3>
|
||||||
|
<p class="text">
|
||||||
|
Goal: low-latency ASR + TTS for meetings and media. Results should stream back into apps like Jitsi and Pegasus.
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>Models: whisper-style ASR, lightweight TTS with multilingual support.</li>
|
||||||
|
<li>Targets: titan-20/21 Jetsons for acceleration; fall back to CPU-only if needed.</li>
|
||||||
|
<li>Status: planned (no UI yet).</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="card">
|
||||||
|
<h2>What’s live now?</h2>
|
||||||
|
<p class="text">
|
||||||
|
Atlas AI chat is running on local GPU hardware at <code>chat.ai.bstein.dev</code>. The chat page streams responses and
|
||||||
|
reports latency per turn. As larger models come online on the Jetsons, the chat endpoint will be upgraded in-place.
|
||||||
|
</p>
|
||||||
|
<div class="pill mono">Next step: migrate chat to Jetsons when available</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page {
|
||||||
|
max-width: 1100px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 32px 22px 72px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.track {
|
||||||
|
border: 1px solid var(--card-border);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
background: rgba(255, 255, 255, 0.02);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 18px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -5,8 +5,8 @@
|
|||||||
<p class="eyebrow">Atlas AI</p>
|
<p class="eyebrow">Atlas AI</p>
|
||||||
<h1>Chat</h1>
|
<h1>Chat</h1>
|
||||||
<p class="lede">
|
<p class="lede">
|
||||||
Lightweight LLM running on titan-24 (RTX 3080, 8GB). Anyone can chat without auth. The client streams responses and
|
Lightweight LLM running on local GPU accelerated hardware. Anyone can chat without auth. The client streams responses
|
||||||
shows round-trip latency for each turn.
|
and shows round-trip latency for each turn.
|
||||||
</p>
|
</p>
|
||||||
<div class="pill mono pill-live">Online</div>
|
<div class="pill mono pill-live">Online</div>
|
||||||
</div>
|
</div>
|
||||||
@ -17,7 +17,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="fact">
|
<div class="fact">
|
||||||
<span class="label mono">GPU</span>
|
<span class="label mono">GPU</span>
|
||||||
<span class="value mono">titan-24 · 3080 (8GB)</span>
|
<span class="value mono">local GPU (dynamic)</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="fact">
|
<div class="fact">
|
||||||
<span class="label mono">Endpoint</span>
|
<span class="label mono">Endpoint</span>
|
||||||
@ -30,7 +30,7 @@
|
|||||||
<div class="chat-window" ref="chatWindow">
|
<div class="chat-window" ref="chatWindow">
|
||||||
<div v-for="(msg, idx) in messages" :key="idx" :class="['chat-row', msg.role]">
|
<div v-for="(msg, idx) in messages" :key="idx" :class="['chat-row', msg.role]">
|
||||||
<div class="bubble" :class="{ streaming: msg.streaming }">
|
<div class="bubble" :class="{ streaming: msg.streaming }">
|
||||||
<div class="role mono">{{ msg.role === 'assistant' ? 'ai' : 'you' }}</div>
|
<div class="role mono">{{ msg.role === 'assistant' ? 'Atlas AI' : 'you' }}</div>
|
||||||
<p>{{ msg.content }}</p>
|
<p>{{ msg.content }}</p>
|
||||||
<div v-if="msg.streaming" class="meta mono typing">streaming…</div>
|
<div v-if="msg.streaming" class="meta mono typing">streaming…</div>
|
||||||
<div v-else-if="msg.latency_ms" class="meta mono">{{ msg.latency_ms }} ms</div>
|
<div v-else-if="msg.latency_ms" class="meta mono">{{ msg.latency_ms }} ms</div>
|
||||||
@ -48,25 +48,17 @@
|
|||||||
v-model="draft"
|
v-model="draft"
|
||||||
placeholder="Ask anything about the lab or general topics..."
|
placeholder="Ask anything about the lab or general topics..."
|
||||||
rows="3"
|
rows="3"
|
||||||
|
@keydown="handleKeydown"
|
||||||
:disabled="sending"
|
:disabled="sending"
|
||||||
/>
|
/>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<span class="hint mono">Shift+Enter for newline</span>
|
<span class="hint mono">Enter to send · Shift+Enter for newline</span>
|
||||||
<button class="primary" type="submit" :disabled="sending || !draft.trim()">
|
<button class="primary" type="submit" :disabled="sending || !draft.trim()">
|
||||||
{{ sending ? "Sending..." : "Send" }}
|
{{ sending ? "Sending..." : "Send" }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="card info-card">
|
|
||||||
<h2>Notes</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Backend proxies requests to Ollama inside the cluster; no external calls are made.</li>
|
|
||||||
<li>Short-term context: the chat history in this page is sent each turn. Refresh clears it.</li>
|
|
||||||
<li>Future: swap in larger models on the Jetsons, add streaming and rate limits.</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -80,7 +72,7 @@ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|||||||
const messages = ref([
|
const messages = ref([
|
||||||
{
|
{
|
||||||
role: "assistant",
|
role: "assistant",
|
||||||
content: "Hi! I'm the Titan Lab assistant running on titan-24. How can I help?",
|
content: "Hi! I'm Atlas AI. How can I help?",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const draft = ref("");
|
const draft = ref("");
|
||||||
@ -162,6 +154,13 @@ async function typeReveal(entry, text) {
|
|||||||
}
|
}
|
||||||
entry.streaming = false;
|
entry.streaming = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleKeydown(e) {
|
||||||
|
if (e.key === "Enter" && !e.shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
sendMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user