You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by gs...@apache.org on 2026/10/12 03:33:38 UTC
(tooling-llmao) 07/08: home: welcome newcomers with the steps to a first request
This is an automated email from the ASF dual-hosted git repository.
gstein pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-llmao.git
commit c33bf5006c9646976c62ddf9d9ad431e6e8f6bf1
Author: Greg Stein <gs...@gmail.com>
AuthorDate: Sun Oct 11 22:26:28 2026 -0500
home: welcome newcomers with the steps to a first request
The signed-in home page was a card saying "Signed in" with one button.
It is now a welcome and four numbered steps: sign in, create a key,
pick a model, connect a tool. Sign in shows as done once you have. The
abbreviation "PAT" is gone; the help pages never used it.
The model card's button said "Request a key" but created one directly,
while My Keys uses that phrase for the approval flow. It now says
"Create a key".
The create-key form preselects your project when you have only one.
Co-authored-by: Claude Opus 5.5 <no...@anthropic.com>
---
pages.py | 3 ++
static/css/llmao.css | 39 ++++++++++++++++++++++++++
templates/home.ezt | 69 ++++++++++++++++++++++++++++------------------
templates/key_create.ezt | 2 +-
templates/models.ezt | 4 +--
tests/test_help.py | 53 +++++++++++++++++++++++++++++++++++
tests/test_pages_models.py | 5 ++--
7 files changed, 143 insertions(+), 32 deletions(-)
diff --git a/pages.py b/pages.py
index 8699bda..2124eed 100644
--- a/pages.py
+++ b/pages.py
@@ -1555,6 +1555,9 @@ async def keys_new_form(result):
model = (quart.request.args.get("model") or "").strip()
result.for_model = model
result.has_for_model = ezt.boolean(bool(model))
+ # Nothing to choose between, so do not make them choose.
+ if not result.form_project and len(result.projects) == 1:
+ result.form_project = result.projects[0].name
return result
diff --git a/static/css/llmao.css b/static/css/llmao.css
index fab1fb6..3703ea2 100644
--- a/static/css/llmao.css
+++ b/static/css/llmao.css
@@ -6,6 +6,45 @@ body {
max-width: 40rem;
}
+/* Home: the steps, numbered down a rail. */
+.home-page .home-steps {
+ counter-reset: step;
+ max-width: 40rem;
+}
+.home-page .home-step {
+ counter-increment: step;
+ position: relative;
+ padding: 0 0 1.75rem 3rem;
+}
+.home-page .home-step::before {
+ content: counter(step);
+ position: absolute;
+ left: 0;
+ top: -0.2rem;
+ width: 2rem;
+ height: 2rem;
+ line-height: 2rem;
+ border-radius: 50%;
+ text-align: center;
+ font-weight: 600;
+ background: var(--bs-secondary-bg);
+ color: var(--bs-body-color);
+}
+/* The rail joining one number to the next. */
+.home-page .home-step:not(:last-child)::after {
+ content: "";
+ position: absolute;
+ left: 1rem;
+ top: 2rem;
+ bottom: 0.2rem;
+ border-left: 2px solid var(--bs-border-color);
+}
+.home-page .home-step-done::before {
+ content: "\2713";
+ background: var(--bs-success);
+ color: #fff;
+}
+
/* Dark grey bar (not pure black) so the logo disc edge stays visible. */
.navbar.llmao-navbar {
background-color: #343a40;
diff --git a/templates/home.ezt b/templates/home.ezt
index 38bf947..c3c551e 100644
--- a/templates/home.ezt
+++ b/templates/home.ezt
@@ -1,38 +1,53 @@
[include "header.ezt"]
-<div class="container py-4">
+<div class="container py-4 home-page">
<div class="mt-2">
[include "flashes.ezt"]
</div>
- <h1 class="h3 mb-3">Apache LLMAO</h1>
+ <h1 class="h3 mb-3">Welcome to Apache LLMAO</h1>
<p class="llmao-lede text-muted">
- The ASF's gateway to AI models. Sign in with your ASF account,
- create a personal access token (PAT), and use Foundation-sanctioned
- models from any OpenAI API-compatible tool, with your
- usage attributed to you.
- </p>
- <p class="text-muted">
- Point your tool at <span class="font-monospace">https://llm.apache.org</span>
- and use your PAT as the key. The endpoint follows the OpenAI API specification.
+ The ASF's gateway to AI models. Use Foundation-sanctioned models from
+ any OpenAI API-compatible tool, with your usage attributed to you.
+ Four steps get you there.
</p>
- [if-any uid]
- <div class="card mb-3" style="max-width: 28rem;">
- <div class="card-body">
- <h2 class="h6 card-title">Signed in</h2>
- <p class="card-text mb-0">
- <span class="font-monospace">[uid]</span>
- [if-any projects_label]
- <br><span class="text-muted small">projects: [projects_label]</span>
- [end]
+ <ol class="home-steps list-unstyled mt-4">
+ <li class="home-step[if-any uid] home-step-done[end]">
+ <h2 class="h6 mb-1">Sign in</h2>
+ [if-any uid]
+ <p class="text-muted mb-0">
+ Signed in as <span class="font-monospace">[uid]</span>[if-any projects_label]
+ · projects: [projects_label][end]
</p>
- <a class="btn btn-primary btn-sm mt-3" href="/keys">Manage my keys</a>
- </div>
- </div>
- [else]
- <p>
- <a class="btn btn-primary" href="/auth?login=/">Sign in with ASF</a>
- </p>
- [end]
+ [else]
+ <p class="text-muted mb-2">Use your ASF account.</p>
+ <a class="btn btn-primary btn-sm" href="/auth?login=/">Sign in with ASF</a>
+ [end]
+ </li>
+ <li class="home-step">
+ <h2 class="h6 mb-1">Create a key</h2>
+ <p class="text-muted mb-2">
+ A personal access token ties your usage to you and one of your
+ projects. The secret is shown once.
+ </p>
+ [if-any uid]<a class="btn btn-primary btn-sm" href="/keys">My Keys</a>[end]
+ </li>
+ <li class="home-step">
+ <h2 class="h6 mb-1">Pick a model</h2>
+ <p class="text-muted mb-2">
+ Each model lists its ID, its context window, and who can see
+ your prompts.
+ </p>
+ [if-any uid]<a class="btn btn-outline-primary btn-sm" href="/models">Models</a>[end]
+ </li>
+ <li class="home-step">
+ <h2 class="h6 mb-1">Connect a tool</h2>
+ <p class="text-muted mb-2">
+ Point it at <span class="font-monospace">https://llm.apache.org</span>
+ and use that token as the key. The endpoint follows the OpenAI API.
+ </p>
+ <a class="btn btn-outline-primary btn-sm" href="/help">Help</a>
+ </li>
+ </ol>
</div>
[include "footer.ezt"]
diff --git a/templates/key_create.ezt b/templates/key_create.ezt
index 1ec89c8..cefdc90 100644
--- a/templates/key_create.ezt
+++ b/templates/key_create.ezt
@@ -22,7 +22,7 @@
<select class="form-select" name="project" id="project" required>
<option value="">Select project…</option>
[for projects]
- <option value="[projects.name]">[projects.name]</option>
+ <option value="[projects.name]"[is projects.name form_project] selected[end]>[projects.name]</option>
[end]
</select>
</div>
diff --git a/templates/models.ezt b/templates/models.ezt
index 225db99..2aa609d 100644
--- a/templates/models.ezt
+++ b/templates/models.ezt
@@ -106,10 +106,10 @@
<span class="badge text-bg-success">[models.rollup_label]</span>
[end]
<a class="btn btn-outline-primary btn-sm"
- href="/keys/new?model=[models.model_name]">Request a key</a>
+ href="/keys/new?model=[models.model_name]">Create a key</a>
[else]
<span class="badge text-bg-secondary">Unavailable</span>
- <button type="button" class="btn btn-outline-secondary btn-sm" disabled>Request a key</button>
+ <button type="button" class="btn btn-outline-secondary btn-sm" disabled>Create a key</button>
[end]
<button type="button" class="btn btn-link btn-sm p-0 ms-auto"
data-bs-toggle="modal" data-bs-target="#modelDetailModal"
diff --git a/tests/test_help.py b/tests/test_help.py
index 25a9351..66aa49d 100644
--- a/tests/test_help.py
+++ b/tests/test_help.py
@@ -137,3 +137,56 @@ def test_home_page_does_not_list_api_paths():
assert "/healthz" not in text
assert "LiteLLM" not in text
assert "llm.apache.org" in text
+ assert "PAT" not in text
+
+
+def _frame(**extra):
+ data = dict(
+ title="t",
+ flashes=[],
+ uid=None,
+ name=None,
+ projects=[],
+ projects_label=None,
+ is_site_admin=ezt.boolean(False),
+ nav="",
+ login_back="/",
+ restarted_at="",
+ repo="https://example.test/llmao",
+ commit="abc",
+ can_create_automation=ezt.boolean(False),
+ )
+ data.update(extra)
+ return edict(data)
+
+
+def _generate(name: str, data) -> str:
+ template = ezt.Template(str(THIS_DIR / "templates" / name), base_format=ezt.FORMAT_HTML)
+ buf = io.StringIO()
+ template.generate(buf, data)
+ return buf.getvalue()
+
+
+def test_home_page_is_the_steps_to_a_first_request():
+ signed_out = _generate("home.ezt", _frame())
+ assert signed_out.count('<li class="home-step') == 4
+ assert "home-step-done" not in signed_out
+ assert 'href="/auth?login=/"' in signed_out
+ assert 'href="/help"' in signed_out
+
+ signed_in = _generate("home.ezt", _frame(uid="gstein", name="Greg", projects_label="infra"))
+ assert signed_in.count("home-step-done") == 1
+ assert "Signed in as" in signed_in
+ for href in ("/keys", "/models", "/help"):
+ assert f'btn-sm" href="{href}"' in signed_in
+
+
+def test_create_key_form_preselects_a_given_project():
+ common = dict(uid="gstein", name="Greg", form_purpose="", for_model="", has_for_model=ezt.boolean(False))
+ one = _generate("key_create.ezt", _frame(projects=[edict(name="infra")], form_project="infra", **common))
+ assert '<option value="infra" selected>infra</option>' in one
+ two = _generate(
+ "key_create.ezt",
+ _frame(projects=[edict(name="infra"), edict(name="tooling")], form_project="", **common),
+ )
+ assert " selected>" not in two
diff --git a/tests/test_pages_models.py b/tests/test_pages_models.py
index f5b07e4..0b4a240 100644
--- a/tests/test_pages_models.py
+++ b/tests/test_pages_models.py
@@ -230,7 +230,8 @@ def test_models_template_renders_the_catalog_row():
assert "<strong>Private.</strong>" in out
assert "4 long requests at once" in out
assert 'title="Accepts images"' in out
- assert "Request a key" in out
+ assert "Create a key" in out
+ assert "Request a key" not in out
def test_admin_rows_carry_their_models_deployments_and_summary():
@@ -565,7 +566,7 @@ def test_mixed_card_and_unavailable_card():
assert "you can't choose which" in out
assert "model-privacy-mixed" in out
# The unavailable card keeps its button, disabled, and says why once.
- assert "disabled>Request a key</button>" in out
+ assert "disabled>Create a key</button>" in out
assert out.count(">Unavailable<") == 1
assert "Unavailable right now" not in out
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org