Refine lab status and AI defaults

This commit is contained in:
codex 2026-06-29 16:45:06 -03:00
parent 8703e6933a
commit 824a2187ea
8 changed files with 138 additions and 29 deletions

View File

@ -22,18 +22,18 @@ def register(app) -> None:
payload = request.get_json(silent=True) or {}
user_message = (payload.get("message") or "").strip()
profile = (payload.get("profile") or payload.get("mode") or "atlas-quick").strip().lower()
profile = (payload.get("profile") or payload.get("mode") or "atlas-smart").strip().lower()
conversation_id = payload.get("conversation_id") if isinstance(payload.get("conversation_id"), str) else ""
if not user_message:
return jsonify({"error": "message required"}), 400
started = time.time()
mode = "quick"
if profile in {"atlas-smart", "smart"}:
mode = "smart"
elif profile in {"atlas-genius", "genius"}:
mode = "smart"
if profile in {"atlas-genius", "genius"}:
mode = "genius"
elif profile in {"atlas-quick", "quick"}:
mode = "quick"
reply = _atlasbot_answer(user_message, mode, conversation_id)
source = f"atlas-{mode}"
if reply:
@ -67,7 +67,7 @@ def register(app) -> None:
def ai_info() -> Any:
"""Return model and placement metadata for the requested AI profile."""
profile = (request.args.get("profile") or "atlas-quick").strip().lower()
profile = (request.args.get("profile") or "atlas-smart").strip().lower()
meta = _discover_ai_meta(profile)
return jsonify(meta)

View File

@ -45,12 +45,28 @@ class AiRouteTests(TestCase):
self.assertEqual(data.get("source"), f"atlas-{expected_mode}")
self.assertEqual(data.get("reply"), f"{expected_mode}:conv-{profile}")
resp = self.client.post(
"/api/chat",
data=json.dumps(
{
"message": "How is Titan doing?",
"conversation_id": "conv-default",
}
),
content_type="application/json",
)
data = resp.get_json()
self.assertEqual(resp.status_code, 200)
self.assertEqual(data.get("source"), "atlas-smart")
self.assertEqual(data.get("reply"), "smart:conv-default")
self.assertEqual(
seen,
[
("quick", "conv-atlas-quick"),
("smart", "conv-atlas-smart"),
("genius", "conv-atlas-genius"),
("smart", "conv-default"),
],
)
@ -63,6 +79,14 @@ class AiRouteTests(TestCase):
self.assertEqual(data.get("profile"), "atlas-genius")
self.assertEqual(data.get("model"), "genius-model")
with mock.patch.object(ai.settings, "AI_ATLASBOT_MODEL_SMART", "smart-model"):
resp = self.client.get("/api/ai/info")
data = resp.get_json()
self.assertEqual(resp.status_code, 200)
self.assertEqual(data.get("profile"), "atlas-smart")
self.assertEqual(data.get("model"), "smart-model")
def test_atlasbot_answer_uses_profile_specific_timeout(self):
captured: dict[str, object] = {}

View File

@ -27,7 +27,7 @@
<div class="status-list">
<div v-for="item in statusItems" :key="item.label" class="status-row">
<span class="status-dot" :class="item.state" aria-hidden="true"></span>
<div>
<div class="status-row-copy">
<a v-if="item.labelHref" class="status-label status-title-link" :href="item.labelHref">
{{ item.label }}
</a>
@ -45,6 +45,13 @@
{{ item.value }}
</RouterLink>
<div v-else class="status-value mono">{{ item.value }}</div>
<div v-if="item.children?.length" class="status-children" :aria-label="`${item.label} details`">
<div v-for="child in item.children" :key="child.label" class="status-child">
<span class="status-child-dot" :class="child.state" aria-hidden="true"></span>
<span class="status-child-label">{{ child.label }}</span>
<span class="status-child-value mono">{{ child.value }}</span>
</div>
</div>
</div>
</div>
</div>
@ -230,10 +237,18 @@ function isExternal(href) {
background: var(--accent-cyan);
}
.status-dot.warn {
background: #f2c94c;
}
.status-dot.bad {
background: var(--accent-rose);
}
.status-row-copy {
min-width: 0;
}
.status-label {
font-weight: 800;
color: var(--text-strong);
@ -258,6 +273,45 @@ function isExternal(href) {
color: var(--accent-cyan);
}
.status-children {
display: grid;
gap: 6px;
margin-top: 10px;
}
.status-child {
display: grid;
grid-template-columns: 9px minmax(0, 1fr) auto;
gap: 8px;
align-items: center;
color: var(--text-muted);
font-size: 13px;
}
.status-child-dot {
width: 8px;
height: 8px;
border-radius: 999px;
background: var(--text-muted);
}
.status-child-dot.ok {
background: var(--accent-cyan);
}
.status-child-dot.warn {
background: #f2c94c;
}
.status-child-dot.bad {
background: var(--accent-rose);
}
.status-child-value {
color: var(--text-strong);
font-size: 12px;
}
.status-note {
margin: 14px 0 0;
}

View File

@ -85,11 +85,10 @@ const apiDisplay = apiUrl.host + apiUrl.pathname;
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const profiles = [
{ id: "atlas-quick", label: "Atlas Quick" },
{ id: "atlas-smart", label: "Atlas Smart" },
{ id: "atlas-genius", label: "Atlas Genius" },
];
const activeProfile = ref("atlas-quick");
const activeProfile = ref("atlas-smart");
const profileState = reactive(
Object.fromEntries(
profiles.map((profile) => [
@ -244,7 +243,8 @@ function handleKeydown(e) {
async function copyCurl() {
const target = current.value.meta.endpoint || apiUrl.toString();
const curl = `curl -X POST ${target} -H 'content-type: application/json' -d '{\"message\":\"hi\"}'`;
const body = JSON.stringify({ message: "hi", profile: activeProfile.value });
const curl = `curl -X POST ${target} -H 'content-type: application/json' -d '${body}'`;
try {
await navigator.clipboard.writeText(curl);
copied.value = true;

View File

@ -65,18 +65,19 @@ const mailtoHref = contactMailto();
const statusItems = computed(() => [
{
label: "Atlas platform",
label: "Atlas cluster",
labelHref: "#platform",
value: statusLabelFrom(contentStatus.value.atlas),
state: contentStatus.value.atlas,
},
{
label: "Dedicated hosts",
label: "Outpost hosts",
value: dedicatedHostsLabel.value,
state: dedicatedHostsState.value,
children: dedicatedHostItems.value,
},
{
label: "Most active service",
label: "Most requested service",
value: activeServiceLabel.value,
href: activeServiceHref.value,
state: activeServiceState.value,
@ -98,14 +99,27 @@ const activeServiceState = computed(() => (activeService.value?.known ? "ok" : "
const dedicatedHosts = computed(() => props.labStatus?.dedicated_hosts || null);
const dedicatedHostsState = computed(() => {
if (!dedicatedHosts.value?.known) return "unknown";
return dedicatedHosts.value.up ? "ok" : "bad";
const total = dedicatedHostTotal(dedicatedHosts.value);
const upCount = Number.isFinite(dedicatedHosts.value.up_count) ? dedicatedHosts.value.up_count : 0;
if (upCount <= 0) return "bad";
if (upCount < total) return "warn";
return "ok";
});
const dedicatedHostsLabel = computed(() => {
const hosts = dedicatedHosts.value;
if (!hosts?.known) return "Waiting for public checks";
const total = Number.isFinite(hosts.total) ? hosts.total : 2;
const total = dedicatedHostTotal(hosts);
const upCount = Number.isFinite(hosts.up_count) ? hosts.up_count : 0;
return `${upCount} of ${total} responding`;
return `${upCount} of ${total} online`;
});
const dedicatedHostItems = computed(() => {
const hosts = dedicatedHosts.value?.hosts;
if (!Array.isArray(hosts) || !hosts.length) return [];
return hosts.map((host) => ({
label: dedicatedHostLabel(host.label),
value: host.known ? (host.up ? "Online" : "Offline") : "Unknown",
state: statusState(host),
}));
});
const lastCheckedLabel = computed(() => {
@ -135,6 +149,18 @@ function statusLabelFrom(state) {
if (state === "bad") return "Needs attention";
return "Unknown";
}
function dedicatedHostTotal(hosts) {
if (Number.isFinite(hosts?.total)) return hosts.total;
return Array.isArray(hosts?.hosts) && hosts.hosts.length ? hosts.hosts.length : 2;
}
function dedicatedHostLabel(label = "") {
const normalized = label.toLowerCase();
if (normalized.includes("database") || normalized.includes("db")) return "Database machine";
if (normalized.includes("jump") || normalized.includes("theia")) return "Jumphost";
return label || "Host";
}
</script>
<style scoped>

View File

@ -85,7 +85,8 @@ test("shows Brad's professional homepage and opens the platform diagram overlay"
await expect(page.getByRole("link", { name: "Register" })).toBeVisible();
await expect(page.getByRole("heading", { name: "From 0 to 100" })).toBeVisible();
await expect(page.getByRole("link", { name: "Nextcloud", exact: true })).toBeVisible();
await expect(page.getByText("2 of 2 responding")).toBeVisible();
await expect(page.getByText("2 of 2 online")).toBeVisible();
await expect(page.getByText("Database machine")).toBeVisible();
await page.getByRole("link", { name: "Explore the Live Platform" }).click();
await expect(page.getByRole("heading", { name: "Titan Lab: Live Platform" })).toBeVisible();

View File

@ -55,7 +55,7 @@ describe("AI chat view", () => {
installFetch((url) => {
if (url.includes("/api/ai/info")) {
return jsonResponse({
model: url.includes("atlas-smart") ? "smart-model" : "quick-model",
model: url.includes("atlas-genius") ? "genius-model" : "smart-model",
gpu: "titan-24",
node: "titan-24",
endpoint: "https://ai.example.dev/chat",
@ -76,16 +76,18 @@ describe("AI chat view", () => {
const wrapper = mount(AiView);
await flushPromises();
expect(wrapper.text()).toContain("quick-model");
expect(wrapper.text()).not.toContain("Atlas Quick");
expect(wrapper.text()).toContain("smart-model");
expect(wrapper.text()).toContain("titan-24");
await wrapper.find(".endpoint-copy").trigger("click");
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(expect.stringContaining("curl -X POST https://ai.example.dev/chat"));
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(expect.stringContaining('"profile":"atlas-smart"'));
expect(wrapper.text()).toContain("copied");
await wrapper.findAll(".profile-tab").find((button) => button.text() === "Atlas Smart").trigger("click");
await wrapper.findAll(".profile-tab").find((button) => button.text() === "Atlas Genius").trigger("click");
await flushPromises();
expect(wrapper.text()).toContain("smart-model");
expect(wrapper.text()).toContain("genius-model");
Object.defineProperty(navigator, "clipboard", {
configurable: true,
@ -100,7 +102,7 @@ describe("AI chat view", () => {
it("sends JSON chat requests and reveals typed responses", async () => {
const bodies = [];
installFetch((url, options) => {
if (url.includes("/api/ai/info")) return jsonResponse({ model: "quick-model" });
if (url.includes("/api/ai/info")) return jsonResponse({ model: "smart-model" });
bodies.push(JSON.parse(options.body));
return jsonResponse({ reply: "typed assistant response", latency_ms: 42 });
});
@ -117,10 +119,10 @@ describe("AI chat view", () => {
expect(wrapper.text()).toContain("42 ms");
expect(bodies[0]).toMatchObject({
message: "hello atlas",
profile: "atlas-quick",
conversation_id: expect.stringContaining("atlas-quick"),
profile: "atlas-smart",
conversation_id: expect.stringContaining("atlas-smart"),
});
expect(localStorage.getItem("atlas-ai-conversation:atlas-quick")).toContain("uuid-1");
expect(localStorage.getItem("atlas-ai-conversation:atlas-smart")).toContain("uuid-1");
await wrapper.find("textarea").setValue("second");
await wrapper.find("form").trigger("submit.prevent");

View File

@ -96,11 +96,13 @@ describe("HomeView", () => {
expect(wrapper.text()).not.toContain("Looking for hosted services or an existing Titan Lab account");
expect(wrapper.find("a[href^='mailto:brad@bstein.dev']").exists()).toBe(true);
expect(wrapper.findComponent(PlatformSection).exists()).toBe(true);
expect(wrapper.text()).toContain("Atlas platform");
expect(wrapper.text()).toContain("Most active service");
expect(wrapper.text()).toContain("Atlas cluster");
expect(wrapper.text()).toContain("Most requested service");
expect(wrapper.text()).toContain("Nextcloud");
expect(wrapper.text()).toContain("Dedicated hosts");
expect(wrapper.text()).toContain("2 of 2 responding");
expect(wrapper.text()).toContain("Outpost hosts");
expect(wrapper.text()).toContain("2 of 2 online");
expect(wrapper.text()).toContain("Database machine");
expect(wrapper.text()).toContain("Jumphost");
expect(wrapper.text()).toContain("Responding");
});