Skip to content

OBEE-26 FE: add user setting for opt-in LLM capabilities - #1359

Open
tslil-topos wants to merge 1 commit into
mainfrom
tslil/push-kzwxpkosoouo
Open

OBEE-26 FE: add user setting for opt-in LLM capabilities#1359
tslil-topos wants to merge 1 commit into
mainfrom
tslil/push-kzwxpkosoouo

Conversation

@tslil-topos

@tslil-topos tslil-topos commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Caveat emptor

I don't really understand Solid/JSX yet. I tried to follow existing codebase conventions, but the mapping of my intent onto code may be imperfect here. In particular, the UI is ugly. I leave that to someone more capable.

What this achieves

image image

Unless you tick this box, you can't:

  • run inference
  • see LLMConversation documents in any documents view
  • make LLMConversation documents (in forthcoming UI)

Design decisions

  • Store frontend-owned user settings in a separate settings subtree of the existing Automerge user-state document. Missing settings resolve to disabled, so existing users do not require a migration.

  • Expose resolved settings and shallow patch updates through Solid context following automerge.

  • Inference keys are requested only after the authenticated user's settings have loaded and LLM capabilities are enabled.

  • Keep LLM Conversation documents in raw user state, but hide them from document lists and picker completions while the setting is disabled.

Implementation details

The fundamental way that this is all achieved is by making a new AppUserState

export type AppUserState = UserState & {
    settings?: Partial<UserSettings>;
};

and migrating uses of UserState (which is the document controlled, in some sense, by the back-end) to AppUserState in the FE.

Tests

Add a frontend behavior test verifying that disabled LLM capabilities prevent inference-key provisioning and hide LLM Conversation documents.

## Design decisions

* Store frontend-owned user settings in a separate `settings` subtree of the existing Automerge user-state document. Missing settings resolve to disabled, so existing users do not require a migration.

* Expose resolved settings and shallow patch updates through Solid context following automerge.

* Inference keys are requested only after the authenticated user's settings have loaded and LLM capabilities are enabled.

* Keep LLM Conversation documents in raw user state, but hide them from document lists and picker completions while the setting is disabled.

## Tests

Add a frontend behavior test verifying that disabled LLM capabilities prevent inference-key provisioning and hide LLM Conversation documents.
@tslil-topos tslil-topos self-assigned this Jul 29, 2026
@tslil-topos
tslil-topos requested review from epatters and kasbah and removed request for epatters July 29, 2026 14:49
@github-actions

Copy link
Copy Markdown

Notion Integration

if (doc.settings === undefined) {
doc.settings = {};
}
Object.assign(doc.settings, patch);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking around this appears to be the standard idiom for applying shallow mutations. I admit to not really understanding what's going on here, if there's a better way i'd be happy to adopt.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually we expose the document handle and call docHandle.change with a specific change, i.e., setting some specific field, rather than wrapping the docHandle.change with a patch.

@tslil-topos
tslil-topos requested a review from epatters July 30, 2026 09:49
@epatters epatters added enhancement New feature or request frontend TypeScript frontend and Rust-wasm integrations external Work on interfacing with other tools labels Jul 31, 2026

@epatters epatters left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making a start on this! Solidjs definitely takes some getting used to because it has lots of idioms (sometimes actual requirements) that aren't enforced by the type system or API but that the programmer is supposed to understand.

I left a few comments below.

A question I have for @kasbah (who should also review this PR :) is whether it's safe to add new fields to the user state solely on the frontend side, without ever persisting them to the DB. I know that the backend regularly updates the user state but I don't recall how destructive that is. My recollection of the design is that the user state is intended to be ephemeral.

Comment thread packages/frontend/src/user/profile.tsx
Comment thread packages/frontend/src/user/profile.tsx
const [userState, setUserState] = createStore<UserState>(INITIAL_USER_STATE);
const [userState, setUserState] = createStore<AppUserState>(INITIAL_USER_STATE);
const [isReady, setIsReady] = createSignal(false);
const settings = createMemo<UserSettings>(() => ({

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A Solidjs store has fine-grained reactivity, and the settings data is just a field in that store, so it shouldn't be necessary, and doesn't feel very idiomatic, to construct this memo derived from the user state. Consumers of the settings can just pull out the fields they need from the user state on demand.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, my approach was indeed convoluted here, i'm working on making this and the other simplifications as i come to understand the solid way ™️

return (
<UserStateContext.Provider value={userState}>{props.children}</UserStateContext.Provider>
<UserStateContext.Provider value={userState}>
<UserSettingsContext.Provider value={settingsContext}>

@epatters epatters Jul 31, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Building on the previous comment, on my understanding, you shouldn't need to create a separate context and provider for the settings, since it's already available through the context and provider for the user state.

const firebaseApp = useFirebaseApp();
const [userState, setUserState] = createStore<UserState>(INITIAL_USER_STATE);
const [userState, setUserState] = createStore<AppUserState>(INITIAL_USER_STATE);
const [isReady, setIsReady] = createSignal(false);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this isReady signal necessary? Outside of tests we don't usually wait around for docHandle.whenReady.

if (doc.settings === undefined) {
doc.settings = {};
}
Object.assign(doc.settings, patch);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually we expose the document handle and call docHandle.change with a specific change, i.e., setting some specific field, rather than wrapping the docHandle.change with a patch.

@tslil-topos

Copy link
Copy Markdown
Collaborator Author

thanks for the review Evan! I hope to have this resolved by (your) Monday SOD.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request external Work on interfacing with other tools frontend TypeScript frontend and Rust-wasm integrations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants