-
Notifications
You must be signed in to change notification settings - Fork 557
feat(agent): route load-session through a no-op session store port #4768
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,13 +12,15 @@ | |
| from agenta.sdk.contexts.tracing import tracing_context_manager | ||
| from agenta.sdk.models.workflows import ( | ||
| LoadSessionRequest, | ||
| LoadSessionResponse, | ||
| WorkflowBatchResponse, | ||
| WorkflowInvokeRequest, | ||
| WorkflowRequestData, | ||
| WorkflowStreamingResponse, | ||
| ) | ||
|
|
||
| from .messages import vercel_ui_messages_to_messages | ||
| from ...interfaces import NoopSessionStore, SessionStore | ||
| from .messages import message_to_vercel_ui_message, vercel_ui_messages_to_messages | ||
|
|
||
| # An opaque, project-scoped session id (RFC §4.1): bounded length, restricted charset. | ||
| _SESSION_ID_RE = re.compile(r"^[A-Za-z0-9._:-]{1,128}$") | ||
|
|
@@ -122,12 +124,24 @@ async def messages_endpoint(req: Request, request: WorkflowInvokeRequest): | |
| return messages_endpoint | ||
|
|
||
|
|
||
| def make_load_session_endpoint(): | ||
| """Build the v1 ``POST /load-session`` stub endpoint.""" | ||
| def make_load_session_endpoint( | ||
| *, | ||
| session_store: Optional[SessionStore] = None, | ||
| ): | ||
| """Build the v1 ``POST /load-session`` endpoint over the session-store port.""" | ||
| store = session_store or NoopSessionStore() | ||
|
|
||
| async def load_session_endpoint(req: Request, request: LoadSessionRequest): | ||
| messages = await store.load(request.session_id) | ||
| response = LoadSessionResponse( | ||
| session_id=request.session_id, | ||
| messages=[ | ||
| message_to_vercel_ui_message(message, message_id=f"msg-{idx}") | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Loaded neutral messages convert back to Vercel |
||
| for idx, message in enumerate(messages, start=1) | ||
| ], | ||
| ) | ||
| return JSONResponse( | ||
| content={"session_id": request.session_id, "messages": []}, | ||
| content=response.model_dump(mode="json"), | ||
| ) | ||
|
|
||
| return load_session_endpoint | ||
|
|
@@ -146,6 +160,7 @@ def register_agent_message_routes( | |
| make_not_acceptable_response: Callable[[str, Any], Response], | ||
| make_stream_response: Callable[[WorkflowStreamingResponse, str], Response], | ||
| handle_failure: Callable[[Exception], Any], | ||
| session_store: Optional[SessionStore] = None, | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
| ) -> None: | ||
| """Register ``/messages`` and ``/load-session`` on a FastAPI app/router target.""" | ||
| target.add_api_route( | ||
|
|
@@ -165,6 +180,6 @@ def register_agent_message_routes( | |
| ) | ||
| target.add_api_route( | ||
| prefix + "/load-session", | ||
| make_load_session_endpoint(), | ||
| make_load_session_endpoint(session_store=session_store), | ||
| methods=["POST"], | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -86,6 +86,44 @@ async def destroy(self) -> None: | |
| return None | ||
|
|
||
|
|
||
| class SessionStore(ABC): | ||
| """Durable conversation history behind the agent session id. | ||
|
|
||
| The cold runtime still receives the full message history on every turn. This port is the | ||
| place a platform-backed or file-backed store attaches when the server owns that history. | ||
| """ | ||
|
|
||
| @abstractmethod | ||
| async def load(self, session_id: str) -> Sequence[Message]: | ||
| """Return the neutral message history for ``session_id``.""" | ||
|
|
||
| @abstractmethod | ||
| async def save_turn( | ||
| self, | ||
| session_id: str, | ||
| *, | ||
| messages: Sequence[Message], | ||
| result: Optional[AgentResult] = None, | ||
| ) -> None: | ||
| """Persist one completed cold turn.""" | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the contract being frozen ahead of any real store. Confirm |
||
|
|
||
|
|
||
| class NoopSessionStore(SessionStore): | ||
| """Session store adapter used until server-owned history persistence lands.""" | ||
|
|
||
| async def load(self, session_id: str) -> Sequence[Message]: | ||
| return () | ||
|
|
||
| async def save_turn( | ||
| self, | ||
| session_id: str, | ||
| *, | ||
| messages: Sequence[Message], | ||
| result: Optional[AgentResult] = None, | ||
| ) -> None: | ||
| return None | ||
|
|
||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Backend (the engine) | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
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
or NoopSessionStore()default is what keeps the response identical to the old hardcoded stub when no adapter is wired. Worth confirming this is the only place the default is chosen.