# Agenten Plattform # # (c) 2024 Magnus Bender # Institute of Humanities-Centered Artificial Intelligence (CHAI) # Universitaet Hamburg # https://www.chai.uni-hamburg.de/~bender # # source code released under the terms of GNU Public License Version 3 # https://www.gnu.org/licenses/gpl-3.0.txt """ Access to the management, e.g., get the list of messages and single messages. Manually send messages (if necessary, the platforms should do this). ### Example ```python m_request = ManagementRequest() m_request.get_message(count=12) # MessageDbRow(count=12 sender='from' recipient='to' ... m_request.list_messages(id="test", limit=2) # [ # MessageDbRow(count=7256, sender='management', ...), # MessageDbRow(count=7255, sender='management', ...), # ] m_request.total_messages(id="test") # 31 ``` See also `ums.example.__main__` and run in Docker via ``docker compose exec management python -m ums.example`` """ import os from typing import List, Dict, Any import requests from pydantic import validate_call from ums.utils.types import AgentMessage, AgentResponse, MessageDbRow class RequestException(Exception): """ Raised on http and similar errors. """ pass class ManagementRequest(): MANAGEMENT_URL = os.environ.get('MANAGEMENT_URL', 'http://127.0.0.1:80').strip().strip('/') @validate_call def __init__(self, allow_lazy:bool=True): """ If `allow_lazy` is active, the type checking (by pydantic) is less strict. E.g. it does not require that all files in the data section of messages must exist on the file system. """ self._allow_lazy = allow_lazy self._pydantic_context = { "require_file_exists": not self._allow_lazy } @validate_call def get_message(self, count:int) -> MessageDbRow: """ Get a message (like a table row) from the management by using the `count`. May raise `RequestException`. """ row = self._get_request( 'list/single', {"count": count} ) return MessageDbRow.model_validate( row, context=self._pydantic_context ) @validate_call def list_messages(self, id:str|None=None, sender:str|None=None, recipient:str|None=None, processed:bool|None=None, solution:bool|None=None, time_after:int|None=None, time_before:int|None=None, limit:int=10, offset:int=0 ) -> List[MessageDbRow]: """ Get the rows in the tables as list of messages. The arguments are used for filtering. May raise `RequestException`. """ kwargs = locals().copy() params = {} for k,v in kwargs.items(): if k not in ('self',) and not v is None: params[k] = v rows = self._get_request('list', params) return [ MessageDbRow.model_validate( row, context=self._pydantic_context ) for row in rows ] @validate_call def total_messages(self, id:str|None=None, sender:str|None=None, recipient:str|None=None, processed:bool|None=None, solution:bool|None=None, time_after:int|None=None, time_before:int|None=None ) -> int: """ Get the total number of rows in the tables matching the filters. May raise `RequestException`. """ kwargs = locals().copy() params = {} for k,v in kwargs.items(): if k not in ('self',) and not v is None: params[k] = v return int(self._get_request('app/table/total', params)) def _get_request(self, endpoint:str, params:Dict[str, Any]): r = requests.get( "{}/{}".format(self.MANAGEMENT_URL, endpoint), params=params ) if r.status_code == 200: return r.json() else: raise RequestException(str(r.text)+"\n"+str(r.headers)) @validate_call def send_message(self, message:AgentMessage) -> AgentResponse: """ Send the `message` to the management and return the management's agent response. (On error an agent response with error message). """ try: return AgentResponse.model_validate( self._post_request( "message", message.model_dump_json() ) ) except RequestException as e: return AgentResponse( count=-1, error=True, error_msg=str(e) ) def _post_request(self, endpoint:str, data:Dict[str, Any]): r = requests.post( "{}/{}".format(self.MANAGEMENT_URL, endpoint), data=data, headers={"accept" : "application/json", "content-type" : "application/json"} ) if r.status_code == 200: return r.json() else: return RequestException(str(r.text)+"\n"+str(r.headers))