Skip to content

Commit 08e687c

Browse files
Clarify launched agent profile provenance
Rename the conversation-start provenance model and API field to explicitly reference agent profiles, while accepting the short-lived legacy payload names during validation. Co-authored-by: openhands <openhands@all-hands.dev>
1 parent 76e0e79 commit 08e687c

5 files changed

Lines changed: 99 additions & 66 deletions

File tree

openhands-agent-server/openhands/agent_server/conversation_service.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
ConversationInfo,
2222
ConversationPage,
2323
ConversationSortOrder,
24-
LaunchedProfile,
24+
LaunchedAgentProfile,
2525
StartConversationRequest,
2626
StoredConversation,
2727
UpdateConversationRequest,
@@ -245,7 +245,7 @@ def _resolve_agent_from_profile(
245245
profile_id: "UUID",
246246
cipher: "Cipher | None",
247247
mcp_config: "Any",
248-
) -> "tuple[AgentBase, LaunchedProfile]":
248+
) -> "tuple[AgentBase, LaunchedAgentProfile]":
249249
"""Load and resolve an agent profile by id, returning the built agent + provenance.
250250
251251
Runs synchronously (call via ``asyncio.to_thread`` from async context).
@@ -290,7 +290,10 @@ def _resolve_agent_from_profile(
290290
raise ValueError(f"Profile '{profile_name}' failed to resolve: {exc}") from exc
291291

292292
agent = settings_config.create_agent()
293-
launched = LaunchedProfile(profile_id=profile.id, revision=profile.revision)
293+
launched = LaunchedAgentProfile(
294+
agent_profile_id=profile.id,
295+
revision=profile.revision,
296+
)
294297
return agent, launched
295298

296299

@@ -363,7 +366,7 @@ def _compose_conversation_info(
363366
available_models=available_models,
364367
supports_runtime_model_switch=supports_runtime_model_switch,
365368
client_tools=stored.client_tools,
366-
launched_profile=stored.launched_profile,
369+
launched_agent_profile=stored.launched_agent_profile,
367370
)
368371

369372

@@ -670,7 +673,7 @@ async def _start_conversation(
670673
# Profile resolution must happen before _prepare_request_workspace (which
671674
# asserts request.agent is not None) and before model_dump so the resolved
672675
# agent is captured in request_data.
673-
launched_profile: LaunchedProfile | None = None
676+
launched_agent_profile: LaunchedAgentProfile | None = None
674677
if request.agent_profile_id is not None:
675678
# get_settings_store() is safe here: get_instance() initialises the
676679
# singleton with the server cipher before any conversation can start.
@@ -681,7 +684,7 @@ async def _start_conversation(
681684

682685
settings = get_settings_store().load() or PersistedSettings()
683686
mcp_config = settings.agent_settings.mcp_config
684-
resolved_agent, launched_profile = await asyncio.to_thread(
687+
resolved_agent, launched_agent_profile = await asyncio.to_thread(
685688
_resolve_agent_from_profile,
686689
request.agent_profile_id,
687690
self.cipher,
@@ -751,7 +754,7 @@ async def _start_conversation(
751754
# serialize to plain strings. Pass expose_secrets=True so StaticSecret values
752755
# are preserved through the round-trip; the dict is only used in-process to
753756
# construct StoredConversation, not sent over the network.
754-
# agent_profile_id is excluded: it was resolved into `launched_profile`
757+
# agent_profile_id is excluded: it was resolved into `launched_agent_profile`
755758
# above and must not re-trigger the mutual-exclusivity validator.
756759
request_data = request.model_dump(
757760
mode="json",
@@ -772,9 +775,9 @@ async def _start_conversation(
772775
{
773776
"id": conversation_id,
774777
**request_data,
775-
"launched_profile": (
776-
launched_profile.model_dump(mode="json")
777-
if launched_profile is not None
778+
"launched_agent_profile": (
779+
launched_agent_profile.model_dump(mode="json")
780+
if launched_agent_profile is not None
778781
else None
779782
),
780783
},
@@ -783,7 +786,7 @@ async def _start_conversation(
783786
else:
784787
stored = StoredConversation(
785788
id=conversation_id,
786-
launched_profile=launched_profile,
789+
launched_agent_profile=launched_agent_profile,
787790
**request_data,
788791
)
789792
event_service = await self._start_event_service(stored)

openhands-agent-server/openhands/agent_server/models.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from typing import Any, TypeAlias
77
from uuid import UUID, uuid4
88

9-
from pydantic import BaseModel, Field, field_validator
9+
from pydantic import AliasChoices, BaseModel, Field, field_validator
1010

1111
from openhands.sdk import LLM
1212
from openhands.sdk.agent.acp_models import ACPModelInfo
@@ -28,7 +28,9 @@
2828
TextContent as TextContent,
2929
)
3030
from openhands.sdk.llm.utils.metrics import MetricsSnapshot
31-
from openhands.sdk.profiles.agent_profile import LaunchedProfile as LaunchedProfile
31+
from openhands.sdk.profiles.agent_profile import (
32+
LaunchedAgentProfile as LaunchedAgentProfile,
33+
)
3234
from openhands.sdk.secret import SecretSource
3335
from openhands.sdk.security.analyzer import SecurityAnalyzerBase
3436
from openhands.sdk.security.confirmation_policy import (
@@ -79,7 +81,7 @@ class StoredConversation(StartConversationRequest):
7981
Extends StartConversationRequest with server-assigned fields.
8082
"""
8183

