Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Introduce class for SessionInitData rather than using a dict #5406

Merged
merged 14 commits into from
Dec 5, 2024

Conversation

tofarr
Copy link
Collaborator

@tofarr tofarr commented Dec 4, 2024

Introduce class for SessionConfig rather than using a dict

  • Include this change in the Release Notes. If checked, you must provide an end-user friendly description for your change below

  • SessionConfig is now specified more formally.
  • We retain the existing serialization in init for backwards compatibility
  • Config changes are now applied only to the current session rather than to the shared global config.

Open Questions

  • I placed SessionConfig in the session module rather than the core/config module as it is specific to web and the session and different from the rest of the configuration. If it is more understandable to place this with the rest of the config I am open to doing this.

To run this PR locally, use the following command:

docker run -it --rm   -p 3000:3000   -v /var/run/docker.sock:/var/run/docker.sock   --add-host host.docker.internal:host-gateway   -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:4294c29-nikolaik   --name openhands-app-4294c29   docker.all-hands.dev/all-hands-ai/openhands:4294c29

@tofarr tofarr marked this pull request as ready for review December 4, 2024 17:59
Copy link
Collaborator

@enyst enyst left a comment

Choose a reason for hiding this comment

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

This PR is refactoring the existing dict, and it looks nice overall. However, it seems to conflict with what we tracked as the way forward here:
#3220
(please see comment)

For example, the PR defines duplicates of llm_model etc attributes making it more clearly disconnected from the underlying config-based configuration. I know this is the current behavior - this PR makes more clear! 😅, but I would appreciate if @neubig and @rbren could take a look at where this is heading, and we can figure out the way forward.

Related question: are you considering multiple session_configs per user?

@enyst
Copy link
Collaborator

enyst commented Dec 4, 2024

Allow me to note another detail, related to the above, but not quite the same:

Currently, the UI settings offer to users only a handful of the settings the application actually has. To my knowledge, we are moving towards offering to users in the UI all or most of our settings. That will mean that this SessionConfig class would become a completely or almost completely duplicate hierarchy of our settings?

I'd appreciate to take the time to think this one over.

@neubig
Copy link
Contributor

neubig commented Dec 4, 2024

Hi! I don't have a huge amount of time to look at this today unfortunately...

But basically what I have been saying for a while is:

  1. It'd be good to have a single source of truth for all settings, and that source of truth gets updated when we change the settings anywhere (by modifying things in the UI, or in config files, etc)
  2. That should be well documented

I think it's definitely OK to have session-specific (or project-specific) settings as well that override global defaults.

Comment on lines 78 to 80
default_llm_config.model = session_config.llm_model or default_llm_config.model
default_llm_config.api_key = session_config.llm_api_key or default_llm_config.api_key
default_llm_config.base_url = session_config.llm_base_url or default_llm_config.base_url
Copy link
Collaborator

Choose a reason for hiding this comment

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

seems like we're still mucking with the global defaults here

Copy link
Collaborator

Choose a reason for hiding this comment

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

It's the current behavior though. 🤔 What do you think it should do?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Modifying global state with a single session would potentially create a ton of bugs and security issues, so I think the deepcopy we have above now makes this much safer. Will be much more important in a multi-Conversation world too (which we're working on now)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Right, I'm not thinking of it as global state, but it kinda is. It's the fallback LLMConfig/etc instance one gets if they didn't make / retrieve a specific instance, right?

If what we need here is a an LLMConfig / SecurityConfig instance per sid, could we make an LLMConfig / SecurityConfig instance per sid?

Comment on lines 78 to 80
default_llm_config.model = session_config.llm_model or default_llm_config.model
default_llm_config.api_key = session_config.llm_api_key or default_llm_config.api_key
default_llm_config.base_url = session_config.llm_base_url or default_llm_config.base_url
Copy link
Collaborator

Choose a reason for hiding this comment

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

seems like we're still mucking with the global defaults here

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We do a deep copy of the global config when we create a session, so settings are not copied to the global shared object here.

@tofarr
Copy link
Collaborator Author

tofarr commented Dec 4, 2024

I did not envision this one resulting in so much discussion! Here is the rationale / use case as briefly as I can:

There are already 2 types of config:

  • Global shared defaults derived from config.toml / environment applying to the whole server
  • Session specific parameters from the Web UI, and apply only to the current user / session

Currently the session specific parameters are not formalized and represented by a dict. This PR really only formalizes them.

The justification for not simply using the existing config classes here is:

  • These entities have distinct origins (Config file vs web UI)
  • These entities have distinct scopes (A user session vs global)
  • The SessionConfig is likely to only be a subset of the global config (But this is not guaranteed - it may end up containing attributes for which no default makes logical sense)

I doubt we will ever offer a full listing of everything in the config.toml in the web ui:

  • Such a UI is likely to be very complex (and experienced users tend to prefer a config file for this level of detail anyway)
  • There are technical concerns - Some values require a server restart to be effective.
  • There are security concerns - Some values should NOT be shared between users.

If we later have config that makes sense in the context of a user rather than a session, I suggest we add such config at that time - in any case this is an interim step in that direction.

@enyst
Copy link
Collaborator

enyst commented Dec 4, 2024

Those are good points! Please let me take a simpler part of it all, LLMConfig.

