Skip to content

Commit 16109f0

Browse files
Gaia integration (#43)
* Added integration: Use your own Gaia node with XMTP * Update README.md Updated details to match with `integrations/gaia` * update linter * update pr * push commits * yarn lock --------- Co-authored-by: Harish Kotra <harishkotra@gmail.com> Co-authored-by: Harish Kotra <harishkotra@users.noreply.github.com>
1 parent 7a6aa2d commit 16109f0

File tree

5 files changed

+329
-0
lines changed

5 files changed

+329
-0
lines changed

integrations/gaia/.env.example

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#GAIA Integration
2+
GAIA_NODE_URL= # the URL of the Gaia node (Ex: https://llama8b.gaia.domains/v1)
3+
GAIA_API_KEY= # the API key for the Gaia node (Ex: gaia-xxxxxxxxxx)
4+
GAIA_MODEL_NAME= # the name of model running on your Gaia node (Ex: llama)

integrations/gaia/README.md

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Gaia agent example
2+
3+
This example uses a [Gaia](https://docs.gaianet.ai) API for AI based responses and [XMTP](https://xmtp.org) for secure messaging. You can test your agent on [xmtp.chat](https://xmtp.chat) or any other XMTP-compatible client.
4+
5+
Using Gaia, you can also run your own [node](https://docs.gaianet.ai/getting-started/quick-start) and use the OpenAI compatible API in this library.
6+
7+
## Environment variables
8+
9+
Add the following keys to a `.env` file:
10+
11+
```bash
12+
WALLET_KEY= # the private key of the wallet
13+
ENCRYPTION_KEY= # a second random 32 bytes encryption key for local db encryption
14+
GAIA_API_KEY= # Your API key from https://gaianet.ai
15+
GAIA_NODE_URL= # Your custom Gaia node URL or a public node, ex: https://llama8b.gaia.domains/v1
16+
GAIA_MODEL_NAME= # Model name running in your Gaia node or a public node, ex: llama
17+
```
18+
19+
You can generate random keys with the following command:
20+
21+
```bash
22+
yarn gen:keys
23+
```
24+
25+
> [!WARNING]
26+
> Running the `gen:keys` script will overwrite the existing `.env` file.
27+
28+
## Usage
29+
30+
```tsx
31+
import { Client, type XmtpEnv } from "@xmtp/node-sdk";
32+
import OpenAI from "openai";
33+
import { createSigner, getEncryptionKeyFromHex } from "@/helpers";
34+
35+
const { WALLET_KEY, ENCRYPTION_KEY, GAIA_NODE_URL, GAIA_API_KEY, GAIA_MODEL_NAME } = process.env;
36+
37+
if (!WALLET_KEY) {
38+
throw new Error("WALLET_KEY must be set");
39+
}
40+
41+
if (!ENCRYPTION_KEY) {
42+
throw new Error("ENCRYPTION_KEY must be set");
43+
}
44+
45+
/* Check if the Gaia API key is set */
46+
if (!GAIA_API_KEY) {
47+
throw new Error("GAIA_API_KEY must be set");
48+
}
49+
50+
/* Check if the Gaia node's base URL is set */
51+
if (!GAIA_NODE_URL) {
52+
throw new Error("GAIA_NODE_URL must be set");
53+
}
54+
55+
/* Check if the the model name for the Gaia node is set */
56+
if (!GAIA_MODEL_NAME) {
57+
throw new Error("GAIA_MODEL_NAME must be set");
58+
}
59+
60+
const signer = createSigner(WALLET_KEY);
61+
const encryptionKey = getEncryptionKeyFromHex(ENCRYPTION_KEY);
62+
const openai = new OpenAI({
63+
baseURL: GAIA_NODE_URL,
64+
apiKey: GAIA_API_KEY
65+
});
66+
67+
/* Set the environment to dev or production */
68+
const env: XmtpEnv = "dev";
69+
70+
async function main() {
71+
console.log(`Creating client on the '${env}' network...`);
72+
const client = await Client.create(signer, encryptionKey, {
73+
env,
74+
});
75+
76+
console.log("Syncing conversations...");
77+
/* Sync the conversations from the network to update the local db */
78+
await client.conversations.sync();
79+
80+
console.log(
81+
`Agent initialized on ${client.accountAddress}\nSend a message on http://xmtp.chat/dm/${client.accountAddress}`,
82+
);
83+
84+
console.log("Waiting for messages...");
85+
const stream = client.conversations.streamAllMessages();
86+
87+
for await (const message of await stream) {
88+
/* Ignore messages from the same agent or non-text messages */
89+
if (
90+
message?.senderInboxId.toLowerCase() === client.inboxId.toLowerCase() ||
91+
message?.contentType?.typeId !== "text"
92+
) {
93+
continue;
94+
}
95+
96+
console.log(
97+
`Received message: ${message.content as string} by ${message.senderInboxId}`,
98+
);
99+
100+
const conversation = client.conversations.getConversationById(
101+
message.conversationId,
102+
);
103+
104+
if (!conversation) {
105+
console.log("Unable to find conversation, skipping");
106+
continue;
107+
}
108+
109+
try {
110+
const completion = await openai.chat.completions.create({
111+
messages: [{ role: "user", content: message.content as string }],
112+
model: GAIA_MODEL_NAME,
113+
});
114+
115+
/* Get the AI response */
116+
const response =
117+
completion.choices[0]?.message?.content ||
118+
"I'm not sure how to respond to that.";
119+
120+
console.log(`Sending AI response: ${response}`);
121+
await conversation.send(response);
122+
} catch (error) {
123+
console.error("Error getting AI response:", error);
124+
await conversation.send(
125+
"Sorry, I encountered an error processing your message.",
126+
);
127+
}
128+
129+
console.log("Waiting for messages...");
130+
}
131+
}
132+
133+
main().catch(console.error);
134+
```
135+
136+
## Run the agent
137+
138+
```bash
139+
# git clone repo
140+
git clone https://github.com/ephemeraHQ/xmtp-agent-examples.git
141+
# go to the folder
142+
cd xmtp-agent-examples
143+
# install packages
144+
yarn
145+
146+
cd integrations/gaia
147+
yarn dev
148+
```

integrations/gaia/index.ts

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { Client, type XmtpEnv } from "@xmtp/node-sdk";
2+
import OpenAI from "openai";
3+
import { createSigner, getEncryptionKeyFromHex } from "@/helpers";
4+
5+
/* Get the wallet key associated to the public key of
6+
* the agent and the encryption key for the local db
7+
* that stores your agent's messages */
8+
const {
9+
WALLET_KEY,
10+
ENCRYPTION_KEY,
11+
GAIA_NODE_URL,
12+
GAIA_API_KEY,
13+
GAIA_MODEL_NAME,
14+
} = process.env;
15+
16+
/* Check if the environment variables are set */
17+
if (!WALLET_KEY) {
18+
throw new Error("WALLET_KEY must be set");
19+
}
20+
21+
/* Check if the encryption key is set */
22+
if (!ENCRYPTION_KEY) {
23+
throw new Error("ENCRYPTION_KEY must be set");
24+
}
25+
26+
/* Check if the OpenAI API key is set */
27+
if (!GAIA_API_KEY) {
28+
throw new Error("GAIA_API_KEY must be set");
29+
}
30+
31+
/* Check if the Gaia node's base URL is set */
32+
if (!GAIA_NODE_URL) {
33+
throw new Error("GAIA_NODE_URL must be set");
34+
}
35+
36+
/* Check if the the model name for the Gaia node is set */
37+
if (!GAIA_MODEL_NAME) {
38+
throw new Error("GAIA_MODEL_NAME must be set");
39+
}
40+
41+
/* Create the signer using viem and parse the encryption key for the local db */
42+
const signer = createSigner(WALLET_KEY);
43+
const encryptionKey = getEncryptionKeyFromHex(ENCRYPTION_KEY);
44+
45+
/* Initialize the OpenAI client */
46+
const openai = new OpenAI({
47+
baseURL: GAIA_NODE_URL,
48+
apiKey: GAIA_API_KEY,
49+
});
50+
51+
/* Set the environment to dev or production */
52+
const env: XmtpEnv = "dev";
53+
54+
/**
55+
* Main function to run the agent
56+
*/
57+
async function main() {
58+
console.log(`Creating client on the '${env}' network...`);
59+
/* Initialize the xmtp client */
60+
const client = await Client.create(signer, encryptionKey, {
61+
env,
62+
});
63+
64+
console.log("Syncing conversations...");
65+
/* Sync the conversations from the network to update the local db */
66+
await client.conversations.sync();
67+
68+
console.log(
69+
`Agent initialized on ${client.accountAddress}\nSend a message on http://xmtp.chat/dm/${client.accountAddress}`,
70+
);
71+
72+
console.log("Waiting for messages...");
73+
/* Stream all messages from the network */
74+
const stream = client.conversations.streamAllMessages();
75+
76+
for await (const message of await stream) {
77+
/* Ignore messages from the same agent or non-text messages */
78+
if (
79+
message?.senderInboxId.toLowerCase() === client.inboxId.toLowerCase() ||
80+
message?.contentType?.typeId !== "text"
81+
) {
82+
continue;
83+
}
84+
85+
console.log(
86+
`Received message: ${message.content as string} by ${message.senderInboxId}`,
87+
);
88+
89+
/* Get the conversation from the local db */
90+
const conversation = client.conversations.getDmByInboxId(
91+
message.senderInboxId,
92+
);
93+
94+
/* If the conversation is not found, skip the message */
95+
if (!conversation) {
96+
console.log("Unable to find conversation, skipping");
97+
continue;
98+
}
99+
100+
try {
101+
/* Get the AI response */
102+
const completion = await openai.chat.completions.create({
103+
messages: [{ role: "user", content: message.content as string }],
104+
model: GAIA_MODEL_NAME as string,
105+
});
106+
107+
/* Get the AI response */
108+
const response =
109+
completion.choices[0]?.message?.content ||
110+
"I'm not sure how to respond to that.";
111+
112+
console.log(`Sending AI response: ${response}`);
113+
/* Send the AI response to the conversation */
114+
await conversation.send(response);
115+
} catch (error) {
116+
console.error("Error getting AI response:", error);
117+
await conversation.send(
118+
"Sorry, I encountered an error processing your message.",
119+
);
120+
}
121+
122+
console.log("Waiting for messages...");
123+
}
124+
}
125+
126+
main().catch(console.error);

integrations/gaia/package.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "@integrations/gaia",
3+
"version": "0.0.1",
4+
"private": true,
5+
"type": "module",
6+
"scripts": {
7+
"dev": "tsx --env-file=.env index.ts",
8+
"gen:keys": "tsx ../../scripts/generateKeys.ts"
9+
},
10+
"dependencies": {
11+
"@xmtp/node-sdk": "0.0.47",
12+
"tsx": "^4.19.2",
13+
"uint8arrays": "^5.1.0",
14+
"viem": "^2.22.17"
15+
},
16+
"packageManager": "yarn@4.6.0",
17+
"engines": {
18+
"node": ">=20"
19+
}
20+
}

yarn.lock

+31
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,17 @@ __metadata:
808808
languageName: node
809809
linkType: hard
810810

811+
"@integrations/gaia@workspace:integrations/gaia":
812+
version: 0.0.0-use.local
813+
resolution: "@integrations/gaia@workspace:integrations/gaia"
814+
dependencies:
815+
"@xmtp/node-sdk": "npm:0.0.47"
816+
tsx: "npm:^4.19.2"
817+
uint8arrays: "npm:^5.1.0"
818+
viem: "npm:^2.22.17"
819+
languageName: unknown
820+
linkType: soft
821+
811822
"@integrations/gpt@workspace:integrations/grok":
812823
version: 0.0.0-use.local
813824
resolution: "@integrations/gpt@workspace:integrations/grok"
@@ -1288,6 +1299,13 @@ __metadata:
12881299
languageName: node
12891300
linkType: hard
12901301

1302+
"@xmtp/node-bindings@npm:^0.0.41":
1303+
version: 0.0.41
1304+
resolution: "@xmtp/node-bindings@npm:0.0.41"
1305+
checksum: 10/809347d36de2b4f205fe46cc3869b1eab7595107fd14b0535457732745d14851121d1e7b0f22a0845deb32e48dfca29a7ad36a12bf9fc5c46b445a308a589c10
1306+
languageName: node
1307+
linkType: hard
1308+
12911309
"@xmtp/node-sdk@npm:0.0.42":
12921310
version: 0.0.42
12931311
resolution: "@xmtp/node-sdk@npm:0.0.42"
@@ -1314,6 +1332,19 @@ __metadata:
13141332
languageName: node
13151333
linkType: hard
13161334

1335+
"@xmtp/node-sdk@npm:0.0.47":
1336+
version: 0.0.47
1337+
resolution: "@xmtp/node-sdk@npm:0.0.47"
1338+
dependencies:
1339+
"@xmtp/content-type-group-updated": "npm:^2.0.0"
1340+
"@xmtp/content-type-primitives": "npm:^2.0.0"
1341+
"@xmtp/content-type-text": "npm:^2.0.0"
1342+
"@xmtp/node-bindings": "npm:^0.0.41"
1343+
"@xmtp/proto": "npm:^3.72.3"
1344+
checksum: 10/bf3b5e59cc5be0b9ec02acd2d7cf59bcae5f478971b5ffc772e6153c8d59515032c95436faf2c741e1de334d9572dc1991098395d9813ec5364f0eb09666a815
1345+
languageName: node
1346+
linkType: hard
1347+
13171348
"@xmtp/proto@npm:^3.72.0, @xmtp/proto@npm:^3.72.3":
13181349
version: 3.73.0
13191350
resolution: "@xmtp/proto@npm:3.73.0"

0 commit comments

Comments
 (0)