working v1

This commit is contained in:
Andrew Gundersen 2020-11-21 14:43:17 -06:00
commit 5b54f3f636
19 changed files with 403 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

98
agent.py Normal file
View file

@ -0,0 +1,98 @@
import json
import asyncio
import websockets
import typs
class Agent:
"""Session interface for Crimata Agent
Receive core functionalities for communicating with Crimata
Agent in an OOP way.
"""
def __init__(self, connection):
self.connection = connection
# Submit a request for info, return response.
# Must disclose what service it's for.
async def fetch(self, entities, service):
# Assert list.
if type(entities) == str:
entities = [entities]
print(f"Fetching {[e for e in entities]} for {service}")
# Build intent.
params = {
"service": service,
"entities": entities
}; intent = typs.Intent("fetch", params)
# Send intent to agent, wait for response.
await self.send_agent_intent(intent)
response = await self.recv_agent_intent()
# Parse response and return entities.
entities = response.params.get("found")
print(f"Fetch response: {entities}")
return entities
# Text the client something. Also, let agent know
# when your service is complete via the complete
# attribute.
# Must disclose what service it's for.
async def notify(self, text, end=False):
print(f"Notifying: {text} for {self.service}")
params = {
"service": self.service,
"text": text,
"end": end
}
# Build the intent
intent = typs.Intent("notify", params)
# Send intent to agent.
await self.send_agent_intent(intent)
# Get user info.
async def sync(self):
intent = typs.Intent("sync", params={})
# Send intent to agent.
await self.send_agent_intent(intent)
# Recv and return profile.
intent = await self.recv_agent_intent()
profile = intent.params.get("profile")
return profile
async def recv_agent_intent(self):
package = await self.connection.recv()
intent = self.__decode(package)
return intent
async def send_agent_intent(self, intent):
package = self.__encode(intent)
await self.connection.send(package)
def __encode(self, intent: typs.Intent):
package = json.dumps(intent.__dict__)
return package
def __decode(self, package) -> typs.Intent:
jpackage = json.loads(package)
name = jpackage.get("name")
params = jpackage.get("params")
intent = typs.Intent(name, params)
return intent

42
mail.py Normal file
View file

@ -0,0 +1,42 @@
# mail.py
import typs
import mailroom
class Mail:
"""Session interface for mailroom.
Two main IO methods. Will translate intents to mail and vice versa.
"""
def __init__(self):
pass
# Receive intent.
async def mailbox_recv(self):
mail = await mailroom.get_mail(self.crimata_id)
intent = self.__decode(mail)
return intent
# Send intent.
def mailbox_send(self, intent):
mail = self.__encode(intent)
mailroom.put_mail(self.crimata_id, mail)
def __encode(self, intent: typs.Intent) -> typs.Mail:
p = intent.params
owner = self.crimata_id
target = p.get("target")
text = p.get("text")
mail = typs.Mail(owner, target, text)
return mail
@staticmethod
def __decode(mail: typs.Mail) -> typs.Intent:
name = "notify"
params = {
"text": mail.text #dev only
}
intent = typs.Intent(name, params)
return intent

42
mailroom.py Normal file
View file

@ -0,0 +1,42 @@
# mailroom.py
import queue
import asyncio
import typs
# Subcribers (Crimata IDs)
subs = []
# Mailroom (all mailboxes)
que = queue.Queue()
ref = {}
# Mailroom functions
# ------------------
# Create mailbox and add to mailroom.
def create_mailbox(crimata_id):
mailbox = typs.MailBox(name=crimata_id)
que.put(mailbox)
ref.update({crimata_id: mailbox})
subs.append(crimata_id)
# Get mailbox by Crimata ID.
def get_mailbox(crimata_id):
if not crimata_id in subs:
create_mailbox(crimata_id)
mailbox = ref.get(crimata_id)
return mailbox
# Inbox -> receiving mail.
async def get_mail(crimata_id):
mailbox = get_mailbox(crimata_id)
mail = await mailbox.inbox.get()
return mail
# Outbox -> sending mail.
def put_mail(crimata_id, mail: typs.Mail):
mailbox = get_mailbox(crimata_id)
mailbox.outbox.put(mail)

