Skip to content
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

Open
wants to merge 53 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
7f6022e
Adds async convenience functions
BWMac Jan 6, 2025
60dc255
expose convenience functions
BWMac Jan 7, 2025
3fd345c
updates convenience functions
BWMac Jan 7, 2025
9c2c6d9
updates agent_services
BWMac Jan 8, 2025
52078b3
removes rest_get_async exception handling
BWMac Jan 8, 2025
fbcfcef
pre-commit fixes
BWMac Jan 8, 2025
079a02c
delete accidentally committed script
BWMac Jan 8, 2025
5ae3257
adds initial agent implementation
BWMac Jan 8, 2025
84edb76
clean up agent
BWMac Jan 8, 2025
88c6f06
adds missing docstrings
BWMac Jan 8, 2025
f34e9cc
pre-commit
BWMac Jan 8, 2025
05e73f3
updates agent_services
BWMac Jan 10, 2025
9b6e035
updates agent.py
BWMac Jan 10, 2025
dadbac3
Updates alias ID handling
BWMac Jan 10, 2025
9c7547a
adds syncronous interface
BWMac Jan 10, 2025
7f44822
prevent cicular import in storable_entity_components
BWMac Jan 14, 2025
691799a
remove promt sending and receiving from agent_service
BWMac Jan 14, 2025
389dd86
adds initial (dirty) async job mixin
BWMac Jan 14, 2025
15db364
pre-commit run
BWMac Jan 14, 2025
165570c
[SYNPY-1544] potential changes to mixin (#1153)
BryanFauble Jan 14, 2025
8adf5e5
cleans up agent logic
BWMac Jan 15, 2025
3360390
adds async job unit tests
BWMac Jan 15, 2025
40842b2
updates async job tests
BWMac Jan 15, 2025
05cc2dd
adds agent unit tests
BWMac Jan 16, 2025
e1b00cd
adds integration tests
BWMac Jan 16, 2025
717b695
pre-commit
BWMac Jan 16, 2025
f294cdd
adds examples to agent.py
BWMac Jan 16, 2025
fdd665f
removes todos
BWMac Jan 16, 2025
6197f38
adds POC script
BWMac Jan 16, 2025
33639a6
add to mixins
BWMac Jan 16, 2025
72cdff1
adds agent docs
BWMac Jan 16, 2025
7a3fd13
updates agent docs
BWMac Jan 16, 2025
2bcc349
reorganize documentation
BWMac Jan 16, 2025
0082ea6
updates poc script
BWMac Jan 17, 2025
6adde6a
clean up
BWMac Jan 17, 2025
c20b952
add docstring
BWMac Jan 17, 2025
6d80b59
removes unused imports
BWMac Jan 17, 2025
89db642
split too long lines
BWMac Jan 17, 2025
066504a
force synapse_client kwarg
BWMac Jan 17, 2025
807287f
updates agent.py
BWMac Jan 17, 2025
6274364
updates synapse_client docstring description
BWMac Jan 17, 2025
6750ef6
updates asynchronous_job
BWMac Jan 17, 2025
af56bee
updates integration tests
BWMac Jan 17, 2025
724a8b0
pre-commit
BWMac Jan 17, 2025
a96b1a9
agent inherited members
BWMac Jan 17, 2025
f860fa6
updates docs for inherited members
BWMac Jan 17, 2025
1ca93d8
missing inherited members
BWMac Jan 17, 2025
7537f4e
updates doc formatting
BWMac Jan 17, 2025
d2f4ade
try team formatting change
BWMac Jan 17, 2025
9b4a168
updates script description
BWMac Jan 17, 2025
f567d4c
adds Annotation lazy import
BWMac Jan 17, 2025
aede05f
try team formatting change
BWMac Jan 17, 2025
34be93d
more formatting changes
BWMac Jan 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions synapseclient/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
# These are all of the models that are used by the Synapse client.
from .agent_services import (
get_agent,
get_response,
get_session,
get_trace,
register_agent,
send_prompt,
start_session,
update_session,
)
from .annotations import set_annotations, set_annotations_async
from .configuration_services import (
get_client_authenticated_s3_profile,
Expand Down Expand Up @@ -78,4 +88,13 @@
"get_transfer_config",
# entity_factory
"get_from_entity_factory",
# agent_services
"register_agent",
"get_agent",
"start_session",
"get_session",
"update_session",
"send_prompt",
"get_response",
"get_trace",
]
259 changes: 259 additions & 0 deletions synapseclient/api/agent_services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
"""This module is responsible for exposing the services defined at:
<https://rest-docs.synapse.org/rest/#org.sagebionetworks.repo.web.controller.AgentController>
"""

import asyncio
import json
from typing import TYPE_CHECKING, Any, Dict, Optional

if TYPE_CHECKING:
from synapseclient import Synapse

from synapseclient.core.exceptions import SynapseTimeoutError


async def register_agent(
cloud_agent_id: str,
cloud_alias_id: Optional[str] = None,
synapse_client: Optional["Synapse"] = None,
) -> Dict[str, Any]:
"""
Registers an agent with Synapse OR gets existing agent registration.

Arguments:
cloud_agent_id: The cloud provider ID of the agent to register.
cloud_alias_id: The cloud provider alias ID of the agent to register.
In the Synapse API, this defaults to 'TSTALIASID'.
synapse_client: If not passed in and caching was not disabled by
`Synapse.allow_client_caching(False)` this will use the last created
instance from the Synapse class constructor.

Returns:
The registered agent matching
<https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/agent/AgentRegistration.html>
"""
from synapseclient import Synapse

client = Synapse.get_client(synapse_client=synapse_client)

# 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,
BWMac marked this conversation as resolved.
Show resolved Hide resolved
}
return await client.rest_put_async(
uri="/agent/registration", body=json.dumps(request)
)


async def get_agent(
registration_id: str, synapse_client: Optional["Synapse"] = None
) -> Dict[str, Any]:
"""
Gets information about an existing agent registration.

Arguments:
registration_id: The ID of the agent registration to get.
synapse_client: If not passed in and caching was not disabled by
`Synapse.allow_client_caching(False)` this will use the last created
instance from the Synapse class constructor.

Returns:
The requested agent registration matching
<https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/agent/AgentRegistration.html>
"""
from synapseclient import Synapse

client = Synapse.get_client(synapse_client=synapse_client)
return await client.rest_get_async(uri=f"/agent/registration/{registration_id}")


async def start_session(
access_level: str,
agent_registration_id: str,
synapse_client: Optional["Synapse"] = None,
BWMac marked this conversation as resolved.
Show resolved Hide resolved
) -> Dict[str, Any]:
"""
Starts a new chat session with an agent.

Arguments:
access_level: The access level of the agent.
agent_registration_id: The ID of the agent registration to start the session for.
synapse_client: If not passed in and caching was not disabled by
`Synapse.allow_client_caching(False)` this will use the last created
instance from the Synapse class constructor.
"""
from synapseclient import Synapse

client = Synapse.get_client(synapse_client=synapse_client)

# Request matching <https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/agent/CreateAgentSessionRequest.html>
BWMac marked this conversation as resolved.
Show resolved Hide resolved
request = {
"agentAccessLevel": access_level,
"agentRegistrationId": agent_registration_id,
}
return await client.rest_post_async(uri="/agent/session", body=json.dumps(request))


async def get_session(
id: str,
synapse_client: Optional["Synapse"] = None,
) -> Dict[str, Any]:
"""
Gets information about an existing chat session.

Arguments:
id: The ID of the session to get.
synapse_client: If not passed in and caching was not disabled by
`Synapse.allow_client_caching(False)` this will use the last created
instance from the Synapse class constructor.

Returns:
The requested session matching
<https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/agent/AgentSession.html>
"""
from synapseclient import Synapse

client = Synapse.get_client(synapse_client=synapse_client)
return await client.rest_get_async(uri=f"/agent/session/{id}")


async def update_session(
id: str,
access_level: str,
synapse_client: Optional["Synapse"] = None,
) -> Dict[str, Any]:
"""
Updates the access level for a chat session.

Arguments:
id: The ID of the session to update.
access_level: The access level of the agent.
synapse_client: If not passed in and caching was not disabled by
`Synapse.allow_client_caching(False)` this will use the last created
instance from the Synapse class constructor.
"""
from synapseclient import Synapse

client = Synapse.get_client(synapse_client=synapse_client)

