Skip to content

Commit

Permalink
feat: add initial integrations
Browse files Browse the repository at this point in the history
  • Loading branch information
jrriehl committed Aug 14, 2023
1 parent b5bdc32 commit 9a4d706
Show file tree
Hide file tree
Showing 18 changed files with 2,828 additions and 0 deletions.
94 changes: 94 additions & 0 deletions integrations/fetch-holiday/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# uAgent Holiday integrations Examples
### Step 1: Prerequisites
Before starting, you'll need the following:
* Python (3.8+ is recommended)
* Poetry (a packaging and dependency management tool for Python)

### Step 2: Set up .env file
To run the demo, you need API keys from:
* RapidAPI
* OpenAI
* SerpAPI

##### RapidAPI Key
* Visit RapidAPI.
* Sign up or log in.
* Search for the Skyscanner API and subscribe.
* Once subscribed, copy your X-RapidAPI-Key

##### OpenAI API Key
* Visit OpenAI.
* Sign up or log in.
* Navigate to the API section to obtain your API key.

Note that if you’ve run out of OpenAI credits, you will not be able to get results for this example.

##### SerpAPI Key

* Visit SerpAPI.
* Sign up or log in.
* Your API key will be available on the dashboard.

Once you have all three keys, create a .env file in the holiday-integrations/src directory.
```bash
export RAPIDAPI_API_KEY="{GET THE API KEY}"
export OPENAI_API_KEY="{GET THE API KEY}"
export SERPAPI_API_KEY="{GET THE API KEY}"
```
To use the environment variables from .env and install the project:
```bash
cd src
source .env
poetry intall
```
### Step 3: Run the main script
To run the project and its agents:
```bash
poetry run python main.py
```
You need to look for the following output in the logs:
```
Adding top destinations agent to Bureau: {top_dest_address}
```
Copy the {top_dest_address} value and paste it somewhere safe. You will need it in the next step.
### Step 4: Set up the client script
Now that we have set up the integrations, let’s run a client script that will showcase the ‘top destinations’. To do this, create a new Python file in the src folder called top_dest_client.py, and paste the following:
```python
from messages import TopDestinations, UAgentResponse
from uagents import Agent, Context
from uagents.setup import fund_agent_if_low
import os
TOP_DESTINATIONS_CLIENT_SEED = os.getenv("TOP_DESTINATIONS_CLIENT_SEED", "top_destinations_client really secret phrase :)")
top_dest_client = Agent(
name="top_destinations_client",
port=8008,
seed=TOP_DESTINATIONS_CLIENT_SEED,
endpoint=["http://127.0.0.1:8008/submit"],
)
fund_agent_if_low(top_dest_client.wallet.address())
top_dest_request = TopDestinations(preferences="new york")
@top_dest_client.on_interval(period=10.0)
async def send_message(ctx: Context):
await ctx.send("{top_dest_address}", top_dest_request)
@top_dest_client.on_message(model=UAgentResponse)
async def message_handler(ctx: Context, _: str, msg: UAgentResponse):
ctx.logger.info(f"Received top destination options from: {msg.options}")
if __name__ == "__main__":
top_dest_client.run()
```
Remember to replace the address in ctx.send with the value you received in the previous step.

This code sends a request to get the top destinations (in this example, from New York). To do this, it sends a request to the ‘top destinations agent’ every 10 seconds and displays the options in the console.
### Step 5: Run the client script
Open a new terminal (let the previous one be as is), and navigate to the src folder to run the client.
```bash
cd src
poetry run python top_dest_client.py
```
Once you hit enter, a request will be sent to the top destinations agent every 10 seconds, and you will be able to see your results in the console!
2,337 changes: 2,337 additions & 0 deletions integrations/fetch-holiday/poetry.lock

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions integrations/fetch-holiday/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[tool.poetry]
name = "holiday-integrations"
version = "0.1.0"
description = "A starter template for creating holiday integration examples"
authors = ["Fetch.AI Limited"]

[tool.poetry.dependencies]
python = ">=3.10,<3.12"
google-search-results = "^2.4.2"
langchain = "^0.0.149"
openai = "^0.27.4"
uagents = "*"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Empty file.
Empty file.
Empty file.
62 changes: 62 additions & 0 deletions integrations/fetch-holiday/src/agents/activities/top_activities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from langchain.chat_models import ChatOpenAI
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.output_parsers import CommaSeparatedListOutputParser
from langchain.prompts import PromptTemplate
from messages import UAgentResponse, UAgentResponseType, TopActivities, KeyValue
from uagents import Agent, Context, Protocol
from uagents.setup import fund_agent_if_low
import os


TOP_ACTIVITIES_SEED = os.getenv("TOP_ACTIVITIES_SEED", "top_activities really secret phrase :)")

agent = Agent(
name="top_activities",
seed=TOP_ACTIVITIES_SEED
)

fund_agent_if_low(agent.wallet.address())