Currently, the UI only offers like three attributes, from all of it.

  • there's no reason, is it, why we wouldn't let all the attrs of litellm completion to be customized by users (there's a bunch of historical posts that converge towards the intention to do it, too)
  • because the UI offers the user the ability to override 3/n, the users experience the confusing issue linked above (and there are others like it), where they feel like modifying the toml should work and it doesn't. (it works for n-3 😅)
  • there are multiple LLMs possible in the backend, but the UI only offers one; that may change - e.g. we have a draft llm that users can configure
  • there is an LLM config per agent in the backend, and that... can be configured in the toml partially, and strange things may happen because the user HAS the ability to choose both stuff in the UI (an agent) and stuff in config.toml (agent llm) and the interference with the default LLM attributes... sorry, I don't remember what it breaks, but it breaks some expectations. 😅

What do think about a session-based configuration that starts like:

class SessionConfig
    llm_config: LLMConfig # main LLM
    draft_llm_config: LLMConfig # draft LLM
    ...

@tofarr
Copy link
Collaborator Author

tofarr commented Dec 4, 2024

What do think about a session-based configuration that starts like:

class SessionConfig
    llm_config: LLMConfig # main LLM
    draft_llm_config: LLMConfig # draft LLM
    ...

I think you may be on to something here - If I understand you correctly, you refer to the issue where values that are specified in the WebUI seem to ignore any value from the config.toml / ENV.

Maybe we do need to separate these out for Web - though the CLI and Headless modes still accept values from the config.toml at present AFAIK. Let me think a little more and update the proposal.

@tofarr tofarr changed the title Feat: Introduce class for SessionConfig rather than using a dict Feat: Introduce class for SessionInitData rather than using a dict Dec 5, 2024
@rbren
Copy link
Collaborator

rbren commented Dec 5, 2024

@enyst I agree we have a real problem with the config.toml + UI configuration

Right now, I'm mainly concerned about the 80% of users who are UI-only. Keeping a simple UI in place, and making sure those settings persist across sessions (and across different browsers connected to the same server), is the main objective we're going after right now.

I do think we'll need to figure out how to properly sync between config.toml and the UI. Tim's next PR (which will create long-term server-side storage for the LLM configuration) will hopefully set us up a little better here. By default it'll create a file on the user's machine that they can modify (though it'd be separate from config.toml)

@rbren
Copy link
Collaborator

rbren commented Dec 5, 2024

Also

are you considering multiple session_configs per user?

Good question--probably not in the near term. I don't see much use in e.g. having 1 session going with GPT and another going with Claude, except for just mucking about and experimenting

@tofarr
Copy link
Collaborator Author

tofarr commented Dec 5, 2024

@enyst - the overall plan here is...

  • This PR formalizes the SessionInitData from a dict to a dataclass
  • The next PR will introduce server side storage for this so it is no longer in local storage. (In the OSS this will be a file store)

There will be no change to the current extensive config available to power users, aside from the documentation updates included in this PR

@enyst
Copy link
Collaborator

enyst commented Dec 5, 2024

@rbren I see, and sure, I don't think this PR needs to solve our age-old problem there in a blink. 😅

@tofarr your point of view is most interesting: you're describing how things look like from the perspective of building the web ui / remote app. It's instructive.

I do want to note that, of course the distinction between user configuration and server/global configuration is real. And we're not currently making it anywhere really. We should. But I don't think it lies in "what happens to be available in the Web client today" vs "what is in files". That can't be right, there are other clients than the web client, there are other user-specific or session-specific settings. I would even argue that there is such thing as CLI sessions, although not formalized.

Re:

Tim's next PR (which will create long-term server-side storage for the LLM configuration) will hopefully set us up a little better here. By default it'll create a file on the user's machine that they can modify (though it'd be separate from config.toml)

For the record, we have the ability to define the location of the config.toml intended per user (e.g. can drop it in ~/config.toml), coming from previous chat, configurable location PR.

This was intended as a step for multi-user use, albeit a different one.

args = {key: value for key, value in data.get('args', {}).items()}
agent_cls = args.get(ConfigType.AGENT, self.config.default_agent)
self.config.security.confirmation_mode = args.get(
ConfigType.CONFIRMATION_MODE, self.config.security.confirmation_mode
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe we can now remove ConfigType. Last I checked, it was only used here.

@@ -52,41 +53,31 @@ def __init__(
self.agent_session.event_stream.subscribe(
EventStreamSubscriber.SERVER, self.on_event, self.sid
)
self.config = config
# Copying this means that when we update variables they are not applied to the shared global configuration!
self.config = deepcopy(config)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This means we need one AppConfig per sid, with its other stuff. For other clients, when we need a specific instance of a config class, the config module is able to make them, under predefined names (like draft_llm_config) or user-defined names (like these).

Maybe we can take that into account for these config instances? I don't think it needs to be in this PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The reason I added this in here was the idea of writing back into the global config from the web input is really scary!
For example this line (I preserved the existing logic):
default_llm_config.api_key = session_init_data.llm_api_key or default_llm_config.api_key

Before the deep copy, this would write a user's api_key back into the global config. Another user could come along and call init without an API key (They would need to modify the client side code) and boom - they use an API key owned by the last person who entered one! (This was actually the primary motivation behind this PR.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This had me 😱 when I spotted it!

Copy link
Collaborator

@enyst enyst Dec 5, 2024

Choose a reason for hiding this comment

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

Argh. By the way, this is why these methods exist. Their only reason for existence is to be used by the UI to get defaults - actual defaults, dataclass values only, not from config, not from env, not from, uh, previous values.

Currently, the UI isn't using them, so nothing is using them. They're just sitting there and be pretty. 😅

@tofarr tofarr merged commit de81020 into main Dec 5, 2024
21 checks passed
@tofarr tofarr deleted the feat-config-store-interface branch December 5, 2024 20:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants