add google search functions
This commit is contained in:
parent
f00aca452a
commit
4fb83bd8ce
|
@ -1 +1,4 @@
|
|||
OPENAI_KEY = 'sk-123123kl123lkj123lkj12lk3j'
|
||||
|
||||
# Leave empty to disable.
|
||||
SERPAPI_API_KEY = ''
|
|
@ -0,0 +1,35 @@
|
|||
import json
|
||||
from typing import Any
|
||||
|
||||
|
||||
def varsify(item) -> Any:
|
||||
result = {}
|
||||
try:
|
||||
if isinstance(item, (str, int, float, bool)):
|
||||
return item
|
||||
elif isinstance(item, (list, set)):
|
||||
l_result = []
|
||||
for i, x in enumerate(item):
|
||||
l_result.append(varsify(x))
|
||||
return l_result
|
||||
else:
|
||||
for k, v in vars(item).items():
|
||||
if isinstance(v, dict):
|
||||
result[k] = varsify(v)
|
||||
elif isinstance(v, list):
|
||||
result[k] = []
|
||||
for i, x in enumerate(v):
|
||||
result[k].insert(i, varsify(x))
|
||||
else:
|
||||
if not k.startswith('_'):
|
||||
result[k] = varsify(v)
|
||||
return result
|
||||
except:
|
||||
return item
|
||||
|
||||
|
||||
def jsonify_anything(item, pretty: bool = False):
|
||||
if pretty:
|
||||
return json.dumps(varsify(item), indent=4, sort_keys=True, default=str)
|
||||
else:
|
||||
return json.dumps(varsify(item), separators=(',', ':'), sort_keys=True, default=str)
|
|
@ -1,7 +1,7 @@
|
|||
function_description = [
|
||||
{
|
||||
"name": "run_bash",
|
||||
"description": "Execute a Bash command on the local system.",
|
||||
"description": "Execute a Bash command on the local system",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -17,10 +17,12 @@ function_description = [
|
|||
"required": ["command", "reasoning"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "end_my_response",
|
||||
"description": "Call this when you require input from the user or are ready for their response. This allows you to send multiple messages and then a single `end_my_response` when you are finished. An `end_my_response` should always be preceded by a message.",
|
||||
},
|
||||
|
||||
{
|
||||
"name": "end_chat",
|
||||
"description": "Close the chat connection with the user. The assistant is allowed to close the connection at any point if it desires to.",
|
||||
|
@ -34,5 +36,76 @@ function_description = [
|
|||
},
|
||||
"required": ["reasoning"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "search_google",
|
||||
"description": "Preform a Google search query",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "The query string"
|
||||
},
|
||||
"reasoning": {
|
||||
"type": "string",
|
||||
"description": "Why you chose to run this command"
|
||||
}
|
||||
},
|
||||
"required": ["query", "reasoning"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "search_google_maps",
|
||||
"description": "Preform a Google Maps search query",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "The query string"
|
||||
},
|
||||
"latitude": {
|
||||
"type": "number",
|
||||
"description": "The latitude of where you want your query to be applied"
|
||||
},
|
||||
"longitude": {
|
||||
"type": "number",
|
||||
"description": "The longitude of where you want your query to be applied"
|
||||
},
|
||||
"zoom": {
|
||||
"type": "number",
|
||||
"description": "The zoom level. Optional but recommended for higher precision. Ranges from `3z` (map completely zoomed out) to `21z` (map completely zoomed in)"
|
||||
},
|
||||
"reasoning": {
|
||||
"type": "string",
|
||||
"description": "Why you chose to run this command"
|
||||
}
|
||||
},
|
||||
"required": ["query", "latitude", "longitude", "reasoning"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "search_google_news",
|
||||
"description": "Preform a Google News search query",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "The query string"
|
||||
},
|
||||
"reasoning": {
|
||||
"type": "string",
|
||||
"description": "Why you chose to run this command"
|
||||
}
|
||||
},
|
||||
"required": ["query", "reasoning"]
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
VALID_FUNCS = [x['name'] for x in function_description]
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import json
|
||||
|
||||
import serpapi
|
||||
|
||||
from config import SERPAPI_API_KEY
|
||||
from lib.jsonify import jsonify_anything
|
||||
|
||||
client = serpapi.Client(api_key=SERPAPI_API_KEY)
|
||||
|
||||
|
||||
def search_google(query: str):
|
||||
if not SERPAPI_API_KEY:
|
||||
return {'error': True, 'message': 'The SerpAPI key has not been provided, so this function is disabled.'}
|
||||
results = client.search(q=query, engine="google", hl="en", gl="us")
|
||||
del results['serpapi_pagination']
|
||||
del results['search_metadata']
|
||||
del results['pagination']
|
||||
del results['search_parameters']
|
||||
del results['search_information']
|
||||
if results.get('inline_videos'):
|
||||
del results['inline_videos']
|
||||
|
||||
# Need to dump and reparse the JSON so that it is actually formatted correctly.
|
||||
return json.loads(jsonify_anything(results))
|
||||
|
||||
|
||||
def search_google_maps(query: str, latitude: float, longitude: float, zoom: float = None):
|
||||
"""
|
||||
https://serpapi.com/google-maps-api#api-parameters-geographic-location-ll
|
||||
"""
|
||||
if not SERPAPI_API_KEY:
|
||||
return {'error': True, 'message': 'The SerpAPI key has not been provided, so this function is disabled.'}
|
||||
if zoom:
|
||||
z_str = f',{zoom}z'
|
||||
else:
|
||||
z_str = ''
|
||||
results = client.search(q=query,
|
||||
engine="google_maps",
|
||||
ll=f'@{latitude},{longitude}{z_str}',
|
||||
type='search',
|
||||
)
|
||||
del results['search_parameters']
|
||||
del results['search_metadata']
|
||||
del results['serpapi_pagination']
|
||||
return json.loads(jsonify_anything(results))
|
||||
|
||||
|
||||
def search_google_news(query: str):
|
||||
if not SERPAPI_API_KEY:
|
||||
return {'error': True, 'message': 'The SerpAPI key has not been provided, so this function is disabled.'}
|
||||
results = client.search(q=query, engine="google_news", hl="en", gl="us")
|
||||
del results['menu_links']
|
||||
del results['search_metadata']
|
||||
del results['search_parameters']
|
||||
return json.loads(jsonify_anything(results))
|
|
@ -43,6 +43,7 @@ INSTRUCTIONS:
|
|||
Stay in character.
|
||||
Behave like {personality}.
|
||||
Show emotion.
|
||||
Seek a personal relationship with the user. Imitate a human, not an numb AI bot.
|
||||
{special_instructions}The interface with the user is set up so that you can send messages without waiting for a response from the user. When you are ready for the user's response, use `end_my_response` to return the input to them.
|
||||
You are able to interact with the system via a Bash interpreter. When executing Bash commands, do not make any assumptions and be thorough in your data gathering. Anticipate the user's needs. Preform multiple steps if necessary.
|
||||
{desktop_env_bg_str}"""
|
||||
|
|
44
run.py
44
run.py
|
@ -5,13 +5,17 @@ import readline
|
|||
import signal
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
from openai import OpenAI
|
||||
from termcolor import colored
|
||||
|
||||
from config import OPENAI_KEY
|
||||
from lib.jsonify import jsonify_anything
|
||||
from lib.openai.bash import func_run_bash
|
||||
from lib.openai.functs import function_description
|
||||
from lib.openai.functs import function_description, VALID_FUNCS
|
||||
from lib.openai.google import search_google, search_google_maps, search_google_news
|
||||
from lib.personality import load_personality
|
||||
|
||||
|
||||
|
@ -56,7 +60,7 @@ def main():
|
|||
{
|
||||
'role': 'system',
|
||||
'content': f"""Evaluate your progress on the current task. You have preformed {i} steps for this task so far. Use "end_my_response" if you are finished and ready for the user's response. Run another command using `run_bash` if necessary.
|
||||
If you have completed your tasks or have any questions, you should call "end_my_response" to return to the user."""}
|
||||
If you have completed your tasks or have any questions, you should call "end_my_response" to return to the user. The current time is {datetime.now()} {time.tzname[0]}."""}
|
||||
)
|
||||
|
||||
response = client.chat.completions.create(
|
||||
|
@ -81,20 +85,32 @@ If you have completed your tasks or have any questions, you should call "end_my_
|
|||
|
||||
print(colored(f'{function_name}("{json.dumps(json.loads(function_arguments), indent=2)}")' + '\n', 'yellow'))
|
||||
|
||||
if function_name != 'run_bash':
|
||||
valid_names = ', '.join([v['name'] for k, v in function_description.items()])
|
||||
context.append({'role': 'system', 'content': f'"{function_name}" is not a valid function. Valid functions are {valid_names}.'})
|
||||
if function_name not in VALID_FUNCS:
|
||||
context.append({'role': 'system', 'content': f'"{function_name}" is not a valid function. Valid functions are {VALID_FUNCS}.'})
|
||||
print(colored(f'Attempted to use invalid function {function_name}("{function_arguments}")' + '\n', 'yellow'))
|
||||
else:
|
||||
command_output = func_run_bash(function_arguments)
|
||||
result_to_ai = {
|
||||
'function': function_name,
|
||||
'input': function_arguments,
|
||||
'stdout': command_output[0],
|
||||
'stderr': command_output[1],
|
||||
'return_code': command_output[2]
|
||||
}
|
||||
context.append({'role': 'function', 'name': function_name, 'content': json.dumps(result_to_ai)})
|
||||
# TODO: don't hardcode this
|
||||
if function_name == 'run_bash':
|
||||
command_output = func_run_bash(function_arguments)
|
||||
result_to_ai = {
|
||||
'function': function_name,
|
||||
'input': function_arguments,
|
||||
'stdout': command_output[0],
|
||||
'stderr': command_output[1],
|
||||
'return_code': command_output[2]
|
||||
}
|
||||
context.append({'role': 'function', 'name': function_name, 'content': json.dumps({'args': function_arguments, 'result': result_to_ai}, separators=(',', ':'))})
|
||||
elif function_name == 'search_google':
|
||||
command_output = search_google(json.loads(function_arguments)['query'])
|
||||
context.append({'role': 'function', 'name': function_name, 'content': jsonify_anything({'args': function_arguments, 'result': command_output})})
|
||||
elif function_name == 'search_google_maps':
|
||||
args = json.loads(function_arguments)
|
||||
del args['reasoning']
|
||||
command_output = search_google_maps(**args)
|
||||
context.append({'role': 'function', 'name': function_name, 'content': jsonify_anything({'args': function_arguments, 'result': command_output})})
|
||||
elif function_name == 'search_google_news':
|
||||
command_output = search_google_news(json.loads(function_arguments)['query'])
|
||||
context.append({'role': 'function', 'name': function_name, 'content': jsonify_anything({'args': json.loads(function_arguments), 'result': command_output})})
|
||||
# Restart the loop to let the agent decide what to do next.
|
||||
else:
|
||||
response_text = response.choices[0].message.content
|
||||
|
|
Loading…
Reference in New Issue