v1.2.0: advisor is per session and off by default (new and existing sessions); old global setting ignored
This commit is contained in:
1 parent
f85f445ef6
commit
0f423e3699
6 files changed
+152
-55
No files matched your search
@@ -85,14 +85,15 @@ window.__ModuleLoader__.load({
|
||||
const fill = (s, v) => String(s).replace(/\{(\w+)\}/g, (_, k) => (v && v[k] !== undefined ? v[k] : ''));
|
||||
const effortName = (e) => (e?.id === 'xhigh' ? 'Extra' : e?.name ?? e?.id);
|
||||
|
||||
// ---- host settings store (shared by every session's control) ----
|
||||
const settings = (() => {
|
||||
// ---- host settings, one store per session (the advisor is chosen per session) ----
|
||||
function makeStore(sessionId) {
|
||||
let value = null;
|
||||
let inflight = null;
|
||||
const subs = new Set();
|
||||
const emit = () => subs.forEach((f) => f());
|
||||
const url = `omp-advisor/settings?session=${encodeURIComponent(sessionId)}`;
|
||||
async function call(init) {
|
||||
const res = await fetch('omp-advisor/settings', { ...init, headers: { accept: 'application/json', ...(init?.body ? { 'content-type': 'application/json' } : {}) } });
|
||||
const res = await fetch(url, { ...init, headers: { accept: 'application/json', ...(init?.body ? { 'content-type': 'application/json' } : {}) } });
|
||||
let json = {};
|
||||
try { json = await res.json(); } catch { /* empty */ }
|
||||
if (!res.ok) throw new Error(json.message || res.statusText || String(res.status));
|
||||
@@ -112,11 +113,17 @@ window.__ModuleLoader__.load({
|
||||
try { value = await call({ method: 'POST', body: JSON.stringify(patch) }); emit(); } catch (e) { value = prev; emit(); throw e; }
|
||||
},
|
||||
};
|
||||
})();
|
||||
}
|
||||
const stores = new Map();
|
||||
const storeFor = (sessionId) => {
|
||||
let s = stores.get(sessionId);
|
||||
if (!s) { s = makeStore(sessionId); stores.set(sessionId, s); }
|
||||
return s;
|
||||
};
|
||||
|
||||
function useSettings() {
|
||||
const v = React.useSyncExternalStore(settings.subscribe, settings.get);
|
||||
React.useEffect(() => { if (v === null) settings.load(); }, [v]);
|
||||
function useSettings(store) {
|
||||
const v = React.useSyncExternalStore(store.subscribe, store.get);
|
||||
React.useEffect(() => { if (v === null) store.load(); }, [store, v]);
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -136,9 +143,15 @@ window.__ModuleLoader__.load({
|
||||
return { left: Math.max(8, left), top: Math.max(8, Math.min(top, vh - m.height - 8)) };
|
||||
}
|
||||
|
||||
function AdvisorPicker({ directory, load, t }) {
|
||||
React.useEffect(installCss, []);
|
||||
const st = useSettings();
|
||||
function AdvisorPicker(props) {
|
||||
// Session-scoped slot; without an id (draft composer) there is nothing to configure yet.
|
||||
if (typeof props.sessionId !== 'string' || !props.sessionId) return null;
|
||||
return h(AdvisorPickerFor, props);
|
||||
}
|
||||
|
||||
function AdvisorPickerFor({ sessionId, directory, load, t }) {
|
||||
const store = storeFor(sessionId);
|
||||
const st = useSettings(store);
|
||||
const dir = React.useSyncExternalStore((f) => directory.subscribe(f), () => directory.getSnapshot());
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [hover, setHover] = React.useState(null); // { key, el, model, group }
|
||||
@@ -151,7 +164,7 @@ window.__ModuleLoader__.load({
|
||||
const subRef = React.useRef(null);
|
||||
const hoverTimer = React.useRef(null);
|
||||
|
||||
const enabled = st?.enabled !== false;
|
||||
const enabled = st?.enabled === true; // off until this session turns it on (also while loading)
|
||||
const own = !!(st?.provider && st?.model);
|
||||
const groups = dir?.groups ?? [];
|
||||
const current = own ? groups.find((g) => g.id === st.provider)?.models.find((m) => m.id === st.model) : null;
|
||||
@@ -187,7 +200,7 @@ window.__ModuleLoader__.load({
|
||||
const choose = async (patch) => {
|
||||
setErr(null);
|
||||
setOpen(false);
|
||||
try { await settings.save(patch); } catch (e) { setErr(String(e.message || e)); setOpen(true); }
|
||||
try { await store.save(patch); } catch (e) { setErr(String(e.message || e)); setOpen(true); }
|
||||
};
|
||||
const pickModel = (group, model, effort) => choose({
|
||||
enabled: true, provider: group.id, model: model.id,
|
||||
@@ -292,7 +305,7 @@ window.__ModuleLoader__.load({
|
||||
const directory = ctx.modelDirectories.directoryFor(sessionId);
|
||||
// Read-only use of the directory: the advisor never calls directory.select (that would
|
||||
// switch the agent's own model).
|
||||
return { directory: directory.store, load: () => { directory.load().catch(() => {}); } };
|
||||
return { sessionId, directory: directory.store, load: () => { directory.load().catch(() => {}); } };
|
||||
},
|
||||
}, AdvisorPicker));
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user