agora-demo / negotiator.py
Samuele Marro
Initial upload to test HF Spaces.
3cad23b
import json
import uuid
from toolformers.base import Toolformer
from utils import extract_substring
NEGOTIATION_RULES = '''
Here are some rules (that should also be explained to the other GPT):
- You can assume that the protocol has a sender and a receiver. Do not worry about how the messages will be delivered, focus only on the content of the messages.
- Keep the protocol short and simple. It should be easy to understand and implement.
- The protocol must specify the exact format of what is sent and received. Do not leave it open to interpretation.
- The implementation will be written by a programmer that does not have access to the negotiation process, so make sure the protocol is clear and unambiguous.
- The implementation will receive a string and return a string, so structure your protocol accordingly.
- The other party might have a different internal data schema or set of tools, so make sure that the protocol is flexible enough to accommodate that.
- There will only be one message sent by the sender and one message sent by the receiver. Design the protocol accordingly.
- Keep the negotiation short: no need to repeat the same things over and over.
- If the other party has proposed a protocol and you're good with it, there's no reason to keep negotiating or to repeat the protocol to the other party.
- Do not restate parts of the protocols that have already been agreed upon.
And remember: keep the protocol as simple and unequivocal as necessary. The programmer that will implement the protocol can code, but they are not a mind reader.
'''
TASK_NEGOTIATOR_PROMPT = f'''
You are ProtocolNegotiatorGPT. Your task is to negotiate a protocol that can be used to query a service.
You will receive a JSON schema of the task that the service must perform. Negotiate with the service to determine a protocol that can be used to query it.
To do so, you will chat with another GPT (role: user) that will negotiate on behalf of the service.
{NEGOTIATION_RULES}
Once you are ready to save the protocol, reply wrapping the final version of the protocol, as agreed in your negotiation, between the tags <FINALPROTOCOL> and </FINALPROTOCOL>.
Within the body of the tag, add the tags <NAME></NAME> and <DESCRIPTION></DESCRIPTION> to specify the name and description of the protocol.
Remember that the <FINALPROTOCOL></FINALPROTOCOL> tags should also contain the protocol itself. Nothing outside such tags will be stored.
'''
class SenderNegotiator:
def __init__(self, toolformer : Toolformer):
self.toolformer = toolformer
def negotiate_protocol_for_task(self, task_schema, callback_fn, final_message_callback_fn=None):
found_protocol = None
prompt = TASK_NEGOTIATOR_PROMPT + '\nThe JSON schema of the task is the following:\n\n' + json.dumps(task_schema, indent=2)
conversation = self.toolformer.new_conversation(prompt, [], category='negotiation')
other_message = 'Hello! How may I help you?'
conversation_id = None
for i in range(10):
print('===NegotiatorGPT===')
message = conversation.chat(other_message, print_output=True)
print('Checking if we can extract from:', message)
print('---------')
protocol = extract_substring(message, '<FINALPROTOCOL>', '</FINALPROTOCOL>')
if protocol is None:
print('Could not extract')
other_message = callback_fn(message)
print()
print('===Other GPT===')
print(other_message)
print()
else:
if final_message_callback_fn:
rest_of_message = message.split('<FINALPROTOCOL>')[0]
final_message_callback_fn(rest_of_message)
name = extract_substring(protocol, '<NAME>', '</NAME>')
description = extract_substring(protocol, '<DESCRIPTION>', '</DESCRIPTION>')
if name is None:
name = 'Unnamed protocol'
if description is None:
description = 'No description provided'
found_protocol = {
'name': name,
'description': description,
'protocol': protocol
}
break
return found_protocol
TOOLS_NEGOTIATOR_PROMPT = f'''
You are ProtocolNegotiatorGPT. You are negotiating a protocol on behalf of a web service that can perform a task.
The other party is a GPT that is negotiating on behalf of the user. Your goal is to negotiate a protocol that is simple and clear, \
but also expressive enough to allow the service to perform the task. A protocol is sufficiently expressive if you could write code \
that, given the query formatted according to the protocol and the tools at the service's disposal, can parse the query according to \
the protocol's specification, perform the task (if any) and send a reply.
{NEGOTIATION_RULES}
You will receive a list of tools that are available to the programmer that will implement the protocol.
When you are okay with the protocol, don't further repeat everything, just tell to the other party that you are done.
'''
class ReceiverNegotiator:
def __init__(self, toolformer : Toolformer, tools, additional_info):
prompt = TOOLS_NEGOTIATOR_PROMPT
prompt += '\n\n' + additional_info
prompt += '\n\nThe tools that the implementer will have access to are:\n\n'
if len(tools) == 0:
prompt += 'No additional tools provided'
else:
for tool in tools:
prompt += tool.as_documented_python() + '\n\n'
print('Prompt:', prompt)
self.conversation = toolformer.new_conversation(prompt, tools, category='negotiation')
def handle_negotiation(self, message):
reply = self.conversation.chat(message, print_output=True)
return reply