-
Notifications
You must be signed in to change notification settings - Fork 68
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
[SYNPY-1544] Synapse Agent OOP Model #1152
base: develop
Are you sure you want to change the base?
Conversation
Hello @BWMac! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:
Comment last updated at 2025-01-08 21:53:48 UTC |
# Request matching <https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/agent/AgentRegistrationRequest.html> | ||
request = { | ||
"awsAgentId": cloud_agent_id, | ||
"awsAliasId": cloud_alias_id if cloud_alias_id else None, |
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.
Will this request work if you pass in None? My assumption is that the key in the http body wouldn't be sent if there is no value
|
||
client = Synapse.get_client(synapse_client=synapse_client) | ||
|
||
# Request matching <https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/agent/CreateAgentSessionRequest.html> |
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.
Might make sense to put this in the docstring so it can be referenced to understand what access_level is supposed to be
|
||
# Request matching <https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/agent/AgentChatRequest.html> | ||
request = { | ||
"concreteType": "org.sagebionetworks.repo.model.agent.AgentChatRequest", |
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.
There is a constant file to throw this in to:
https://github.com/Sage-Bionetworks/synapsePythonClient/blob/develop/synapseclient/core/constants/concrete_types.py
start_time = asyncio.get_event_loop().time() | ||
TIMEOUT = 60 | ||
|
||
while True: |
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.
synapsePythonClient/synapseclient/client.py
Line 5029 in 8bd5e19
def _waitForAsync(self, uri, request, endpoint=None): |
This function exists on the client, but it not built with asyncio in mind. There is some additional error handling it does for the failed state. I could see an asyncio compliant version being written and used here.
Maybe it could go in this file? https://github.com/Sage-Bionetworks/synapsePythonClient/blob/8bd5e195c389e3a83bd1308be3c6a42f25d87b8f/synapseclient/core/async_utils.py
|
||
Arguments: | ||
prompt_id: The token of the prompt to get the trace for. | ||
newer_than: The timestamp to get trace results newer than. Defaults to None (all results). |
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.
Link to the rest docs that define what format the timestamp should be in
from synapseclient.core.async_utils import otel_trace_method | ||
|
||
|
||
class AgentType(Enum): |
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.
Usually I've have been using (str, Enum) types for these. Mostly so they emulate string like behavior and print to console nicely. In python 3.11 they introduced a StrEnum for this. Small ai code difference between the 2:
from enum import Enum
class ColumnType(str, Enum):
INTEGER = "integer"
FLOAT = "float"
# Behaves like a string
print(ColumnType.INTEGER == "integer") # True
print(len(ColumnType.INTEGER)) # 7
print(ColumnType.INTEGER.upper()) # INTEGER
# JSON serialization
import json
print(json.dumps({"type": ColumnType.INTEGER})) # {"type": "integer"}
Vs
from enum import Enum
class ColumnType(Enum):
INTEGER = "integer"
FLOAT = "float"
# Comparison needs `.value`
print(ColumnType.INTEGER == "integer") # False
print(ColumnType.INTEGER.value == "integer") # True
# JSON serialization needs customization
import json
print(json.dumps({"type": ColumnType.INTEGER.value})) # {"type": "integer"}
""" | ||
Enum representing the type of agent as defined in | ||
<https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/agent/AgentType.html> | ||
'BASELINE' is a default agent provided by Synapse. |
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.
Nit. Can be a list. Syntax is a empty newline before and each entry starts with -
.
Might need to make sure it renders properly on my docs.
Related to ^
These will be "Less" experimental APIs, so we can add a new non-experimental section to
synapsePythonClient/mkdocs.yml
Line 55 in 8bd5e19
- API Reference: |
agent_registration_id: The registration ID of the agent that will be used for this session. | ||
etag: The etag of the agent session. | ||
""" | ||
|
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.
Check out the tables refactor PR for examples of the syntax for an Examples section. I'd like for the docstring to have scripts (Copy/pastable) that folks could run and get started.
return self | ||
|
||
@otel_trace_method( | ||
method_to_trace_name=lambda self, **kwargs: f"Start_Session: {self.id}" |
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.
Will id be filled in at this point in time before a session has started?
Returns: | ||
The retrieved AgentSession object. | ||
""" | ||
syn = Synapse.get_client(synapse_client=synapse_client) |
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.
If you don't need to use the instance of the client you may pass the synapse_client
along to get_session
""" | ||
syn = Synapse.get_client(synapse_client=synapse_client) | ||
|
||
self.access_level = access_level |
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.
My expectation was that folks would be updating attributes on the instance and then calling the store/update functions, rather than passing the updated values into those functions.
My idea is that I wanted any arguments you pass into the function to determine how it would execute (Or data not otherwise stored on the class instance), rather than the data it would be acting on.
In short the idea being:
agent_session.access_level = new_value
agent_session.update()
async def prompt_async( | ||
self, | ||
*, | ||
prompt: str, |
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.
*
forces things to be kwargs. It is acceptable that you may want that, but i wanted to call it out if that was not the intention.
I always want synapse_client to be a kwarg so it will never end up after a positional argument
session = await AgentSession(id=session_id).get_async(synapse_client=syn) | ||
if session.id not in self.sessions: | ||
self.sessions[session.id] = session | ||
self.current_session = session.id |
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.
Rather than storing a string of the id you could just store the session object itself. Both places (list of sessions) and current_session should just have pointers to the same object instance since they are not deep copies of one another.
async def prompt( | ||
self, | ||
*, | ||
session_id: Optional[str] = None, |
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.
Id think you would rather pass in a AgentSession object here
self, | ||
*, | ||
session_id: Optional[str] = None, | ||
prompt: str, |
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.
Required args cannot show up after optional args. It should be fine to put this as the first argument
def get_chat_history(self) -> List[AgentPrompt]: | ||
"""Gets the chat history for the current session.""" | ||
# TODO: Is this the best way to do this? | ||
return self.sessions[self.current_session].chat_history |
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.
With the suggestion above this could swap to
self.current_session.chat_history if self.current_session else None
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.
WIP
Implementation of design doc: https://sagebionetworks.jira.com/wiki/spaces/DPE/pages/3833954306/SYNPY-1544+Synapse+Agent+OOP+Model+Mini-Design