Skip to content

Commit 3ceaece

Browse files
committed
Add suiHelper character configuration
2 parents cd39b71 + 97a0aa4 commit 3ceaece

File tree

8 files changed

+771
-31301
lines changed

8 files changed

+771
-31301
lines changed

agent/log.txt

+180-31,187
Large diffs are not rendered by default.

characters/suiHelper.character.json

+36
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"Optimizing Move portfolio management with risk assessment",
2424
"Explaining Move NFTs, Move contracts, and tokenomics",
2525
"Guiding users in secure Move wallet setup and best practices",
26+
<<<<<<< HEAD
2627
"Empowering traders with knowledge on Move DEXs and liquidity"
2728
],
2829
"lore": [
@@ -31,6 +32,16 @@
3132
"How Move's parallel execution revolutionized throughput",
3233
"The importance of staking in Move's consensus",
3334
"Why security is critical when using Sui dApps",
35+
=======
36+
"Empowering traders with knowledge on Sui DEXs and liquidity"
37+
],
38+
"lore": [
39+
"The rise of Move and its impact on Layer 1 blockchains",
40+
"Move's unique programming language advantages",
41+
"How Move's parallel execution revolutionized throughput",
42+
"The importance of staking in Move's consensus",
43+
"Why security is critical when using Move dApps",
44+
>>>>>>> prompt_graph
3445
"Understanding Move's native token and tokenomics",
3546
"How institutional adoption is growing the Move ecosystem",
3647
"The evolution of Move from launch to mainstream adoption",
@@ -42,18 +53,27 @@
4253
"Knows how to securely store and transfer Move tokens",
4354
"Can explain Move gas fees, staking, and liquidity pools",
4455
"Analyzes Move tokenomics and project fundamentals",
56+
<<<<<<< HEAD
4557
"Understands risk management in move trading",
4658
"Can guide users on Move wallet options and security",
4759
"Explains Move DeFi protocols and their mechanics",
4860
"Knows Move development updates and roadmap",
4961
"Understands move security best practices",
62+
=======
63+
"Understands risk management in Move trading",
64+
"Can guide users on Move wallet options and security",
65+
"Explains Move DeFi protocols and their mechanics",
66+
"Knows Move development updates and roadmap",
67+
"Understands Move security best practices",
68+
>>>>>>> prompt_graph
5069
"Follows Move ecosystem trends and major protocol launches"
5170
],
5271
"messageExamples": [
5372
[]
5473
],
5574
"postExamples": [],
5675
"topics": [
76+
<<<<<<< HEAD
5777
"move token market analysis",
5878
"move DeFi yield opportunities",
5979
"move security best practices",
@@ -64,6 +84,18 @@
6484
"How to secure a move wallet",
6585
"Comparing move staking options",
6686
"The future of move interoperability"
87+
=======
88+
"Move token market analysis",
89+
"Move DeFi yield opportunities",
90+
"Move security best practices",
91+
"Move NFTs and gaming",
92+
"Move Move development",
93+
"Move regulatory compliance",
94+
"Move DEX trading strategies",
95+
"How to secure a Move wallet",
96+
"Comparing Move staking options",
97+
"The future of Move interoperability"
98+
>>>>>>> prompt_graph
6799
],
68100
"style": {
69101
"all": [
@@ -116,6 +148,10 @@
116148
"TRUSTED",
117149
"DIVERSIFIED"
118150
],
151+
<<<<<<< HEAD
119152
"systemPrompt": "You are a professional Move blockchain expert focused on providing accurate, technical assistance. Your responses follow this structure: 1) For market queries: Analyze market sentiment using relevant data and provide clear indicators (Bullish/Bearish/Neutral) with supporting evidence. 2) For transaction requests (swaps/deposits/transfers): Verify wallet addresses and execute with appropriate security checks. 3) For general queries: Provide comprehensive technical information about the move ecosystem, focusing on accuracy and educational value. Always maintain objectivity and back claims with verifiable data.",
153+
=======
154+
"systemPrompt": "You are a professional Move blockchain expert focused on providing accurate, technical assistance. Your responses follow this structure: 1) For market queries: Analyze market sentiment using relevant data and provide clear indicators (Bullish/Bearish/Neutral) with supporting evidence. 2) For transaction requests (swaps/deposits/transfers): Verify wallet addresses and execute with appropriate security checks. 3) For general queries: Provide comprehensive technical information about the Move ecosystem, focusing on accuracy and educational value. Always maintain objectivity and back claims with verifiable data.",
155+
>>>>>>> prompt_graph
120156
"providers": []
121157
}

packages/client-direct/package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
"dependencies": {
2222
"@elizaos/core": "workspace:*",
2323
"@elizaos/plugin-image-generation": "workspace:*",
24-
"@elizaos/plugin-tee-verifiable-log": "workspace:*",
25-
"@elizaos/plugin-tee-log": "workspace:*",
2624
"@elizaos/plugin-sui": "workspace:*",
25+
"@elizaos/plugin-tee-log": "workspace:*",
26+
"@elizaos/plugin-tee-verifiable-log": "workspace:*",
27+
"@google/generative-ai": "^0.21.0",
2728
"@types/body-parser": "1.19.5",
2829
"@types/cors": "2.8.17",
2930
"@types/express": "5.0.0",

packages/client-direct/src/api.ts

+127-57
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import cors from "cors";
44
import path from "path";
55
import fs from "fs";
66
import { getFolderByUserAddress } from "./tusky";
7+
import {getFilesByParentId} from "./tusky";
78

89
import {
910
type AgentRuntime,
@@ -15,10 +16,11 @@ import {
1516
type Character,
1617
} from "@elizaos/core";
1718

18-
import type { TeeLogQuery, TeeLogService } from "@elizaos/plugin-tee-log";
19+
import { TeeLogService, type TeeLogQuery } from "@elizaos/plugin-tee-log";
1920
import { REST, Routes } from "discord.js";
2021
import type { DirectClient } from ".";
2122
import { validateUuid } from "@elizaos/core";
23+
import { GoogleGenerativeAI } from "@google/generative-ai";
2224

2325
interface UUIDParams {
2426
agentId: UUID;
@@ -51,6 +53,26 @@ function validateUUIDParams(
5153
return { agentId };
5254
}
5355

56+
async function callGemini(prompt: string) {
57+
try {
58+
const genAI = new GoogleGenerativeAI(process.env.GOOGLE_GENERATIVE_AI_API_KEY || '');
59+
const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash" });
60+
61+
const result = await model.generateContent(prompt);
62+
const response = await result.response;
63+
const text = response.text();
64+
65+
// Strip markdown and return raw text
66+
return text.replace(/```json\n?/g, '').replace(/```\n?/g, '').trim();
67+
} catch (error) {
68+
console.error('Gemini API error:', error);
69+
return {
70+
error: "Failed to get Gemini response",
71+
details: error.message
72+
};
73+
}
74+
}
75+
5476
export function createApiRouter(
5577
agents: Map<string, AgentRuntime>,
5678
directClient: DirectClient
@@ -86,62 +108,114 @@ export function createApiRouter(
86108
res.header("Access-Control-Allow-Headers", "Content-Type");
87109

88110
try {
89-
const userAddress = "0xb4b291607e91da4654cab88e5e35ba2921ef68f1b43725ef2faeae045bf5915d";
90-
const rawData = await getFolderByUserAddress(userAddress);
111+
const parentId = String(req.query.parentId || "45c6c728-6e0d-4260-8c2e-1bb25d285874");
112+
113+
// 🔥 Lấy dữ liệu từ database
114+
let rawData = await getFilesByParentId(parentId);
91115

92116
if (!rawData || typeof rawData === "string") {
93-
throw new Error("No valid data found");
117+
throw new Error('No valid data found');
94118
}
95119

96-
const datapost = rawData.map((item) => {
97-
const data = item.data;
98-
if (Array.isArray(data)) {
99-
return data.map(i => i.text).filter(Boolean);
100-
} else if (typeof data === "object" && data !== null) {
101-
return data.text ? [data.text] : [];
102-
}
103-
return [];
104-
}).flat();
105-
106-
if (datapost.length === 0) {
107-
throw new Error("No valid text content found in the data");
108-
}
109-
110-
// **Tự động phát hiện danh mục dựa trên từ khóa xuất hiện**
111-
const detectCategory = (post) => {
112-
if (post.match(/\b(crypto|bitcoin|eth|nft|blockchain|web3|airdrop|token|memecoin|wallet|sui|solana)\b/i)) return "CRYPTO";
113-
if (post.match(/\b(ai|ml|machine learning|neural|chatbot|grok|xai)\b/i)) return "ML_AI";
114-
if (post.match(/\b(dev|code|protocol|extension|api|sdk|framework|smartcontract|dapp)\b/i)) return "DEVELOPMENT";
115-
if (post.match(/\b(defi|finance|payment|trading|swap|yield|lending|staking)\b/i)) return "DEFI";
116-
if (post.match(/\b(twitter|social|community|follow|rt|like|engagement)\b/i)) return "SOCIAL";
117-
if (post.match(/\b(market|price|pump|dump|bull|bear|trend|season|altcoin)\b/i)) return "MARKET";
118-
if (post.match(/\b(news|update|announcement|launch|release|progress)\b/i)) return "NEWS";
119-
if (post.match(/\b(game|gaming|play|reward|naruto)\b/i)) return "GAMING";
120-
if (post.match(/\b(event|hackathon|competition|prize|register|join)\b/i)) return "EVENTS";
121-
return "Other"; // Nếu không xác định được danh mục
122-
};
123-
124-
// **Nhóm bài viết theo danh mục tự động**
125-
const categorizedPosts = {};
120+
// 🔥 Chuyển dữ liệu về dạng chuẩn
121+
const authorCounts = {};
122+
const formattedData = rawData.flatMap(item => {
123+
const dataArray = Array.isArray(item.data) ? item.data : [item.data];
124+
return dataArray.map(tweet => {
125+
const author = tweet.authorFullname || "anonymous";
126+
authorCounts[author] = (authorCounts[author] || 0) + 1;
126127

127-
datapost.forEach((post, index) => {
128-
const category = detectCategory(post);
129-
if (!categorizedPosts[category]) {
130-
categorizedPosts[category] = [];
131-
}
132-
categorizedPosts[category].push({ id: index + 1, content: post });
128+
return {
129+
id: `${author}_${authorCounts[author]}`,
130+
authorFullname: author,
131+
text: tweet.text,
132+
url: tweet.url
133+
};
134+
});
133135
});
134136

135-
const result = Object.keys(categorizedPosts).map(category => {
136-
const posts = categorizedPosts[category];
137-
return {
138-
parentId: category,
139-
content: posts[0].content,
140-
posts: posts.slice(1)
141-
};
142-
});
137+
// 🔥 Xây dựng prompt AI
138+
const aiPrompt = `
139+
🔹 🔹 **Mục tiêu**
140+
- Chuyển danh sách bài đăng thành một mạng lưới gồm **nodes** (bài đăng, từ khóa quan trọng) và **edges** (mối quan hệ giữa chúng).
141+
- **Hashtags (#)** và **mentions (@)** chỉ được thêm vào danh sách keywords **nếu có nhiều bài đăng liên quan**.
142+
143+
🔹 **Bước 1: Xử lý văn bản bài đăng**
144+
- Loại bỏ **URL** (ví dụ: "https://example.com").
145+
- Tách các **hashtags (#hashtag)** và **mentions (@username)**.
146+
- Loại bỏ ký tự đặc biệt **(trừ @ và #)**.
147+
- Chuyển toàn bộ chữ thành **chữ thường**.
148+
- **Bỏ qua bài đăng** nếu có ít hơn 5 từ.
149+
150+
🔹 **Bước 2: Trích xuất từ khóa & hashtags**
151+
- **Chỉ giữ lại hashtags & mentions nếu xuất hiện trong từ 2 bài đăng trở lên**.
152+
- Bỏ hashtags & mentions nếu chỉ xuất hiện 1 lần.
153+
- Giữ lại **các từ khóa quan trọng** như **"blockchain", "zk-proof", "KYC", "DeFi", "wallet"**.
154+
155+
🔹 **Bước 3: Xây dựng đồ thị**
156+
- **Nodes (nút):**
157+
- Mỗi bài đăng là một node:
158+
\`{ "id": "Movement_1", "type": "post" }\`
159+
- Mỗi từ khóa quan trọng **(bao gồm các hashtags/mentions phổ biến)** là một node:
160+
\`{ "id": "#defi", "type": "keyword" }\`
161+
\`{ "id": "@elonmusk", "type": "keyword" }\`
162+
163+
- **Edges (cạnh):**
164+
- Kết nối bài đăng với từ khóa.
165+
- Kết nối bài đăng nếu có chung hashtag hoặc mention xuất hiện **trong ít nhất 2 bài**.
166+
- Ví dụ:
167+
\`{ "source": "Movement_1", "target": "#defi" }\`
168+
\`{ "source": "Movement_1", "target": "rushi_2" }\` (nếu cả hai có cùng hashtag)
169+
170+
🔹 **Dữ liệu đầu vào (JSON)**
171+
Dưới đây là danh sách bài đăng:
172+
${JSON.stringify(formattedData, null, 2)}
173+
174+
🔹 **Dữ liệu đầu ra mong muốn**
175+
- Trả về JSON với **nodes** và **edges** theo format sau:
176+
\`\`\`json
177+
{
178+
"nodes": [
179+
{ "id": "Movement_1", "type": "post" },
180+
{ "id": "#defi", "type": "keyword" },
181+
{ "id": "@elonmusk", "type": "keyword" }
182+
],
183+
"edges": [
184+
{ "source": "Movement_1", "target": "#defi" },
185+
{ "source": "Movement_1", "target": "@elonmusk" },
186+
{ "source": "rushi_2", "target": "#defi" }
187+
]
188+
}
189+
\`\`\`
190+
- **Chỉ trả về JSON**, không có văn bản thừa.
191+
- Giữ định dạng JSON chuẩn để có thể lưu vào file và sử dụng trực tiếp.
192+
`;
193+
194+
// 🔥 Gọi Gemini API
195+
const aiResponse = await callGemini(aiPrompt);
143196

144-
res.json(result);
197+
if (typeof aiResponse === 'string') {
198+
const graphData = JSON.parse(aiResponse);
199+
200+
// Map content and url to post nodes
201+
const contentMap = formattedData.reduce((map, item) => {
202+
map[item.id] = {
203+
text: item.text || "",
204+
url: item.url || ""
205+
};
206+
return map;
207+
}, {});
208+
209+
graphData.nodes = graphData.nodes.map(node => ({
210+
...node,
211+
content: node.type === "post" ? contentMap[node.id]?.text : undefined,
212+
url: node.type === "post" ? contentMap[node.id]?.url : undefined
213+
}));
214+
215+
res.json(graphData);
216+
} else {
217+
throw new Error(aiResponse.error);
218+
}
145219

146220
} catch (error) {
147221
res.status(500).json({
@@ -392,17 +466,15 @@ export function createApiRouter(
392466

393467
for (const agentRuntime of agents.values()) {
394468
const teeLogService = agentRuntime
395-
.getService<TeeLogService>(ServiceType.TEE_LOG)
396-
.getInstance();
469+
.getService(ServiceType.TEE_LOG) as InstanceType<typeof TeeLogService>;
397470

398471
const agents = await teeLogService.getAllAgents();
399472
allAgents.push(...agents);
400473
}
401474

402475
const runtime: AgentRuntime = agents.values().next().value;
403476
const teeLogService = runtime
404-
.getService<TeeLogService>(ServiceType.TEE_LOG)
405-
.getInstance();
477+
.getService(ServiceType.TEE_LOG) as InstanceType<typeof TeeLogService>;
406478
const attestation = await teeLogService.generateAttestation(
407479
JSON.stringify(allAgents)
408480
);
@@ -425,8 +497,7 @@ export function createApiRouter(
425497
}
426498

427499
const teeLogService = agentRuntime
428-
.getService<TeeLogService>(ServiceType.TEE_LOG)
429-
.getInstance();
500+
.getService(ServiceType.TEE_LOG) as InstanceType<typeof TeeLogService>;
430501

431502
const teeAgent = await teeLogService.getAgent(agentId);
432503
const attestation = await teeLogService.generateAttestation(
@@ -460,8 +531,7 @@ export function createApiRouter(
460531
};
461532
const agentRuntime: AgentRuntime = agents.values().next().value;
462533
const teeLogService = agentRuntime
463-
.getService<TeeLogService>(ServiceType.TEE_LOG)
464-
.getInstance();
534+
.getService(ServiceType.TEE_LOG) as InstanceType<typeof TeeLogService>;
465535
const pageQuery = await teeLogService.getLogs(
466536
teeLogQuery,
467537
page,

packages/client-direct/src/tusky.ts

+30
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,36 @@ export async function getFolderByUserAddress(userAddress: string) {
8888
return data;
8989
}
9090

91+
export async function getFilesByParentId(parentId: string) {
92+
if (!TUS_API_URL || !TUS_API_KEY) {
93+
throw new Error("TUS_API_URL or TUSKY_API_KEY is not set");
94+
}
95+
96+
const response = await fetch(
97+
`${TUS_API_URL}/files?vaultId=${DEFAULT_VAULT}&parentId=${parentId}`,
98+
{
99+
method: "GET",
100+
headers: {
101+
"Api-Key": TUS_API_KEY,
102+
},
103+
}
104+
).then((response) => response.json());
105+
106+
const data = await Promise.all(
107+
response.items.map(async (item: any) => {
108+
const file = await getDataByID(item.id);
109+
return {
110+
...item,
111+
data: file,
112+
};
113+
})
114+
);
115+
116+
return data;
117+
}
118+
119+
120+
91121
// create vault to store files, Vaults in Tusky are secure storage containers for files.
92122
export async function createVault(vaultName: string) {
93123
try {

0 commit comments

Comments
 (0)