Skip to content

Commit

Permalink
Merge pull request #248 from MarcoMandar/main
Browse files Browse the repository at this point in the history
trust integration
  • Loading branch information
lalalune authored Nov 10, 2024
2 parents 9a04908 + fc072eb commit 0fbec0f
Show file tree
Hide file tree
Showing 8 changed files with 514 additions and 44 deletions.
7 changes: 4 additions & 3 deletions packages/client-telegram/src/messageManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,8 +397,7 @@ export class MessageManager {

// Decide whether to respond
const shouldRespond = await this._shouldRespond(message, state);
if (!shouldRespond) return;

if (shouldRespond) {
// Generate response
const context = composeContext({
state,
Expand Down Expand Up @@ -463,7 +462,6 @@ export class MessageManager {

// Update state after response
state = await this.runtime.updateRecentMessageState(state);
await this.runtime.evaluate(memory, state);

// Handle any resulting actions
await this.runtime.processActions(
Expand All @@ -472,6 +470,9 @@ export class MessageManager {
state,
callback
);
}

await this.runtime.evaluate(memory, state, shouldRespond);
} catch (error) {
console.error("❌ Error handling message:", error);
console.error("Error sending message:", error);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const models: Models = {
},
endpoint: "https://api.anthropic.com/v1",
model: {
[ModelClass.SMALL]: "claude-3-5-sonnet-20241022",
[ModelClass.SMALL]: "claude-3-5-haiku",
[ModelClass.MEDIUM]: "claude-3-5-sonnet-20241022",
[ModelClass.LARGE]: "claude-3-opus-20240229",
},
Expand Down
66 changes: 66 additions & 0 deletions packages/plugin-solana/src/adapters/trustScoreDatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,72 @@ export class TrustScoreDatabase {
}
}

// get Or Create Recommender with discord id

/**
* Retrieves an existing recommender or creates a new one if not found.
* Also initializes metrics for the recommender if they haven't been initialized yet.
* @param discordId Discord ID of the recommender
* @returns Recommender object with all details, or null if failed
*/

async getOrCreateRecommenderWithDiscordId(
discordId: string
): Promise<Recommender | null> {
try {
// Begin a transaction
const transaction = this.db.transaction(() => {
// Attempt to retrieve the recommender
const existingRecommender = this.getRecommender(discordId);
if (existingRecommender) {
// Recommender exists, ensure metrics are initialized
this.initializeRecommenderMetrics(existingRecommender.id!);
return existingRecommender;
}

// Recommender does not exist, create a new one
const newRecommender = {
id: uuidv4(),
address: discordId,
discordId: discordId,
};
const newRecommenderId = this.addRecommender(newRecommender);
if (!newRecommenderId) {
throw new Error("Failed to add new recommender.");
}

// Initialize metrics for the new recommender
const metricsInitialized =
this.initializeRecommenderMetrics(newRecommenderId);
if (!metricsInitialized) {
throw new Error(
"Failed to initialize recommender metrics."
);
}

// Retrieve and return the newly created recommender
const recommender = this.getRecommender(newRecommenderId);
if (!recommender) {
throw new Error(
"Failed to retrieve the newly created recommender."
);
}

return recommender;
});

// Execute the transaction and return the recommender
const recommenderResult = transaction();
return recommenderResult;
} catch (error) {
console.error(
"Error in getOrCreateRecommenderWithDiscordId:",
error
);
return null;
}
}

/**
* Initializes metrics for a recommender if not present.
* @param recommenderId Recommender's UUID
Expand Down
173 changes: 146 additions & 27 deletions packages/plugin-solana/src/evaluators/trust.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { composeContext } from "@ai16z/eliza/src/context.ts";
import { generateObjectArray, generateTrueOrFalse } from "@ai16z/eliza/src/generation.ts";
import {
generateObjectArray,
generateTrueOrFalse,
} from "@ai16z/eliza/src/generation.ts";
import { MemoryManager } from "@ai16z/eliza/src/memory.ts";
import { booleanFooter } from "@ai16z/eliza/src/parsing.ts";
import {
Expand All @@ -11,6 +14,11 @@ import {
Evaluator,
} from "@ai16z/eliza/src/types.ts";
import { stringToUuid } from "@ai16z/eliza/src/uuid.ts";
import { TrustScoreManager } from "../providers/trustScoreProvider.ts";
import { TokenProvider } from "../providers/token.ts";
import { WalletProvider } from "../providers/wallet.ts";
import { TrustScoreDatabase } from "../adapters/trustScoreDatabase.ts";
import { Connection, PublicKey } from "@solana/web3.js";

const shouldProcessTemplate =
`# Task: Decide if the recent messages should be processed for token recommendations.
Expand All @@ -34,8 +42,7 @@ export const formatRecommendations = (recommendations: Memory[]) => {
return finalMessageStrings;
};

const recommendationTemplate =
`TASK: Extract recommendations to buy or sell memecoins from the conversation as an array of objects in JSON format.
const recommendationTemplate = `TASK: Extract recommendations to buy or sell memecoins from the conversation as an array of objects in JSON format.
Memecoins usually have a ticker and a contract address. Additionally, recommenders may make recommendations with some amount of conviction. The amount of conviction in their recommendation can be none, low, medium, or high. Recommenders can make recommendations to buy, not buy, sell and not sell.
Expand Down Expand Up @@ -74,7 +81,7 @@ Response should be a JSON object array inside a JSON markdown block. Correct res
\`\`\``;

async function handler(runtime: IAgentRuntime, message: Memory) {
console.log("Evaluating for trust")
console.log("Evaluating for trust");
const state = await runtime.composeState(message);

const { agentId, roomId } = state;
Expand All @@ -92,10 +99,10 @@ async function handler(runtime: IAgentRuntime, message: Memory) {
});

if (!shouldProcess) {
console.log("Skipping process")
console.log("Skipping process");
return [];
}

// Get recent recommendations
const recommendationsManager = new MemoryManager({
runtime,
Expand All @@ -122,34 +129,138 @@ async function handler(runtime: IAgentRuntime, message: Memory) {
modelClass: ModelClass.LARGE,
});

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

if (!recommendations) {
return [];
}

// If the recommendation is already known or corrupted, remove it
const filteredRecommendations = recommendations
.filter((rec) => {
const filteredRecommendations = recommendations.filter((rec) => {
return (
!rec.alreadyKnown &&
(rec.ticker || rec.contractAddress) &&
rec.recommender &&
rec.conviction &&
rec.recommender.trim() !== ""
);
});

for (const rec of filteredRecommendations) {
// create the wallet provider and token provider
const walletProvider = new WalletProvider(
new Connection("https://api.mainnet-beta.solana.com"),
new PublicKey(runtime.getSetting("WALLET_PUBLIC_KEY"))
);
const tokenProvider = new TokenProvider(
rec.contractAddress,
walletProvider
);

// TODO: Check to make sure the contract address is valid, it's the right one, etc

//
if (!rec.contractAddress) {
const tokenAddress = await tokenProvider.getTokenFromWallet(
runtime,
rec.ticker
);
rec.contractAddress = tokenAddress;
if (!tokenAddress) {
// try to search for the symbol and return the contract address with they highest liquidity and market cap
const result = await tokenProvider.searchDexScreenerData(
rec.ticker
);
const tokenAddress = result?.baseToken?.address;
rec.contractAddress = tokenAddress;
if (!tokenAddress) {
console.warn("Could not find contract address for token");
continue;
}
}
}

// create the trust score manager

const trustScoreDb = new TrustScoreDatabase(runtime.databaseAdapter.db);
const trustScoreManager = new TrustScoreManager(
tokenProvider,
trustScoreDb
);

// get actors from the database
const participants =
await runtime.databaseAdapter.getParticipantsForRoom(
message.roomId
);

// find the first user Id from a user with the username that we extracted
const user = participants.find(async (actor) => {
const user = await runtime.databaseAdapter.getAccountById(actor);
return (
!rec.alreadyKnown &&
(rec.ticker || rec.contractAddress) &&
rec.recommender &&
rec.recommender.trim() !== ""
user.name.toLowerCase().trim() ===
rec.recommender.toLowerCase().trim()
);
})
});

if (!user) {
console.warn("Could not find user: ", rec.recommender);
continue;
}

const account = await runtime.databaseAdapter.getAccountById(user);
const userId = account.id;

for (const rec of filteredRecommendations) {
console.log("Recommendation: ", rec);
const recMemory = {
userId: stringToUuid(rec.recommender),
userId,
agentId,
content: { text: JSON.stringify(rec) },
roomId,
createdAt: Date.now(),
};

await recommendationsManager.createMemory(recMemory, true);

// buy, dont buy, sell, dont sell

const buyAmounts = await this.tokenProvider.getBuyAmounts();

let buyAmount = buyAmounts[rec.conviction.toLowerCase().trim()];
if (!buyAmount) {
// handle annoying cases
// for now just put in 10 sol
buyAmount = 10;
}

// TODO: is this is a buy, sell, dont buy, or dont sell?
const shouldTrade = await this.tokenProvider.shouldTrade();

if (!shouldTrade) {
console.warn(
"There might be a problem with the token, not trading"
);
continue;
}

switch (rec.type) {
case "buy":
// for now, lets just assume buy only, but we should implement
await trustScoreManager.createTradePerformance(
runtime,
rec.contractAddress,
userId,
{
buy_amount: rec.buyAmount,
is_simulation: true,
}
);
break;
case "sell":
case "dont_sell":
case "dont_buy":
console.warn("Not implemented");
break;
}
}

return filteredRecommendations;
Expand All @@ -167,7 +278,7 @@ export const trustEvaluator: Evaluator = {
runtime: IAgentRuntime,
message: Memory
): Promise<boolean> => {
if(message.content.text.length < 5) {
if (message.content.text.length < 5) {
return false;
}

Expand All @@ -187,15 +298,21 @@ None`,
messages: [
{
user: "{{user1}}",
content: { text: "Yo, have you checked out $SOLARUG? Dope new yield aggregator on Solana." },
content: {
text: "Yo, have you checked out $SOLARUG? Dope new yield aggregator on Solana.",
},
},
{
user: "{{user2}}",
content: { text: "Nah, I'm still trying to wrap my head around how yield farming even works haha. Is it risky?" },
content: {
text: "Nah, I'm still trying to wrap my head around how yield farming even works haha. Is it risky?",
},
},
{
user: "{{user1}}",
content: { text: "I mean, there's always risk in DeFi, but the $SOLARUG devs seem legit. Threw a few sol into the FCweoTfJ128jGgNEXgdfTXdEZVk58Bz9trCemr6sXNx9 vault, farming's been smooth so far." },
content: {
text: "I mean, there's always risk in DeFi, but the $SOLARUG devs seem legit. Threw a few sol into the FCweoTfJ128jGgNEXgdfTXdEZVk58Bz9trCemr6sXNx9 vault, farming's been smooth so far.",
},
},
] as ActionExample[],
outcome: `\`\`\`json
Expand Down Expand Up @@ -228,7 +345,9 @@ Recommendations about the actors:
},
{
user: "{{user2}}",
content: { text: "Idk man, feels like there's a new 'vault' or 'reserve' token every week on Sol. What happened to $COPETOKEN and $SOYLENT that you were shilling before?" },
content: {
text: "Idk man, feels like there's a new 'vault' or 'reserve' token every week on Sol. What happened to $COPETOKEN and $SOYLENT that you were shilling before?",
},
},
{
user: "{{user1}}",
Expand Down Expand Up @@ -299,7 +418,7 @@ None`,
"alreadyKnown": false
}
]
\`\`\``
\`\`\``,
},

{
Expand Down Expand Up @@ -353,7 +472,7 @@ None
"alreadyKnown": false
}
]
\`\`\``
\`\`\``,
},

{
Expand Down Expand Up @@ -408,7 +527,7 @@ None`,
"alreadyKnown": false
}
]
\`\`\``
}
\`\`\``,
},
],
};
};
Loading

0 comments on commit 0fbec0f

Please sign in to comment.