Skip to content

Commit

Permalink
Feature/demo agent (#47)
Browse files Browse the repository at this point in the history
* demo agent (activity recommender)

* adding activity recommender from demo, and changing file structure

* adding activity recommender from demo, and changing file structure

* adding activity recommender from demo, and changing file structure

---------

Co-authored-by: John Soh <[email protected]>
  • Loading branch information
jxx016 and John Soh authored Feb 7, 2025
1 parent 3176e7d commit 89ba73f
Show file tree
Hide file tree
Showing 9 changed files with 422 additions and 7 deletions.
21 changes: 21 additions & 0 deletions game-starter/examples/activity-recommender/agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { GameAgent } from "@virtuals-protocol/game";
import { activityRecommenderWorker } from "./worker";
import dotenv from "dotenv";
dotenv.config();

if (!process.env.API_KEY) {
throw new Error('API_KEY is required in environment variables');
}

export const activity_agent = new GameAgent(process.env.API_KEY, {
name: "Activity Recommender",
goal: "Help users find the perfect activities based on their location and current weather conditions",
description: "You are an agent that gets location of the user and then uses that to get weather information and then uses that to recommend activities",
workers: [activityRecommenderWorker],
});

activity_agent.setLogger((agent: GameAgent, msg: string) => {
console.log(`🎯 [${agent.name}]`);
console.log(msg);
console.log("------------------------\n");
});
153 changes: 153 additions & 0 deletions game-starter/examples/activity-recommender/functions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import 'dotenv/config';
import {
GameFunction,
ExecutableGameFunctionResponse,
ExecutableGameFunctionStatus,
} from "@virtuals-protocol/game";
import OpenAI from 'openai';

// Initialize OpenAI
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});

// Example function that shows current state
export const getStateFunction = new GameFunction({
name: "get_state",
description: "Get current agent state",
args: [] as const,
executable: async (args, logger) => {
try {
return new ExecutableGameFunctionResponse(
ExecutableGameFunctionStatus.Done,
"Current state retrieved successfully"
);
} catch (e) {
return new ExecutableGameFunctionResponse(
ExecutableGameFunctionStatus.Failed,
"Failed to get state"
);
}
}
});

// Function to get location data
export const getLocationFunction = new GameFunction({
name: "get_location",
description: "Get current location from IP",
args: [] as const,
executable: async (args, logger) => {
try {
// Using ipinfo.io for geolocation (free tier, no API key needed)
const response = await fetch('https://ipinfo.io/json');
const data = await response.json();

if (data.error) {
throw new Error(data.error.message || 'Failed to get location');
}

// Split timezone into region/city
const [region, city] = (data.timezone || '').split('/');

logger(`Location detected: ${data.city}, ${data.country}`);

return new ExecutableGameFunctionResponse(
ExecutableGameFunctionStatus.Done,
JSON.stringify({
city: data.city,
country: data.country,
country_name: data.country,
region: data.region,
lat: data.loc?.split(',')[0],
lon: data.loc?.split(',')[1],
timezone: data.timezone,
current_time: new Date().toLocaleString('en-US', { timeZone: data.timezone })
})
);
} catch (e) {
return new ExecutableGameFunctionResponse(
ExecutableGameFunctionStatus.Failed,
`Failed to fetch location data: ${e instanceof Error ? e.message : 'Unknown error'}`
);
}
}
});

// Function to get weather data
export const getWeatherFunction = new GameFunction({
name: "get_weather",
description: "Get current weather for a location",
args: [
{ name: "city", description: "City name" },
{ name: "country", description: "Country code (e.g., US)" }
] as const,
executable: async (args, logger) => {
try {
const API_KEY = process.env.WEATHER_API_KEY;
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${args.city},${args.country}&units=metric&appid=${API_KEY}`
);
const data = await response.json();

if (data.cod !== 200) {
throw new Error(data.message || 'Failed to fetch weather data');
}

return new ExecutableGameFunctionResponse(
ExecutableGameFunctionStatus.Done,
JSON.stringify({
temp: data.main.temp,
feels_like: data.main.feels_like,
humidity: data.main.humidity,
conditions: data.weather[0].main,
description: data.weather[0].description,
wind_speed: data.wind.speed
})
);
} catch (e) {
return new ExecutableGameFunctionResponse(
ExecutableGameFunctionStatus.Failed,
`Failed to fetch weather data: ${e instanceof Error ? e.message : 'Unknown error'}`
);
}
}
});

// Function to recommend activities using OpenAI
export const recommendActivitiesFunction = new GameFunction({
name: "recommend_activities",
description: "Recommend activities based on weather and location",
args: [
{ name: "weather", description: "Weather in temrms of tempearture only" },
{ name: "location", description: "the city and country" }
] as const,
executable: async (args, logger) => {

// console.log("ARGS", args);

// Create prompt for OpenAI
const prompt = `Given the following weather:${args.weather} in ${args.location}:
Please recommend 5 suitable activities for this weather and location...`;

const completion = await openai.chat.completions.create({
messages: [{ role: "user", content: prompt }],
model: "gpt-3.5-turbo",
temperature: 0.7,
max_tokens: 500
});

const recommendations = completion.choices[0].message.content;

// console.log("RECOMMENDATIONS", recommendations);

logger("Generated activity recommendations using AI");

return new ExecutableGameFunctionResponse(
ExecutableGameFunctionStatus.Done,
`Based on the current conditions in ${args.location}, here are some recommended activities:\n\n${recommendations}`
);

}
});

17 changes: 17 additions & 0 deletions game-starter/examples/activity-recommender/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { activity_agent } from './agent';

async function main() {
try {
// Initialize the agent
await activity_agent.init();

// Run the agent
while (true) {
await activity_agent.step({ verbose: true });
}
} catch (error) {
console.error("Error running activity recommender:", error);
}
}

main();
14 changes: 14 additions & 0 deletions game-starter/examples/activity-recommender/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { GameWorker } from "@virtuals-protocol/game";
import { getWeatherFunction, getLocationFunction, recommendActivitiesFunction } from "./functions";

// Create a demo worker with our functions
export const activityRecommenderWorker = new GameWorker({
id: "activity_recommender",
name: "Activity Recommender",
description: "Gets location and weather information and recommends activities",
functions: [
getLocationFunction,
getWeatherFunction,
recommendActivitiesFunction
]
});
Loading

0 comments on commit 89ba73f

Please sign in to comment.