@data-club/ai-hub 0.0.8 → 0.0.10
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.
- package/dist/auth/locales/cs.d.ts +1 -1
- package/dist/auth/locales/cs.d.ts.map +1 -1
- package/dist/auth/locales/cs.js +1 -16
- package/dist/auth/locales/cs.js.map +1 -1
- package/dist/auth/locales/sk.d.ts +1 -1
- package/dist/auth/locales/sk.d.ts.map +1 -1
- package/dist/auth/locales/sk.js +1 -16
- package/dist/auth/locales/sk.js.map +1 -1
- package/dist/auth/texts.d.ts +0 -26
- package/dist/auth/texts.d.ts.map +1 -1
- package/dist/auth/texts.js +0 -15
- package/dist/auth/texts.js.map +1 -1
- package/dist/client/AIHubClient.d.ts +2 -39
- package/dist/client/AIHubClient.d.ts.map +1 -1
- package/dist/client/AIHubClient.js +2 -13
- package/dist/client/AIHubClient.js.map +1 -1
- package/dist/components/ChatShell/ShellHeader.d.ts +4 -4
- package/dist/components/ChatShell/UserMenu.d.ts +4 -10
- package/dist/components/ChatShell/UserMenu.d.ts.map +1 -1
- package/dist/components/ChatShell/UserMenu.js +8 -15
- package/dist/components/ChatShell/UserMenu.js.map +1 -1
- package/dist/components/DataClubAIHubAdmin/AgentsPage.d.ts +1 -1
- package/dist/components/DataClubAIHubAdmin/AgentsPage.d.ts.map +1 -1
- package/dist/components/DataClubAIHubAdmin/AgentsPage.js +2 -11
- package/dist/components/DataClubAIHubAdmin/AgentsPage.js.map +1 -1
- package/dist/components/DataClubAIHubAdmin/SettingsPage.d.ts +7 -8
- package/dist/components/DataClubAIHubAdmin/SettingsPage.d.ts.map +1 -1
- package/dist/components/DataClubAIHubAdmin/SettingsPage.js +8 -87
- package/dist/components/DataClubAIHubAdmin/SettingsPage.js.map +1 -1
- package/dist/components/DataClubAIHubAdmin/index.d.ts +1 -1
- package/dist/components/DataClubAIHubAdmin/index.js +1 -1
- package/dist/components/MessageList/index.d.ts.map +1 -1
- package/dist/components/MessageList/index.js +1 -2
- package/dist/components/MessageList/index.js.map +1 -1
- package/dist/context/AIHubProvider.d.ts.map +1 -1
- package/dist/context/AIHubProvider.js +1 -31
- package/dist/context/AIHubProvider.js.map +1 -1
- package/dist/index.d.ts +1 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/state/stateReducer.d.ts.map +1 -1
- package/dist/state/stateReducer.js +0 -1
- package/dist/state/stateReducer.js.map +1 -1
- package/dist/types.d.ts +1 -73
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/auth/locales/cs.ts +1 -23
- package/src/auth/locales/sk.ts +1 -23
- package/src/auth/texts.ts +0 -49
- package/src/client/AIHubClient.ts +2 -42
- package/src/components/ChatShell/ShellHeader.tsx +4 -4
- package/src/components/ChatShell/UserMenu.tsx +7 -27
- package/src/components/DataClubAIHubAdmin/AgentsPage.tsx +1 -21
- package/src/components/DataClubAIHubAdmin/SettingsPage.tsx +8 -197
- package/src/components/DataClubAIHubAdmin/index.tsx +1 -1
- package/src/components/MessageList/index.tsx +0 -4
- package/src/context/AIHubProvider.tsx +1 -32
- package/src/index.ts +0 -8
- package/src/state/stateReducer.ts +0 -1
- package/src/styles/CLASSES.md +1 -1
- package/src/styles/default.css +0 -67
- package/src/types.ts +0 -75
- package/src/components/CacheBadge/index.tsx +0 -245
|
@@ -2,20 +2,19 @@
|
|
|
2
2
|
* `<AdminSettingsPage />` — the super_admin-only Settings sub-page mounted
|
|
3
3
|
* inside `<DataClubAIHubAdmin />` ([08-admin-settings.md]).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* "Don't make the user save all
|
|
5
|
+
* Independent sections, each with its own Save button — per the spec,
|
|
6
|
+
* "Don't make the user save all sections together":
|
|
7
7
|
*
|
|
8
|
-
* 1. **
|
|
9
|
-
* 2. **
|
|
10
|
-
* 3. **
|
|
11
|
-
* 4. **Signing key** — rotate with `ROTATE`-typing friction. Rotating
|
|
8
|
+
* 1. **Theme** — default theme + switcher-enabled checkbox.
|
|
9
|
+
* 2. **UI feature toggles** — the §8.4 toggle blob.
|
|
10
|
+
* 3. **Signing key** — rotate with `ROTATE`-typing friction. Rotating
|
|
12
11
|
* invalidates every JWT, including the calling super-admin's.
|
|
13
|
-
*
|
|
12
|
+
* 4. **Metrics scrape token** — rotate with the same friction. The new token
|
|
14
13
|
* is shown ONCE via a reveal modal; subsequent settings reads no longer
|
|
15
14
|
* expose it. Does NOT invalidate the calling JWT.
|
|
16
15
|
*
|
|
17
16
|
* Each section owns its own dirty-state and save handler; sibling sections
|
|
18
|
-
* never share state. This means a half-typed
|
|
17
|
+
* never share state. This means a half-typed value won't be pushed when
|
|
19
18
|
* the admin clicks `Save toggles` in a different section.
|
|
20
19
|
*
|
|
21
20
|
* Visible to `super_admin` only — the parent shell hides the nav item for
|
|
@@ -176,11 +175,6 @@ export function AdminSettingsPage(props: AdminSettingsPageProps): ReactNode {
|
|
|
176
175
|
) : null}
|
|
177
176
|
{settings !== null ? (
|
|
178
177
|
<>
|
|
179
|
-
<LettaConnectionSection
|
|
180
|
-
settings={settings}
|
|
181
|
-
texts={props.texts}
|
|
182
|
-
onSaved={handleSectionSaved}
|
|
183
|
-
/>
|
|
184
178
|
<ThemeSection
|
|
185
179
|
settings={settings}
|
|
186
180
|
texts={props.texts}
|
|
@@ -230,7 +224,7 @@ export function AdminSettingsPage(props: AdminSettingsPageProps): ReactNode {
|
|
|
230
224
|
}
|
|
231
225
|
|
|
232
226
|
/* ────────────────────────────────────────────────────────────────────────── */
|
|
233
|
-
/* Section:
|
|
227
|
+
/* Section: Theme */
|
|
234
228
|
/* ────────────────────────────────────────────────────────────────────────── */
|
|
235
229
|
|
|
236
230
|
interface SectionProps {
|
|
@@ -238,189 +232,6 @@ interface SectionProps {
|
|
|
238
232
|
texts: AIHubAdminTexts;
|
|
239
233
|
}
|
|
240
234
|
|
|
241
|
-
interface LettaConnectionSectionProps extends SectionProps {
|
|
242
|
-
onSaved: (next: AdminSettingsView) => void;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function LettaConnectionSection(props: LettaConnectionSectionProps): ReactNode {
|
|
246
|
-
const { client } = useAIHub();
|
|
247
|
-
const [baseUrl, setBaseUrl] = useState(props.settings.letta_base_url ?? "");
|
|
248
|
-
const [adeUrl, setAdeUrl] = useState(props.settings.letta_ade_url ?? "");
|
|
249
|
-
// Empty string = leave-as-is; non-empty = replace.
|
|
250
|
-
const [apiKey, setApiKey] = useState("");
|
|
251
|
-
const [saving, setSaving] = useState(false);
|
|
252
|
-
const [flash, setFlash] = useState<{ tone: "success" | "error"; text: string } | null>(
|
|
253
|
-
null,
|
|
254
|
-
);
|
|
255
|
-
|
|
256
|
-
async function handleSave(event: React.FormEvent): Promise<void> {
|
|
257
|
-
event.preventDefault();
|
|
258
|
-
setSaving(true);
|
|
259
|
-
setFlash(null);
|
|
260
|
-
try {
|
|
261
|
-
const body: Parameters<typeof client.patchAdminSettings>[0] = {};
|
|
262
|
-
// Compare against current to avoid stomping the existing value with the
|
|
263
|
-
// identical one. The server treats null + value identically, but the
|
|
264
|
-
// schema forbids `null + ""` so we coerce explicitly.
|
|
265
|
-
if (baseUrl !== (props.settings.letta_base_url ?? "")) {
|
|
266
|
-
body.lettaBaseUrl = baseUrl === "" ? null : baseUrl;
|
|
267
|
-
}
|
|
268
|
-
if (adeUrl !== (props.settings.letta_ade_url ?? "")) {
|
|
269
|
-
body.lettaAdeUrl = adeUrl === "" ? null : adeUrl;
|
|
270
|
-
}
|
|
271
|
-
if (apiKey !== "") body.lettaApiKey = apiKey;
|
|
272
|
-
const next = await client.patchAdminSettings(body);
|
|
273
|
-
props.onSaved(next);
|
|
274
|
-
setApiKey("");
|
|
275
|
-
setFlash({ tone: "success", text: props.texts.settingsSaveSuccessFlash });
|
|
276
|
-
} catch (err) {
|
|
277
|
-
setFlash({
|
|
278
|
-
tone: "error",
|
|
279
|
-
text:
|
|
280
|
-
err instanceof Error && err.message.length > 0
|
|
281
|
-
? err.message
|
|
282
|
-
: props.texts.settingsSaveGenericError,
|
|
283
|
-
});
|
|
284
|
-
} finally {
|
|
285
|
-
setSaving(false);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Clears the DB column (sends `letta_api_key: ""`), which makes the
|
|
290
|
-
// server fall back to env.LETTA_API_KEY at runtime per the
|
|
291
|
-
// resolveApiKey precedence in letta/client.ts. Same code path as the
|
|
292
|
-
// base/ADE URL nulling — only it targets the API-key column.
|
|
293
|
-
async function handleClearApiKeyOverride(): Promise<void> {
|
|
294
|
-
setSaving(true);
|
|
295
|
-
setFlash(null);
|
|
296
|
-
try {
|
|
297
|
-
const next = await client.patchAdminSettings({ lettaApiKey: "" });
|
|
298
|
-
props.onSaved(next);
|
|
299
|
-
setFlash({ tone: "success", text: props.texts.settingsSaveSuccessFlash });
|
|
300
|
-
} catch (err) {
|
|
301
|
-
setFlash({
|
|
302
|
-
tone: "error",
|
|
303
|
-
text:
|
|
304
|
-
err instanceof Error && err.message.length > 0
|
|
305
|
-
? err.message
|
|
306
|
-
: props.texts.settingsSaveGenericError,
|
|
307
|
-
});
|
|
308
|
-
} finally {
|
|
309
|
-
setSaving(false);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return (
|
|
314
|
-
<section
|
|
315
|
-
className="data-club-ai-hub-admin__settings-section"
|
|
316
|
-
data-testid="admin-settings-letta-section"
|
|
317
|
-
>
|
|
318
|
-
<h2 className="data-club-ai-hub-admin__settings-section-heading">
|
|
319
|
-
{props.texts.settingsLettaSectionHeading}
|
|
320
|
-
</h2>
|
|
321
|
-
<form onSubmit={(event) => void handleSave(event)} noValidate>
|
|
322
|
-
<label className="data-club-ai-hub-admin__form-field">
|
|
323
|
-
<span className="data-club-ai-hub-admin__form-label">
|
|
324
|
-
{props.texts.settingsLettaBaseUrlLabel}
|
|
325
|
-
</span>
|
|
326
|
-
<input
|
|
327
|
-
type="url"
|
|
328
|
-
className="data-club-ai-hub-admin__form-input"
|
|
329
|
-
data-testid="admin-settings-letta-base-url"
|
|
330
|
-
value={baseUrl}
|
|
331
|
-
onChange={(event) => setBaseUrl(event.target.value)}
|
|
332
|
-
disabled={saving}
|
|
333
|
-
/>
|
|
334
|
-
</label>
|
|
335
|
-
<label className="data-club-ai-hub-admin__form-field">
|
|
336
|
-
<span className="data-club-ai-hub-admin__form-label">
|
|
337
|
-
{props.texts.settingsLettaAdeUrlLabel}
|
|
338
|
-
</span>
|
|
339
|
-
<input
|
|
340
|
-
type="url"
|
|
341
|
-
className="data-club-ai-hub-admin__form-input"
|
|
342
|
-
data-testid="admin-settings-letta-ade-url"
|
|
343
|
-
value={adeUrl}
|
|
344
|
-
onChange={(event) => setAdeUrl(event.target.value)}
|
|
345
|
-
disabled={saving}
|
|
346
|
-
/>
|
|
347
|
-
<span className="data-club-ai-hub-admin__form-helper">
|
|
348
|
-
{props.texts.settingsLettaAdeUrlHelper}
|
|
349
|
-
</span>
|
|
350
|
-
</label>
|
|
351
|
-
<label className="data-club-ai-hub-admin__form-field">
|
|
352
|
-
<span className="data-club-ai-hub-admin__form-label">
|
|
353
|
-
{props.texts.settingsLettaApiKeyLabel}
|
|
354
|
-
</span>
|
|
355
|
-
<input
|
|
356
|
-
type="password"
|
|
357
|
-
className="data-club-ai-hub-admin__form-input"
|
|
358
|
-
data-testid="admin-settings-letta-api-key"
|
|
359
|
-
value={apiKey}
|
|
360
|
-
placeholder={
|
|
361
|
-
props.settings.letta_api_key_source === "db"
|
|
362
|
-
? props.texts.settingsLettaApiKeyPlaceholder
|
|
363
|
-
: ""
|
|
364
|
-
}
|
|
365
|
-
onChange={(event) => setApiKey(event.target.value)}
|
|
366
|
-
disabled={saving}
|
|
367
|
-
autoComplete="new-password"
|
|
368
|
-
/>
|
|
369
|
-
<span
|
|
370
|
-
className="data-club-ai-hub-admin__form-helper"
|
|
371
|
-
data-testid="admin-settings-letta-api-key-helper"
|
|
372
|
-
data-source={props.settings.letta_api_key_source}
|
|
373
|
-
>
|
|
374
|
-
{props.settings.letta_api_key_source === "db"
|
|
375
|
-
? props.texts.settingsLettaApiKeyDbHelper
|
|
376
|
-
: props.settings.letta_api_key_source === "env"
|
|
377
|
-
? props.texts.settingsLettaApiKeyEnvHelper
|
|
378
|
-
: props.texts.settingsLettaApiKeyNoneHelper}
|
|
379
|
-
</span>
|
|
380
|
-
{props.settings.letta_api_key_source === "db" ? (
|
|
381
|
-
<button
|
|
382
|
-
type="button"
|
|
383
|
-
className="data-club-ai-hub-admin__button"
|
|
384
|
-
data-testid="admin-settings-letta-api-key-clear"
|
|
385
|
-
onClick={() => void handleClearApiKeyOverride()}
|
|
386
|
-
disabled={saving}
|
|
387
|
-
>
|
|
388
|
-
{props.texts.settingsLettaApiKeyClearOverrideButton}
|
|
389
|
-
</button>
|
|
390
|
-
) : null}
|
|
391
|
-
</label>
|
|
392
|
-
{flash !== null ? (
|
|
393
|
-
<div
|
|
394
|
-
className={
|
|
395
|
-
flash.tone === "success"
|
|
396
|
-
? "data-club-ai-hub-admin__flash data-club-ai-hub-admin__flash--success"
|
|
397
|
-
: "data-club-ai-hub-admin__flash data-club-ai-hub-admin__flash--error"
|
|
398
|
-
}
|
|
399
|
-
data-testid="admin-settings-letta-flash"
|
|
400
|
-
role={flash.tone === "error" ? "alert" : "status"}
|
|
401
|
-
>
|
|
402
|
-
{flash.text}
|
|
403
|
-
</div>
|
|
404
|
-
) : null}
|
|
405
|
-
<button
|
|
406
|
-
type="submit"
|
|
407
|
-
className="data-club-ai-hub-admin__button data-club-ai-hub-admin__button--primary"
|
|
408
|
-
data-testid="admin-settings-letta-save"
|
|
409
|
-
disabled={saving}
|
|
410
|
-
>
|
|
411
|
-
{saving
|
|
412
|
-
? props.texts.settingsSaveSavingButton
|
|
413
|
-
: props.texts.settingsSaveLettaButton}
|
|
414
|
-
</button>
|
|
415
|
-
</form>
|
|
416
|
-
</section>
|
|
417
|
-
);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/* ────────────────────────────────────────────────────────────────────────── */
|
|
421
|
-
/* Section: Theme */
|
|
422
|
-
/* ────────────────────────────────────────────────────────────────────────── */
|
|
423
|
-
|
|
424
235
|
interface ThemeSectionProps extends SectionProps {
|
|
425
236
|
onSaved: (next: AdminSettingsView) => void;
|
|
426
237
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* `user`-role caller the component returns `null` outright.
|
|
9
9
|
*
|
|
10
10
|
* The shell **renders its own `<ShellHeader />`** so the user-menu (log
|
|
11
|
-
* out, theme toggle,
|
|
11
|
+
* out, theme toggle, ← Back to chat) stays available
|
|
12
12
|
* from inside the admin view too — [00-shell.md] makes the shell header
|
|
13
13
|
* part of every authenticated page. The component is identical to the
|
|
14
14
|
* one ChatShell renders.
|
|
@@ -54,7 +54,6 @@ import type {
|
|
|
54
54
|
AIHubMessageBubbleRenderer,
|
|
55
55
|
} from "../../types.js";
|
|
56
56
|
|
|
57
|
-
import { CacheBadge } from "../CacheBadge/index.js";
|
|
58
57
|
import { MarkdownRenderer } from "../MarkdownRenderer/index.js";
|
|
59
58
|
import { ReasoningView } from "../ReasoningView/index.js";
|
|
60
59
|
import { ScrollToPresentButton } from "../ScrollToPresentButton/index.js";
|
|
@@ -713,9 +712,6 @@ function MessageBubble(props: {
|
|
|
713
712
|
message.content.trim()
|
|
714
713
|
)}
|
|
715
714
|
</span>
|
|
716
|
-
{isAssistant && message.usage ? (
|
|
717
|
-
<CacheBadge usage={message.usage} />
|
|
718
|
-
) : null}
|
|
719
715
|
{props.isFailedAssistant ? (
|
|
720
716
|
<div className={cls.bubbleActions}>
|
|
721
717
|
<span>{props.failedLabel}</span>
|
|
@@ -108,7 +108,7 @@ function mergeOptimisticAffordances(
|
|
|
108
108
|
): AIHubMessage[] {
|
|
109
109
|
const optimisticById = new Map<string, AIHubMessage>();
|
|
110
110
|
for (const m of optimistic) {
|
|
111
|
-
if (m.role === "assistant" && (m.reasoning || m.tool_calls?.length
|
|
111
|
+
if (m.role === "assistant" && (m.reasoning || m.tool_calls?.length)) {
|
|
112
112
|
optimisticById.set(m.id, m);
|
|
113
113
|
}
|
|
114
114
|
}
|
|
@@ -123,13 +123,6 @@ function mergeOptimisticAffordances(
|
|
|
123
123
|
if ((!next.tool_calls || next.tool_calls.length === 0) && o.tool_calls?.length) {
|
|
124
124
|
next.tool_calls = o.tool_calls;
|
|
125
125
|
}
|
|
126
|
-
// Usage rides through too — if the canonical refetch happens
|
|
127
|
-
// before the server stamped `message_usage` (small race window),
|
|
128
|
-
// the live `usage` event we already handled keeps the badge
|
|
129
|
-
// rendered until the next refresh confirms it.
|
|
130
|
-
if (!next.usage && o.usage) {
|
|
131
|
-
next.usage = o.usage;
|
|
132
|
-
}
|
|
133
126
|
return next;
|
|
134
127
|
});
|
|
135
128
|
}
|
|
@@ -828,30 +821,6 @@ export function AIHubProvider(props: AIHubProviderProps): ReactNode {
|
|
|
828
821
|
setStreamingReasoning(null);
|
|
829
822
|
setStreamingToolCalls([]);
|
|
830
823
|
sawAssistantOutput = true;
|
|
831
|
-
} else if (event.event === "usage") {
|
|
832
|
-
// Live usage stamp — the runner emits this right before
|
|
833
|
-
// `stream_end{done}` carrying the cache-hit/write counts for
|
|
834
|
-
// the turn. We attach it to the matching assistant bubble so
|
|
835
|
-
// `<CacheBadge />` appears immediately (without waiting for
|
|
836
|
-
// the post-stream `/messages` refetch). The refetch path
|
|
837
|
-
// also stamps the same `usage` from the persisted
|
|
838
|
-
// `message_usage` row, so refresh + history retrieval show
|
|
839
|
-
// the badge too.
|
|
840
|
-
const targetId = event.data.letta_message_id;
|
|
841
|
-
if (typeof targetId === "string") {
|
|
842
|
-
const usage = {
|
|
843
|
-
input: event.data.input ?? null,
|
|
844
|
-
output: event.data.output ?? null,
|
|
845
|
-
cache_read: event.data.cache_read ?? null,
|
|
846
|
-
cache_write: event.data.cache_write ?? null,
|
|
847
|
-
reasoning: event.data.reasoning ?? null,
|
|
848
|
-
context: event.data.context ?? null,
|
|
849
|
-
total: event.data.total ?? null,
|
|
850
|
-
};
|
|
851
|
-
setMessages((prev) =>
|
|
852
|
-
prev.map((m) => (m.id === targetId ? { ...m, usage } : m)),
|
|
853
|
-
);
|
|
854
|
-
}
|
|
855
824
|
} else if (event.event === "error") {
|
|
856
825
|
setLastTurnError(event.data.message);
|
|
857
826
|
sawError = true;
|
package/src/index.ts
CHANGED
|
@@ -26,7 +26,6 @@ export type {
|
|
|
26
26
|
Session,
|
|
27
27
|
AIHubMessage,
|
|
28
28
|
AIHubMessageOverride,
|
|
29
|
-
AIHubMessageUsage,
|
|
30
29
|
AIHubToolCallRecord,
|
|
31
30
|
TurnStatus,
|
|
32
31
|
RunningPhase,
|
|
@@ -232,13 +231,6 @@ export type {
|
|
|
232
231
|
} from "./components/ToolCallView/index.js";
|
|
233
232
|
export { formatToolContentForDisplay } from "./components/ToolCallView/formatContent.js";
|
|
234
233
|
|
|
235
|
-
export { CacheBadge, computeCacheHitRatio } from "./components/CacheBadge/index.js";
|
|
236
|
-
export type {
|
|
237
|
-
CacheBadgeProps,
|
|
238
|
-
CacheBadgeClassNames,
|
|
239
|
-
CacheBadgeTexts,
|
|
240
|
-
} from "./components/CacheBadge/index.js";
|
|
241
|
-
|
|
242
234
|
export { ScrollToPresentButton } from "./components/ScrollToPresentButton/index.js";
|
|
243
235
|
export type {
|
|
244
236
|
ScrollToPresentButtonProps,
|
package/src/styles/CLASSES.md
CHANGED
|
@@ -177,7 +177,7 @@ block and modifier.
|
|
|
177
177
|
| `data-club-ai-hub__user-menu__header` | Header row inside the dropdown (username + role). |
|
|
178
178
|
| `data-club-ai-hub__user-menu__username-large` | Larger username text in the dropdown header. |
|
|
179
179
|
| `data-club-ai-hub__user-menu__role` | Role label below the username. |
|
|
180
|
-
| `data-club-ai-hub__user-menu__item` | A menu item (Sign out today; Theme / Admin
|
|
180
|
+
| `data-club-ai-hub__user-menu__item` | A menu item (Sign out today; Theme / Admin in Steps 10–11). |
|
|
181
181
|
|
|
182
182
|
### Agents picker — Step 9 (`ui-sketches/03-chat.md`)
|
|
183
183
|
|
package/src/styles/default.css
CHANGED
|
@@ -660,10 +660,6 @@
|
|
|
660
660
|
background-color: color-mix(in srgb, var(--dc-aih-accent) 12%, transparent);
|
|
661
661
|
}
|
|
662
662
|
|
|
663
|
-
.data-club-ai-hub__user-menu__item--link {
|
|
664
|
-
text-decoration: none;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
663
|
/* ── Language switcher (left of the user menu in the shell header) ────── */
|
|
668
664
|
|
|
669
665
|
.data-club-ai-hub__language-switcher {
|
|
@@ -2264,69 +2260,6 @@
|
|
|
2264
2260
|
letter-spacing: 0.04em;
|
|
2265
2261
|
}
|
|
2266
2262
|
|
|
2267
|
-
/* ─────────────────────────────────────────────────────────────────── */
|
|
2268
|
-
/* Cache badge — per-assistant-message prompt-cache hit indicator */
|
|
2269
|
-
/* */
|
|
2270
|
-
/* Renders as a small chip in the bottom-right corner of the assistant */
|
|
2271
|
-
/* bubble, showing the cache-hit % over the prompt-side tokens. The */
|
|
2272
|
-
/* `data-cache-state` attribute (none|cold|warm|hot) drives the color */
|
|
2273
|
-
/* so skinners can override per-state via the CSS-vars without */
|
|
2274
|
-
/* duplicating the layout rules. */
|
|
2275
|
-
/* ─────────────────────────────────────────────────────────────────── */
|
|
2276
|
-
.data-club-ai-hub__cache-badge {
|
|
2277
|
-
display: inline-flex;
|
|
2278
|
-
align-items: center;
|
|
2279
|
-
gap: 0.25em;
|
|
2280
|
-
margin-top: 0.4em;
|
|
2281
|
-
margin-left: auto;
|
|
2282
|
-
align-self: flex-end;
|
|
2283
|
-
padding: 0.1em 0.5em;
|
|
2284
|
-
font-size: 0.75em;
|
|
2285
|
-
line-height: 1.2;
|
|
2286
|
-
border-radius: 999px;
|
|
2287
|
-
background: var(--dc-aih-cache-badge-bg, rgba(127, 127, 127, 0.12));
|
|
2288
|
-
color: var(--dc-aih-cache-badge-fg, var(--dc-aih-text-muted, #888));
|
|
2289
|
-
border: 1px solid var(--dc-aih-cache-badge-border, transparent);
|
|
2290
|
-
cursor: help; /* signals the title-attribute tooltip */
|
|
2291
|
-
user-select: none;
|
|
2292
|
-
transition: background 0.15s ease, border-color 0.15s ease;
|
|
2293
|
-
}
|
|
2294
|
-
.data-club-ai-hub__cache-badge[data-cache-state="hot"] {
|
|
2295
|
-
background: var(--dc-aih-cache-badge-hot-bg, rgba(40, 180, 99, 0.14));
|
|
2296
|
-
color: var(--dc-aih-cache-badge-hot-fg, #28b463);
|
|
2297
|
-
}
|
|
2298
|
-
.data-club-ai-hub__cache-badge[data-cache-state="warm"] {
|
|
2299
|
-
background: var(--dc-aih-cache-badge-warm-bg, rgba(255, 142, 78, 0.14));
|
|
2300
|
-
color: var(--dc-aih-cache-badge-warm-fg, #ff8e4e);
|
|
2301
|
-
}
|
|
2302
|
-
.data-club-ai-hub__cache-badge[data-cache-state="cold"] {
|
|
2303
|
-
background: var(--dc-aih-cache-badge-cold-bg, rgba(160, 160, 160, 0.14));
|
|
2304
|
-
color: var(--dc-aih-cache-badge-cold-fg, #888);
|
|
2305
|
-
}
|
|
2306
|
-
.data-club-ai-hub__cache-badge[data-cache-state="miss"] {
|
|
2307
|
-
/* Full cache miss — provider had to reinitialize the whole prompt.
|
|
2308
|
-
Red-tinted to signal "nothing cached this turn". */
|
|
2309
|
-
background: var(--dc-aih-cache-badge-miss-bg, rgba(216, 44, 246, 0.12));
|
|
2310
|
-
color: var(--dc-aih-cache-badge-miss-fg, #d82cf6);
|
|
2311
|
-
}
|
|
2312
|
-
.data-club-ai-hub__cache-badge[data-cache-state="priming"] {
|
|
2313
|
-
/* This turn WROTE to the provider's cache. Next turn benefits.
|
|
2314
|
-
Blue-tinted to distinguish from cold miss. */
|
|
2315
|
-
background: var(--dc-aih-cache-badge-priming-bg, rgba(41, 189, 239, 0.14));
|
|
2316
|
-
color: var(--dc-aih-cache-badge-priming-fg, #29bdef);
|
|
2317
|
-
}
|
|
2318
|
-
.data-club-ai-hub__cache-badge[data-cache-state="none"] {
|
|
2319
|
-
font-style: italic;
|
|
2320
|
-
opacity: 0.7;
|
|
2321
|
-
}
|
|
2322
|
-
.data-club-ai-hub__cache-badge__icon {
|
|
2323
|
-
font-size: 0.9em;
|
|
2324
|
-
}
|
|
2325
|
-
.data-club-ai-hub__cache-badge__label {
|
|
2326
|
-
font-variant-numeric: tabular-nums;
|
|
2327
|
-
font-weight: 600;
|
|
2328
|
-
}
|
|
2329
|
-
|
|
2330
2263
|
/* ────────────────────────────────────────────────────────────────────────── */
|
|
2331
2264
|
/* Step 12 — admin Agents / Sessions overview / Settings sub-pages */
|
|
2332
2265
|
/* ────────────────────────────────────────────────────────────────────────── */
|
package/src/types.ts
CHANGED
|
@@ -81,10 +81,6 @@ export interface AIHubUser {
|
|
|
81
81
|
* `user.theme_preference + deployment.{theme_default, theme_switcher_enabled}`
|
|
82
82
|
* per §7.6 (two lines in the React provider).
|
|
83
83
|
*
|
|
84
|
-
* `letta_ade_url` is included only when the caller's role is `power_user`
|
|
85
|
-
* or higher; for `user`-role callers the field is absent — by design
|
|
86
|
-
* (§6.3.4: the URL is an admin-surface concern).
|
|
87
|
-
*
|
|
88
84
|
* @see SPECIFICATIONS.md §6.3.4 — `GET /auth/me`
|
|
89
85
|
* @see SPECIFICATIONS.md §7.6 — theme resolution
|
|
90
86
|
*/
|
|
@@ -123,11 +119,6 @@ export interface AIHubDeployment {
|
|
|
123
119
|
* hidden. (Also hidden whenever only one agent exists, regardless of this.)
|
|
124
120
|
*/
|
|
125
121
|
agent_switcher_enabled: boolean;
|
|
126
|
-
/**
|
|
127
|
-
* Browser-facing URL of the self-hosted Letta ADE — only present when the
|
|
128
|
-
* caller's role is `power_user` or higher. Absent for `user`-role callers.
|
|
129
|
-
*/
|
|
130
|
-
letta_ade_url?: string;
|
|
131
122
|
}
|
|
132
123
|
|
|
133
124
|
/**
|
|
@@ -261,16 +252,6 @@ export interface AIHubMessage {
|
|
|
261
252
|
* @see ui-sketches/03-chat.md — tool_call_card
|
|
262
253
|
*/
|
|
263
254
|
tool_calls?: AIHubToolCallRecord[] | null;
|
|
264
|
-
/**
|
|
265
|
-
* Provider-reported token usage for the turn that produced this
|
|
266
|
-
* message. Set on the LAST assistant message of each turn (the
|
|
267
|
-
* `usage_statistics` event is per-run, not per-message; we stamp it
|
|
268
|
-
* on the visible final bubble). Renders as a small cache-status
|
|
269
|
-
* badge in the chat UI; hover for the full breakdown.
|
|
270
|
-
*
|
|
271
|
-
* @see AIHubMessageUsage
|
|
272
|
-
*/
|
|
273
|
-
usage?: AIHubMessageUsage | null;
|
|
274
255
|
}
|
|
275
256
|
|
|
276
257
|
/**
|
|
@@ -638,61 +619,6 @@ export interface AIHubSystemNoticeEvent extends AIHubEventBase {
|
|
|
638
619
|
};
|
|
639
620
|
}
|
|
640
621
|
|
|
641
|
-
/**
|
|
642
|
-
* System: per-turn token-usage counters propagated from Letta. Stamped
|
|
643
|
-
* on the LAST assistant message of the turn — the React reducer uses
|
|
644
|
-
* `letta_message_id` to attach the badge to the right bubble. Also
|
|
645
|
-
* persisted server-side so a refresh keeps the badge.
|
|
646
|
-
*
|
|
647
|
-
* @see SPECIFICATIONS.md §9.4
|
|
648
|
-
* @see docs/server/chat-sse.md
|
|
649
|
-
*/
|
|
650
|
-
export interface AIHubSystemUsageEvent extends AIHubEventBase {
|
|
651
|
-
event: "usage";
|
|
652
|
-
data: {
|
|
653
|
-
/**
|
|
654
|
-
* Letta message id this usage attaches to (the turn's last assistant
|
|
655
|
-
* message). The reducer joins this against the chat history to put
|
|
656
|
-
* the cache badge on the right bubble. Optional for forward
|
|
657
|
-
* compatibility with future per-message usage events.
|
|
658
|
-
*/
|
|
659
|
-
letta_message_id?: string;
|
|
660
|
-
/** Non-cached input-token count (prompt_tokens). */
|
|
661
|
-
input?: number;
|
|
662
|
-
/** Output-token count (completion_tokens). */
|
|
663
|
-
output?: number;
|
|
664
|
-
/** Cache-read-token count — Anthropic / OpenAI / Gemini all report. */
|
|
665
|
-
cache_read?: number;
|
|
666
|
-
/** Cache-write-token count — Anthropic only; null elsewhere. */
|
|
667
|
-
cache_write?: number;
|
|
668
|
-
/** Reasoning / thinking tokens for models that produce them. */
|
|
669
|
-
reasoning?: number;
|
|
670
|
-
/** Letta's rolling context-window-size estimate post-turn. */
|
|
671
|
-
context?: number;
|
|
672
|
-
/** Sum reported by provider (may differ from input+output when reasoning is counted separately). */
|
|
673
|
-
total?: number;
|
|
674
|
-
};
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
/**
|
|
678
|
-
* Per-assistant-message cache + token usage, attached to the
|
|
679
|
-
* historical chat record. Returned from `GET /sessions/{id}/messages`
|
|
680
|
-
* via `AIHubMessage.usage`. Same shape as the live wire event's
|
|
681
|
-
* `data` minus the `letta_message_id` (which is already the message
|
|
682
|
-
* itself).
|
|
683
|
-
*
|
|
684
|
-
* @see AIHubSystemUsageEvent
|
|
685
|
-
*/
|
|
686
|
-
export interface AIHubMessageUsage {
|
|
687
|
-
input?: number | null;
|
|
688
|
-
output?: number | null;
|
|
689
|
-
cache_read?: number | null;
|
|
690
|
-
cache_write?: number | null;
|
|
691
|
-
reasoning?: number | null;
|
|
692
|
-
context?: number | null;
|
|
693
|
-
total?: number | null;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
622
|
/**
|
|
697
623
|
* System: non-recoverable error. Followed by `stream_end {reason: "error"}`.
|
|
698
624
|
* The turn transitions to `failed`; the session remains usable.
|
|
@@ -734,7 +660,6 @@ export type AIHubEvent =
|
|
|
734
660
|
| AIHubToolCallProgressEvent
|
|
735
661
|
| AIHubToolCallResultEvent
|
|
736
662
|
| AIHubSystemNoticeEvent
|
|
737
|
-
| AIHubSystemUsageEvent
|
|
738
663
|
| AIHubSystemErrorEvent;
|
|
739
664
|
|
|
740
665
|
/* ────────────────────────────────────────────────────────────────────────── */
|