82-
# agent_profile_id is resolved into launched_profile at creation; exclude from
84+
# agent_profile_id is resolved into launched_agent_profile at creation; exclude from
8385
# the persistence payload so it does not re-appear in meta.json.
8486
agent_profile_id: UUID | None = Field(default=None, exclude=True)
8587

@@ -90,8 +92,9 @@ class StoredConversation(StartConversationRequest):
9092
metrics: MetricsSnapshot | None = None
9193
created_at: datetime = Field(default_factory=utc_now)
9294
updated_at: datetime = Field(default_factory=utc_now)
93-
launched_profile: LaunchedProfile | None = Field(
95+
launched_agent_profile: LaunchedAgentProfile | None = Field(
9496
default=None,
97+
validation_alias=AliasChoices("launched_agent_profile", "launched_profile"),
9598
description=(
9699
"Provenance snapshot of the agent profile that launched this "
97100
"conversation. Set at creation when `agent_profile_id` is supplied; "
@@ -248,14 +251,15 @@ class _ConversationInfoBase(BaseModel):
248251
"and before the conversation has started a session."
249252
),
250253
)
251-
launched_profile: LaunchedProfile | None = Field(
254+
launched_agent_profile: LaunchedAgentProfile | None = Field(
252255
default=None,
256+
validation_alias=AliasChoices("launched_agent_profile", "launched_profile"),
253257
description=(
254258
"Provenance snapshot of the agent profile that launched this "
255259
"conversation. Set at creation when the conversation was started via "
256260
"``agent_profile_id``; ``None`` for conversations started directly "
257261
"with ``agent`` or ``agent_settings``. Clients use this to identify "
258-
"which profile is current without fragile settings-comparison."
262+
"which agent profile is current without fragile settings-comparison."
259263
),
260264
)
261265

openhands-sdk/openhands/sdk/profiles/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
ACPAgentProfile,
66
AgentProfile,
77
AgentProfileBase,
8-
LaunchedProfile,
8+
LaunchedAgentProfile,
99
OpenHandsAgentProfile,
1010
ProfileVerificationSettings,
1111
validate_agent_profile,
@@ -38,7 +38,7 @@
3838
"AgentProfileDiagnostics",
3939
"AgentProfileStore",
4040
"DanglingMcpServerRef",
41-
"LaunchedProfile",
41+
"LaunchedAgentProfile",
4242
"OpenHandsAgentProfile",
4343
"ProfileLimitExceeded",
4444
"ProfileNotFound",

openhands-sdk/openhands/sdk/profiles/agent_profile.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from uuid import UUID, uuid4
1515

1616
from pydantic import (
17+
AliasChoices,
1718
BaseModel,
1819
ConfigDict,
1920
Discriminator,
@@ -209,20 +210,21 @@ class ACPAgentProfile(AgentProfileBase):
209210
)
210211

211212

212-
class LaunchedProfile(BaseModel):
213-
"""Provenance snapshot recorded when a profile launches a conversation.
213+
class LaunchedAgentProfile(BaseModel):
214+
"""Provenance snapshot recorded when an agent profile launches a conversation.
214215
215216
Stored on ``StoredConversation`` and projected onto ``ConversationInfo`` so
216-
ts-client ``deriveSwitchPlan`` can identify which profile is current without
217-
fragile settings-comparison. See #3720.
217+
ts-client ``deriveSwitchPlan`` can identify which agent profile is current
218+
without fragile settings-comparison. See #3720.
218219
"""
219220

220-
profile_id: UUID = Field(
221-
description="Stable id of the profile that launched the conversation."
221+
agent_profile_id: UUID = Field(
222+
validation_alias=AliasChoices("agent_profile_id", "profile_id"),
223+
description="Stable id of the agent profile that launched the conversation.",
222224
)
223225
revision: int = Field(
224226
ge=0,
225-
description="Revision of the profile at launch time.",
227+
description="Revision of the agent profile at launch time.",
226228
)
227229

228230

0 commit comments

Comments
 (0)