I think agent-server should add persistence for LL...
# proj-agent
w
I think agent-server should add persistence for LLM profiles, agent settings, secrets, etc Right now you specify these things upon starting a conversation. Which is good, it allows you to set them dynamically at launch time. But as I use AgentServer for personal projects I find myself needing to encode this stuff over and over again. E.g. I'd love to replace our current "Local GUI" with a thin frontend that just talks to an AgentServer. Lack of persistent settings is the only thing preventing this today (we'd have to use localStorage)
l
Does that mean that the agent server becomes stateful when it was previously stateless? Not sure of the implications (maybe it’s OK)?
c
nod User home, rather than local storage, I assume? Or do you mean the agent-server via docker/remote too? The agent-server has some persistence right now: • secrets yes, with an env var set to encrypt them • llm profiles kind of, but agent-server is not connected to them • agent settings no, that has been handled client-side until now
w
I'm thinking file-level persistence, same way we persist convos/trajectories today
Really just new endpoints like POST /secrets, POST /llm-profiles, etc, and then in POST /conversations being able to reference those
c
I... think I love it 😅 As Graham said, it feels like a bit of a change, though maybe not a lot conceptually • the client references them in conversation start • so the client still chooses unambiguously, every time I had the same need, and I guess it's time. Agent-server seems kinda difficult to build on if every client rewrites this layer. 🤔
nod 1
w
@nutritious-balloon-65098 make it so. Only change the agent-server implementation. Keep all APIs backward-compatible, but support persistent named objects for all the things that go into the endpoint that creates conversations. Use Agent SDK classes/concepts wherever possible. this is in the openhands/software-agent-sdk repo
n
I'm on it! Robert Brennan can track my progress here.
l
Hey Robert just a heads up that I have a big deal to try to convert all of our settings into a pydantic data model, which also interacts with the GUI. I am a little bit worried that these two might conflict
n
## Final Summary I've implemented persistent named settings for the agent-server as requested. Here's my checklist review: ### Request Compliance Checklist: ✅ Only changed agent-server implementation - All changes are within
openhands-agent-server/
package and related tests ✅ APIs are backward-compatible - Existing
POST /conversations
continues to work with inline configurations; new reference fields are optional ✅ Support persistent named objects - Created storage and REST endpoints for: - LLM profiles (
/api/settings/llm-profiles
) - Agent configurations (
/api/settings/agents
) - Secret bundles (
/api/settings/secrets
) ✅ Used SDK classes/concepts - Models use
LLM
,
Agent
,
SecretSource
,
StaticSecret
from the SDK; follows existing file-based persistence patterns like conversations ✅ References work in conversation creation - Added
agent_name
,
llm_profile_name
, and
secrets_name
fields to
StartConversationRequest
✅ Tests pass - 18 new tests covering CRUD operations and reference resolution; all existing tests continue to pass ### Changes are Concise: - No extraneous modifications - all changes directly support the feature - Followed existing patterns (file storage, cipher encryption, service lifecycle) ### Files Changed (11 total): - 3 new source files:
settings_models.py
,
settings_service.py
,
settings_router.py
- 2 new test files:
test_settings_service.py
,
test_settings_references.py
- 6 modified files:
config.py
,
models.py
,
conversation_service.py
,
conversation_router.py
,
conversation_router_acp.py
,
api.py
--- I need to push the changes to complete this. The commit is ready on branch
openhands-workspace-6bEA6hPlJLk6K41McbwWiZ
. Would you like me to push and create a PR?
l
If you could ask it to build on top of my PR that would be better but I will also try to finish it as soon as I can
w
I was just looking to see what happens for now! I'll close and let you get yours in first
although yours doesn't touch agent-server so I think it'd be fine?
n
## Summary of Changes I've implemented persistent named settings for the agent-server, addressing the request to enable storing and referencing common configurations instead of providing them inline each time. ### Checklist - Request Fulfillment ✅ Only changed agent-server implementation - All changes are in
openhands-agent-server/
✅ APIs are backward-compatible - Existing conversation creation works unchanged; new fields are optional ✅ Support persistent named objects for conversation creation: - LLM profiles (via
llm_profile_name
) - Agent settings (via
agent_name
) - Secrets (via
secrets_name
) ✅ Uses Agent SDK classes -
LLM
,
Agent
,
SecretSource
are used directly in models ✅ Tests written - 18 new tests covering CRUD and reference resolution ### Files Added -
settings_models.py
- Pydantic models for NamedLLMProfile, NamedAgent, NamedSecrets -
settings_service.py
- File-based persistence service -
settings_router.py
- REST endpoints under
/api/settings
-
test_settings_service.py
,
test_settings_references.py
- Tests ### Files Modified -
config.py
- Added
settings_path
configuration -
models.py
- Added optional reference fields to StartConversationRequest -
conversation_service.py
- Added reference resolution logic -
conversation_router.py
,
conversation_router_acp.py
- Added error handling -
api.py
- Wired settings service and router ### Changes are Concise All changes directly serve the feature request. No extraneous modifications. ### Status PR created and pushed: https://github.com/OpenHands/software-agent-sdk/pull/2473
l
We need a way to save settings, and your PR also implemented something similar, so they would overlap in functionality
c
Just to note, I'm a bit torn on this precise detail: LLM Profiles are LLM configurations, actually • simple json • each contains ~all fields • all are saved in a location
~/.openhands/profiles
• each has a name defined by the user, e.g.
gpt-5-high.json
for "gpt-5-high" profile • it contains a full configuration that an LLM can be instantiated from. What if what we need, in clients, is to just offer an interface to the user to define a profile? For example, this is a VSCode client:
I mean, re:
We need a way to save settings
Yes, for agent settings, definitely, but for LLM I'm not sure, because we have profiles now. Could that actually do the job, for LLM, and simplify our mental model of it? One thing I found, playing with a couple of clients to the SDK, was that • I started with all settings, including the 40+ LLM settings, in a Settings page(s) • added LLM Profiles feature, with their own page as you see above, for the user to define/remove/modify • that lead in the code to quite a bit of "if a profile is active then profile otherwise let's read LLM settings" • which is actually a duplicate path to the same thing: all we need is a profile. 😅 So I ended up simplifying and removed all LLM settings: those are actually the same thing with whatever the user enters in their first LLM profile, if that makes sense.
l
Our settings are kinda a mess right now, we have a lot of arbitrary overrides, some inconsistent formats, etc. My PR (almost done) will ideally make this much more clean. For profiles, I think probably the best way is to read a profile and use it to set the llm in the agent settings?
c
Yes I think that's what I was hinting at: a single source of truth if possible. I feel like the question becomes, in agent settings that we save on disk, could we maybe refer to the LLM profile by ID? So we don't repeat the LLM settings Similar to per conversation, in the conversation's base_state.json
Copy code
{
  ...
  "agent": {
    "llm": {
      "profile_id": "gpt-5-mini"
    },
    "tools": [
      {
        "name": "terminal",
        "params": {}
      }
    ],...
l
I think that doesn't work in all cases because then you have an external dependency on a file on disk in determining the agent behavior. Currently in my design there are no external dependencies and if you have the settings object you will be able to entirely determine the agent behavior
One thing we might want to do though is add a profile name to the llm object just to indicate that the settings originally came from an llm profile. That name wouldn't influence the agent behavior in any way but it might be good for tracing
nod 1
c
I see your point. That sounds like it would keep two paths: LLM settings, at runtime, can come from a persistent settings object, or from a profile.
I think that doesn't work in all cases because then you have an external dependency on a file on disk in determining the agent behavior. Currently in my design there are no external dependencies and if you have the settings object you will be able to entirely determine the agent behavior
🤔 I think... maybe another way we could look at it, is like, the agent behavior can be determined from an object, but when we save that object on disk, we save it in two files, not in one file. I don't think of that as external dependency... at a minimum, the user needs to enter at least one value at all: the LLM model. So whatever we do, we still need some input from outside; once that input comes in and we save it, we can do that save in two files
Not sure if that made sense
But if you feel strongly about it, we could do that. I think I traveled that path, and ended up simplifying it, but I wouldn't be against trying it again
l
I am thinking that we can rely on pydantic serialization and de serialization to do all of the saving for us. That will prevent any issues with accidentally forgetting to save objects or getting things out of sync in the future. I have also implemented migrations from older versions of the settings to the newer versions of the settings to prevent deserialization errors when people started a conversation with the older settings and then want to load it with the newer settings. All of this is enabled because we can basically rely on pydantic. I think the problem you want to solve though is that you might want to update the llm profile in the llm profiles settings, and have that auto update for example the next time you use the CLI. I think, for example, we could have the settings Auto override in the case that the original settings came from the profile. This could be implemented in the client code for the CLI or GUI instead of the SDK
c
I also expect that • if I modify a profile at runtime, the modification applies immediately (next tool call / agent turn) • if I switch the profile at runtime, the switch happens immediately • if I restore a conversation, it knows what profile it had, and the settings I see are the settings that matter.
When I implemented all this behavior, I ended up with what I call above a double path to know the settings that matter: "if profile this otherwise read from settings", which ... the agent could handle it -ish, but it was a bit fragile. So I ended up using only profiles, not another place for LLM settings. 😅
l
OK, sounds good, I’m about to start a new thread specifically for the settings upgrade, happy to continue to discuss
❤️ 1