@aria-framework/ai 0.26.1 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,182 +1,182 @@
1
- <%# One job, and the path of endpoints that serves it.
2
- ────────────────────────────────────────────────────────────────────────────────────────────
3
- TWO MODES, NOT A FOLD. A stack panel folds because its detail is worth reading occasionally; a
4
- job's detail is a thing you EDIT, and the editing controls are useless the other 99% of the
5
- time. So the card reads as a sentence — this job, this path, this is what it is for — until
6
- somebody says they want to change it.
7
-
8
- THE PATH IS THE HEADLINE, because it is the only part that varies and the only part anybody
9
- comes here to check. `a → b → c` is the whole configuration of a job in one line: the first
10
- that answers serves, the rest are its fallbacks in order.
11
-
12
- ORDER IS CARRIED IN TYPED POSITIONS, never in the order the browser serialises fields. A form
13
- posts in document order, so a list that relied on that would be silently un-reorderable — and
14
- the handler this posts to already reads `position_<id>` for exactly that reason. The list is
15
- renumbered from its own DOM order on submit, so what you see is what is sent.
16
-
17
- ASSIGNED IS NOT THE SAME AS RESOLVED. A job with no assignment is not unconfigured, it is
18
- following the default — every enabled endpoint in listed order — and saying so stops somebody
19
- "fixing" a job that is already doing the right thing. A job whose assigned endpoints are all
20
- disabled resolves to nothing at all, which is a different and much worse state.
21
-
22
- Locals:
23
- job { id, label, character, detail }
24
- assigned [endpointId] explicitly chosen for this job, in order (may be empty)
25
- resolved [endpointId] what would actually serve right now, in order
26
- endpoints [{ id, label, enabled }] every endpoint that exists
27
- spend { calls, total_tokens } | null
28
- spendDays how many days `spend` covers, for the label beside it (default 14)
29
- canEdit render the edit affordance at all
30
- csrfToken () => token
31
- basePath where this app mounts its AI admin, e.g. '/admin/ai'
32
- %>
33
- <%
34
- var _edit = (typeof canEdit !== 'undefined') && canEdit;
35
- var _base = (typeof basePath !== 'undefined' && basePath) ? basePath : '/admin/ai';
36
- var _csrf = (typeof csrfToken === 'function') ? csrfToken : function () { return ''; };
37
- var _assigned = assigned || [];
38
- var _resolved = resolved || [];
39
- var _eps = endpoints || [];
40
- var _spend = (typeof spend !== 'undefined') ? spend : null;
41
- // THE WINDOW THAT PRODUCED `spend`. Defaulted to what the label used to say as a literal, so a
42
- // consumer that passes nothing renders exactly as it did before.
43
- var _spendDays = (typeof spendDays !== 'undefined' && Number(spendDays) > 0) ? Number(spendDays) : 14;
44
- var _isDefault = !_assigned.length;
45
- var _byId = {};
46
- _eps.forEach(function (e) { _byId[e.id] = e; });
47
- // The path being EDITED is the assignment, not the resolution: editing what is currently serving
48
- // would silently drop a disabled endpoint from the assignment the moment anybody pressed Save.
49
- var _path = _assigned.slice();
50
- var _rest = _eps.filter(function (e) { return _path.indexOf(e.id) === -1; });
51
- %>
52
- <div class="border rounded mb-2" data-job="<%= job.id %>">
53
-
54
- <%# ── READING MODE ────────────────────────────────────────────────────────────────────────── %>
55
- <div class="p-3" data-job-view>
56
- <%# THE ACTIONS STAY ON THE RIGHT, whatever the description does.
57
- `flex-wrap` plus a growing child with no `min-width: 0` is the classic way to lose a
58
- button: a flex item will not shrink below its content's intrinsic width, so a long
59
- description keeps the text column at full width, the actions wrap onto a line of their own,
60
- and `justify-content-between` then puts that lone item at the START — bottom LEFT, which is
61
- precisely where the Edit button went.
62
- `min-width: 0` lets the text column shrink so its own text wraps instead; `flex-shrink-0`
63
- keeps the buttons their natural size; and with no wrapping there is no second line to fall
64
- onto. %>
65
- <div class="d-flex justify-content-between align-items-start gap-3">
66
- <div class="flex-grow-1" style="min-width:0">
67
- <div class="d-flex align-items-center gap-2 flex-wrap">
68
- <strong><%= job.label %></strong>
69
- <span class="badge border bg-body-tertiary text-body-secondary text-uppercase fw-semibold"><%= job.character %></span>
70
- <% if (_isDefault) { %>
71
- <span class="badge rounded-pill border bg-body-secondary text-body-secondary fw-semibold">default</span>
72
- <% } %>
73
- </div>
74
- <%# THE PATH, directly under the name — the one line that says how this job is configured. %>
75
- <div class="font-monospace small mt-1" style="word-break:break-all">
76
- <% if (_resolved.length) { %>
77
- <% _resolved.forEach(function (id, i) { %><% if (i) { %><span class="text-body-tertiary"> → </span><% } %><span class="<%= i ? 'text-body-secondary' : '' %>"><%= id %></span><% }) %>
78
- <% } else { %>
79
- <span class="text-danger">nothing available — every endpoint for this job is disabled or missing</span>
80
- <% } %>
81
- </div>
82
- <div class="form-text mt-1 mb-0"><%= job.detail %></div>
83
- </div>
84
-
85
- <div class="text-end d-flex flex-column align-items-end gap-2 flex-shrink-0">
86
- <%# WHAT THIS JOB COSTS, which is the number worth acting on: a job can be pointed at a
87
- cheaper machine, run less often, or switched off. An endpoint total only says where the
88
- money went, never what asked for it.
89
-
90
- THE WINDOW IS PRINTED FROM THE SAME PLACE THE FIGURE COMES FROM. It used to be the
91
- literal "14 days" here, while the figure beside it was aggregated by the consuming app
92
- over a window only that app knew — two halves of one sentence in two repositories. A
93
- consumer changing its window relabelled nothing, and every card then stated a period
94
- its own number had never covered. Support101 hit exactly that on the other side of the
95
- same screen, where a total labelled "14 days" came from an unwindowed query and ran
96
- 139,792 tokens adrift of the tab beside it. %>
97
- <% if (_spend && _spend.total_tokens) { %>
98
- <div class="small text-body-secondary" style="font-variant-numeric: tabular-nums">
99
- <%= Number(_spend.total_tokens).toLocaleString() %> tokens in <%= _spend.calls %> call<%= _spend.calls === 1 ? '' : 's' %>
100
- <span class="text-body-tertiary">· <%= _spendDays %> days</span>
101
- </div>
102
- <% } %>
103
- <% if (_edit && _eps.length) { %>
104
- <button type="button" class="btn btn-sm btn-outline-secondary" data-job-edit-open="<%= job.id %>">Edit</button>
105
- <% } %>
106
- </div>
107
- </div>
108
- </div>
109
-
110
- <%# ── EDITING MODE ───────────────────────────────────────────────────────────────────────────
111
- Rendered by the server and hidden, rather than built by script when Edit is pressed: the
112
- whole list is here in the markup, so it works from a keyboard, survives a script that fails
113
- to load badly enough to matter, and posts the same fields the handler has always read. %>
114
- <% if (_edit && _eps.length) { %>
115
- <form method="POST" action="<%= _base %>/routes/<%= encodeURIComponent(job.id) %>"
116
- class="p-3 border-top" data-job-edit hidden>
117
- <input type="hidden" name="_csrf" value="<%= _csrf() %>">
118
-
119
- <div class="d-flex justify-content-between align-items-baseline mb-2">
120
- <span class="small text-uppercase fw-semibold text-body-secondary" style="letter-spacing:.08em">
121
- <%= job.label %> — the path, in order
122
- </span>
123
- <span class="small text-body-tertiary">first to answer serves</span>
124
- </div>
125
-
126
- <ol class="list-unstyled mb-2" data-job-list>
127
- <% _path.forEach(function (id, i) { %>
128
- <% var ep = _byId[id]; %>
129
- <li class="d-flex align-items-center gap-2 border rounded px-2 py-2 mb-1 bg-body-tertiary"
130
- draggable="true" data-job-item="<%= id %>">
131
- <span class="text-body-tertiary" aria-hidden="true" style="cursor:grab">⠿</span>
132
- <span class="badge rounded-pill border bg-body-secondary text-body-secondary font-monospace fw-normal"
133
- data-job-rank><%= i + 1 %></span>
134
- <span class="flex-grow-1 font-monospace small">
135
- <%= id %>
136
- <% if (ep && ep.label && ep.label !== id) { %><span class="text-body-tertiary"> · <%= ep.label %></span><% } %>
137
- <%# AN ENDPOINT IN THE PATH BUT DISABLED IS KEPT, and said out loud. Dropping it on
138
- save would quietly rewrite somebody's configuration because a machine happened to
139
- be switched off this afternoon. %>
140
- <% if (ep && !ep.enabled) { %>
141
- <span class="badge rounded-pill border bg-warning-subtle text-warning-emphasis border-warning-subtle ms-1">disabled — skipped while off</span>
142
- <% } %>
143
- <% if (!ep) { %>
144
- <span class="badge rounded-pill border bg-danger-subtle text-danger-emphasis border-danger-subtle ms-1">no such endpoint</span>
145
- <% } %>
146
- </span>
147
- <span class="btn-group btn-group-sm" role="group">
148
- <button type="button" class="btn btn-outline-secondary" data-job-up
149
- aria-label="Move <%= id %> earlier">↑</button>
150
- <button type="button" class="btn btn-outline-secondary" data-job-down
151
- aria-label="Move <%= id %> later">↓</button>
152
- <button type="button" class="btn btn-outline-secondary" data-job-remove
153
- aria-label="Remove <%= id %> from this job">✕</button>
154
- </span>
155
- </li>
156
- <% }) %>
157
- </ol>
158
-
159
- <p class="form-text mt-0" data-job-empty<% if (_path.length) { %> hidden<% } %>>
160
- Nothing chosen, so this job uses <strong>every enabled endpoint</strong> in the order they
161
- are listed above. That is a working configuration, not a missing one.
162
- </p>
163
-
164
- <% if (_rest.length) { %>
165
- <div class="small text-uppercase fw-semibold text-body-secondary mt-3 mb-2" style="letter-spacing:.08em">Not in this job</div>
166
- <div class="d-flex flex-wrap gap-2" data-job-pool>
167
- <% _rest.forEach(function (e) { %>
168
- <button type="button" class="btn btn-sm btn-outline-secondary font-monospace"
169
- data-job-add="<%= e.id %>"<% if (!e.enabled) { %> disabled title="This endpoint is switched off"<% } %>>
170
- + <%= e.id %><% if (!e.enabled) { %> (disabled)<% } %>
171
- </button>
172
- <% }) %>
173
- </div>
174
- <% } %>
175
-
176
- <div class="d-flex gap-2 mt-3">
177
- <button type="submit" class="btn btn-sm btn-primary">Save</button>
178
- <button type="button" class="btn btn-sm btn-outline-secondary" data-job-cancel>Cancel</button>
179
- </div>
180
- </form>
181
- <% } %>
182
- </div>
1
+ <%# One job, and the path of endpoints that serves it.
2
+ ────────────────────────────────────────────────────────────────────────────────────────────
3
+ TWO MODES, NOT A FOLD. A stack panel folds because its detail is worth reading occasionally; a
4
+ job's detail is a thing you EDIT, and the editing controls are useless the other 99% of the
5
+ time. So the card reads as a sentence — this job, this path, this is what it is for — until
6
+ somebody says they want to change it.
7
+
8
+ THE PATH IS THE HEADLINE, because it is the only part that varies and the only part anybody
9
+ comes here to check. `a → b → c` is the whole configuration of a job in one line: the first
10
+ that answers serves, the rest are its fallbacks in order.
11
+
12
+ ORDER IS CARRIED IN TYPED POSITIONS, never in the order the browser serialises fields. A form
13
+ posts in document order, so a list that relied on that would be silently un-reorderable — and
14
+ the handler this posts to already reads `position_<id>` for exactly that reason. The list is
15
+ renumbered from its own DOM order on submit, so what you see is what is sent.
16
+
17
+ ASSIGNED IS NOT THE SAME AS RESOLVED. A job with no assignment is not unconfigured, it is
18
+ following the default — every enabled endpoint in listed order — and saying so stops somebody
19
+ "fixing" a job that is already doing the right thing. A job whose assigned endpoints are all
20
+ disabled resolves to nothing at all, which is a different and much worse state.
21
+
22
+ Locals:
23
+ job { id, label, character, detail }
24
+ assigned [endpointId] explicitly chosen for this job, in order (may be empty)
25
+ resolved [endpointId] what would actually serve right now, in order
26
+ endpoints [{ id, label, enabled }] every endpoint that exists
27
+ spend { calls, total_tokens } | null
28
+ spendDays how many days `spend` covers, for the label beside it (default 14)
29
+ canEdit render the edit affordance at all
30
+ csrfToken () => token
31
+ basePath where this app mounts its AI admin, e.g. '/admin/ai'
32
+ %>
33
+ <%
34
+ var _edit = (typeof canEdit !== 'undefined') && canEdit;
35
+ var _base = (typeof basePath !== 'undefined' && basePath) ? basePath : '/admin/ai';
36
+ var _csrf = (typeof csrfToken === 'function') ? csrfToken : function () { return ''; };
37
+ var _assigned = assigned || [];
38
+ var _resolved = resolved || [];
39
+ var _eps = endpoints || [];
40
+ var _spend = (typeof spend !== 'undefined') ? spend : null;
41
+ // THE WINDOW THAT PRODUCED `spend`. Defaulted to what the label used to say as a literal, so a
42
+ // consumer that passes nothing renders exactly as it did before.
43
+ var _spendDays = (typeof spendDays !== 'undefined' && Number(spendDays) > 0) ? Number(spendDays) : 14;
44
+ var _isDefault = !_assigned.length;
45
+ var _byId = {};
46
+ _eps.forEach(function (e) { _byId[e.id] = e; });
47
+ // The path being EDITED is the assignment, not the resolution: editing what is currently serving
48
+ // would silently drop a disabled endpoint from the assignment the moment anybody pressed Save.
49
+ var _path = _assigned.slice();
50
+ var _rest = _eps.filter(function (e) { return _path.indexOf(e.id) === -1; });
51
+ %>
52
+ <div class="border rounded mb-2" data-job="<%= job.id %>">
53
+
54
+ <%# ── READING MODE ────────────────────────────────────────────────────────────────────────── %>
55
+ <div class="p-3" data-job-view>
56
+ <%# THE ACTIONS STAY ON THE RIGHT, whatever the description does.
57
+ `flex-wrap` plus a growing child with no `min-width: 0` is the classic way to lose a
58
+ button: a flex item will not shrink below its content's intrinsic width, so a long
59
+ description keeps the text column at full width, the actions wrap onto a line of their own,
60
+ and `justify-content-between` then puts that lone item at the START — bottom LEFT, which is
61
+ precisely where the Edit button went.
62
+ `min-width: 0` lets the text column shrink so its own text wraps instead; `flex-shrink-0`
63
+ keeps the buttons their natural size; and with no wrapping there is no second line to fall
64
+ onto. %>
65
+ <div class="d-flex justify-content-between align-items-start gap-3">
66
+ <div class="flex-grow-1" style="min-width:0">
67
+ <div class="d-flex align-items-center gap-2 flex-wrap">
68
+ <strong><%= job.label %></strong>
69
+ <span class="badge border bg-body-tertiary text-body-secondary text-uppercase fw-semibold"><%= job.character %></span>
70
+ <% if (_isDefault) { %>
71
+ <span class="badge rounded-pill border bg-body-secondary text-body-secondary fw-semibold">default</span>
72
+ <% } %>
73
+ </div>
74
+ <%# THE PATH, directly under the name — the one line that says how this job is configured. %>
75
+ <div class="font-monospace small mt-1" style="word-break:break-all">
76
+ <% if (_resolved.length) { %>
77
+ <% _resolved.forEach(function (id, i) { %><% if (i) { %><span class="text-body-tertiary"> → </span><% } %><span class="<%= i ? 'text-body-secondary' : '' %>"><%= id %></span><% }) %>
78
+ <% } else { %>
79
+ <span class="text-danger">nothing available — every endpoint for this job is disabled or missing</span>
80
+ <% } %>
81
+ </div>
82
+ <div class="form-text mt-1 mb-0"><%= job.detail %></div>
83
+ </div>
84
+
85
+ <div class="text-end d-flex flex-column align-items-end gap-2 flex-shrink-0">
86
+ <%# WHAT THIS JOB COSTS, which is the number worth acting on: a job can be pointed at a
87
+ cheaper machine, run less often, or switched off. An endpoint total only says where the
88
+ money went, never what asked for it.
89
+
90
+ THE WINDOW IS PRINTED FROM THE SAME PLACE THE FIGURE COMES FROM. It used to be the
91
+ literal "14 days" here, while the figure beside it was aggregated by the consuming app
92
+ over a window only that app knew — two halves of one sentence in two repositories. A
93
+ consumer changing its window relabelled nothing, and every card then stated a period
94
+ its own number had never covered. Support101 hit exactly that on the other side of the
95
+ same screen, where a total labelled "14 days" came from an unwindowed query and ran
96
+ 139,792 tokens adrift of the tab beside it. %>
97
+ <% if (_spend && _spend.total_tokens) { %>
98
+ <div class="small text-body-secondary" style="font-variant-numeric: tabular-nums">
99
+ <%= Number(_spend.total_tokens).toLocaleString() %> tokens in <%= _spend.calls %> call<%= _spend.calls === 1 ? '' : 's' %>
100
+ <span class="text-body-tertiary">· <%= _spendDays %> days</span>
101
+ </div>
102
+ <% } %>
103
+ <% if (_edit && _eps.length) { %>
104
+ <button type="button" class="btn btn-sm btn-outline-secondary" data-job-edit-open="<%= job.id %>">Edit</button>
105
+ <% } %>
106
+ </div>
107
+ </div>
108
+ </div>
109
+
110
+ <%# ── EDITING MODE ───────────────────────────────────────────────────────────────────────────
111
+ Rendered by the server and hidden, rather than built by script when Edit is pressed: the
112
+ whole list is here in the markup, so it works from a keyboard, survives a script that fails
113
+ to load badly enough to matter, and posts the same fields the handler has always read. %>
114
+ <% if (_edit && _eps.length) { %>
115
+ <form method="POST" action="<%= _base %>/routes/<%= encodeURIComponent(job.id) %>"
116
+ class="p-3 border-top" data-job-edit hidden>
117
+ <input type="hidden" name="_csrf" value="<%= _csrf() %>">
118
+
119
+ <div class="d-flex justify-content-between align-items-baseline mb-2">
120
+ <span class="small text-uppercase fw-semibold text-body-secondary" style="letter-spacing:.08em">
121
+ <%= job.label %> — the path, in order
122
+ </span>
123
+ <span class="small text-body-tertiary">first to answer serves</span>
124
+ </div>
125
+
126
+ <ol class="list-unstyled mb-2" data-job-list>
127
+ <% _path.forEach(function (id, i) { %>
128
+ <% var ep = _byId[id]; %>
129
+ <li class="d-flex align-items-center gap-2 border rounded px-2 py-2 mb-1 bg-body-tertiary"
130
+ draggable="true" data-job-item="<%= id %>">
131
+ <span class="text-body-tertiary" aria-hidden="true" style="cursor:grab">⠿</span>
132
+ <span class="badge rounded-pill border bg-body-secondary text-body-secondary font-monospace fw-normal"
133
+ data-job-rank><%= i + 1 %></span>
134
+ <span class="flex-grow-1 font-monospace small">
135
+ <%= id %>
136
+ <% if (ep && ep.label && ep.label !== id) { %><span class="text-body-tertiary"> · <%= ep.label %></span><% } %>
137
+ <%# AN ENDPOINT IN THE PATH BUT DISABLED IS KEPT, and said out loud. Dropping it on
138
+ save would quietly rewrite somebody's configuration because a machine happened to
139
+ be switched off this afternoon. %>
140
+ <% if (ep && !ep.enabled) { %>
141
+ <span class="badge rounded-pill border bg-warning-subtle text-warning-emphasis border-warning-subtle ms-1">disabled — skipped while off</span>
142
+ <% } %>
143
+ <% if (!ep) { %>
144
+ <span class="badge rounded-pill border bg-danger-subtle text-danger-emphasis border-danger-subtle ms-1">no such endpoint</span>
145
+ <% } %>
146
+ </span>
147
+ <span class="btn-group btn-group-sm" role="group">
148
+ <button type="button" class="btn btn-outline-secondary" data-job-up
149
+ aria-label="Move <%= id %> earlier">↑</button>
150
+ <button type="button" class="btn btn-outline-secondary" data-job-down
151
+ aria-label="Move <%= id %> later">↓</button>
152
+ <button type="button" class="btn btn-outline-secondary" data-job-remove
153
+ aria-label="Remove <%= id %> from this job">✕</button>
154
+ </span>
155
+ </li>
156
+ <% }) %>
157
+ </ol>
158
+
159
+ <p class="form-text mt-0" data-job-empty<% if (_path.length) { %> hidden<% } %>>
160
+ Nothing chosen, so this job uses <strong>every enabled endpoint</strong> in the order they
161
+ are listed above. That is a working configuration, not a missing one.
162
+ </p>
163
+
164
+ <% if (_rest.length) { %>
165
+ <div class="small text-uppercase fw-semibold text-body-secondary mt-3 mb-2" style="letter-spacing:.08em">Not in this job</div>
166
+ <div class="d-flex flex-wrap gap-2" data-job-pool>
167
+ <% _rest.forEach(function (e) { %>
168
+ <button type="button" class="btn btn-sm btn-outline-secondary font-monospace"
169
+ data-job-add="<%= e.id %>"<% if (!e.enabled) { %> disabled title="This endpoint is switched off"<% } %>>
170
+ + <%= e.id %><% if (!e.enabled) { %> (disabled)<% } %>
171
+ </button>
172
+ <% }) %>
173
+ </div>
174
+ <% } %>
175
+
176
+ <div class="d-flex gap-2 mt-3">
177
+ <button type="submit" class="btn btn-sm btn-primary">Save</button>
178
+ <button type="button" class="btn btn-sm btn-outline-secondary" data-job-cancel>Cancel</button>
179
+ </div>
180
+ </form>
181
+ <% } %>
182
+ </div>