-
Notifications
You must be signed in to change notification settings - Fork 5.7k
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
Conversation
There was a problem hiding this 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?
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. |
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:
I think it's definitely OK to have session-specific (or project-specific) settings as well that override global defaults. |
openhands/server/session/session.py
Outdated
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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?
openhands/server/session/session.py
Outdated
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
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:
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:
I doubt we will ever offer a full listing of everything in the config.toml in the web ui:
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. |
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.
What do think about a session-based configuration that starts like:
|
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. |
@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) |
Also
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 |
@enyst - the overall plan here is...
There will be no change to the current extensive config available to power users, aside from the documentation updates included in this PR |
@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:
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 |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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. 😅
Introduce class for SessionConfig rather than using a dict
Open Questions
To run this PR locally, use the following command: