Files
ai-app/app/androidApp/src/test/kotlin/com/example/aiapp/SessionOrderTest.kt
T
iris-aiandClaude Opus 5 81ab564a09 Declare provider settings, and give the context figure a denominator
Two things a session could not say, and one it was saying wrongly.

**Every provider setting is reachable.** `-np 1`, the MTP draft depth, the
tool set, the sampling parameters -- most were hardcoded to what measured
best on this machine, which is right as a default and wrong as a constant:
the next machine has a different GPU and a different core count, and
nobody running this app can edit the source. `DriverKind::params` now
declares what a provider takes -- key, label, shape, what blank means, and
whether a change waits for a restart -- and the phone renders whatever
arrives, on the spawn form and in the session settings dialog. Adding a
setting to a driver is one entry in that table and no app change.
`POST /sessions/{id}/params` takes the whole map, so an absent key is the
instruction to unset; the sampling half applies at once and the session is
told in words which of the rest are waiting for a restart.

`tools` is one of them, because it is the biggest lever on a tight
context: the seven built-in definitions are ~1,300 tokens of every prompt
(2,191 against 887 with none). `"none"` omits the flag rather than passing
it on, since `--tools none` is `unknown tool "none"` and a server that
exits.

**The context figure has a denominator.** `Event::ContextWindow` carries
it, read from `llama-server`'s `/props` once the model is up -- the
measurement rather than the request, since a session that named no context
size gets the model's own. Neither coding CLI states its window, so those
keep the bare figure: "2,042" and "2,042 / 8,192" are deliberately
different-looking, and a missing ceiling is never drawn as a proportion of
an assumed one.

**And the numerator was wrong**, by the length of the last reply: it was
the prompt alone, so a five-word answer reported 2,042 against a slot
holding 2,355. It is the turn's total now, which matches `llama-server`'s
own `n_tokens` to within a token.

Two defects the review found, both of which would have shipped: changing
settings on a *stopped* session reported "no process running, so it can't
take new settings", when a stopped session is exactly when you would set
them for the next start; and `GET /tools` answers **403** rather than an
empty list on a server started without `--tools`, so reading it as a
failure made the no-tools session one that never started.

Verified against real models: settings spawned and changed live, the
restart note, a session with two tools and one with none, and the counter
checked against the server's own slot occupancy each time.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 13:58:08 -04:00

88 lines
3.1 KiB
Kotlin

package com.example.aiapp
import kotlin.test.Test
import kotlin.test.assertEquals
class SessionOrderTest {
@Test
fun `running sessions keep the order their agents were turned on in`() {
val first = session("first", status = "running", started = 10.0, lastActivity = 900.0)
val second =
session("second", status = "awaitingInput", started = 20.0, lastActivity = 20.0)
val third = session("third", status = "waiting", started = 30.0, lastActivity = 500.0)
assertEquals(
listOf("first", "second", "third"),
sessionsInListOrder(listOf(third, second, first)).map { it.id },
)
}
@Test
fun `a session started again joins the bottom of the running group`() {
val old = session("old", status = "idle", started = 10.0, lastActivity = 10.0)
val restarted = session("restarted", status = "idle", started = 99.0, lastActivity = 99.0)
assertEquals(
listOf("old", "restarted"),
sessionsInListOrder(listOf(restarted, old)).map { it.id },
)
}
@Test
fun `stopped sessions come after the running ones, most recent first`() {
val running = session("running", status = "idle", started = 100.0, lastActivity = 100.0)
val stale = session("stale", status = "exited", started = 1.0, lastActivity = 5.0)
val recent = session("recent", status = "exited", started = 2.0, lastActivity = 50.0)
assertEquals(
listOf("running", "recent", "stale"),
sessionsInListOrder(listOf(stale, recent, running)).map { it.id },
)
}
/**
* A session whose process nobody could ask about still has one, so it stays where it was rather
* than dropping into the stopped group the moment a machine goes quiet.
*/
@Test
fun `a session of unknown state is one of the running ones`() {
val unknown = session("unknown", status = "unknown", started = 10.0, lastActivity = 10.0)
val stopped = session("stopped", status = "exited", started = 5.0, lastActivity = 999.0)
assertEquals(
listOf("unknown", "stopped"),
sessionsInListOrder(listOf(stopped, unknown)).map { it.id },
)
}
private fun session(id: String, status: String, started: Double, lastActivity: Double) =
SessionSummary(
id = id,
machine = "machine",
machineName = "machine",
provider = "echo",
title = id,
model = null,
keepsOwnTranscript = false,
ownTranscriptName = null,
permissionMode = null,
effort = null,
takesEffort = false,
imported = false,
notify = true,
autoResume = false,
autoResumeMessage = DEFAULT_RESUME_MESSAGE,
resumeAt = null,
cwd = null,
contextTokens = null,
contextLimit = null,
params = emptyMap(),
maxImageEdge = null,
usageProvider = null,
status = status,
lastActivity = lastActivity,
started = started,
backgroundTasks = 0,
)
}