18
main.py Normal file
View file

@ -0,0 +1,18 @@
# server.py
import typs
import queue
import asyncio
import websockets
import server
import message
# Run the ws server and the messenger concurrently.
async def main():
print(f"Crimata Messenger")
await asyncio.gather(server.lift(), message.run())
if __name__ == '__main__':
asyncio.run(main())

37
message.py Normal file
View file

@ -0,0 +1,37 @@
# messenge.py
import asyncio
import mailroom
# Independent loop that updates mailboxes.
async def messenger():
print("Running Messenger")
while True:
# Yield if no mailboxes.
if mailroom.que.empty():
await asyncio.sleep(1)
continue
# Grab a mailbox.
mailbox = mailroom.que.get()
print(f"Handling mailbox: {mailbox.name}.")
# Handle every message in outbox.
while not mailbox.outbox.empty():
message = mailbox.outbox.get()
# Get mailbox of target and put mail there.
target_mailbox = mailroom.get_mailbox(message.target)
await target_mailbox.inbox.put(message)
# Put the mailbox back
mailroom.que.put(mailbox)
await asyncio.sleep(1)
async def run():
await messenger()

30
schemes.py Normal file
View file

@ -0,0 +1,30 @@
# schemes.py
class Schemes:
def __init__(self):
self.service = None
async def handle_intent(self, intent):
print(f"Handling intent {intent.name}.")
# Set the service
self.service = intent.name
if intent.name == "init":
await self.init(intent)
if intent.name == "message":
self.message(intent)
async def init(self, intent):
self.crimata_id = intent.params.get("crimata_id")
await self.notify("Messaging is live.")
def message(self, intent):
text = intent.params.get("text")
target = intent.params.get("target") #crimata_id
print(f"Messaging {target}: '{text}'")
# Uses mail.Mail interface.
self.mailbox_send(intent)

27
server.py Normal file
View file

@ -0,0 +1,27 @@
# server.py
import asyncio
import websockets
import session
HOST = "localhost"
PORT = 8764
# Makes instance of a Messenger session for every client that connects.
async def launch_session(connection, path):
s = session.Session(connection)
await asyncio.gather(
s.do_agent_intents(),
s.deliver_mail()
)
# Run run for every new connection.
async def lift():
print(f"Listening for connections on {HOST}:{PORT}")
await websockets.serve(launch_session, HOST, PORT)

81
session.py Normal file
View file

@ -0,0 +1,81 @@
# session.py
import time
import asyncio
import mail
import agent
import schemes
class Session(schemes.Schemes, agent.Agent, mail.Mail):
"""Launch instance for every client connection.
Will recv messages from client and push them to mailbox.
Also, will pull messages from mailbox and send to client.
"""
def __init__(self, connection):
agent.Agent.__init__(self, connection)
self.crimata_id = False
print("Launched new session")
# Recv intents run desired endpoint.
async def do_agent_intents(self):
while True:
# Recv intent from agent.
intent = await self.recv_agent_intent()
print(f"Intent: {intent.name}")
# Call corresponding endpoint.
await self.handle_intent(intent)
await asyncio.sleep(0.1)
# Send mail back to agent.
async def deliver_mail(self):
while True:
# Sleep until Crimata ID.
if not self.crimata_id:
await asyncio.sleep(1)
continue
intent = await self.mailbox_recv()
await self.send_agent_intent(intent)
await asyncio.sleep(0.1)
# async def hello(self):
# intent = await self.recv_agent_intent()
# response = await self.fetch("crimata_id", service="message")
# self.profile = response.get("profile")
# target = intent.params.get("target")
# await self.notify(f"Messenger APP: Received Message Intent",
# service="message", complete=True)
# print("Shutting down in 10s")
# await asyncio.sleep(10)

28
typs.py Normal file
View file

@ -0,0 +1,28 @@
# typs.py
import queue
import asyncio
class Mail:
def __init__(self, owner, target, text):
self.owner = owner
self.target = target
self.text = text
class MailBox:
def __init__(self, name):
self.name = name # just for logging purposes.
self.inbox = asyncio.Queue()
self.outbox = queue.Queue()
class Intent:
def __init__(self, name, params: dict):
self.name = name
self.params = params