output_parser = CommaSeparatedListOutputParser()
format_instructions = output_parser.get_format_instructions()
prompt = PromptTemplate(
template="""
You are an expert AI in suggesting travel, holiday activities based on the date and city specified in user input.\n
The question that SerpAPI has to answer: What are the top 5 tourist activities in {city} on {date}?\n
{preferred_activities_str}\n
You should find tourist attractions and programs which are available exactly on the specified date.\n
{format_instructions}""",
input_variables=["city", "date", "preferred_activities_str"],
partial_variables={"format_instructions": format_instructions}
)

llm = ChatOpenAI(temperature=0.1)
tools = load_tools(["serpapi"], llm=llm)
langchain_agent = initialize_agent(tools, llm, agent="chat-zero-shot-react-description", verbose=True)

top_activities_protocol = Protocol("TopActivities")

@top_activities_protocol.on_message(model=TopActivities, replies=UAgentResponse)
async def get_top_activity(ctx: Context, sender: str, msg: TopActivities):
ctx.logger.info(f"Received message from {sender}, session: {ctx.session}")

preferred_activities_str = f"You should only offer programs and activities related to {msg.preferred_activities}" if msg.preferred_activities else ""
_input = prompt.format(city=msg.city, date=msg.date, preferred_activities_str = preferred_activities_str)
try:
output = await langchain_agent.arun(_input)
result = output_parser.parse(output)
options = list(map(lambda x: KeyValue(key=x, value=x), result))
ctx.logger.info(f"Agent executed and got following result: {result}. Mapped to options: {options}")
await ctx.send(
sender,
UAgentResponse(
options=options,
type=UAgentResponseType.FINAL_OPTIONS,
)
)
except Exception as exc:
ctx.logger.warn(exc)
await ctx.send(sender, UAgentResponse(message=str(exc), type=UAgentResponseType.ERROR))

agent.include(top_activities_protocol)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from uagents import Agent, Context, Protocol
from messages import TopDestinations, UAgentResponse, UAgentResponseType, KeyValue
from uagents.setup import fund_agent_if_low
from utils.llm import get_llm
import os


TOP_DESTINATIONS_SEED = os.getenv("TOP_DESTINATIONS_SEED", "top_destinations really secret phrase :)")

agent = Agent(
name="top_destinations",
seed=TOP_DESTINATIONS_SEED
)

fund_agent_if_low(agent.wallet.address())

llm = get_llm()
top_destinations_protocol = Protocol("TopDestinations")

@top_destinations_protocol.on_message(model=TopDestinations, replies=UAgentResponse)
async def get_top_destinations(ctx: Context, sender: str, msg: TopDestinations):
ctx.logger.info(f"Received message from {sender}, session: {ctx.session}")
prompt = f"""You are an expert AI in suggesting travel, holiday destination based on some user input.
User input might not be provided, in which case suggest popular destinations.
If user input is present, then suggest destinations based on user input.
The response should be a list of destinations, each destination should have information about why it is a good destination.
After listing all the suggestions say END. Every destination should be separated by a new line.
Example:
User input: I want to go to a place with good weather and beaches.
Response:
1. Goa, India. Goa is a popular destination for tourists. It has good weather and beaches.
2. Malé, Maldives. Maldives is a popular destination for tourists. It has good weather and beaches.
END
User preferences: {msg.preferences}
"""
try:
response = await llm.complete("", prompt, "Response:", max_tokens=500, stop=["END"])
result = response.strip()
result = result.split("\n")
await ctx.send(
sender,
UAgentResponse(
options=list(map(lambda x: KeyValue(key=x, value=x), result)),
type=UAgentResponseType.FINAL_OPTIONS
)
)
except Exception as exc:
ctx.logger.warn(exc)
await ctx.send(sender, UAgentResponse(message=str(exc), type=UAgentResponseType.ERROR))

agent.include(top_destinations_protocol)
Empty file.
113 changes: 113 additions & 0 deletions integrations/fetch-holiday/src/agents/flights/flights.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
from uagents import Agent, Context, Protocol
from uagents.setup import fund_agent_if_low
import requests
from messages import Flights, UAgentResponse, UAgentResponseType, KeyValue
import os
import uuid


FLIGHTS_SEED = os.getenv("FLIGHTS_SEED", "flights really secret phrase :)")

agent = Agent(
name="flights_adaptor",
seed=FLIGHTS_SEED
)

fund_agent_if_low(agent.wallet.address())

RAPIDAPI_API_KEY = os.environ.get("RAPIDAPI_API_KEY", "")

assert RAPIDAPI_API_KEY, "RAPIDAPI_API_KEY environment variable is missing from .env"

SKY_SCANNER_URL = "https://skyscanner-api.p.rapidapi.com/v3e/flights/live/search/synced"
headers = {
"content-type": "application/json",
"X-RapidAPI-Key": RAPIDAPI_API_KEY,
"X-RapidAPI-Host": "skyscanner-api.p.rapidapi.com"
}

def skyscanner_format_data(data):
r = data["content"]["results"]
carriers = r["carriers"]
itineraries = r["itineraries"]
segments = r["segments"]
sorted_itineraries = data["content"]["sortingOptions"]["cheapest"]
results = []
for o in sorted_itineraries:
_id = o["itineraryId"]
it = itineraries[_id]
for option in it["pricingOptions"]:
price = option["price"]["amount"]
if len(option["items"]) != 1:
continue
fares = option["items"][0]["fares"]
if len(fares) != 1:
continue
segment_id = fares[0]["segmentId"]
seg = segments[segment_id]
carrier = carriers[seg["marketingCarrierId"]]
duration = seg["durationInMinutes"]
departure = seg["departureDateTime"]
arrival = seg["arrivalDateTime"]
results.append({
"price": price,
"duration": duration,
"departure": departure,
"arrival": arrival,
"carrier": carrier
})
return results

def skyscanner_top5(fares):
return [fares[i] for i in range(len(fares)) if i < 5]

flights_protocol = Protocol("Flights")

@flights_protocol.on_message(model=Flights, replies={UAgentResponse})
async def flight_offers(ctx: Context, sender: str, msg: Flights):
ctx.logger.info(f"Received message from {sender}, session: {ctx.session}")

dept_year, dept_month, dept_day = msg.date.split("-")
payload = {
"query": {
"market": "UK",
"locale": "en-GB",
"currency": "GBP",
"queryLegs": [
{
"originPlaceId": {"iata": msg.from_},
"destinationPlaceId": {"iata": msg.to},
"date": {
"year": int(dept_year),
"month": int(dept_month),
"day": int(dept_day)
}
}
],
"cabinClass": "CABIN_CLASS_ECONOMY",
"adults": msg.persons
}
}
print("CALLING SKYSCANNER, payload ", payload)
try:
response = requests.request("POST", SKY_SCANNER_URL, json=payload, headers=headers)
if response.status_code != 200:
print("SKYSCANNER STATUS CODE not 200: ", response.json())
await ctx.send(sender, UAgentResponse(message=response.text, type=UAgentResponseType.ERROR))
return
formatted = skyscanner_format_data(response.json())
top5 = skyscanner_top5(formatted)
print("SKYSCANNER TOP5 RESPONSE: ", top5)
request_id = str(uuid.uuid4())
options = []
for idx, o in enumerate(top5):
dep = f"{o['departure']['hour']:02}:{o['departure']['minute']:02}"
arr = f"{o['arrival']['hour']:02}:{o['arrival']['minute']:02}"
option = f"""{o["carrier"]["name"]} for £{o['price']}, {dep} - {arr}, flight time {o['duration']} min"""
options.append(KeyValue(key=idx, value=option))
await ctx.send(sender, UAgentResponse(options=options, type=UAgentResponseType.SELECT_FROM_OPTIONS, request_id=request_id))
except Exception as exc:
ctx.logger.error(exc)
await ctx.send(sender, UAgentResponse(message=str(exc), type=UAgentResponseType.ERROR))

agent.include(flights_protocol)
16 changes: 16 additions & 0 deletions integrations/fetch-holiday/src/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from uagents import Bureau

from agents.activities.top_activities import agent as top_activities_agent
from agents.destinations.top_destinations import agent as top_destinations_agent
from agents.flights.flights import agent as flights_agent


if __name__ == "__main__":
bureau = Bureau(endpoint="http://127.0.0.1:8000/submit", port=8000)
print(f"Adding top activities agent to Bureau: {top_activities_agent.address}")
bureau.add(top_activities_agent)
print(f"Adding top destinations agent to Bureau: {top_destinations_agent.address}")
bureau.add(top_destinations_agent)
print(f"Adding flights agent to Bureau: {flights_agent.address}")
bureau.add(flights_agent)
bureau.run()
4 changes: 4 additions & 0 deletions integrations/fetch-holiday/src/messages/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .flight import Flights
from .top_destinations import TopDestinations
from .top_activities import TopActivities
from .general import *
15 changes: 15 additions & 0 deletions integrations/fetch-holiday/src/messages/flight.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from uagents import Model
from pydantic import Field
from typing import Optional

class Flights(Model):
from_: str = Field(alias="from", description="This field is the airport IATA code for of the airport from where the user wants to fly from. This should be airport IATA code. IATA airport code is a three-character alphanumeric geocode.")
to: str = Field(description="This field is the airport IATA code of the destination airport! This should be airport IATA code. IATA airport code is a three-character alphanumeric geocode.")
trip: Optional[str] = Field(description="This can be oneway or return")
date: str = Field(description="Contains the date of flying out.")
back_date: Optional[str] = Field(description="Optional field only for return flight. This is the date when the user wants to fly back")
route: Optional[int] = Field(description="Selects the maximum number of stops, 0 means direct flight, 1 means with maximum 1 stop.")
persons: int = Field(description="Describes how many persons are going to fly.")

class Config:
allow_population_by_field_name = True
Loading

0 comments on commit 9a4d706

Please sign in to comment.