# Request matching <https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/agent/UpdateAgentSessionRequest.html>
request = {
"sessionId": id,
"agentAccessLevel": access_level,
}
return await client.rest_put_async(
uri=f"/agent/session/{id}", body=json.dumps(request)
)


async def send_prompt(
id: str,
prompt: str,
enable_trace: bool = False,
synapse_client: Optional["Synapse"] = None,
) -> Dict[str, Any]:
"""
Sends a prompt to an agent starting an asyncronous job.

Arguments:
id: The ID of the session to send the prompt to.
prompt: The prompt to send to the agent.
enable_trace: Whether to enable trace for the prompt. Defaults to False.
synapse_client: If not passed in and caching was not disabled by
`Synapse.allow_client_caching(False)` this will use the last created
instance from the Synapse class constructor.

Returns:
The response matching
<https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/asynch/AsyncJobId.html>
"""
from synapseclient import Synapse

client = Synapse.get_client(synapse_client=synapse_client)

# Request matching <https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/agent/AgentChatRequest.html>
request = {
"concreteType": "org.sagebionetworks.repo.model.agent.AgentChatRequest",
BWMac marked this conversation as resolved.
Show resolved Hide resolved
"sessionId": id,
"chatText": prompt,
"enableTrace": enable_trace,
}
return await client.rest_post_async(
uri="/agent/chat/async/start", body=json.dumps(request)
)


async def get_response(
prompt_id: str,
synapse_client: Optional["Synapse"] = None,
) -> Dict[str, Any]:
"""
Gets the response to a prompt. If the response is not ready, the endpoint will return a reponse matching
<https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/asynch/AsynchronousJobStatus.html>.
In this case, the response retrieval is retried every second until the response is ready or a timeout is reached.

Arguments:
prompt_id: The token of the prompt to get the response for.
synapse_client: If not passed in and caching was not disabled by
`Synapse.allow_client_caching(False)` this will use the last created
instance from the Synapse class constructor.

Returns:
The response matching
<https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/agent/AgentChatResponse.html>

Raises:
TimeoutError: If the response is not ready after 1 minute
"""
from synapseclient import Synapse

client = Synapse.get_client(synapse_client=synapse_client)
start_time = asyncio.get_event_loop().time()
TIMEOUT = 60

while True:
BWMac marked this conversation as resolved.
Show resolved Hide resolved
if asyncio.get_event_loop().time() - start_time > TIMEOUT:
raise SynapseTimeoutError(
f"Timeout waiting for response: {TIMEOUT} seconds"
)

response = await client.rest_get_async(uri=f"/agent/chat/async/get/{prompt_id}")
if response.get("jobState") == "PROCESSING":
await asyncio.sleep(1)
continue

return response


async def get_trace(
prompt_id: str,
newer_than: Optional[int] = None,
synapse_client: Optional["Synapse"] = None,
) -> Dict[str, Any]:
"""
Gets the trace of a prompt.

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).
BWMac marked this conversation as resolved.
Show resolved Hide resolved
synapse_client: If not passed in and caching was not disabled by
`Synapse.allow_client_caching(False)` this will use the last created
instance from the Synapse class constructor.

Returns:
The trace matching
<https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/agent/TraceEventsResponse.html>
"""
from synapseclient import Synapse

client = Synapse.get_client(synapse_client=synapse_client)

# Request matching <https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/agent/TraceEventsRequest.html>
request = {
"jobId": prompt_id,
"newerThanTimestamp": newer_than,
}
return await client.rest_post_async(
uri=f"/agent/chat/trace/{prompt_id}", body=json.dumps(request)
)
25 changes: 11 additions & 14 deletions synapseclient/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6373,20 +6373,17 @@ async def rest_get_async(
Returns:
JSON encoding of response
"""
try:
response = await self._rest_call_async(
"get",
uri,
None,
endpoint,
headers,
retry_policy,
requests_session_async_synapse,
**kwargs,
)
return self._return_rest_body(response)
except Exception:
self.logger.exception("Error in rest_get_async")
response = await self._rest_call_async(
BWMac marked this conversation as resolved.
Show resolved Hide resolved
"get",
uri,
None,
endpoint,
headers,
retry_policy,
requests_session_async_synapse,
**kwargs,
)
return self._return_rest_body(response)

async def rest_post_async(
self,
Expand Down
Loading
Loading