Add full shell, replaceable Omics adapter and bilingual interaction
Implement approved specification #32 and tickets #33-#37. Keep host authentication server-verified and pin session interaction language. Compile scoped base selectors for browser compatibility and retain full gutters during CSS pruning.
This commit is contained in:
@@ -1,6 +1,23 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { backendBaseUrl, joinBackendPath, resolveBackendUrl } from "./runtime-config";
|
||||
import { backendBaseUrl, joinBackendPath, resolveBackendUrl, resolveShellConfig, type RuntimeConfig } from "./runtime-config";
|
||||
|
||||
describe("resolveShellConfig", () => {
|
||||
it("preserves legacy embedded Omics configuration", () => {
|
||||
expect(resolveShellConfig({})).toEqual({ mode: "embedded", defaultLocale: "en", adapter: "omics-portal" });
|
||||
expect(resolveShellConfig({ shell: { mode: "embedded", defaultLocale: "it" } }).adapter).toBe("omics-portal");
|
||||
});
|
||||
it("full does not use an adapter", () => {
|
||||
expect(resolveShellConfig({ shell: { mode: "full", defaultLocale: "en", adapter: "omics-portal" } })).toEqual({ mode: "full", defaultLocale: "en" });
|
||||
});
|
||||
it.each([
|
||||
{ mode: "other", defaultLocale: "en" },
|
||||
{ mode: "full", defaultLocale: "" },
|
||||
{ mode: "embedded", defaultLocale: "en", adapter: "unknown" },
|
||||
])("rejects invalid shell configuration %j", shell => {
|
||||
expect(() => resolveShellConfig({ shell } as RuntimeConfig)).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveBackendUrl", () => {
|
||||
it("uses same-origin /api by default", () => {
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
export interface RuntimeConfig {
|
||||
backendBaseUrl?: string;
|
||||
shell?: ShellConfig;
|
||||
}
|
||||
|
||||
export interface ShellConfig {
|
||||
mode: "full" | "embedded";
|
||||
defaultLocale: string;
|
||||
adapter?: "omics-portal";
|
||||
}
|
||||
|
||||
export function resolveShellConfig(config: RuntimeConfig | undefined = window.__THOTHII_CONFIG__): ShellConfig {
|
||||
const shell = config?.shell;
|
||||
if (shell === undefined) return { mode: "embedded", defaultLocale: "en", adapter: "omics-portal" };
|
||||
if (!shell || (shell.mode !== "full" && shell.mode !== "embedded")) {
|
||||
throw new Error("Invalid shell mode. Use full or embedded.");
|
||||
}
|
||||
if (typeof shell.defaultLocale !== "string" || !shell.defaultLocale.trim()) {
|
||||
throw new Error("A default interface language is required.");
|
||||
}
|
||||
if (shell.adapter !== undefined && shell.adapter !== "omics-portal") {
|
||||
throw new Error("Unknown portal adapter. Check the installation configuration.");
|
||||
}
|
||||
return shell.mode === "full"
|
||||
? { mode: "full", defaultLocale: shell.defaultLocale }
|
||||
: { ...shell, adapter: "omics-portal" };
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -7,6 +7,30 @@ import {
|
||||
deleteSession, getSessionDocuments,
|
||||
} from "./sessions";
|
||||
import { workspacePreferences } from "../workspaces/preferences";
|
||||
import { setLocale } from "../i18n";
|
||||
|
||||
afterEach(() => setLocale("en"));
|
||||
|
||||
test("new sessions capture the selected interaction language before asynchronous preparation", async () => {
|
||||
workspacePreferences.save({
|
||||
workspaceId: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "low",
|
||||
});
|
||||
let body: Record<string, unknown> | undefined;
|
||||
server.use(
|
||||
http.get("/api/workspaces", () => HttpResponse.json([
|
||||
workspaceSummaryFixture("psd-clinical", { revision: workspaceRevisionFixture("psd-clinical") }),
|
||||
])),
|
||||
http.post("/api/sessions", async ({ request }) => {
|
||||
body = await request.json() as Record<string, unknown>;
|
||||
return HttpResponse.json({ id: "italian-session" });
|
||||
}),
|
||||
);
|
||||
setLocale("it-IT");
|
||||
const creation = createSession({ question: "Pazienti con fibrillazione" });
|
||||
setLocale("en");
|
||||
await creation;
|
||||
expect(body?.interactionLanguage).toBe("it");
|
||||
});
|
||||
|
||||
test("createSession seeds ephemeral selections and posts them", async () => {
|
||||
localStorage.clear();
|
||||
@@ -33,6 +57,7 @@ test("createSession seeds ephemeral selections and posts them", async () => {
|
||||
expect(await createSession({ question: "q" })).toEqual({ id: "s1" });
|
||||
expect(body).toEqual({
|
||||
question: "q", workspaceId: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "low",
|
||||
interactionLanguage: "en",
|
||||
});
|
||||
expect(workspacePreferences.load()).toEqual({
|
||||
workspaceId: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "low",
|
||||
@@ -138,6 +163,7 @@ test("createSession replaces a stale ephemeral workspace with the current instal
|
||||
expect(body).toEqual({
|
||||
question: "q", workspaceId: "psd-clinical",
|
||||
provider: "zai", model: "glm-5.2", thinking: "low",
|
||||
interactionLanguage: "en",
|
||||
});
|
||||
expect(workspacePreferences.load()).toEqual({
|
||||
workspaceId: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "low",
|
||||
@@ -161,6 +187,7 @@ test("createSession preserves a local legacy selection when the registry is empt
|
||||
expect(body).toEqual({
|
||||
question: "q", workspaceId: "legacy-workspace",
|
||||
provider: "zai", model: "glm-5.2", thinking: "low",
|
||||
interactionLanguage: "en",
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { apiFetch } from "./client";
|
||||
import { getLocale } from "../i18n";
|
||||
import {
|
||||
captureAuthOperation,
|
||||
isAuthOperationCurrent,
|
||||
@@ -93,6 +94,7 @@ async function ensureWorkspaceSelectionPolicy(precondition?: AuthOperationPrecon
|
||||
}
|
||||
|
||||
export async function createSession(i: NewSessionInput, precondition?: AuthOperationPrecondition) {
|
||||
const interactionLanguage = getLocale();
|
||||
const initiatingOperation = captureAuthOperation();
|
||||
const effectivePrecondition = precondition ?? (initiatingOperation ? {
|
||||
operation: initiatingOperation,
|
||||
@@ -108,7 +110,7 @@ export async function createSession(i: NewSessionInput, precondition?: AuthOpera
|
||||
requireAuthOperationPrecondition(effectivePrecondition);
|
||||
return apiFetch<{ id: string }>("/sessions", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ ...i, ...selection }),
|
||||
body: JSON.stringify({ ...i, ...selection, interactionLanguage }),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
export interface WidgetOption {
|
||||
id: string;
|
||||
label: string;
|
||||
/** Present only for harness-authored deterministic chrome; model prose has no key. */
|
||||
label_i18n?: string;
|
||||
detail?: string;
|
||||
rationale?: string;
|
||||
meta?: Record<string, unknown>;
|
||||
|
||||
Reference in New Issue
Block a user