Skip to content

Commit fbf843f

Browse files
committed
grok
1 parent 9bacb0f commit fbf843f

File tree

8 files changed

+327
-14
lines changed

8 files changed

+327
-14
lines changed

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ yarn gen:keys
3838

3939
> See all the available [examples](/examples/).
4040
41+
## Integrations
42+
43+
Examples integrating XMTP with external libraries from the ecosystem
44+
45+
- [grok](/integrations/grok/): Integration to Grok API
46+
4147
## Web inbox
4248

4349
Interact with the XMTP network using [xmtp.chat](https://xmtp.chat), the official web inbox for developers.

examples/gpt/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ async function main() {
4747
await client.conversations.sync();
4848

4949
console.log(
50-
`Agent initialized on ${client.accountAddress}\nSend a message on http://xmtp.chat/dm/${client.accountAddress}`,
50+
`Agent initialized on ${client.accountAddress}\nSend a message on http://xmtp.chat/dm/${client.accountAddress}?env=${env}`,
5151
);
5252

5353
console.log("Waiting for messages...");

integrations/grok/.env.example

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
WALLET_KEY= # the private key of the wallet
2+
ENCRYPTION_KEY= # a second random 32 bytes encryption key for local db encryption
3+
4+
# grok api key
5+
GROK_API_KEY= # the API key for the Grok API

integrations/grok/README.md

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# GPT agent example
2+
3+
This example uses the [OpenAI](https://openai.com) API for GPT-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+
## Environment variables
6+
7+
Add the following keys to a `.env` file:
8+
9+
```bash
10+
WALLET_KEY= # the private key of the wallet
11+
ENCRYPTION_KEY= # a second random 32 bytes encryption key for local db encryption
12+
GROK_API_KEY= # the API key for the Grok API
13+
```
14+
15+
You can generate random keys with the following command:
16+
17+
```bash
18+
yarn gen:keys
19+
```
20+
21+
> [!WARNING]
22+
> Running the `gen:keys` script will overwrite the existing `.env` file.
23+
24+
## Usage
25+
26+
```tsx
27+
import { Client, type XmtpEnv } from "@xmtp/node-sdk";
28+
import OpenAI from "openai";
29+
import { createSigner, getEncryptionKeyFromHex } from "@/helpers";
30+
31+
const { WALLET_KEY, ENCRYPTION_KEY, OPENAI_API_KEY, GROK_API_KEY } =
32+
process.env;
33+
34+
if (!WALLET_KEY) {
35+
throw new Error("WALLET_KEY must be set");
36+
}
37+
38+
if (!ENCRYPTION_KEY) {
39+
throw new Error("ENCRYPTION_KEY must be set");
40+
}
41+
42+
if (!OPENAI_API_KEY) {
43+
throw new Error("OPENAI_API_KEY must be set");
44+
}
45+
46+
const signer = createSigner(WALLET_KEY);
47+
const encryptionKey = getEncryptionKeyFromHex(ENCRYPTION_KEY);
48+
const openai = new OpenAI({ apiKey: OPENAI_API_KEY });
49+
50+
/* Set the environment to dev or production */
51+
const env: XmtpEnv = "dev";
52+
53+
async function main() {
54+
console.log(`Creating client on the '${env}' network...`);
55+
const client = await Client.create(signer, encryptionKey, {
56+
env,
57+
});
58+
59+
console.log("Syncing conversations...");
60+
/* Sync the conversations from the network to update the local db */
61+
await client.conversations.sync();
62+
63+
console.log(
64+
`Agent initialized on ${client.accountAddress}\nSend a message on http://xmtp.chat/dm/${client.accountAddress}`,
65+
);
66+
67+
console.log("Waiting for messages...");
68+
const stream = client.conversations.streamAllMessages();
69+
70+
for await (const message of await stream) {
71+
/* Ignore messages from the same agent or non-text messages */
72+
if (
73+
message?.senderInboxId.toLowerCase() === client.inboxId.toLowerCase() ||
74+
message?.contentType?.typeId !== "text"
75+
) {
76+
continue;
77+
}
78+
79+
console.log(
80+
`Received message: ${message.content as string} by ${message.senderInboxId}`,
81+
);
82+
83+
const conversation = client.conversations.getConversationById(
84+
message.conversationId,
85+
);
86+
87+
if (!conversation) {
88+
console.log("Unable to find conversation, skipping");
89+
continue;
90+
}
91+
92+
try {
93+
// Use Grok API to get AI response
94+
const response = await fetch("https://api.x.ai/v1/chat/completions", {
95+
method: "POST",
96+
headers: {
97+
"Content-Type": "application/json",
98+
Authorization: `Bearer ${GROK_API_KEY}`,
99+
},
100+
body: JSON.stringify({
101+
messages: [
102+
{ role: "system", content: "You are a test assistant." },
103+
{ role: "user", content: message.content as string },
104+
],
105+
model: "grok-2-latest",
106+
stream: false,
107+
temperature: 0,
108+
}),
109+
}).then(
110+
(res) =>
111+
res.json() as Promise<{
112+
choices: { message: { content: string } }[];
113+
}>,
114+
);
115+
const aiResponse = response.choices[0]?.message?.content || "";
116+
console.log(`Sending AI response: ${aiResponse}`);
117+
await conversation.send(aiResponse);
118+
} catch (error) {
119+
console.error("Error getting AI response:", error);
120+
await conversation.send(
121+
"Sorry, I encountered an error processing your message.",
122+
);
123+
}
124+
125+
console.log("Waiting for messages...");
126+
}
127+
}
128+
129+
main().catch(console.error);
130+
```
131+
132+
## Run the agent
133+
134+
```bash
135+
# git clone repo
136+
git clone https://github.com/ephemeraHQ/xmtp-agent-examples.git
137+
# go to the folder
138+
cd xmtp-agent-examples
139+
cd integrations
140+
cd grok
141+
# install packages
142+
yarn
143+
# generate random keys (optional)
144+
yarn gen:keys
145+
# run the example
146+
yarn examples:gpt
147+
```

integrations/grok/index.ts

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

integrations/grok/package.json

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "@integrations/gpt",
3+
"version": "0.0.0",
4+
"private": true,
5+
"type": "module",
6+
"scripts": {
7+
"clean": "rimraf node_modules && yarn clean:dbs",
8+
"clean:dbs": "rimraf *.db3* ||:",
9+
"dev": "tsx --env-file=.env index.ts",
10+
"format": "prettier -w .",
11+
"format:check": "prettier -c .",
12+
"gen:keys": "tsx ../../scripts/generateKeys.ts",
13+
"lint": "eslint .",
14+
"typecheck": "tsc"
15+
},
16+
"dependencies": {
17+
"@xmtp/node-sdk": "0.0.42",
18+
"openai": "latest",
19+
"tsx": "^4.19.2",
20+
"uint8arrays": "^5.1.0",
21+
"viem": "^2.22.17"
22+
},
23+
"packageManager": "yarn@4.6.0",
24+
"engines": {
25+
"node": ">=20"
26+
}
27+
}

package.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
"version": "0.0.0",
44
"private": true,
55
"type": "module",
6+
"workspaces": [
7+
"examples/*",
8+
"integrations/*"
9+
],
610
"scripts": {
711
"clean": "rimraf node_modules && yarn clean:dbs",
812
"clean:dbs": "rimraf *.db3* ||:",
@@ -16,8 +20,7 @@
1620
"typecheck": "tsc"
1721
},
1822
"dependencies": {
19-
"@xmtp/content-type-text": "^2.0.0",
20-
"@xmtp/node-sdk": "0.0.40",
23+
"@xmtp/node-sdk": "0.0.42",
2124
"alchemy-sdk": "^3.0.0",
2225
"openai": "latest",
2326
"tsx": "^4.19.2",

0 commit comments

Comments
 (0)